[images]: fix image load error to write to stderr and use FilePath (#1570)

- Closes #1535.
- Closes #1573.
This commit is contained in:
Saehej Kang
2026-05-20 10:46:17 -07:00
committed by GitHub
parent 38075f3275
commit 4f097eb891
2 changed files with 26 additions and 9 deletions
@@ -19,6 +19,7 @@ import ContainerAPIClient
import Containerization
import ContainerizationError
import Foundation
import SystemPackage
import TerminalProgress
extension Application {
@@ -32,9 +33,13 @@ extension Application {
@Option(
name: .shortAndLong, help: "Path to the image tar archive", completion: .file(),
transform: { str in
URL(fileURLWithPath: str, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false)
let path = FilePath(str)
guard path.isRelative else { return path.lexicallyNormalized() }
return FilePath(FileManager.default.currentDirectoryPath)
.pushing(path)
.lexicallyNormalized()
})
var input: String?
var input: FilePath?
@Flag(name: .shortAndLong, help: "Load images even if the archive contains invalid files")
public var force = false
@@ -49,7 +54,14 @@ extension Application {
}
// Read from stdin; otherwise read from the input file
if input == nil {
let resolvedPath: FilePath
if let input {
guard FileManager.default.fileExists(atPath: input.string) else {
log.error("file does not exist", metadata: ["path": "\(input)"])
Application.exit(withError: ArgumentParser.ExitCode(1))
}
resolvedPath = input
} else {
guard FileManager.default.createFile(atPath: tempFile.path(), contents: nil) else {
throw ContainerizationError(.internalError, message: "unable to create temporary file")
}
@@ -65,11 +77,7 @@ extension Application {
fileHandle.write(chunk)
}
try fileHandle.close()
} else {
guard FileManager.default.fileExists(atPath: input!) else {
print("File does not exist \(input!)")
Application.exit(withError: ArgumentParser.ExitCode(1))
}
resolvedPath = FilePath(tempFile.path())
}
let progressConfig = try ProgressConfig(
@@ -85,7 +93,7 @@ extension Application {
progress.set(description: "Loading tar archive")
let result = try await ClientImage.load(
from: input ?? tempFile.path(),
from: resolvedPath.string,
force: force)
if !result.rejectedMembers.isEmpty {
log.warning("archive contains invalid members", metadata: ["paths": "\(result.rejectedMembers)"])
@@ -599,4 +599,13 @@ class TestCLIImagesCommand: CLITest {
#expect(status != 0, "Expected non-zero exit for missing image")
#expect(error.contains("image not found"))
}
@Test func testImageLoadMissingFileErrorToStderr() throws {
let missingPath = "/path/that/does/not/exist-\(UUID().uuidString)"
let (_, stdout, stderr, status) = try run(arguments: ["image", "load", "-i", missingPath])
#expect(status != 0, "Expected non-zero exit for missing file")
#expect(stdout.isEmpty, "Expected stdout to be empty, got: \(stdout)")
#expect(stderr.contains("file does not exist") && stderr.contains(missingPath), "Expected stderr to contain error message, got: \(stderr)")
}
}