Dispatch Process.run work manually instead of using subscribe(on:) and receive(on:)

The previous implementation was doing something weird when it was used during installation in:

verifySecurityAssessment(of: installedXcode)
    .combineLatest(self.verifySigningCertificate(of: installedXcode.path.url))

It looks like it would subscribe after the Process.run future had already sent a value, and so it would never finish. This seems weird, and I'm assuming that I'm misunderstanding something about this behaviour, but dispatching manually seems reasonable and works.
This commit is contained in:
Brandon Evans 2021-01-03 16:38:29 -07:00
parent bfb3fd9ea5
commit 89143b8751
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93

View file

@ -14,41 +14,52 @@ extension Process {
static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) -> AnyPublisher<ProcessOutput, Error> {
Deferred {
Future<ProcessOutput, Error> { promise in
let process = Process()
process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent()
process.executableURL = executable
process.arguments = arguments
let (stdout, stderr) = (Pipe(), Pipe())
process.standardOutput = stdout
process.standardError = stderr
if let input = input {
let inputPipe = Pipe()
process.standardInput = inputPipe.fileHandleForReading
inputPipe.fileHandleForWriting.write(Data(input.utf8))
inputPipe.fileHandleForWriting.closeFile()
}
do {
try process.run()
process.waitUntilExit()
let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
DispatchQueue.global().async {
let process = Process()
process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent()
process.executableURL = executable
process.arguments = arguments
guard process.terminationReason == .exit, process.terminationStatus == 0 else {
return promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error)))
let (stdout, stderr) = (Pipe(), Pipe())
process.standardOutput = stdout
process.standardError = stderr
if let input = input {
let inputPipe = Pipe()
process.standardInput = inputPipe.fileHandleForReading
inputPipe.fileHandleForWriting.write(Data(input.utf8))
inputPipe.fileHandleForWriting.closeFile()
}
do {
print("Process.run \(executable), \(input), \(arguments.joined(separator: " "))")
try process.run()
process.waitUntilExit()
let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
dump(process)
print(output)
print(error)
guard process.terminationReason == .exit, process.terminationStatus == 0 else {
DispatchQueue.main.async {
promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error)))
}
return
}
DispatchQueue.main.async {
promise(.success((process.terminationStatus, output, error)))
}
} catch {
DispatchQueue.main.async {
promise(.failure(error))
}
}
promise(.success((process.terminationStatus, output, error)))
} catch {
promise(.failure(error))
}
}
}
.subscribe(on: DispatchQueue.global())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}