diff --git a/cmd/oras/internal/display/handler_test.go b/cmd/oras/internal/display/handler_test.go index f71981e5a..c6f5b933b 100644 --- a/cmd/oras/internal/display/handler_test.go +++ b/cmd/oras/internal/display/handler_test.go @@ -16,11 +16,12 @@ limitations under the License. package display import ( - "oras.land/oras/internal/testutils" "os" "reflect" "testing" + "oras.land/oras/internal/testutils" + "oras.land/oras/cmd/oras/internal/display/metadata/text" "oras.land/oras/cmd/oras/internal/display/status" "oras.land/oras/cmd/oras/internal/option" diff --git a/cmd/oras/internal/display/metadata/interface.go b/cmd/oras/internal/display/metadata/interface.go index 32b18f2bf..8099bd872 100644 --- a/cmd/oras/internal/display/metadata/interface.go +++ b/cmd/oras/internal/display/metadata/interface.go @@ -28,9 +28,15 @@ type PushHandler interface { OnCompleted(root ocispec.Descriptor) error } +// Renderer renders metadata information when an operation is complete. +type Renderer interface { + Render() error +} + // AttachHandler handles metadata output for attach events. type AttachHandler interface { - OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error + OnAttached(target *option.Target, root ocispec.Descriptor, subject ocispec.Descriptor) + Renderer } // DiscoverHandler handles metadata output for discover events. diff --git a/cmd/oras/internal/display/metadata/json/attach.go b/cmd/oras/internal/display/metadata/json/attach.go index f1d4545e7..91cda78b2 100644 --- a/cmd/oras/internal/display/metadata/json/attach.go +++ b/cmd/oras/internal/display/metadata/json/attach.go @@ -27,7 +27,9 @@ import ( // AttachHandler handles json metadata output for attach events. type AttachHandler struct { - out io.Writer + out io.Writer + target *option.Target + root ocispec.Descriptor } // NewAttachHandler creates a new handler for attach events. @@ -37,7 +39,13 @@ func NewAttachHandler(out io.Writer) metadata.AttachHandler { } } -// OnCompleted is called when the attach command is completed. -func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return output.PrintPrettyJSON(ah.out, model.NewAttach(root, opts.Path)) +// OnAttached implements AttachHandler. +func (ah *AttachHandler) OnAttached(target *option.Target, root ocispec.Descriptor, _ ocispec.Descriptor) { + ah.target = target + ah.root = root +} + +// Render is called when the attach command is completed. +func (ah *AttachHandler) Render() error { + return output.PrintPrettyJSON(ah.out, model.NewAttach(ah.root, ah.target.Path)) } diff --git a/cmd/oras/internal/display/metadata/template/attach.go b/cmd/oras/internal/display/metadata/template/attach.go index 4ef9d28c0..d792daede 100644 --- a/cmd/oras/internal/display/metadata/template/attach.go +++ b/cmd/oras/internal/display/metadata/template/attach.go @@ -29,6 +29,8 @@ import ( type AttachHandler struct { template string out io.Writer + target *option.Target + root ocispec.Descriptor } // NewAttachHandler returns a new handler for attach metadata events. @@ -39,7 +41,13 @@ func NewAttachHandler(out io.Writer, template string) metadata.AttachHandler { } } -// OnCompleted formats the metadata of attach command. -func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return output.ParseAndWrite(ah.out, model.NewAttach(root, opts.Path), ah.template) +// OnAttached implements AttachHandler. +func (ah *AttachHandler) OnAttached(target *option.Target, root ocispec.Descriptor, _ ocispec.Descriptor) { + ah.target = target + ah.root = root +} + +// Render formats the metadata of attach command. +func (ah *AttachHandler) Render() error { + return output.ParseAndWrite(ah.out, model.NewAttach(ah.root, ah.target.Path), ah.template) } diff --git a/cmd/oras/internal/display/metadata/text/attach.go b/cmd/oras/internal/display/metadata/text/attach.go index 2032704cf..dfc4cfc0f 100644 --- a/cmd/oras/internal/display/metadata/text/attach.go +++ b/cmd/oras/internal/display/metadata/text/attach.go @@ -28,6 +28,9 @@ import ( // AttachHandler handles text metadata output for attach events. type AttachHandler struct { printer *output.Printer + target *option.Target + root ocispec.Descriptor + subject ocispec.Descriptor } // NewAttachHandler returns a new handler for attach events. @@ -37,16 +40,23 @@ func NewAttachHandler(printer *output.Printer) metadata.AttachHandler { } } -// OnCompleted is called when the attach command is complete. -func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - digest := subject.Digest.String() - if !strings.HasSuffix(opts.RawReference, digest) { - opts.RawReference = fmt.Sprintf("%s@%s", opts.Path, subject.Digest) +// OnAttached implements AttachHandler. +func (ah *AttachHandler) OnAttached(target *option.Target, root ocispec.Descriptor, subject ocispec.Descriptor) { + ah.target = target + ah.root = root + ah.subject = subject +} + +// Render is called when the attach command is complete. +func (ah *AttachHandler) Render() error { + digest := ah.subject.Digest.String() + if !strings.HasSuffix(ah.target.RawReference, digest) { + ah.target.RawReference = fmt.Sprintf("%s@%s", ah.target.Path, ah.subject.Digest) } - err := ah.printer.Println("Attached to", opts.AnnotatedReference()) + err := ah.printer.Println("Attached to", ah.target.AnnotatedReference()) if err != nil { return err } - err = ah.printer.Println("Digest:", root.Digest) + err = ah.printer.Println("Digest:", ah.root.Digest) return err } diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index e270d024c..0085deb3c 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -158,25 +158,25 @@ func runAttach(cmd *cobra.Command, opts *attachOptions) error { if err != nil { return fmt.Errorf("failed to resolve %s: %w", opts.Reference, err) } - displayStatus, displayMetadata, err := display.NewAttachHandler(opts.Printer, opts.Format, opts.TTY, store) + statusHandler, metadataHandler, err := display.NewAttachHandler(opts.Printer, opts.Format, opts.TTY, store) if err != nil { return err } - descs, err := loadFiles(ctx, store, opts.Annotations, opts.FileRefs, displayStatus) + descs, err := loadFiles(ctx, store, opts.Annotations, opts.FileRefs, statusHandler) if err != nil { return err } // prepare push - dst, stopTrack, err := displayStatus.TrackTarget(dst) + dst, stopTrack, err := statusHandler.TrackTarget(dst) if err != nil { return err } graphCopyOptions := oras.DefaultCopyGraphOptions graphCopyOptions.Concurrency = opts.concurrency - graphCopyOptions.OnCopySkipped = displayStatus.OnCopySkipped - graphCopyOptions.PreCopy = displayStatus.PreCopy - graphCopyOptions.PostCopy = displayStatus.PostCopy + graphCopyOptions.OnCopySkipped = statusHandler.OnCopySkipped + graphCopyOptions.PreCopy = statusHandler.PreCopy + graphCopyOptions.PostCopy = statusHandler.PostCopy packOpts := oras.PackManifestOptions{ Subject: &subject, @@ -210,7 +210,10 @@ func runAttach(cmd *cobra.Command, opts *attachOptions) error { if err != nil { return err } - err = displayMetadata.OnCompleted(&opts.Target, root, subject) + + metadataHandler.OnAttached(&opts.Target, root, subject) + + err = metadataHandler.Render() if err != nil { return err }