diff --git a/cmd/oras/internal/display/status/text.go b/cmd/oras/internal/display/status/text.go index 39cd1c82d..1a7598146 100644 --- a/cmd/oras/internal/display/status/text.go +++ b/cmd/oras/internal/display/status/text.go @@ -28,24 +28,19 @@ import ( // TextPushHandler handles text status output for push events. type TextPushHandler struct { - verbose bool printer *output.Printer } // NewTextPushHandler returns a new handler for push command. func NewTextPushHandler(out io.Writer, verbose bool) PushHandler { return &TextPushHandler{ - verbose: verbose, - printer: output.NewPrinter(out), + printer: output.NewPrinter(out, verbose), } } // OnFileLoading is called when a file is being prepared for upload. func (ph *TextPushHandler) OnFileLoading(name string) error { - if !ph.verbose { - return nil - } - return ph.printer.Println("Preparing", name) + return ph.printer.PrintVerbose("Preparing", name) } // OnEmptyArtifact is called when an empty artifact is being uploaded. @@ -63,17 +58,17 @@ func (ph *TextPushHandler) UpdateCopyOptions(opts *oras.CopyGraphOptions, fetche committed := &sync.Map{} opts.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - return ph.printer.PrintStatus(desc, promptExists, ph.verbose) + return ph.printer.PrintStatus(desc, promptExists) } opts.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, promptUploading, ph.verbose) + return ph.printer.PrintStatus(desc, promptUploading) } opts.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - if err := output.PrintSuccessorStatus(ctx, desc, fetcher, committed, ph.printer.StatusPrinter(promptSkipped, ph.verbose)); err != nil { + if err := output.PrintSuccessorStatus(ctx, desc, fetcher, committed, ph.printer.StatusPrinter(promptSkipped)); err != nil { return err } - return ph.printer.PrintStatus(desc, promptUploaded, ph.verbose) + return ph.printer.PrintStatus(desc, promptUploaded) } } @@ -84,7 +79,6 @@ func NewTextAttachHandler(out io.Writer, verbose bool) AttachHandler { // TextPullHandler handles text status output for pull events. type TextPullHandler struct { - verbose bool printer *output.Printer } @@ -95,33 +89,32 @@ func (ph *TextPullHandler) TrackTarget(gt oras.GraphTarget) (oras.GraphTarget, S // OnNodeDownloading implements PullHandler. func (ph *TextPullHandler) OnNodeDownloading(desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, PullPromptDownloading, ph.verbose) + return ph.printer.PrintStatus(desc, PullPromptDownloading) } // OnNodeDownloaded implements PullHandler. func (ph *TextPullHandler) OnNodeDownloaded(desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, PullPromptDownloaded, ph.verbose) + return ph.printer.PrintStatus(desc, PullPromptDownloaded) } // OnNodeRestored implements PullHandler. func (ph *TextPullHandler) OnNodeRestored(desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, PullPromptRestored, ph.verbose) + return ph.printer.PrintStatus(desc, PullPromptRestored) } // OnNodeProcessing implements PullHandler. func (ph *TextPullHandler) OnNodeProcessing(desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, PullPromptProcessing, ph.verbose) + return ph.printer.PrintStatus(desc, PullPromptProcessing) } // OnNodeSkipped implements PullHandler. func (ph *TextPullHandler) OnNodeSkipped(desc ocispec.Descriptor) error { - return ph.printer.PrintStatus(desc, PullPromptSkipped, ph.verbose) + return ph.printer.PrintStatus(desc, PullPromptSkipped) } // NewTextPullHandler returns a new handler for pull command. func NewTextPullHandler(out io.Writer, verbose bool) PullHandler { return &TextPullHandler{ - verbose: verbose, - printer: output.NewPrinter(out), + printer: output.NewPrinter(out, verbose), } } diff --git a/cmd/oras/internal/output/print.go b/cmd/oras/internal/output/print.go index 5ff80b879..ac8816aa1 100644 --- a/cmd/oras/internal/output/print.go +++ b/cmd/oras/internal/output/print.go @@ -32,13 +32,14 @@ type PrintFunc func(ocispec.Descriptor) error // Printer prints for status handlers. type Printer struct { - out io.Writer - lock sync.Mutex + out io.Writer + verbose bool + lock sync.Mutex } // NewPrinter creates a new Printer. -func NewPrinter(out io.Writer) *Printer { - return &Printer{out: out} +func NewPrinter(out io.Writer, verbose bool) *Printer { + return &Printer{out: out, verbose: verbose} } // Println prints objects concurrent-safely with newline. @@ -54,12 +55,20 @@ func (p *Printer) Println(a ...any) error { return nil } +// PrintVerbose prints when verbose is true. +func (p *Printer) PrintVerbose(a ...any) error { + if !p.verbose { + return nil + } + return p.Println(a...) +} + // PrintStatus prints transfer status. -func (p *Printer) PrintStatus(desc ocispec.Descriptor, status string, verbose bool) error { +func (p *Printer) PrintStatus(desc ocispec.Descriptor, status string) error { name, ok := desc.Annotations[ocispec.AnnotationTitle] if !ok { // no status for unnamed content - if !verbose { + if !p.verbose { return nil } name = desc.MediaType @@ -68,9 +77,9 @@ func (p *Printer) PrintStatus(desc ocispec.Descriptor, status string, verbose bo } // StatusPrinter returns a tracking function for transfer status. -func (p *Printer) StatusPrinter(status string, verbose bool) PrintFunc { +func (p *Printer) StatusPrinter(status string) PrintFunc { return func(desc ocispec.Descriptor) error { - return p.PrintStatus(desc, status, verbose) + return p.PrintStatus(desc, status) } } diff --git a/cmd/oras/internal/output/print_test.go b/cmd/oras/internal/output/print_test.go index 85991aa9c..f7873523b 100644 --- a/cmd/oras/internal/output/print_test.go +++ b/cmd/oras/internal/output/print_test.go @@ -42,7 +42,7 @@ func (mw *mockWriter) String() string { func TestPrint_Error(t *testing.T) { mockWriter := &mockWriter{} - printer := NewPrinter(mockWriter) + printer := NewPrinter(mockWriter, false) printer.Println("boom") if mockWriter.errorCount != 1 { t.Error("Expected one errors actual <" + strconv.Itoa(mockWriter.errorCount) + ">") @@ -51,7 +51,7 @@ func TestPrint_Error(t *testing.T) { func TestPrint_NoError(t *testing.T) { mockWriter := &mockWriter{} - printer := NewPrinter(mockWriter) + printer := NewPrinter(mockWriter, false) expected := "blah blah" printer.Println(expected) diff --git a/cmd/oras/root/blob/push.go b/cmd/oras/root/blob/push.go index 94c9717ac..daeee8503 100644 --- a/cmd/oras/root/blob/push.go +++ b/cmd/oras/root/blob/push.go @@ -103,7 +103,8 @@ Example - Push blob 'hi.txt' into an OCI image layout folder 'layout-dir': func pushBlob(cmd *cobra.Command, opts *pushBlobOptions) (err error) { ctx, logger := command.GetLogger(cmd, &opts.Common) - printer := output.NewPrinter(cmd.OutOrStdout()) + verbose := opts.Verbose && !opts.OutputDescriptor + printer := output.NewPrinter(cmd.OutOrStdout(), verbose) target, err := opts.NewTarget(opts.Common, logger) if err != nil { @@ -121,9 +122,8 @@ func pushBlob(cmd *cobra.Command, opts *pushBlobOptions) (err error) { if err != nil { return err } - verbose := opts.Verbose && !opts.OutputDescriptor if exists { - err = printer.PrintStatus(desc, "Exists", verbose) + err = printer.PrintStatus(desc, "Exists") } else { err = opts.doPush(ctx, printer, target, desc, rc) } @@ -140,21 +140,21 @@ func pushBlob(cmd *cobra.Command, opts *pushBlobOptions) (err error) { } outWriter := cmd.OutOrStdout() - fmt.Fprintln(outWriter, "Pushed", opts.AnnotatedReference()) - fmt.Fprintln(outWriter, "Digest:", desc.Digest) + _, _ = fmt.Fprintln(outWriter, "Pushed", opts.AnnotatedReference()) + _, _ = fmt.Fprintln(outWriter, "Digest:", desc.Digest) return nil } func (opts *pushBlobOptions) doPush(ctx context.Context, printer *output.Printer, t oras.Target, desc ocispec.Descriptor, r io.Reader) error { if opts.TTY == nil { // none TTY output - if err := printer.PrintStatus(desc, "Uploading", opts.Verbose); err != nil { + if err := printer.PrintStatus(desc, "Uploading"); err != nil { return err } if err := t.Push(ctx, desc, r); err != nil { return err } - return printer.PrintStatus(desc, "Uploaded ", opts.Verbose) + return printer.PrintStatus(desc, "Uploaded ") } // TTY output diff --git a/cmd/oras/root/blob/push_test.go b/cmd/oras/root/blob/push_test.go index 7b5dc276b..da11a986d 100644 --- a/cmd/oras/root/blob/push_test.go +++ b/cmd/oras/root/blob/push_test.go @@ -40,7 +40,7 @@ func Test_pushBlobOptions_doPush(t *testing.T) { src := memory.New() content := []byte("test") r := bytes.NewReader(content) - printer := output.NewPrinter(os.Stdout) + printer := output.NewPrinter(os.Stdout, false) desc := ocispec.Descriptor{ MediaType: "application/octet-stream", Digest: digest.FromBytes(content), diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index 36ced9bba..4a34357dd 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -110,7 +110,7 @@ Example - Copy an artifact with multiple tags with concurrency tuned: func runCopy(cmd *cobra.Command, opts *copyOptions) error { ctx, logger := command.GetLogger(cmd, &opts.Common) - printer := output.NewPrinter(cmd.OutOrStdout()) + printer := output.NewPrinter(cmd.OutOrStdout(), opts.Verbose) // Prepare source src, err := opts.From.NewReadonlyTarget(ctx, opts.Common, logger) @@ -180,21 +180,21 @@ func doCopy(ctx context.Context, printer *output.Printer, src oras.ReadOnlyGraph // none TTY output extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - return printer.PrintStatus(desc, promptExists, opts.Verbose) + return printer.PrintStatus(desc, promptExists) } extendedCopyOptions.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - return printer.PrintStatus(desc, promptCopying, opts.Verbose) + return printer.PrintStatus(desc, promptCopying) } extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - if err := output.PrintSuccessorStatus(ctx, desc, dst, committed, printer.StatusPrinter(promptSkipped, opts.Verbose)); err != nil { + if err := output.PrintSuccessorStatus(ctx, desc, dst, committed, printer.StatusPrinter(promptSkipped)); err != nil { return err } - return printer.PrintStatus(desc, promptCopied, opts.Verbose) + return printer.PrintStatus(desc, promptCopied) } extendedCopyOptions.OnMounted = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - return printer.PrintStatus(desc, promptMounted, opts.Verbose) + return printer.PrintStatus(desc, promptMounted) } } else { // TTY output diff --git a/cmd/oras/root/cp_test.go b/cmd/oras/root/cp_test.go index 40a717c77..1adb5b339 100644 --- a/cmd/oras/root/cp_test.go +++ b/cmd/oras/root/cp_test.go @@ -132,7 +132,7 @@ func Test_doCopy(t *testing.T) { opts.From.Reference = memDesc.Digest.String() dst := memory.New() builder := &strings.Builder{} - printer := output.NewPrinter(builder) + printer := output.NewPrinter(builder, opts.Verbose) // test _, err = doCopy(context.Background(), printer, memStore, dst, &opts) if err != nil { @@ -156,7 +156,7 @@ func Test_doCopy_skipped(t *testing.T) { opts.Verbose = true opts.From.Reference = memDesc.Digest.String() builder := &strings.Builder{} - printer := output.NewPrinter(builder) + printer := output.NewPrinter(builder, opts.Verbose) // test _, err = doCopy(context.Background(), printer, memStore, memStore, &opts) if err != nil { @@ -191,7 +191,7 @@ func Test_doCopy_mounted(t *testing.T) { } to.PlainHTTP = true builder := &strings.Builder{} - printer := output.NewPrinter(builder) + printer := output.NewPrinter(builder, opts.Verbose) // test _, err = doCopy(context.Background(), printer, from, to, &opts) if err != nil { diff --git a/cmd/oras/root/manifest/push.go b/cmd/oras/root/manifest/push.go index fec0fbdca..344953db0 100644 --- a/cmd/oras/root/manifest/push.go +++ b/cmd/oras/root/manifest/push.go @@ -112,7 +112,8 @@ Example - Push a manifest to an OCI image layout folder 'layout-dir' and tag wit func pushManifest(cmd *cobra.Command, opts pushOptions) error { ctx, logger := command.GetLogger(cmd, &opts.Common) - printer := output.NewPrinter(cmd.OutOrStdout()) + verbose := opts.Verbose && !opts.OutputDescriptor + printer := output.NewPrinter(cmd.OutOrStdout(), verbose) var target oras.Target var err error target, err = opts.NewTarget(opts.Common, logger) @@ -156,19 +157,18 @@ func pushManifest(cmd *cobra.Command, opts pushOptions) error { if err != nil { return err } - verbose := opts.Verbose && !opts.OutputDescriptor if match { - if err := printer.PrintStatus(desc, "Exists", verbose); err != nil { + if err := printer.PrintStatus(desc, "Exists"); err != nil { return err } } else { - if err = printer.PrintStatus(desc, "Uploading", verbose); err != nil { + if err = printer.PrintStatus(desc, "Uploading"); err != nil { return err } if _, err := oras.TagBytes(ctx, target, mediaType, contentBytes, ref); err != nil { return err } - if err = printer.PrintStatus(desc, "Uploaded ", verbose); err != nil { + if err = printer.PrintStatus(desc, "Uploaded "); err != nil { return err } } diff --git a/cmd/oras/root/tag.go b/cmd/oras/root/tag.go index eaaddd99d..448cfe2ea 100644 --- a/cmd/oras/root/tag.go +++ b/cmd/oras/root/tag.go @@ -99,7 +99,7 @@ Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI image layout folder 'l func tagManifest(cmd *cobra.Command, opts *tagOptions) error { ctx, logger := command.GetLogger(cmd, &opts.Common) - printer := output.NewPrinter(cmd.OutOrStdout()) + printer := output.NewPrinter(cmd.OutOrStdout(), opts.Verbose) target, err := opts.NewTarget(opts.Common, logger) if err != nil { return err