Skip to content

Conversation

saehejkang
Copy link
Contributor

@saehejkang saehejkang commented Oct 7, 2025

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Closes #559

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

Example Commands with Outputs

Save the docker image to the test.tar file

container image save docker.io/library/nginx:latest > test.tar
⠦ Saving image(s) [3s]

Use the test.tar file to load the image

container image load < test.tar
Loaded images:                  
docker.io/library/nginx:latest

@saehejkang saehejkang force-pushed the image-save-load-support-stdin-stdout branch from 0ac3b6b to 3029055 Compare October 7, 2025 06:36
@saehejkang saehejkang force-pushed the image-save-load-support-stdin-stdout branch from 3029055 to b0de0e0 Compare October 7, 2025 06:37
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) }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not really sure why we need to do this 🤔 .

Also getting an error with that change Cannot convert value of type 'String.Element' (aka 'Character') to expected argument type 'String'

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
Contributor 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)?

Copy link
Contributor

Choose a reason for hiding this comment

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

@saehejkang no prompting whatsoever.

The idea behind the save and load commands is that without any filename, you can use them the same way you'd use many other standard unix commands. Take tar for example. If I run it with just the -t option:

% tar -t
I type in some stuff and then type CTRL-D!
^D
tar: Error opening archive: Unrecognized archive format
%

In this instance the command will just sit there, because it's reading its input from the standard input. I type some stuff in, hit return, then type ^D (EOF), and I get an error message because what I inputted wasn't a tar archive.

To do something more useful, I could run the command to create an archive file containing foo, writing the standard output and redirecting it to foo.tar:

% touch foo
% tar -c -v -f foo.tar foo
a foo
/Users/john/Documents/projects/vessel/vessel main % tar -c foo > foo.tar 
% ls foo*
foo	foo.tar

And then I can print the table of contents of the archive by running with the -t option and hooking up the standard input to the foo.tar file:

/Users/john/Documents/projects/vessel/vessel main % tar -t < foo.tar    
foo

On the load side you will stream data from stdin into a temporary file, and then give the temporary file to the API.

On the save side, you will use the API to save into a temporary file, and then stream the data to stdout

Copy link
Contributor

@jglogan jglogan Oct 16, 2025

Choose a reason for hiding this comment

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

@saehejkang Continuing...

Try a LLM prompt like:

How do I write something like cat in Swift, except it doesn't need to deal with filename arguments, and I want to write stdin to a temporary file, and then read the file out to stdout. It should stream rather than slurp data.

LOL you might want to also ask it to use Swift Foundation APIs instead of low level calls 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Funny enough, I only remember how to do this using C. I have never worked with Swift before, but it was 10000x easier lol.

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.

@saehejkang saehejkang changed the title [image-save-load]: support stdin for filepath [image-save-load]: support for stdin/stdout Oct 17, 2025
}
try await ClientImage.save(references: references, out: output, platform: p)

let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent("temp-file.tar")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking we create the tempFile no matter the --output flag is enabled, so we don't have to do another check after the ClientImage.save call. It is going to be deleted anyway with the defer, no matter it is read from or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Request]: image save and image load should support stdin.

2 participants