commit signing hell
This commit is contained in:
@@ -14,6 +14,17 @@ private let maxMessage = 256 << 20
|
||||
private let maxRead = 64 << 20
|
||||
private let copyTimeoutMs = 10 * 60 * 1000
|
||||
|
||||
// The signature goes to a file, since a login shell's startup files can print to stdout.
|
||||
private let signScript = """
|
||||
format=$(git config gpg.format || echo openpgp)
|
||||
case $format in
|
||||
openpgp) program=$(git config gpg.openpgp.program || git config gpg.program || echo gpg) ;;
|
||||
x509) program=$(git config gpg.x509.program || echo gpgsm) ;;
|
||||
*) echo "byre can't sign with gpg.format $format" >&2; exit 1 ;;
|
||||
esac
|
||||
exec "$program" --status-fd=2 -bsau "$(git config user.signingkey || echo "$1")" >"$2"
|
||||
"""
|
||||
|
||||
@MainActor
|
||||
final class Remote {
|
||||
let api: API
|
||||
@@ -158,9 +169,31 @@ final class Remote {
|
||||
case .hands:
|
||||
let result = try await hands.call(req.tool ?? "", req.arguments?.additionalProperties ?? OpenAPIObjectContainer())
|
||||
return ClientReply(id: req.id, result: .init(additionalProperties: result))
|
||||
case .sign:
|
||||
let (code, status, signature) = try await sign(Data(req.data?.data ?? []), as: req.ident ?? "", timeoutMs: req.timeoutMs ?? 120_000)
|
||||
return ClientReply(id: req.id, exitCode: code, output: status, data: Base64EncodedData(signature))
|
||||
}
|
||||
}
|
||||
|
||||
private func sign(_ payload: Data, as ident: String, timeoutMs: Int) async throws -> (Int, String, Data) {
|
||||
let fm = FileManager.default
|
||||
let dir = fm.temporaryDirectory.appending(path: "byre-sign-\(UUID().uuidString)", directoryHint: .isDirectory)
|
||||
try fm.createDirectory(at: dir, withIntermediateDirectories: false, attributes: [.posixPermissions: 0o700])
|
||||
defer {
|
||||
try? fm.removeItem(at: dir)
|
||||
}
|
||||
let input = dir.appending(path: "commit")
|
||||
let output = dir.appending(path: "signature")
|
||||
try payload.write(to: input)
|
||||
let stdin = try FileHandle(forReadingFrom: input)
|
||||
defer {
|
||||
try? stdin.close()
|
||||
}
|
||||
let home = fm.homeDirectoryForCurrentUser.path
|
||||
let (code, status) = try await spawn(["/bin/zsh", "-l", "-c", signScript, "byre-sign", ident, output.path], in: home, timeoutMs: timeoutMs, input: stdin)
|
||||
return (code, status, (try? Data(contentsOf: output)) ?? Data())
|
||||
}
|
||||
|
||||
private func download(_ path: String, transfer: String) async throws {
|
||||
let source = expand(path)
|
||||
let archive = FileManager.default.temporaryDirectory.appending(path: "byre-download-\(UUID().uuidString).tgz")
|
||||
@@ -258,7 +291,7 @@ private func start(_ p: Process) throws -> AsyncStream<Void> {
|
||||
return exit
|
||||
}
|
||||
|
||||
private func spawn(_ argv: [String], in dir: String, timeoutMs: Int) async throws -> (Int, String) {
|
||||
private func spawn(_ argv: [String], in dir: String, timeoutMs: Int, input: FileHandle = .nullDevice) async throws -> (Int, String) {
|
||||
let fm = FileManager.default
|
||||
let out = fm.temporaryDirectory.appending(path: "byre-\(UUID().uuidString).log")
|
||||
defer {
|
||||
@@ -274,7 +307,7 @@ private func spawn(_ argv: [String], in dir: String, timeoutMs: Int) async throw
|
||||
p.executableURL = URL(filePath: argv[0])
|
||||
p.arguments = Array(argv.dropFirst())
|
||||
p.currentDirectoryURL = URL(filePath: dir)
|
||||
p.standardInput = FileHandle.nullDevice
|
||||
p.standardInput = input
|
||||
p.standardOutput = sink
|
||||
p.standardError = sink
|
||||
let exit: AsyncStream<Void>
|
||||
|
||||
Reference in New Issue
Block a user