Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Sources/ContainerCommands/Image/ImageCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ extension Application {
)
}
}

/// `ImageCommand` errors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, just use ContainerizationError instead of creating new error types. We do have a tech debt issue #268 for improving our error handling.

public enum ImageCommandError: Swift.Error {
case invalidInput
}
17 changes: 13 additions & 4 deletions Sources/ContainerCommands/Image/ImageLoad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,23 @@ extension Application {
transform: { str in
URL(fileURLWithPath: str, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false)
})
var input: String
var input: String?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine but what you'll want to do is change L35 to something like this so input = nil is transformed properly:

str.map { URL(fileURLWithPath: $0, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false) }


@OptionGroup
var global: Flags.Global

public func run() async throws {
guard FileManager.default.fileExists(atPath: input) else {
print("File does not exist \(input)")
var filePath = ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite what #559 is asking for. If input == nil we don't want to prompt the user for a pathname. What image load needs to do in this case is:

  • Create a temporary file, using defer {} to delete the file once we're done using it.
  • Read binary data from the standard input, writing it to the temp file.
  • Use the path to the temporary file as the path to load.

This allows us to do things like moving images between Docker and container without having to mess with intermediate files explicitly:

docker save foo:latest bar:latest | container image load
container save baz:latest qux:latest | docker image load

Copy link
Author

@saehejkang saehejkang Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, I was unsure why the issue was talking about stdout.

How is the binary data coming from the standard input? Are we prompting anything from the user (would it be the path to the temporary file or the path from the file to read the binary data from)?

if input == nil {
print("Path to the image tar archive: ", terminator: "")
guard let userInput = readLine() else {
throw ImageCommandError.invalidInput
}
filePath = userInput
}

guard FileManager.default.fileExists(atPath: input ?? filePath) else {
print("File does not exist \(input ?? filePath)")
Application.exit(withError: ArgumentParser.ExitCode(1))
}

Expand All @@ -57,7 +66,7 @@ extension Application {
progress.start()

progress.set(description: "Loading tar archive")
let loaded = try await ClientImage.load(from: input)
let loaded = try await ClientImage.load(from: input ?? filePath)

let taskManager = ProgressTaskCoordinator()
let unpackTask = await taskManager.startTask()
Expand Down
13 changes: 11 additions & 2 deletions Sources/ContainerCommands/Image/ImageSave.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ extension Application {
transform: { str in
URL(fileURLWithPath: str, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false)
})
var output: String
var output: String?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, map String? to get the absolute path.


@Option(
help: "Platform for the saved image (format: os/arch[/variant], takes precedence over --os and --arch)"
Expand All @@ -59,6 +59,15 @@ extension Application {
@Argument var references: [String]

public func run() async throws {
var filePath = ""
if output == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here if output is nil we create a temporary file, defer the delete, do ClientImage.save to that file, and then copy the binary data in the file to the standard output

print("Pathname for the saved image: ", terminator: "")
guard let userInput = readLine() else {
throw ImageCommandError.invalidInput
}
filePath = userInput
}

var p: Platform?
if let platform {
p = try Platform(from: platform)
Expand Down Expand Up @@ -90,7 +99,7 @@ extension Application {
throw ContainerizationError(.invalidArgument, message: "failed to save image(s)")

}
try await ClientImage.save(references: references, out: output, platform: p)
try await ClientImage.save(references: references, out: output ?? filePath, platform: p)

progress.finish()
for reference in references {
Expand Down