From 6927464d476a2facce5b4b7401d6a9ecbde39122 Mon Sep 17 00:00:00 2001 From: "T. R. Bernstein" Date: Mon, 23 Mar 2026 19:50:58 +0100 Subject: [PATCH] Use Subprocess instead of Shwift Drop Shwift: it is incompatible with musl (used by the Swift static linking SDK), and its API is not meaningfully more concise than Subprocess upon closer inspection. --- Package.resolved | 24 ++++----- Package.swift | 6 ++- Sources/TaskCLI/Command.swift | 2 +- .../GenerateDocumentation Command.swift | 53 ++++++++++++------- Sources/TaskCLI/Global Options.swift | 2 +- Sources/TaskCLI/Test Command.swift | 19 ++++--- 6 files changed, 64 insertions(+), 42 deletions(-) diff --git a/Package.resolved b/Package.resolved index 3bf5f8e..c7b8fe2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "0cb2e87817f52021ac25ffee6b27396f6d94e9fd604ca83db7f20a10e65fe6cf", + "originHash" : "17ce26ba5c862ca674cd3ceeb43a9fe8a5c5251c5561de65e632a06d79916342", "pins" : [ { "identity" : "noora", @@ -28,22 +28,13 @@ "version" : "4.2.1" } }, - { - "identity" : "shwift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/GeorgeLyon/Shwift", - "state" : { - "revision" : "d7be04898d094ddce6140cc6a2e9a83fc994b66d", - "version" : "3.1.1" - } - }, { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615", - "version" : "1.7.0" + "revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b", + "version" : "1.7.1" } }, { @@ -82,6 +73,15 @@ "version" : "2.95.0" } }, + { + "identity" : "swift-subprocess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-subprocess.git", + "state" : { + "revision" : "ba5888ad7758cbcbe7abebac37860b1652af2d9c", + "version" : "0.3.0" + } + }, { "identity" : "swift-system", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 5729d1e..9ba0510 100644 --- a/Package.swift +++ b/Package.swift @@ -11,10 +11,11 @@ let package = Package( ) ], dependencies: [ + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.1"), .package(url: "https://github.com/apple/swift-log", from: "1.10.1"), .package(url: "https://github.com/apple/swift-nio", from: "2.95.0"), .package(url: "https://github.com/apple/swift-system", from: "1.6.4"), - .package(url: "https://github.com/GeorgeLyon/Shwift", from: "3.1.1"), + .package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.3.0"), .package(url: "https://github.com/tuist/Noora", from: "0.55.1") ], targets: [ @@ -38,9 +39,10 @@ let package = Package( .executableTarget( name: "InotifyTaskCLI", dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), .product(name: "_NIOFileSystem", package: "swift-nio"), - .product(name: "Script", package: "Shwift"), + .product(name: "Subprocess", package: "swift-subprocess"), .product(name: "Noora", package: "Noora") ], path: "Sources/TaskCLI" diff --git a/Sources/TaskCLI/Command.swift b/Sources/TaskCLI/Command.swift index 8f04cd4..20d9324 100644 --- a/Sources/TaskCLI/Command.swift +++ b/Sources/TaskCLI/Command.swift @@ -1,4 +1,4 @@ -import Script +import ArgumentParser @main struct Command: AsyncParsableCommand { diff --git a/Sources/TaskCLI/GenerateDocumentation Command.swift b/Sources/TaskCLI/GenerateDocumentation Command.swift index 100821c..28956e5 100644 --- a/Sources/TaskCLI/GenerateDocumentation Command.swift +++ b/Sources/TaskCLI/GenerateDocumentation Command.swift @@ -1,9 +1,10 @@ +import ArgumentParser import Foundation import Logging -import Script import Noora +import Subprocess -struct GenerateDocumentationCommand: Script { +struct GenerateDocumentationCommand: AsyncParsableCommand { static let configuration = CommandConfiguration( commandName: "generate-documentation", abstract: "Generate DocC documentation of all targets inside a Linux container.", @@ -23,7 +24,6 @@ struct GenerateDocumentationCommand: Script { let logger = global.makeLogger(labeled: "swift-inotify.cli.task.generate-documentation") let fileManager = FileManager.default let projectDirectory = URL(fileURLWithPath: fileManager.currentDirectoryPath) - let docker = try await executable(named: "docker") let targets = try await Self.targets(for: projectDirectory) @@ -42,17 +42,21 @@ struct GenerateDocumentationCommand: Script { let script = Self.makeRunScript(for: targets) logger.debug("Container script", metadata: ["script": "\(script)"]) - do { - try await docker( + let dockerRunResult = try await Subprocess.run( + .name("docker"), + arguments: [ "run", "--rm", "-v", "\(tempDirectory.path):/code", "-v", "swift-inotify-build-cache:/code/.build", "--platform", Docker.getLinuxPlatformStringWithHostArchitecture(), "-w", "/code", "swift:latest", - "/bin/bash", "-c", script, - ) - } catch { + "/bin/bash", "-c", script + ], + output: .standardOutput, + error: .standardError + ) + if !dockerRunResult.terminationStatus.isSuccess { noora.error("Documentation generation failed.") return } @@ -104,10 +108,12 @@ struct GenerateDocumentationCommand: Script { } private static func packageTargets() async throws -> [(name: String, path: String)] { - let swift = try await executable(named: "swift") - let packageDescriptionOutput = try await outputOf { - try await swift("package", "describe", "--type", "json") - } + let packageDescriptionResult = try await Subprocess.run( + .name("swift"), + arguments: ["package", "describe", "--type", "json"], + output: .data(limit: 10_000), + error: .standardError + ) struct PackageDescription: Codable { let targets: [Target] @@ -117,8 +123,11 @@ struct GenerateDocumentationCommand: Script { let path: String } - let data = Data(packageDescriptionOutput.utf8) - let package = try JSONDecoder().decode(PackageDescription.self, from: data) + if !packageDescriptionResult.terminationStatus.isSuccess { + throw GenerateDocumentationError.unableToReadPackageDescription + } + + let package = try JSONDecoder().decode(PackageDescription.self, from: packageDescriptionResult.standardOutput) return package.targets.map { ($0.name, $0.path) } } @@ -163,13 +172,16 @@ struct GenerateDocumentationCommand: Script { // MARK: - Dependency Injection private func injectDoccPluginDependency(in directory: URL, logger: Logger) async throws { - let swift = try await executable(named: "swift") - do { - try await swift( + let swiftRunResult = try await Subprocess.run( + .name("swift"), + arguments: [ "package", "--package-path", directory.path(percentEncoded: false), "add-dependency", "--from", Self.doccPluginMinVersion, Self.doccPluginURL - ) - } catch { + ], + output: .standardOutput, + error: .standardError + ) + if !swiftRunResult.terminationStatus.isSuccess { throw GenerateDocumentationError.dependencyInjectionFailed } @@ -179,11 +191,14 @@ struct GenerateDocumentationCommand: Script { enum GenerateDocumentationError: Error, CustomStringConvertible { case dependencyInjectionFailed + case unableToReadPackageDescription var description: String { switch self { case .dependencyInjectionFailed: "Failed to add swift-docc-plugin dependency to Package.swift." + case .unableToReadPackageDescription: + "Failed to read the package description." } } } diff --git a/Sources/TaskCLI/Global Options.swift b/Sources/TaskCLI/Global Options.swift index 8dd20f7..f96ab36 100644 --- a/Sources/TaskCLI/Global Options.swift +++ b/Sources/TaskCLI/Global Options.swift @@ -1,4 +1,4 @@ -import Script +import ArgumentParser import Logging struct GlobalOptions: ParsableArguments { diff --git a/Sources/TaskCLI/Test Command.swift b/Sources/TaskCLI/Test Command.swift index 8bca657..151a5b4 100644 --- a/Sources/TaskCLI/Test Command.swift +++ b/Sources/TaskCLI/Test Command.swift @@ -1,8 +1,9 @@ +import ArgumentParser import Foundation -import Script import Noora +import Subprocess -struct TestCommand: Script { +struct TestCommand: AsyncParsableCommand { static let configuration = CommandConfiguration( commandName: "test", abstract: "Run swift test in a linux container.", @@ -17,12 +18,12 @@ struct TestCommand: Script { let noora = Noora() let logger = global.makeLogger(labeled: "swift-inotify.cli.task.test") let currentDirectory = FileManager.default.currentDirectoryPath - let docker = try await executable(named: "docker") noora.info("Running tests on Linux.") logger.debug("Current directory", metadata: ["current-directory": "\(currentDirectory)"]) - do { - try await docker( + let dockerRunResult = try await Subprocess.run( + .name("docker"), + arguments: [ "run", "-v", "\(currentDirectory):/code", "-v", "swift-inotify-build-cache:/code/.build", @@ -30,9 +31,13 @@ struct TestCommand: Script { "--platform", Docker.getLinuxPlatformStringWithHostArchitecture(), "-w", "/code", "swift:latest", "/bin/bash", "-c", "swift test --skip InotifyLimitTests; swift test --skip-build --filter InotifyLimitTests" - ) + ], + output: .standardOutput, + error: .standardError + ) + if dockerRunResult.terminationStatus.isSuccess { noora.success("All tests completed successfully.") - } catch { + } else { noora.error("Not all tests completed successfully.") } }