diff --git a/cmd/oras/internal/option/common.go b/cmd/oras/internal/option/common.go index 9cd7b622c..60be023b6 100644 --- a/cmd/oras/internal/option/common.go +++ b/cmd/oras/internal/option/common.go @@ -20,6 +20,7 @@ import ( "os" "github.com/sirupsen/logrus" + "github.com/spf13/cobra" "github.com/spf13/pflag" "golang.org/x/term" "oras.land/oras/internal/trace" @@ -49,7 +50,7 @@ func (opts *Common) WithContext(ctx context.Context) (context.Context, logrus.Fi } // Parse gets target options from user input. -func (opts *Common) Parse() error { +func (opts *Common) Parse(*cobra.Command) error { // use STDERR as TTY output since STDOUT is reserved for pipeable output return opts.parseTTY(os.Stderr) } diff --git a/cmd/oras/internal/option/packer.go b/cmd/oras/internal/option/packer.go index 7b74a881e..7217df5ee 100644 --- a/cmd/oras/internal/option/packer.go +++ b/cmd/oras/internal/option/packer.go @@ -25,6 +25,7 @@ import ( "strings" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/spf13/cobra" "github.com/spf13/pflag" "oras.land/oras-go/v2/content" oerrors "oras.land/oras/cmd/oras/internal/errors" @@ -73,7 +74,7 @@ func (opts *Packer) ExportManifest(ctx context.Context, fetcher content.Fetcher, } return os.WriteFile(opts.ManifestExportPath, manifestBytes, 0666) } -func (opts *Packer) Parse() error { +func (opts *Packer) Parse(*cobra.Command) error { if !opts.PathValidationDisabled { var failedPaths []string for _, path := range opts.FileRefs { diff --git a/cmd/oras/internal/option/parser.go b/cmd/oras/internal/option/parser.go index 43d85dd0e..ddfd71209 100644 --- a/cmd/oras/internal/option/parser.go +++ b/cmd/oras/internal/option/parser.go @@ -17,18 +17,20 @@ package option import ( "reflect" + + "github.com/spf13/cobra" ) // FlagParser parses flags in an option. type FlagParser interface { - Parse() error + Parse(cmd *cobra.Command) error } // Parse parses applicable fields of the passed-in option pointer and returns // error during parsing. -func Parse(optsPtr interface{}) error { +func Parse(cmd *cobra.Command, optsPtr interface{}) error { return rangeFields(optsPtr, func(fp FlagParser) error { - return fp.Parse() + return fp.Parse(cmd) }) } diff --git a/cmd/oras/internal/option/parser_test.go b/cmd/oras/internal/option/parser_test.go index f54a232e5..015c9ee26 100644 --- a/cmd/oras/internal/option/parser_test.go +++ b/cmd/oras/internal/option/parser_test.go @@ -19,6 +19,7 @@ import ( "errors" "testing" + "github.com/spf13/cobra" "oras.land/oras/cmd/oras/internal/option" ) @@ -26,7 +27,7 @@ type Test struct { CntPtr *int } -func (t *Test) Parse() error { +func (t *Test) Parse(cmd *cobra.Command) error { *t.CntPtr += 1 if *t.CntPtr == 2 { return errors.New("should not be tried twice") @@ -48,7 +49,7 @@ func TestParse_once(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := option.Parse(&tt.args); (err != nil) != tt.wantErr { + if err := option.Parse(nil, &tt.args); (err != nil) != tt.wantErr { t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) } @@ -76,7 +77,7 @@ func TestParse_err(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := option.Parse(&tt.args); (err != nil) != tt.wantErr { + if err := option.Parse(nil, &tt.args); (err != nil) != tt.wantErr { t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) } diff --git a/cmd/oras/internal/option/platform.go b/cmd/oras/internal/option/platform.go index fa0fdf363..96954ff01 100644 --- a/cmd/oras/internal/option/platform.go +++ b/cmd/oras/internal/option/platform.go @@ -21,6 +21,7 @@ import ( "strings" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -36,7 +37,7 @@ func (opts *Platform) ApplyFlags(fs *pflag.FlagSet) { } // parse parses the input platform flag to an oci platform type. -func (opts *Platform) Parse() error { +func (opts *Platform) Parse(*cobra.Command) error { if opts.platform == "" { return nil } diff --git a/cmd/oras/internal/option/platform_test.go b/cmd/oras/internal/option/platform_test.go index 8864bbede..9af257aa6 100644 --- a/cmd/oras/internal/option/platform_test.go +++ b/cmd/oras/internal/option/platform_test.go @@ -43,7 +43,7 @@ func TestPlatform_Parse_err(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.opts.Parse() + err := tt.opts.Parse(nil) if err == nil { t.Errorf("Platform.Parse() error = %v, wantErr %v", err, true) return @@ -68,7 +68,7 @@ func TestPlatform_Parse(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := tt.opts.Parse(); err != nil { + if err := tt.opts.Parse(nil); err != nil { t.Errorf("Platform.Parse() error = %v", err) } got := tt.opts.Platform diff --git a/cmd/oras/internal/option/remote.go b/cmd/oras/internal/option/remote.go index fa28bfafd..9620292bf 100644 --- a/cmd/oras/internal/option/remote.go +++ b/cmd/oras/internal/option/remote.go @@ -117,7 +117,7 @@ func (opts *Remote) ApplyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description } // Parse tries to read password with optional cmd prompt. -func (opts *Remote) Parse() error { +func (opts *Remote) Parse(*cobra.Command) error { if err := opts.parseCustomHeaders(); err != nil { return err } diff --git a/cmd/oras/internal/option/target.go b/cmd/oras/internal/option/target.go index db5ac8357..b15613b13 100644 --- a/cmd/oras/internal/option/target.go +++ b/cmd/oras/internal/option/target.go @@ -95,7 +95,7 @@ func (opts *Target) ApplyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description } // Parse gets target options from user input. -func (opts *Target) Parse() error { +func (opts *Target) Parse(cmd *cobra.Command) error { switch { case opts.IsOCILayout: opts.Type = TargetTypeOCILayout @@ -111,7 +111,7 @@ func (opts *Target) Parse() error { Recommendation: "Please make sure the provided reference is in the form of /[:tag|@digest]", } } - return opts.Remote.Parse() + return opts.Remote.Parse(cmd) } } @@ -330,13 +330,13 @@ func (opts *BinaryTarget) ApplyFlags(fs *pflag.FlagSet) { } // Parse parses user-provided flags and arguments into option struct. -func (opts *BinaryTarget) Parse() error { +func (opts *BinaryTarget) Parse(cmd *cobra.Command) error { opts.From.warned = make(map[string]*sync.Map) opts.To.warned = opts.From.warned // resolve are parsed in array order, latter will overwrite former opts.From.resolveFlag = append(opts.resolveFlag, opts.From.resolveFlag...) opts.To.resolveFlag = append(opts.resolveFlag, opts.To.resolveFlag...) - return Parse(opts) + return Parse(cmd, opts) } // Modify handles error during cmd execution. diff --git a/cmd/oras/internal/option/target_test.go b/cmd/oras/internal/option/target_test.go index 5dc94249d..7c9a33917 100644 --- a/cmd/oras/internal/option/target_test.go +++ b/cmd/oras/internal/option/target_test.go @@ -30,7 +30,7 @@ import ( func TestTarget_Parse_oci(t *testing.T) { opts := Target{IsOCILayout: true} - err := opts.Parse() + err := opts.Parse(nil) if !errors.Is(err, errdef.ErrInvalidReference) { t.Errorf("Target.Parse() error = %v, expect %v", err, errdef.ErrInvalidReference) } @@ -44,7 +44,7 @@ func TestTarget_Parse_remote(t *testing.T) { RawReference: "mocked/test", IsOCILayout: false, } - if err := opts.Parse(); err != nil { + if err := opts.Parse(nil); err != nil { t.Errorf("Target.Parse() error = %v", err) } if opts.Type != TargetTypeRemote { @@ -57,7 +57,7 @@ func TestTarget_Parse_remote_err(t *testing.T) { RawReference: "/test", IsOCILayout: false, } - if err := opts.Parse(); err == nil { + if err := opts.Parse(nil); err == nil { t.Errorf("expect Target.Parse() to fail but not") } } diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index 0e6bbbe75..fbc7cead3 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -82,7 +82,7 @@ Example - Attach file to the manifest tagged 'v1' in an OCI image layout folder PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] opts.FileRefs = args[1:] - if err := option.Parse(&opts); err != nil { + if err := option.Parse(cmd, &opts); err != nil { return err } return nil diff --git a/cmd/oras/root/blob/delete.go b/cmd/oras/root/blob/delete.go index b5e08bebb..3dcca3307 100644 --- a/cmd/oras/root/blob/delete.go +++ b/cmd/oras/root/blob/delete.go @@ -60,7 +60,7 @@ Example - Delete a blob and print its descriptor: if opts.OutputDescriptor && !opts.Force { return errors.New("must apply --force to confirm the deletion if the descriptor is outputted") } - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return deleteBlob(cmd, &opts) diff --git a/cmd/oras/root/blob/fetch.go b/cmd/oras/root/blob/fetch.go index f81c822fa..acab90668 100644 --- a/cmd/oras/root/blob/fetch.go +++ b/cmd/oras/root/blob/fetch.go @@ -77,7 +77,7 @@ Example - Fetch and print a blob from OCI image layout archive file 'layout.tar' return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } opts.RawReference = args[0] - err := option.Parse(&opts) + err := option.Parse(cmd, &opts) if err == nil { opts.UpdateTTY(cmd.Flags().Changed(option.NoTTYFlag), opts.outputPath == "-") } diff --git a/cmd/oras/root/blob/push.go b/cmd/oras/root/blob/push.go index c82ed1e5f..5d562e50f 100644 --- a/cmd/oras/root/blob/push.go +++ b/cmd/oras/root/blob/push.go @@ -87,7 +87,7 @@ Example - Push blob 'hi.txt' into an OCI image layout folder 'layout-dir': return errors.New("`--size` must be provided if the blob is read from stdin") } } - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return pushBlob(cmd, &opts) diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index a5f76b48c..fb844baef 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -93,7 +93,7 @@ Example - Copy an artifact with multiple tags with concurrency tuned: refs := strings.Split(args[1], ",") opts.To.RawReference = refs[0] opts.extraRefs = refs[1:] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return runCopy(cmd, &opts) diff --git a/cmd/oras/root/discover.go b/cmd/oras/root/discover.go index a9d71d3f8..5c3607bcd 100644 --- a/cmd/oras/root/discover.go +++ b/cmd/oras/root/discover.go @@ -79,7 +79,7 @@ Example - Discover referrers of the manifest tagged 'v1' in an OCI image layout Args: oerrors.CheckArgs(argument.Exactly(1), "the target artifact to discover referrers from"), PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return runDiscover(cmd, &opts) diff --git a/cmd/oras/root/login.go b/cmd/oras/root/login.go index bbf551f07..3535d1a3f 100644 --- a/cmd/oras/root/login.go +++ b/cmd/oras/root/login.go @@ -65,7 +65,7 @@ Example - Log in with username and password in an interactive terminal and no TL `, Args: oerrors.CheckArgs(argument.Exactly(1), "the registry to log in to"), PreRunE: func(cmd *cobra.Command, args []string) error { - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.Hostname = args[0] diff --git a/cmd/oras/root/manifest/delete.go b/cmd/oras/root/manifest/delete.go index e2b0833f4..22ea206cd 100644 --- a/cmd/oras/root/manifest/delete.go +++ b/cmd/oras/root/manifest/delete.go @@ -63,7 +63,7 @@ Example - Delete a manifest by digest 'sha256:99e4703fbf30916f549cd6bfa9cdbab614 if opts.OutputDescriptor && !opts.Force { return errors.New("must apply --force to confirm the deletion if the descriptor is outputted") } - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return deleteManifest(cmd, &opts) diff --git a/cmd/oras/root/manifest/fetch.go b/cmd/oras/root/manifest/fetch.go index 5726552a4..245883443 100644 --- a/cmd/oras/root/manifest/fetch.go +++ b/cmd/oras/root/manifest/fetch.go @@ -76,7 +76,7 @@ Example - Fetch raw manifest from an OCI layout archive file 'layout.tar': return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, Aliases: []string{"get"}, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/oras/root/manifest/fetch_config.go b/cmd/oras/root/manifest/fetch_config.go index 8eadcf25c..6e10acc27 100644 --- a/cmd/oras/root/manifest/fetch_config.go +++ b/cmd/oras/root/manifest/fetch_config.go @@ -75,7 +75,7 @@ Example - Fetch and print the prettified descriptor of the config: return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return fetchConfig(cmd, &opts) diff --git a/cmd/oras/root/manifest/push.go b/cmd/oras/root/manifest/push.go index 5d30cdbda..facaf7ce0 100644 --- a/cmd/oras/root/manifest/push.go +++ b/cmd/oras/root/manifest/push.go @@ -92,7 +92,7 @@ Example - Push a manifest to an OCI image layout folder 'layout-dir' and tag wit refs := strings.Split(args[0], ",") opts.RawReference = refs[0] opts.extraRefs = refs[1:] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return pushManifest(cmd, opts) diff --git a/cmd/oras/root/pull.go b/cmd/oras/root/pull.go index 7a7bab421..3e4402eed 100644 --- a/cmd/oras/root/pull.go +++ b/cmd/oras/root/pull.go @@ -90,7 +90,7 @@ Example - Pull artifact files from an OCI layout archive 'layout.tar': Args: oerrors.CheckArgs(argument.Exactly(1), "the artifact reference you want to pull"), PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return runPull(cmd, &opts) diff --git a/cmd/oras/root/push.go b/cmd/oras/root/push.go index a6541286e..fefd62d4a 100644 --- a/cmd/oras/root/push.go +++ b/cmd/oras/root/push.go @@ -105,7 +105,7 @@ Example - Push file "hi.txt" into an OCI image layout folder 'layout-dir' with t opts.RawReference = refs[0] opts.extraRefs = refs[1:] opts.FileRefs = args[1:] - if err := option.Parse(&opts); err != nil { + if err := option.Parse(cmd, &opts); err != nil { return err } switch opts.PackVersion { diff --git a/cmd/oras/root/repo/ls.go b/cmd/oras/root/repo/ls.go index aba613ed8..6f8ec933d 100644 --- a/cmd/oras/root/repo/ls.go +++ b/cmd/oras/root/repo/ls.go @@ -54,7 +54,7 @@ Example - List the repositories under the registry that include values lexically Args: oerrors.CheckArgs(argument.Exactly(1), "the target registry to list repositories from"), Aliases: []string{"list"}, PreRunE: func(cmd *cobra.Command, args []string) error { - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { var err error diff --git a/cmd/oras/root/repo/tags.go b/cmd/oras/root/repo/tags.go index 311ef6e71..ca7d96679 100644 --- a/cmd/oras/root/repo/tags.go +++ b/cmd/oras/root/repo/tags.go @@ -66,7 +66,7 @@ Example - [Experimental] Show tags associated with a digest: Aliases: []string{"show-tags"}, PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return showTags(cmd, &opts) diff --git a/cmd/oras/root/resolve.go b/cmd/oras/root/resolve.go index 966725fad..b3ea41cc8 100644 --- a/cmd/oras/root/resolve.go +++ b/cmd/oras/root/resolve.go @@ -48,7 +48,7 @@ Example - Resolve digest of the target artifact: Aliases: []string{"digest"}, PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] - return option.Parse(&opts) + return option.Parse(cmd, &opts) }, RunE: func(cmd *cobra.Command, args []string) error { return runResolve(cmd, &opts) diff --git a/cmd/oras/root/tag.go b/cmd/oras/root/tag.go index 7dc923be8..65eda710c 100644 --- a/cmd/oras/root/tag.go +++ b/cmd/oras/root/tag.go @@ -75,7 +75,7 @@ Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI image layout folder 'l PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] opts.targetRefs = args[1:] - if err := option.Parse(&opts); err != nil { + if err := option.Parse(cmd, &opts); err != nil { if inner, ok := err.(*oerrors.Error); ok { if errors.Is(inner, errdef.ErrInvalidReference) { inner.Err = fmt.Errorf("unable to add tag for '%s': %w", opts.RawReference, inner.Err)