Write log/error output from commands to stderr. (#1632)

- Closes #1631.
- The standard output should only contain result data,
  so that scripts consuming stdout don't need to scrape.
This commit is contained in:
J Logan
2026-06-03 16:17:59 -07:00
committed by GitHub
parent 94d6d0ac36
commit 89fa98c55a
16 changed files with 26 additions and 25 deletions
@@ -59,7 +59,7 @@ extension Application {
} catch {
if let czError = error as? ContainerizationError, czError.code == .notFound {
if !quiet {
print("builder is not running")
log.warning("builder is not running")
return
}
}
@@ -40,7 +40,7 @@ extension Application {
} catch {
if error is ContainerizationError {
if (error as? ContainerizationError)?.code == .notFound {
print("builder is not running")
log.warning("builder is not running")
return
}
}
@@ -96,8 +96,9 @@ extension Application {
if !self.processFlags.tty {
var handler = SignalThreshold(threshold: 3, signals: [SIGINT, SIGTERM])
let log = self.log
handler.start {
print("Received 3 SIGINT/SIGTERM's, forcefully exiting.")
log.warning("Received 3 SIGINT/SIGTERM's, forcefully exiting.")
Darwin.exit(1)
}
}
@@ -60,7 +60,7 @@ extension Application {
for name in prunedContainerIds {
print(name)
}
print("Reclaimed \(freed) in disk space")
log.info("Reclaimed \(freed) in disk space")
}
}
}
@@ -160,8 +160,9 @@ extension Application {
if !self.processFlags.tty {
var handler = SignalThreshold(threshold: 3, signals: [SIGINT, SIGTERM])
let log = self.log
handler.start {
print("Received 3 SIGINT/SIGTERM's, forcefully exiting.")
log.warning("Received 3 SIGINT/SIGTERM's, forcefully exiting.")
Darwin.exit(1)
}
}
@@ -82,7 +82,7 @@ extension Application {
let freed = formatter.string(fromByteCount: Int64(size))
if didDeleteAnyImage {
print("Reclaimed \(freed) in disk space")
log.info("Reclaimed \(freed) in disk space")
}
if failures.count > 0 {
throw ContainerizationError(.internalError, message: "failed to delete one or more images: \(failures)")
@@ -108,7 +108,6 @@ extension Application {
}
await taskManager.finish()
progress.finish()
print("Loaded images:")
for image in result.images {
print(image.reference)
}
@@ -82,7 +82,7 @@ extension Application {
let formatter = ByteCountFormatter()
formatter.countStyle = .file
let freed = formatter.string(fromByteCount: Int64(size))
print("Reclaimed \(freed) in disk space")
log.info("Reclaimed \(freed) in disk space")
}
private func hasTag(_ reference: String) -> Bool {
@@ -84,7 +84,7 @@ extension Application {
do {
images.append(try await ClientImage.get(reference: reference, containerSystemConfig: containerSystemConfig).description)
} catch {
print("failed to get image for reference \(reference): \(error)")
log.error("failed to get image for reference \(reference): \(error)")
}
}
@@ -94,7 +94,7 @@ extension Application {
)
try await client.ping()
try keychain.save(hostname: server, username: username, password: password)
print("Login succeeded")
log.info("Login succeeded")
}
}
}
@@ -57,7 +57,7 @@ extension Application {
if recommended {
let url = containerSystemConfig.kernel.url
let path: String = containerSystemConfig.kernel.binaryPath
print("Installing the recommended kernel from \(url)...")
log.info("Installing the recommended kernel from \(url)...")
try await Self.downloadAndInstallWithProgressBar(tarRemoteURL: url, kernelFilePath: path, force: force)
return
}
@@ -125,12 +125,12 @@ extension Application {
let data = try plist.encode()
try data.write(to: plistURL)
print("Registering API server with launchd...")
log.info("Launching container-apiserver...")
try ServiceManager.register(plistPath: plistURL.path)
// Now ping our friendly daemon. Fail if we don't get a response.
do {
print("Verifying apiserver is running...")
log.info("Testing access to container-apiserver...")
_ = try await ClientHealthCheck.ping(timeout: timeout)
} catch {
throw ContainerizationError(
@@ -152,7 +152,7 @@ extension Application {
private func installInitialFilesystem(initImage: String) async throws {
var pullCommand = try ImagePull.parse()
pullCommand.reference = initImage
print("Installing base container filesystem...")
log.info("Installing base container filesystem...")
do {
try await pullCommand.run()
} catch {
@@ -169,7 +169,7 @@ extension Application {
throw ContainerizationError(.internalError, message: "failed to read user input")
}
guard read.lowercased() == "y" || read.count == 0 else {
print("Please use the `container system kernel set --recommended` command to configure the default kernel")
log.info("Please use the `container system kernel set --recommended` command to configure the default kernel")
return
}
shouldInstallKernel = true
@@ -179,7 +179,7 @@ extension Application {
guard shouldInstallKernel else {
return
}
print("Installing kernel...")
log.info("Installing kernel...")
try await KernelSet.downloadAndInstallWithProgressBar(tarRemoteURL: kernelURL, kernelFilePath: kernelBinaryPath, force: true)
}
@@ -72,7 +72,7 @@ extension Application.VolumeCommand {
let formatter = ByteCountFormatter()
let freed = formatter.string(fromByteCount: Int64(totalSize))
print("Reclaimed \(freed) in disk space")
log.info("Reclaimed \(freed) in disk space")
}
}
}
@@ -24,12 +24,12 @@ class TestCLIPruneCommand: CLITest {
}
@Test func testContainerPruneNoContainers() throws {
let (_, output, error, status) = try run(arguments: ["prune"])
let (_, _, error, status) = try run(arguments: ["prune"])
if status != 0 {
throw CLIError.executionFailed("container prune failed: \(error)")
}
#expect(output.contains("Reclaimed Zero KB in disk space"), "should show no containers message")
#expect(error.contains("Reclaimed Zero KB in disk space"), "should show no containers message")
}
@Test func testContainerPruneStoppedContainers() throws {
@@ -79,7 +79,7 @@ class TestCLIPruneCommand: CLITest {
}
#expect(output.contains(pc0Id) && output.contains(pc1Id), "should show the stopped containers id")
#expect(!output.contains("Reclaimed Zero KB in disk space"), "reclaimed spaces should not Zero KB")
#expect(!error.contains("Reclaimed Zero KB in disk space"), "reclaimed spaces should not Zero KB")
let checkStatus = try getContainerStatus(npcName)
#expect(checkStatus == "running", "not pruned container should still be running")
@@ -329,12 +329,12 @@ class TestCLIVolumes: CLITest {
@Test func testVolumePruneNoVolumes() throws {
// Prune with no volumes should succeed with 0 reclaimed
let (_, output, error, status) = try run(arguments: ["volume", "prune"])
let (_, _, error, status) = try run(arguments: ["volume", "prune"])
if status != 0 {
throw CLIError.executionFailed("volume prune failed: \(error)")
}
#expect(output.contains("Zero KB"), "should show no space reclaimed")
#expect(error.contains("Zero KB"), "should show no space reclaimed")
}
@Test func testVolumePruneUnusedVolumes() throws {
@@ -366,7 +366,7 @@ class TestCLIVolumes: CLITest {
#expect(output.contains(volumeName1) || !output.contains("No volumes to prune"), "should prune volume1")
#expect(output.contains(volumeName2) || !output.contains("No volumes to prune"), "should prune volume2")
#expect(output.contains("Reclaimed"), "should show reclaimed space")
#expect(error.contains("Reclaimed"), "should show reclaimed space")
// Verify volumes are gone
let (_, listAfter, _, statusAfter) = try run(arguments: ["volume", "list", "--quiet"])
+2 -2
View File
@@ -61,12 +61,12 @@ class TestCLINoParallelCases: CLITest {
@Test func testImagePruneNoImages() throws {
// Prune with no images should succeed
_ = try? run(arguments: ["image", "rm", "--all"])
let (_, output, error, status) = try run(arguments: ["image", "prune"])
let (_, _, error, status) = try run(arguments: ["image", "prune"])
if status != 0 {
throw CLIError.executionFailed("image prune failed: \(error)")
}
#expect(output.contains("Zero KB"), "should show no space reclaimed")
#expect(error.contains("Zero KB"), "should show no space reclaimed")
}
@Test func testImagePruneUnusedImages() throws {