Skip to content

Route container image save reference list to stderr in stdout mode - #1804

Merged
jglogan merged 1 commit into
apple:mainfrom
costajohnt:fix/1801-image-save-stdout-references
Jun 26, 2026
Merged

Route container image save reference list to stderr in stdout mode#1804
jglogan merged 1 commit into
apple:mainfrom
costajohnt:fix/1801-image-save-stdout-references

Conversation

@costajohnt

Copy link
Copy Markdown
Contributor

Type of Change

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

Motivation and Context

Fixes #1801.

When container image save runs without --output, stdout carries the OCI tar archive. The command writes the archive bytes to stdout and then print(reference)s each saved image reference to stdout afterward, appending non-archive text after the tar EOF marker. That corrupts the stream for strict tar/OCI consumers, so a command like:

container image save alpine:latest > alpine.tar

produces an archive with trailing non-archive bytes. The existing round-trip test passes only because container image load tolerates the trailing data, while other tooling rejects or warns about it.

This routes the saved-reference list to stderr in the no---output branch, so stdout contains only archive bytes. When saving to a file via --output, stdout is free, so the references continue to print to stdout exactly as before.

I used a raw FileHandle.standardError.write(...) to match the existing stderr writes in this codebase (e.g. Application.swift) and to keep the reference list as plain text rather than formatted log lines. Happy to route it through the logger instead if you'd prefer.

Testing

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

Added testImageSaveToStdoutProducesCleanArchive: it saves an image to stdout and asserts that the captured stdout ends at the tar EOF marker (1024 zero bytes), with the saved reference surfaced on stderr. With the bug, the trailing bytes are reference text and the assertion fails.

swift build and swift format are clean. I was not able to run the CLI integration suite locally (it needs the running container runtime), so I left "Tested locally" unchecked. The new test sits alongside the existing image save/load integration tests and is written to run in CI.

@jglogan jglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@costajohnt Thank you for the fix! Please see the one suggested change.

// saved-reference list goes to stderr. Printing it to stdout
// appends non-archive bytes after the tar EOF and corrupts
// the stream for redirection and pipelines (#1801).
FileHandle.standardError.write(Data("\(reference)\n".utf8))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use log.info for this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, could you rebase your change onto the latest main and push it again? We needed to disable a CI test that was flaky, so rebase and push will allow us to build this change without fear of an unrelated test failing.

@costajohnt
costajohnt force-pushed the fix/1801-image-save-stdout-references branch from c4f18a1 to d9f888a Compare June 25, 2026 21:39
@jglogan

jglogan commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

@costajohnt The fix looks good now, but one more thing - we need you to submit signed commits to merge the change. See https://github.com/apple/containerization/blob/main/CONTRIBUTING.md#pull-requests for details.

When `container image save` runs without `--output`, stdout carries the
OCI tar archive. The command wrote the archive bytes to stdout and then
printed each saved image reference to stdout, appending non-archive text
after the tar EOF marker and corrupting the stream for strict tar/OCI
consumers (the existing round-trip test passed only because `image load`
tolerates the trailing data).

Emit the saved-reference list via the logger (`log.info`, which the
StderrLogHandler routes to stderr) when stdout is carrying the archive;
keep printing to stdout when saving to a file (`--output`), where stdout
is free. Add a regression test asserting the stdout archive ends at the
tar EOF marker (1024 zero bytes) with the reference surfaced on stderr.

Fixes #1801

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vEx9M9Bkph3CdvqmMj392
@costajohnt
costajohnt force-pushed the fix/1801-image-save-stdout-references branch from 2137bea to 0d7eaa6 Compare June 26, 2026 04:24
@github-actions

Copy link
Copy Markdown

Code Coverage

Tier Line Coverage
Unit 33.56%
Integration 23.05%
Combined 55.77%

@jglogan jglogan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@costajohnt Thank you for the fix and the test!

@jglogan
jglogan merged commit aa7fef3 into apple:main Jun 26, 2026
3 checks passed
@jglogan

jglogan commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

@costajohnt The point of using the logger is that by default it's like a print(stderr, msg) - no special log formatting, just the message. But if you use --debug you'll get log-formatted messages with timestamps.

@costajohnt
costajohnt deleted the fix/1801-image-save-stdout-references branch June 27, 2026 04:43
saehejkang pushed a commit to saehejkang/container that referenced this pull request Jul 16, 2026
…pple#1804)

- Fixes apple#1801.
- When `container image save` runs without `--output`,
  stdout carries the OCI tar archive. The command writes 
  the archive bytes to stdout and then `print(reference)`s
  each saved image reference to stdout afterward,
  appending non-archive text after the tar EOF marker,
  which will cause strict tar/OCI consumers to fail.
- This routes the saved-reference list to stderr in the
  no-`--output` branch, so stdout contains only archive
  bytes. When saving to a file via `--output`, stdout is
  free, so the references continue to print to stdout
  exactly as before.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jianliang00 pushed a commit to jianliang00/container that referenced this pull request Aug 28, 2026
…pple#1804)

- Fixes apple#1801.
- When `container image save` runs without `--output`,
  stdout carries the OCI tar archive. The command writes 
  the archive bytes to stdout and then `print(reference)`s
  each saved image reference to stdout afterward,
  appending non-archive text after the tar EOF marker,
  which will cause strict tar/OCI consumers to fail.
- This routes the saved-reference list to stderr in the
  no-`--output` branch, so stdout contains only archive
  bytes. When saving to a file via `--output`, stdout is
  free, so the references continue to print to stdout
  exactly as before.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

[Bug]: container image save appends image references to stdout archive

2 participants