So let's say I have a bunch of tasks that are better suited to the abilities/permissiveness of a command plugin, but I do run them every time I build.
What (other than the process environment issues which will cause build tools not to cache their work fixed in 5.10) is the harm in creating a command plugin that does them all for me and then... calls swift run
on its way out? That way build plugins get to stay all cozy and in safe in their sandboxes while I still get to do what needs to be done before every build.
This is geared towards packages. And it works. It works both if called via a shell or directly by the process. But it has to be called with swift package --disable-sandbox plugin doit
I don't know how to run command plugins via Xcode with their sanboxes off.
Is there a better way to tell the package system "And go ahead and start a build on your way out" than this? Would there be away to do it with the sandbox still enabled so it would work for folks who want to use it via Xcode?
import PackagePlugin
import Foundation
@main
struct ShellCommand: CommandPlugin {
// Entry point for command plugins applied to Swift Packages.
func performCommand(context: PackagePlugin.PluginContext, arguments: [String]) async throws {
let packageDirectory = context.package.directory
let command = "cd \(packageDirectory); ls; swift run"
let result = try shellOneShot(command)
// let packageDirectoryURL = URL(fileURLWithPath: packageDirectory.string)
//
// let result = try runIt(packageDirectoryURL)
print(result)
}
}
enum CommandError: Error {
case unknownError(exitCode: Int32)
}
@discardableResult
func runIt(_ url:URL) throws -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["run"]
task.currentDirectoryURL = url
//task.qualityOfService
//task.environment
task.standardInput = nil
task.executableURL = URL(fileURLWithPath: "/usr/bin/swift")
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
task.waitUntilExit()
if task.terminationStatus == 0 || task.terminationStatus == 2 {
return output
} else {
print(output)
throw CommandError.unknownError(exitCode: task.terminationStatus)
}
}
//MARK: Shell Caller
@discardableResult
func shellOneShot(_ command: String) throws -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
//task.currentDirectoryURL
//task.qualityOfService
//task.environment
task.standardInput = nil
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
task.waitUntilExit()
if task.terminationStatus == 0 || task.terminationStatus == 2 {
return output
} else {
print(output)
throw CommandError.unknownError(exitCode: task.terminationStatus)
}
}