diff --git a/Sources/ContainerCommands/Image/ImageLoad.swift b/Sources/ContainerCommands/Image/ImageLoad.swift index 118772b39..08e66d4f7 100644 --- a/Sources/ContainerCommands/Image/ImageLoad.swift +++ b/Sources/ContainerCommands/Image/ImageLoad.swift @@ -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)"]) diff --git a/Tests/CLITests/Subcommands/Images/TestCLIImagesCommand.swift b/Tests/CLITests/Subcommands/Images/TestCLIImagesCommand.swift index e3d2aa5b3..307b40767 100644 --- a/Tests/CLITests/Subcommands/Images/TestCLIImagesCommand.swift +++ b/Tests/CLITests/Subcommands/Images/TestCLIImagesCommand.swift @@ -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)") + } }