From 24c2acf98e7a039f16038b35c16793cf421a8702 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 28 Mar 2024 11:54:58 +0800 Subject: [PATCH] refactor: add stdout to metadata handlers (#1312) Signed-off-by: Billy Zha --- cmd/oras/internal/display/handler.go | 17 +++++++++-------- .../internal/display/metadata/json/attach.go | 16 +++++++++++----- .../internal/display/metadata/json/json.go | 6 +++--- .../internal/display/metadata/json/push.go | 11 ++++++++--- .../display/metadata/template/attach.go | 12 +++++++++--- .../display/metadata/template/push.go | 12 +++++++++--- .../display/metadata/template/template.go | 6 +++--- .../internal/display/metadata/text/attach.go | 17 +++++++++++------ .../internal/display/metadata/text/push.go | 19 ++++++++++++------- cmd/oras/root/attach.go | 2 +- cmd/oras/root/push.go | 2 +- 11 files changed, 77 insertions(+), 43 deletions(-) diff --git a/cmd/oras/internal/display/handler.go b/cmd/oras/internal/display/handler.go index 56e05ae63..24d52ac44 100644 --- a/cmd/oras/internal/display/handler.go +++ b/cmd/oras/internal/display/handler.go @@ -16,6 +16,7 @@ limitations under the License. package display import ( + "io" "os" "oras.land/oras/cmd/oras/internal/display/metadata" @@ -26,7 +27,7 @@ import ( ) // NewPushHandler returns status and metadata handlers for push command. -func NewPushHandler(format string, tty *os.File, verbose bool) (status.PushHandler, metadata.PushHandler) { +func NewPushHandler(format string, tty *os.File, out io.Writer, verbose bool) (status.PushHandler, metadata.PushHandler) { var statusHandler status.PushHandler if tty != nil { statusHandler = status.NewTTYPushHandler(tty) @@ -39,18 +40,18 @@ func NewPushHandler(format string, tty *os.File, verbose bool) (status.PushHandl var metadataHandler metadata.PushHandler switch format { case "": - metadataHandler = text.NewPushHandler() + metadataHandler = text.NewPushHandler(out) case "json": - metadataHandler = json.NewPushHandler() + metadataHandler = json.NewPushHandler(out) default: - metadataHandler = template.NewPushHandler(format) + metadataHandler = template.NewPushHandler(out, format) } return statusHandler, metadataHandler } // NewAttachHandler returns status and metadata handlers for attach command. -func NewAttachHandler(format string, tty *os.File, verbose bool) (status.AttachHandler, metadata.AttachHandler) { +func NewAttachHandler(format string, tty *os.File, out io.Writer, verbose bool) (status.AttachHandler, metadata.AttachHandler) { var statusHandler status.AttachHandler if tty != nil { statusHandler = status.NewTTYAttachHandler(tty) @@ -63,11 +64,11 @@ func NewAttachHandler(format string, tty *os.File, verbose bool) (status.AttachH var metadataHandler metadata.AttachHandler switch format { case "": - metadataHandler = text.NewAttachHandler() + metadataHandler = text.NewAttachHandler(out) case "json": - metadataHandler = json.NewAttachHandler() + metadataHandler = json.NewAttachHandler(out) default: - metadataHandler = template.NewAttachHandler(format) + metadataHandler = template.NewAttachHandler(out, format) } return statusHandler, metadataHandler diff --git a/cmd/oras/internal/display/metadata/json/attach.go b/cmd/oras/internal/display/metadata/json/attach.go index 47548452e..283c9965d 100644 --- a/cmd/oras/internal/display/metadata/json/attach.go +++ b/cmd/oras/internal/display/metadata/json/attach.go @@ -16,6 +16,8 @@ limitations under the License. package json import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" @@ -23,14 +25,18 @@ import ( ) // AttachHandler handles json metadata output for attach events. -type AttachHandler struct{} +type AttachHandler struct { + out io.Writer +} // NewAttachHandler creates a new handler for attach events. -func NewAttachHandler() metadata.AttachHandler { - return AttachHandler{} +func NewAttachHandler(out io.Writer) metadata.AttachHandler { + return &AttachHandler{ + out: out, + } } // OnCompleted is called when the attach command is completed. -func (AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return printJSON(model.NewPush(root, opts.Path)) +func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { + return printJSON(ah.out, model.NewPush(root, opts.Path)) } diff --git a/cmd/oras/internal/display/metadata/json/json.go b/cmd/oras/internal/display/metadata/json/json.go index 4f060f34b..ec3fde8ae 100644 --- a/cmd/oras/internal/display/metadata/json/json.go +++ b/cmd/oras/internal/display/metadata/json/json.go @@ -17,11 +17,11 @@ package json import ( "encoding/json" - "os" + "io" ) -func printJSON(object any) error { - encoder := json.NewEncoder(os.Stdout) +func printJSON(out io.Writer, object any) error { + encoder := json.NewEncoder(out) encoder.SetIndent("", " ") return encoder.Encode(object) } diff --git a/cmd/oras/internal/display/metadata/json/push.go b/cmd/oras/internal/display/metadata/json/push.go index ef3af142c..7781e5cf8 100644 --- a/cmd/oras/internal/display/metadata/json/push.go +++ b/cmd/oras/internal/display/metadata/json/push.go @@ -16,6 +16,8 @@ limitations under the License. package json import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" @@ -25,11 +27,14 @@ import ( // PushHandler handles JSON metadata output for push events. type PushHandler struct { path string + out io.Writer } // NewPushHandler creates a new handler for push events. -func NewPushHandler() metadata.PushHandler { - return &PushHandler{} +func NewPushHandler(out io.Writer) metadata.PushHandler { + return &PushHandler{ + out: out, + } } // OnCopied is called after files are copied. @@ -40,5 +45,5 @@ func (ph *PushHandler) OnCopied(opts *option.Target) error { // OnCompleted is called after the push is completed. func (ph *PushHandler) OnCompleted(root ocispec.Descriptor) error { - return printJSON(model.NewPush(root, ph.path)) + return printJSON(ph.out, model.NewPush(root, ph.path)) } diff --git a/cmd/oras/internal/display/metadata/template/attach.go b/cmd/oras/internal/display/metadata/template/attach.go index 4338dc0b5..724a8a50f 100644 --- a/cmd/oras/internal/display/metadata/template/attach.go +++ b/cmd/oras/internal/display/metadata/template/attach.go @@ -16,6 +16,8 @@ limitations under the License. package template import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" @@ -25,14 +27,18 @@ import ( // AttachHandler handles go-template metadata output for attach events. type AttachHandler struct { template string + out io.Writer } // NewAttachHandler returns a new handler for attach metadata events. -func NewAttachHandler(template string) metadata.AttachHandler { - return &AttachHandler{template: template} +func NewAttachHandler(out io.Writer, template string) metadata.AttachHandler { + return &AttachHandler{ + out: out, + template: template, + } } // OnCompleted formats the metadata of attach command. func (ah *AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { - return parseAndWrite(model.NewPush(root, opts.Path), ah.template) + return parseAndWrite(ah.out, model.NewPush(root, opts.Path), ah.template) } diff --git a/cmd/oras/internal/display/metadata/template/push.go b/cmd/oras/internal/display/metadata/template/push.go index 6fe8d385a..aded1e9ce 100644 --- a/cmd/oras/internal/display/metadata/template/push.go +++ b/cmd/oras/internal/display/metadata/template/push.go @@ -16,6 +16,8 @@ limitations under the License. package template import ( + "io" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" "oras.land/oras/cmd/oras/internal/display/metadata/model" @@ -26,11 +28,15 @@ import ( type PushHandler struct { template string path string + out io.Writer } // NewPushHandler returns a new handler for push events. -func NewPushHandler(template string) metadata.PushHandler { - return &PushHandler{template: template} +func NewPushHandler(out io.Writer, template string) metadata.PushHandler { + return &PushHandler{ + out: out, + template: template, + } } // OnStarted is called after files are copied. @@ -41,5 +47,5 @@ func (ph *PushHandler) OnCopied(opts *option.Target) error { // OnCompleted is called after the push is completed. func (ph *PushHandler) OnCompleted(root ocispec.Descriptor) error { - return parseAndWrite(model.NewPush(root, ph.path), ph.template) + return parseAndWrite(ph.out, model.NewPush(root, ph.path), ph.template) } diff --git a/cmd/oras/internal/display/metadata/template/template.go b/cmd/oras/internal/display/metadata/template/template.go index 3f8038f3a..151a5d1d9 100644 --- a/cmd/oras/internal/display/metadata/template/template.go +++ b/cmd/oras/internal/display/metadata/template/template.go @@ -16,16 +16,16 @@ limitations under the License. package template import ( - "os" + "io" "text/template" "github.com/Masterminds/sprig/v3" ) -func parseAndWrite(object any, templateStr string) error { +func parseAndWrite(out io.Writer, object any, templateStr string) error { t, err := template.New("format output").Funcs(sprig.FuncMap()).Parse(templateStr) if err != nil { return err } - return t.Execute(os.Stdout, object) + return t.Execute(out, object) } diff --git a/cmd/oras/internal/display/metadata/text/attach.go b/cmd/oras/internal/display/metadata/text/attach.go index 94c4bec0a..d73d32bbb 100644 --- a/cmd/oras/internal/display/metadata/text/attach.go +++ b/cmd/oras/internal/display/metadata/text/attach.go @@ -17,6 +17,7 @@ package text import ( "fmt" + "io" "strings" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -25,23 +26,27 @@ import ( ) // AttachHandler handles text metadata output for attach events. -type AttachHandler struct{} +type AttachHandler struct { + out io.Writer +} // NewAttachHandler returns a new handler for attach events. -func NewAttachHandler() metadata.AttachHandler { - return AttachHandler{} +func NewAttachHandler(out io.Writer) metadata.AttachHandler { + return &AttachHandler{ + out: out, + } } // OnCompleted is called when the attach command is completed. -func (AttachHandler) OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error { +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) } - _, err := fmt.Println("Attached to", opts.AnnotatedReference()) + _, err := fmt.Fprintln(ah.out, "Attached to", opts.AnnotatedReference()) if err != nil { return err } - _, err = fmt.Println("Digest:", root.Digest) + _, err = fmt.Fprintln(ah.out, "Digest:", root.Digest) return err } diff --git a/cmd/oras/internal/display/metadata/text/push.go b/cmd/oras/internal/display/metadata/text/push.go index ef5fa4680..c6581d786 100644 --- a/cmd/oras/internal/display/metadata/text/push.go +++ b/cmd/oras/internal/display/metadata/text/push.go @@ -17,6 +17,7 @@ package text import ( "fmt" + "io" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras/cmd/oras/internal/display/metadata" @@ -24,21 +25,25 @@ import ( ) // PushHandler handles text metadata output for push events. -type PushHandler struct{} +type PushHandler struct { + out io.Writer +} // NewPushHandler returns a new handler for push events. -func NewPushHandler() metadata.PushHandler { - return PushHandler{} +func NewPushHandler(out io.Writer) metadata.PushHandler { + return &PushHandler{ + out: out, + } } // OnCopied is called after files are copied. -func (PushHandler) OnCopied(opts *option.Target) error { - _, err := fmt.Println("Pushed", opts.AnnotatedReference()) +func (p *PushHandler) OnCopied(opts *option.Target) error { + _, err := fmt.Fprintln(p.out, "Pushed", opts.AnnotatedReference()) return err } // OnCompleted is called after the push is completed. -func (PushHandler) OnCompleted(root ocispec.Descriptor) error { - _, err := fmt.Println("Digest:", root.Digest) +func (p *PushHandler) OnCompleted(root ocispec.Descriptor) error { + _, err := fmt.Fprintln(p.out, "Digest:", root.Digest) return err } diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index b1711e869..944810b2f 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -113,7 +113,7 @@ func runAttach(cmd *cobra.Command, opts *attachOptions) error { Recommendation: `To attach to an existing artifact, please provide files via argument or annotations via flag "--annotation". Run "oras attach -h" for more options and examples`, } } - displayStatus, displayMetadata := display.NewAttachHandler(opts.Template, opts.TTY, opts.Verbose) + displayStatus, displayMetadata := display.NewAttachHandler(opts.Template, opts.TTY, cmd.OutOrStdout(), opts.Verbose) // prepare manifest store, err := file.New("") diff --git a/cmd/oras/root/push.go b/cmd/oras/root/push.go index f41c9f8bc..fb357e71e 100644 --- a/cmd/oras/root/push.go +++ b/cmd/oras/root/push.go @@ -138,7 +138,7 @@ func runPush(cmd *cobra.Command, opts *pushOptions) error { if err != nil { return err } - displayStatus, displayMetadata := display.NewPushHandler(opts.Template, opts.TTY, opts.Verbose) + displayStatus, displayMetadata := display.NewPushHandler(opts.Template, opts.TTY, cmd.OutOrStdout(), opts.Verbose) // prepare pack packOpts := oras.PackManifestOptions{