#!/usr/bin/env swift import Foundation try build() func build() throws { try withVersion(in: "Sources/dots/Version.swift", as: currentVersion()) { try foregroundShell( "swift", "build", "--disable-sandbox", "--configuration", "release", "-Xswiftc", "-cross-module-optimization" ) } } func withVersion(in file: String, as version: String, _ closure: () throws -> ()) throws { let fileURL = URL(fileURLWithPath: file) let originalFileContents = try String(contentsOf: fileURL, encoding: .utf8) // set version try originalFileContents .replacingOccurrences(of: "nil", with: "\"\(version)\"") .write(to: fileURL, atomically: true, encoding: .utf8) defer { // undo set version try! originalFileContents .write(to: fileURL, atomically: true, encoding: .utf8) } // run closure try closure() } func currentVersion() throws -> String { do { let tag = try backgroundShell("git", "describe", "--tags", "--exact-match") return tag } catch { let branch = try backgroundShell("git", "symbolic-ref", "-q", "--short", "HEAD") let commit = try backgroundShell("git", "rev-parse", "--short", "HEAD") return "\(branch) (\(commit))" } } func foregroundShell(_ args: String...) throws { print("$", args.joined(separator: " ")) let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args task.launch() task.waitUntilExit() guard task.terminationStatus == 0 else { throw ShellError(terminationStatus: task.terminationStatus) } } @discardableResult func backgroundShell(_ args: String...) throws -> String { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args // grab stdout let output = Pipe() task.standardOutput = output // ignore stderr let error = Pipe() task.standardError = error task.launch() task.waitUntilExit() guard task.terminationStatus == 0 else { throw ShellError(terminationStatus: task.terminationStatus) } return String(decoding: output.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self) .trimmingCharacters(in: .whitespacesAndNewlines) } struct ShellError: Swift.Error { var terminationStatus: Int32 }