Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
* dev:
  add the new function worker path option to export command
  copy swift libs to project artifacts
  gitignore
  publish command + refactor
  • Loading branch information
SalehAlbuga committed Aug 18, 2020
2 parents 82e533c + 25b7f51 commit a2f9f60
Show file tree
Hide file tree
Showing 5 changed files with 333 additions and 145 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
/*.xcodeproj
xcuserdata/
/.devcontainer
/.swiftpm
/.swiftpm
/DEMO
158 changes: 158 additions & 0 deletions Sources/swiftfunc/Publish.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//
// Publish.swift
// SwiftFunc
//
// Created by Saleh on 08/14/20.
//

import Foundation
import SPMUtility
import Basic
import Files
import Stencil
import Dispatch
import Rainbow

@available(OSX 10.13, *)
final class PublishCommand: Command {

let command = "publish"
let overview = "Publish Swift Function App to Azure to run outside a container (Consumption Plan)"

private let name: PositionalArgument<String>

var tempFolder: Folder!

let sigintSrc = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
let sigtermSrc = DispatchSource.makeSignalSource(signal: SIGTERM, queue: .main)

init(parser: ArgumentParser) {
let subparser = parser.add(subparser: command, overview: overview)
name = subparser.add(positional: "name", kind: String.self, optional: false, usage: "Name: the name of the Function App in Azure", completion: nil)

signal(SIGINT, SIG_IGN)
signal(SIGTERM, SIG_IGN)

sigintSrc.setEventHandler { [weak self] in
print("\nTerminating..".white)
try! self?.tempFolder.delete()
exit(SIGINT)
}
sigintSrc.resume()

sigtermSrc.setEventHandler { [weak self] in
print("\nTerminating..".white)
try! self?.tempFolder.delete()
exit(SIGTERM)
}
sigtermSrc.resume()
}

func run(with arguments: ArgumentParser.Result) throws {
#if os(macOS)
print("This command should be run from a Linux host. Please publish from a Swift Functions Dev Container".yellow)
exit(1)
#endif
let coreToolsPath = "/usr/bin/func"

guard let name = arguments.get(name) else {
print("Please specify the Function App name".yellow)
return
}

guard let srcFolder = try? Folder.init(path: Process().currentDirectoryPath), srcFolder.containsFile(at: "Package.swift") else {
print("Not a Swift Project")
return
}

let projectName = srcFolder.name

tempFolder = try Folder.temporary.createSubfolderIfNeeded(withName: "\(projectName)-\(Int32.random(in: 0 ... INT32_MAX))")

print("Swift Functions tools v\(version)".bold)

print("Building Project.. 💻".bold.blue)

try Shared.buildAndExport(sourceFolder: srcFolder, destFolder:tempFolder, azureWorkerPath: true)

let srcLibFolder = try? Folder.init(path: "/usr/lib/swift/linux")

if let destLibFolder = try? tempFolder.createSubfolderIfNeeded(at: "workers").createSubfolderIfNeeded(at: "swift") {
try srcLibFolder?.copy(to: destLibFolder)
try destLibFolder.subfolders.first?.rename(to: "lib")
}

print("\(tempFolder.path)".bold)

Shared.checkFuncToolsInstallation()

print("Publishing ⚡️ \n\n".blue.bold)

var env: [String] = []
for evar in Process().environment {
env.append("\(evar.key)=\(evar.value)")
}

let dGroup = DispatchGroup()

let _ = FileManager.default.changeCurrentDirectoryPath(tempFolder.path)

let p = PseudoTeletypewriter(path: coreToolsPath, arguments: ["func", "azure", "functionapp", "publish", "\(name)", "--force"], environment: env)!
let fileDescriptor = p.masterFileHandle.fileDescriptor

let global = DispatchQueue.global(qos: .utility)
let channel = DispatchIO(type: .stream, fileDescriptor: fileDescriptor, queue: global) { (int) in
DispatchQueue.main.async {
exit(EXIT_SUCCESS)
}
}
let errChannel = DispatchIO(type: .stream, fileDescriptor: fileDescriptor, queue: global) { (int) in
DispatchQueue.main.async {
exit(EXIT_SUCCESS)
}
}

channel.setLimit(lowWater: Int(10000))
errChannel.setLimit(lowWater: Int(10000))

channel.setInterval(interval: .seconds(3), flags:[.strictInterval])
errChannel.setInterval(interval: .seconds(3), flags:[.strictInterval])

dGroup.enter()
channel.read(offset: 0, length: Int.max, queue: global) { (closed, dispatchData, error) in
if let data = dispatchData, !data.isEmpty {
DispatchQueue.main.async {
if let str = String.init(bytes: data, encoding: .utf8) {
print(str)
}
}
}
if closed {
dGroup.leave()
channel.close()
}
}

dGroup.enter()
errChannel.read(offset: 0, length: Int.max, queue: global) { (closed, dispatchData, error) in
if let data = dispatchData, !data.isEmpty {
DispatchQueue.main.async {
if let str = String.init(bytes: data, encoding: .utf8) {
print(str)
}
}
}
if closed {
dGroup.leave()
channel.close()
}
}

dGroup.notify(queue: DispatchQueue.main) {
try! self.tempFolder.delete()
exit(0)
}

dispatchMain()
}
}
146 changes: 3 additions & 143 deletions Sources/swiftfunc/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,49 +53,17 @@ final class RunCommand: Command {
return
}

Shared.checkFuncToolsInstallation()

let projectName = srcFolder.name

tempFolder = try Folder.temporary.createSubfolderIfNeeded(withName: "\(projectName)-\(Int32.random(in: 0 ... INT32_MAX))")

print("Swift Functions tools v\(version)".bold)

print("Building Project.. 💻".bold.blue)

let cst = try shellStreamOutput(path: "/bin/bash", command: "-c", "swift build -c release", dir: srcFolder.path, waitUntilExit: true)

if cst == 0 {
print("Compiled!".green.bold)
} else {
print("Compilation error 😞".red.bold)
exit(1)
}

let path = "\(srcFolder.path).build/release/functions"
let est = try shellStreamOutput(path: path, command: "export", "--source", "\(srcFolder.path)", "--root", "\(tempFolder.path)", "--debug", dir: srcFolder.path, waitUntilExit: true)


if est == 0 {
print("Exported! \n".green.bold)
} else {
print("Exporting error 😞".red.bold)
exit(1)
}

//detect if Core Tools are installed
var strOutput: String = ""
shell(path: "/bin/bash", command: "-c", "which func", result: &strOutput, dir: srcFolder.path)

if strOutput == "" {
print("Function Core Tools not found 😞 Please install Core Tools: \n".red.bold)

#if os(macOS)
print(" brew tap azure/functions \n brew install azure-functions-core-tools@2 or brew install azure-functions-core-tools@3 \n\n".yellow.bold)
#else
print(" https://github.com/Azure/azure-functions-core-tools#linux \n\n".yellow.bold)
#endif

exit(1)
}
try Shared.buildAndExport(sourceFolder: srcFolder, destFolder:tempFolder)

print("Starting host 🏠 \n\n".blue.bold)

Expand Down Expand Up @@ -167,112 +135,4 @@ final class RunCommand: Command {
dispatchMain()
}


@discardableResult
func shell(path: String, command: String..., result: inout String, dir: String) -> Int32 {

let task = Process()
task.launchPath = path
task.arguments = command
task.qualityOfService = .default
task.currentDirectoryPath = dir
let pipe = Pipe()
task.standardOutput = pipe
do {
try task.run()
} catch {
print("\(error.localizedDescription)")
}

let data = pipe.fileHandleForReading.readDataToEndOfFile()
result = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String

task.waitUntilExit()
return task.terminationStatus
}

@discardableResult
func shellStreamOutput(path: String, command: String..., dir: String?, waitUntilExit: Bool = false) throws -> Int32 {
let task = Process()
task.launchPath = path
task.arguments = command
// task.qualityOfService = .userInitiated
if let curDir = dir {
task.currentDirectoryPath = curDir
}
let pipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = pipe
task.standardError = errorPipe


let global = DispatchQueue.global(qos: .background)
let channel = DispatchIO(type: .stream, fileDescriptor: pipe.fileHandleForReading.fileDescriptor, queue: global) { (int) in
DispatchQueue.main.async {
// print("Clean-up Handler: \(int)")
// exit(EXIT_SUCCESS)
}
}
let errChannel = DispatchIO(type: .stream, fileDescriptor: errorPipe.fileHandleForReading.fileDescriptor, queue: global) { (int) in
DispatchQueue.main.async {
// print("Clean-up Handler: \(int)")
// exit(EXIT_SUCCESS)
}
}

channel.setLimit(lowWater: Int(1000))
errChannel.setLimit(lowWater: Int(1000))

channel.setInterval(interval: .milliseconds(250), flags:[.strictInterval])
errChannel.setInterval(interval: .milliseconds(250), flags:[.strictInterval])


channel.read(offset: 0, length: Int.max, queue: global) { (closed, dispatchData, error) in
if let data = dispatchData, !data.isEmpty {
DispatchQueue.main.async {
if let str = String.init(bytes: data, encoding: .utf8) {
print(str)
}
}
}

if closed {
channel.close()
}
}


errChannel.read(offset: 0, length: Int.max, queue: global) { (closed, dispatchData, error) in
if let data = dispatchData, !data.isEmpty {
DispatchQueue.main.async {
if let str = String.init(bytes: data, encoding: .utf8) {
print("\(str)")
}
}
}

if closed {
channel.close()
}
}


task.terminationHandler = { (process) in
// print("\ndidFinish: \(!process.isRunning)")
}

do {
try task.run()
} catch {
print("exec error: \(error)")
return -1
}

if waitUntilExit {
task.waitUntilExit()
return task.terminationStatus
} else {
return -1
}
}
}
Loading

0 comments on commit a2f9f60

Please sign in to comment.