Skip to content

feat: add default output logs to create#63

Open
arortiz-rh wants to merge 1 commit into
containers:mainfrom
arortiz-rh:create-progress-output
Open

feat: add default output logs to create#63
arortiz-rh wants to merge 1 commit into
containers:mainfrom
arortiz-rh:create-progress-output

Conversation

@arortiz-rh

@arortiz-rh arortiz-rh commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Added a default output to the create option when making a delta.

I included some examples of how the default output now looks compared to the other options to show how it's different than what is currently available. There is also a --quiet/-q flag that suppresses all the default output.

Example Output (New Default)

$ ./oci-delta create oci-base.tar oci-new.tar oci-delta.tar
Found 1 layer(s) to diff, 65 layer(s) unchanged

Processing layers...
   Skipped 65 layer(s) with existing content
   Computing diff for layer 1/1

Delta complete: 1 layer(s) diffed, 65 reused, bytes saved 99.25% (10.80 MB → 0.08 MB)
Delta written to oci-delta.tar

Example Output (Verbose)

$ ./oci-delta create --verbose oci-base.tar oci-new.tar oci-delta.tar
Found 1 layer(s) to diff, 65 layer(s) unchanged

Processing layers...
   Skipped 65 layer(s) with existing content
   Computing diff for layer 1/1

Delta creation statistics:
  Old image layers: 66
  New image layers: 66
  Processed layers: 1
  Skipped layers:   65
  Processed layer bytes:  11327541
  Tar-diff layer bytes:   85071
  Original layer bytes:   0
  Bytes saved:            11242470 (99.2%)

Delta written to oci-delta.tar

Example Output (Debug)

$ ./oci-delta create --debug oci-base.tar oci-new.tar oci-delta.tar
Opening old image: oci-base.tar
Opening new image: oci-new.tar
Parsing old image
  Found 66 layers in old image
Parsing new image
  Found 66 layers in new image
  New layer: 52e681489f1d7527 (diff_id: abc0cf019117b390)
Layers with new content (will process): 1
Layers with existing content (will skip): 65

Processing layers...
  Skipping layer with existing content f71875f418178549
  ... [redacted to save space]
  Skipping layer with existing content ad312c5c40ccd18a
  Analyzing source layers...
  Computing diff for layer 1/1 52e681489f1d7527 (11327541 bytes)
  Layer 52e681489f1d7527: using tar-diff (85071 bytes, saved 11242470)

Writing oci-layout
Writing image manifest and config blobs
Writing layer blobs
Writing delta manifest and index.json

Fixes #43

- Default output shows progress & simple stats about the delta created
- Added quiet flag to supress default output if needed

Fixes containers#43

Signed-off-by: Armando Ortiz <arortiz@redhat.com>

@knecasov knecasov left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I added a few comments.

Comment thread cmd/create.go

// bytesToMB formats a byte count as a human-readable megabyte value. Used in default output for simpler feedback.
func bytesToMB(b int64) string {
return fmt.Sprintf("%.2f MB", float64(b)/(1024*1024))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bytesToMB divides by 1024*1024 but labels the result MB. Either use 1000*1000 for MB or label it MiB.

Comment thread cmd/create.go
log.Default("\nDelta complete: %d layer(s) diffed, %d reused", stats.ProcessedLayers, stats.SkippedLayers)
}

if createVerbose && stats != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

--quiet and --verbose are not mutually exclusive - --quiet --verbose would still print verbose output since the verbose block uses fmt.Printf directly. It is worth either adding a !createQuiet guard to the verbose block or using MarkFlagsMutuallyExclusive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I didn't have the quiet flag affect the verbose flag's output purposefully so that the user could obtain the stats if they really wanted to without any progress information, such as if they're piping the output to a file. It's a niche use case, but I would rather include that functionality since I don't see any other reason why a user would attempt to use --quiet and --verbose at the same time.

If you still think it would be good change that, I can implement it. I just wanted to explain that reasoning before I made that change

Comment thread cmd/create.go
return fmt.Errorf("failed to write output: %w", err)
}

if !createVerbose && stats.ProcessedLayerBytes > 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new default output block dereferences stats without a nil check. I would add stats != nil.

Comment thread cmd/root.go
}

func (l *cmdLogger) Default(format string, args ...interface{}) {
// Debug is a much more detailed version of the default logs, no need for both to exist at the same time.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The Default suppression in debug mode makes sense per the comment, but "Delta written to %s" does not have a log.Debug equivalent - so this message is lost entirely in debug mode. Is that intentional, please?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was a simple comment I thought to add to the default logs initially, but you're right there is no debug equivalent. I will add a line to the log.Debug output that shares the name and file path it was written to, since I think that's good information to have.

Comment thread cmd/create.go
createCmd.Flags().BoolVar(&createDebug, "debug", false, "show detailed progress information")
createCmd.Flags().IntVarP(&createParallelism, "jobs", "j", 0, "max parallel tar-diff workers (default: number of CPUs)")
createCmd.Flags().StringArrayVar(&createSignatures, "signature", nil, "signature OCI artifact to embed (can be specified multiple times)")
createCmd.Flags().BoolVarP(&createQuiet, "quiet", "q", false, "silences all default output")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would write "suppress all default output" to match the imperative style of the other flags.

Comment thread cmd/create.go
}

// bytesToMB formats a byte count as a human-readable megabyte value. Used in default output for simpler feedback.
func bytesToMB(b int64) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I am just curious - is there a plan/ticket/issue to add tests for bytesToMB function and quiet/debug/verbose flag interaction, please?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can add tests for bytesToMB as part of this PR

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.

Add delta creation progression output by default when create is used

2 participants