Skip to content

Commit 32b15ab

Browse files
authored
Use Subprocess for process management (#410)
* Remove the runProgram and runProgramOutput methods from Platform * Remove Configuration and other clones from an earlier version of Subprocess * ModeledCommandLine run() and output() methods use Subprocess * Provide an adapter for the Executable.path() that always takes SystemPackage.FilePath * Generate arguments separately from config generation to permit appending in subcommands * Make an AsyncSequence version of output() to allow in-stream processing of command outputs
1 parent 4a030e1 commit 32b15ab

File tree

18 files changed

+749
-690
lines changed

18 files changed

+749
-690
lines changed

Package.resolved

Lines changed: 56 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:6.0
1+
// swift-tools-version:6.2
22

33
import PackageDescription
44

@@ -31,6 +31,7 @@ let package = Package(
3131
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.7.2"),
3232
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.8.2"),
3333
.package(url: "https://github.com/apple/swift-system", from: "1.4.2"),
34+
.package(url: "https://github.com/swiftlang/swift-subprocess", exact: "0.2.1", traits: []),
3435
// This dependency provides the correct version of the formatter so that you can run `swift run swiftformat Package.swift Plugins/ Sources/ Tests/`
3536
.package(url: "https://github.com/nicklockwood/SwiftFormat", exact: "0.49.18"),
3637
],
@@ -67,6 +68,7 @@ let package = Package(
6768
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
6869
.product(name: "OpenAPIAsyncHTTPClient", package: "swift-openapi-async-http-client"),
6970
.product(name: "SystemPackage", package: "swift-system"),
71+
.product(name: "Subprocess", package: "swift-subprocess"),
7072
],
7173
swiftSettings: swiftSettings,
7274
plugins: ["GenerateCommandModels"]

Sources/LinuxPlatform/Linux.swift

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import Subprocess
23
import SwiftlyCore
34
import SystemPackage
45

@@ -263,7 +264,13 @@ public struct Linux: Platform {
263264
}
264265

265266
if requireSignatureValidation {
266-
guard (try? self.runProgram("gpg", "--version", quiet: true)) != nil else {
267+
let result = try await run(
268+
.name("gpg"),
269+
arguments: ["--version"],
270+
output: .discarded
271+
)
272+
273+
if !result.terminationStatus.isSuccess {
267274
var msg = "gpg is not installed. "
268275
if let manager {
269276
msg += """
@@ -283,11 +290,9 @@ public struct Linux: Platform {
283290
try await fs.withTemporary(files: tmpFile) {
284291
try await ctx.httpClient.getGpgKeys().download(to: tmpFile)
285292
if let mockedHomeDir = ctx.mockedHomeDir {
286-
var env = ProcessInfo.processInfo.environment
287-
env["GNUPGHOME"] = (mockedHomeDir / ".gnupg").string
288-
try await sys.gpg()._import(key: tmpFile).run(self, env: env, quiet: true)
293+
try await sys.gpg()._import(key: tmpFile).run(environment: .inherit.updating(["GNUPGHOME": (mockedHomeDir / ".gnupg").string]), quiet: true)
289294
} else {
290-
try await sys.gpg()._import(key: tmpFile).run(self, quiet: true)
295+
try await sys.gpg()._import(key: tmpFile).run(quiet: true)
291296
}
292297
}
293298
}
@@ -315,7 +320,12 @@ public struct Linux: Platform {
315320
do {
316321
switch manager {
317322
case "apt-get":
318-
if let pkgList = try await self.runProgramOutput("dpkg", "-l", package) {
323+
let result = try await run(.name("dpkg"), arguments: ["-l", package], output: .string(limit: 100 * 1024))
324+
if !result.terminationStatus.isSuccess {
325+
return false
326+
}
327+
328+
if let pkgList = result.standardOutput {
319329
// The package might be listed but not in an installed non-error state.
320330
//
321331
// Look for something like this:
@@ -329,8 +339,8 @@ public struct Linux: Platform {
329339
}
330340
return false
331341
case "yum":
332-
try self.runProgram("yum", "list", "installed", package, quiet: true)
333-
return true
342+
let result = try await run(.name("yum"), arguments: ["list", "installed", package], output: .discarded)
343+
return result.terminationStatus.isSuccess
334344
default:
335345
return true
336346
}
@@ -390,7 +400,15 @@ public struct Linux: Platform {
390400
tmpDir / String(name)
391401
}
392402

393-
try self.runProgram((tmpDir / "swiftly").string, "init")
403+
let config = Configuration(
404+
executable: .path(tmpDir / "swiftly"),
405+
arguments: ["init"]
406+
)
407+
408+
let result = try await run(config, output: .standardOutput, error: .standardError)
409+
if !result.terminationStatus.isSuccess {
410+
throw RunProgramError(terminationStatus: result.terminationStatus, config: config)
411+
}
394412
}
395413
}
396414

@@ -424,11 +442,9 @@ public struct Linux: Platform {
424442
await ctx.message("Verifying toolchain signature...")
425443
do {
426444
if let mockedHomeDir = ctx.mockedHomeDir {
427-
var env = ProcessInfo.processInfo.environment
428-
env["GNUPGHOME"] = (mockedHomeDir / ".gnupg").string
429-
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(self, env: env, quiet: false)
445+
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(environment: .inherit.updating(["GNUPGHOME": (mockedHomeDir / ".gnupg").string]), quiet: false)
430446
} else {
431-
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(self, quiet: !verbose)
447+
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(quiet: !verbose)
432448
}
433449
} catch {
434450
throw SwiftlyError(message: "Signature verification failed: \(error).")
@@ -453,11 +469,9 @@ public struct Linux: Platform {
453469
await ctx.message("Verifying swiftly signature...")
454470
do {
455471
if let mockedHomeDir = ctx.mockedHomeDir {
456-
var env = ProcessInfo.processInfo.environment
457-
env["GNUPGHOME"] = (mockedHomeDir / ".gnupg").string
458-
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(self, env: env, quiet: false)
472+
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(environment: .inherit.updating(["GNUPGHOME": (mockedHomeDir / ".gnupg").string]), quiet: false)
459473
} else {
460-
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(self, quiet: !verbose)
474+
try await sys.gpg().verify(detached_signature: sigFile, signed_data: archive).run(quiet: !verbose)
461475
}
462476
} catch {
463477
throw SwiftlyError(message: "Signature verification failed: \(error).")
@@ -611,7 +625,7 @@ public struct Linux: Platform {
611625

612626
public func getShell() async throws -> String {
613627
let userName = ProcessInfo.processInfo.userName
614-
if let entry = try await sys.getent(database: "passwd", key: userName).entries(self).first {
628+
if let entry = try await sys.getent(database: "passwd", key: userName).entries().first {
615629
if let shell = entry.last { return shell }
616630
}
617631

0 commit comments

Comments
 (0)