4 Commits
2.0.0 ... 2.1.0

Author SHA1 Message Date
T. R. Bernstein
6927464d47 Use Subprocess instead of Shwift
Some checks failed
Docs / docs (push) Has been cancelled
Docs / deploy (push) Has been cancelled
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.
2026-03-23 19:50:58 +01:00
T. R. Bernstein
31ed16c828 Cache build directory of linux containers
SwiftPM uses caches heavily to reduce compilation and download time.
Before this change, we through these caches away with each container.
2026-03-22 17:51:24 +01:00
T. R. Bernstein
ac1c86c431 Run linux container with same architechture as host
As the development team uses both Intel and Apple Silicon Macs,
we have to get the host CPU architecture at compilation time instead of
harcoding it.
If the container has a different architecture, the guest has to be
emulated.
2026-03-22 17:51:24 +01:00
T. R. Bernstein
4b28c293cb Use Shwift library instead of Subprocess
Shwift has a concise API, which makes writing shell code nice and easy.
This is an opinionated decision.
2026-03-22 17:51:05 +01:00
5 changed files with 53 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
{ {
"originHash" : "fd1e824e418c767633bb79b055a4e84d9c86165746bc881d5d27457ad34b0c20", "originHash" : "17ce26ba5c862ca674cd3ceeb43a9fe8a5c5251c5561de65e632a06d79916342",
"pins" : [ "pins" : [
{ {
"identity" : "noora", "identity" : "noora",
@@ -33,17 +33,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser", "location" : "https://github.com/apple/swift-argument-parser",
"state" : { "state" : {
"revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615", "revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b",
"version" : "1.7.0" "version" : "1.7.1"
}
},
{
"identity" : "swift-async-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-async-algorithms",
"state" : {
"revision" : "9d349bcc328ac3c31ce40e746b5882742a0d1272",
"version" : "1.1.3"
} }
}, },
{ {

View File

@@ -11,8 +11,7 @@ let package = Package(
) )
], ],
dependencies: [ dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.1"),
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.1.3"),
.package(url: "https://github.com/apple/swift-log", from: "1.10.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-nio", from: "2.95.0"),
.package(url: "https://github.com/apple/swift-system", from: "1.6.4"), .package(url: "https://github.com/apple/swift-system", from: "1.6.4"),
@@ -41,7 +40,6 @@ let package = Package(
name: "InotifyTaskCLI", name: "InotifyTaskCLI",
dependencies: [ dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
.product(name: "Logging", package: "swift-log"), .product(name: "Logging", package: "swift-log"),
.product(name: "_NIOFileSystem", package: "swift-nio"), .product(name: "_NIOFileSystem", package: "swift-nio"),
.product(name: "Subprocess", package: "swift-subprocess"), .product(name: "Subprocess", package: "swift-subprocess"),

View File

@@ -0,0 +1,9 @@
struct Docker {
static func getLinuxPlatformStringWithHostArchitecture() -> String {
#if arch(x86_64)
return "linux/amd64"
#else
return "linux/arm64"
#endif
}
}

View File

@@ -1,5 +1,4 @@
import ArgumentParser import ArgumentParser
import AsyncAlgorithms
import Foundation import Foundation
import Logging import Logging
import Noora import Noora
@@ -43,28 +42,21 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
let script = Self.makeRunScript(for: targets) let script = Self.makeRunScript(for: targets)
logger.debug("Container script", metadata: ["script": "\(script)"]) logger.debug("Container script", metadata: ["script": "\(script)"])
let dockerResult = try await Subprocess.run( let dockerRunResult = try await Subprocess.run(
.name("docker"), .name("docker"),
arguments: [ arguments: [
"run", "--rm", "run", "--rm",
"-v", "\(tempDirectory.path(percentEncoded: false)):/code", "-v", "\(tempDirectory.path):/code",
"--platform", "linux/arm64", "-v", "swift-inotify-build-cache:/code/.build",
"--platform", Docker.getLinuxPlatformStringWithHostArchitecture(),
"-w", "/code", "-w", "/code",
"swift:latest", "swift:latest",
"/bin/bash", "-c", script, "/bin/bash", "-c", script
], ],
preferredBufferSize: 10, output: .standardOutput,
) { execution, standardInput, standardOutput, standardError in error: .standardError
print("") )
let stdout = standardOutput.lines() if !dockerRunResult.terminationStatus.isSuccess {
let stderr = standardError.lines()
for try await line in merge(stdout, stderr) {
noora.passthrough("\(line)")
}
print("")
}
guard dockerResult.terminationStatus.isSuccess else {
noora.error("Documentation generation failed.") noora.error("Documentation generation failed.")
return return
} }
@@ -116,10 +108,11 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
} }
private static func packageTargets() async throws -> [(name: String, path: String)] { private static func packageTargets() async throws -> [(name: String, path: String)] {
let packageDescription = try await Subprocess.run( let packageDescriptionResult = try await Subprocess.run(
.name("swift"), .name("swift"),
arguments: ["package", "describe", "--type", "json"], arguments: ["package", "describe", "--type", "json"],
output: .data(limit: 20_000) output: .data(limit: 10_000),
error: .standardError
) )
struct PackageDescription: Codable { struct PackageDescription: Codable {
@@ -130,7 +123,11 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
let path: String let path: String
} }
let package = try JSONDecoder().decode(PackageDescription.self, from: packageDescription.standardOutput) 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) } return package.targets.map { ($0.name, $0.path) }
} }
@@ -175,15 +172,16 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
// MARK: - Dependency Injection // MARK: - Dependency Injection
private func injectDoccPluginDependency(in directory: URL, logger: Logger) async throws { private func injectDoccPluginDependency(in directory: URL, logger: Logger) async throws {
let result = try await Subprocess.run( let swiftRunResult = try await Subprocess.run(
.name("swift"), .name("swift"),
arguments: [ arguments: [
"package", "--package-path", directory.path(percentEncoded: false), "package", "--package-path", directory.path(percentEncoded: false),
"add-dependency", "--from", Self.doccPluginMinVersion, Self.doccPluginURL "add-dependency", "--from", Self.doccPluginMinVersion, Self.doccPluginURL
], ],
) { _ in } output: .standardOutput,
error: .standardError
guard result.terminationStatus.isSuccess else { )
if !swiftRunResult.terminationStatus.isSuccess {
throw GenerateDocumentationError.dependencyInjectionFailed throw GenerateDocumentationError.dependencyInjectionFailed
} }
@@ -193,11 +191,14 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
enum GenerateDocumentationError: Error, CustomStringConvertible { enum GenerateDocumentationError: Error, CustomStringConvertible {
case dependencyInjectionFailed case dependencyInjectionFailed
case unableToReadPackageDescription
var description: String { var description: String {
switch self { switch self {
case .dependencyInjectionFailed: case .dependencyInjectionFailed:
"Failed to add swift-docc-plugin dependency to Package.swift." "Failed to add swift-docc-plugin dependency to Package.swift."
case .unableToReadPackageDescription:
"Failed to read the package description."
} }
} }
} }

View File

@@ -1,8 +1,7 @@
import ArgumentParser import ArgumentParser
import AsyncAlgorithms
import Foundation import Foundation
import Subprocess
import Noora import Noora
import Subprocess
struct TestCommand: AsyncParsableCommand { struct TestCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration( static let configuration = CommandConfiguration(
@@ -22,21 +21,21 @@ struct TestCommand: AsyncParsableCommand {
noora.info("Running tests on Linux.") noora.info("Running tests on Linux.")
logger.debug("Current directory", metadata: ["current-directory": "\(currentDirectory)"]) logger.debug("Current directory", metadata: ["current-directory": "\(currentDirectory)"])
async let monitorResult = Subprocess.run( let dockerRunResult = try await Subprocess.run(
.name("docker"), .name("docker"),
arguments: ["run", "-v", "\(currentDirectory):/code", "--security-opt", "systempaths=unconfined", "--platform", "linux/arm64", "-w", "/code", "swift:latest", "/bin/bash", "-c", "swift test --skip InotifyLimitTests; swift test --skip-build --filter InotifyLimitTests"], arguments: [
preferredBufferSize: 10, "run",
) { execution, standardInput, standardOutput, standardError in "-v", "\(currentDirectory):/code",
print("") "-v", "swift-inotify-build-cache:/code/.build",
let stdout = standardOutput.lines() "--security-opt", "systempaths=unconfined",
let stderr = standardError.lines() "--platform", Docker.getLinuxPlatformStringWithHostArchitecture(),
for try await line in merge(stdout, stderr) { "-w", "/code", "swift:latest",
noora.passthrough("\(line)") "/bin/bash", "-c", "swift test --skip InotifyLimitTests; swift test --skip-build --filter InotifyLimitTests"
} ],
print("") output: .standardOutput,
} error: .standardError
)
if (try await monitorResult.terminationStatus.isSuccess) { if dockerRunResult.terminationStatus.isSuccess {
noora.success("All tests completed successfully.") noora.success("All tests completed successfully.")
} else { } else {
noora.error("Not all tests completed successfully.") noora.error("Not all tests completed successfully.")