diff --git a/cmd/oras/attach.go b/cmd/oras/attach.go index c188a01ab..ed032ef6b 100644 --- a/cmd/oras/attach.go +++ b/cmd/oras/attach.go @@ -65,7 +65,7 @@ Example - Attach file 'hi.txt' and export the pushed manifest to 'manifest.json' `, Args: cobra.MinimumNArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] diff --git a/cmd/oras/blob/delete.go b/cmd/oras/blob/delete.go index 4f0f64118..88bf88c2d 100644 --- a/cmd/oras/blob/delete.go +++ b/cmd/oras/blob/delete.go @@ -59,7 +59,7 @@ Example - Delete a blob and print its descriptor: if opts.OutputDescriptor && !opts.Confirmed { return errors.New("must apply --force to confirm the deletion if the descriptor is outputted") } - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] diff --git a/cmd/oras/blob/fetch.go b/cmd/oras/blob/fetch.go index 8e501c7e2..71dee5e1b 100644 --- a/cmd/oras/blob/fetch.go +++ b/cmd/oras/blob/fetch.go @@ -70,7 +70,7 @@ Example - Fetch the blob, save it to a local file and print the descriptor: return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } - return opts.ReadPassword() + return option.Parse(&opts) }, Aliases: []string{"get"}, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/oras/blob/push.go b/cmd/oras/blob/push.go index f0f9537d4..0ae50e950 100644 --- a/cmd/oras/blob/push.go +++ b/cmd/oras/blob/push.go @@ -81,7 +81,7 @@ Example - Push blob without TLS: return errors.New("`--size` must be provided if the blob is read from stdin") } } - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { return pushBlob(opts) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index 59efbaaf9..7fc485d02 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -68,6 +68,9 @@ Example - Copy the artifact tagged with 'v1' from repository 'localhost:5000/net oras cp --concurrency 6 localhost:5000/net-monitor:v1 localhost:5000/net-monitor-copy:v1,tag2,tag3 `, Args: cobra.ExactArgs(2), + PreRunE: func(cmd *cobra.Command, args []string) error { + return option.Parse(&opts) + }, RunE: func(cmd *cobra.Command, args []string) error { opts.srcRef = args[0] refs := strings.Split(args[1], ",") @@ -88,10 +91,6 @@ Example - Copy the artifact tagged with 'v1' from repository 'localhost:5000/net func runCopy(opts copyOptions) error { ctx, _ := opts.SetLoggerLevel() - targetPlatform, err := opts.Parse() - if err != nil { - return err - } // Prepare source src, err := opts.src.NewRepository(opts.srcRef, opts.Common) @@ -145,8 +144,8 @@ func runCopy(opts copyOptions) error { copyOptions := oras.CopyOptions{ CopyGraphOptions: extendedCopyOptions.CopyGraphOptions, } - if targetPlatform != nil { - copyOptions.WithTargetPlatform(targetPlatform) + if opts.Platform.Platform != nil { + copyOptions.WithTargetPlatform(opts.Platform.Platform) } desc, err = oras.Copy(ctx, src, opts.srcRef, dst, opts.dstRef, copyOptions) } diff --git a/cmd/oras/discover.go b/cmd/oras/discover.go index 5401520b9..3063c1a83 100644 --- a/cmd/oras/discover.go +++ b/cmd/oras/discover.go @@ -64,7 +64,7 @@ Example - Discover referrers with type 'test-artifact' of manifest 'hello:latest `, Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] @@ -87,14 +87,10 @@ func runDiscover(opts discoverOptions) error { if repo.Reference.Reference == "" { return errors.NewErrInvalidReference(repo.Reference) } - targetPlatform, err := opts.Parse() - if err != nil { - return err - } // discover artifacts resolveOpts := oras.DefaultResolveOptions - resolveOpts.TargetPlatform = targetPlatform + resolveOpts.TargetPlatform = opts.Platform.Platform desc, err := oras.Resolve(ctx, repo, repo.Reference.Reference, resolveOpts) if err != nil { return err diff --git a/cmd/oras/internal/option/applier.go b/cmd/oras/internal/option/applier.go index 5803a5f76..f47aba14b 100644 --- a/cmd/oras/internal/option/applier.go +++ b/cmd/oras/internal/option/applier.go @@ -16,8 +16,6 @@ limitations under the License. package option import ( - "reflect" - "github.com/spf13/pflag" ) @@ -31,14 +29,8 @@ type FlagApplier interface { // NOTE: The option argument need to be a pointer to the options, so its value // becomes addressable. func ApplyFlags(optsPtr interface{}, target *pflag.FlagSet) { - v := reflect.ValueOf(optsPtr).Elem() - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - if f.CanSet() { - iface := f.Addr().Interface() - if a, ok := iface.(FlagApplier); ok { - a.ApplyFlags(target) - } - } - } + rangeFields(optsPtr, func(fa FlagApplier) error { + fa.ApplyFlags(target) + return nil + }) } diff --git a/cmd/oras/internal/option/applier_test.go b/cmd/oras/internal/option/applier_test.go new file mode 100644 index 000000000..322287fb9 --- /dev/null +++ b/cmd/oras/internal/option/applier_test.go @@ -0,0 +1,49 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package option_test + +import ( + "testing" + + "github.com/spf13/pflag" + "oras.land/oras/cmd/oras/internal/option" +) + +func (t *Test) ApplyFlags(fs *pflag.FlagSet) { + *t.CntPtr += 1 +} + +func TestApplyFlags(t *testing.T) { + cnt := 0 + type args struct { + Test + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"flags should be applied once", args{Test{CntPtr: &cnt}}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + option.ApplyFlags(&tt.args, nil) + if cnt != 1 { + t.Errorf("Expect ApplyFlags() to be called once but got %v", cnt) + } + }) + } +} diff --git a/cmd/oras/internal/option/parser.go b/cmd/oras/internal/option/parser.go new file mode 100644 index 000000000..43d85dd0e --- /dev/null +++ b/cmd/oras/internal/option/parser.go @@ -0,0 +1,51 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package option + +import ( + "reflect" +) + +// FlagParser parses flags in an option. +type FlagParser interface { + Parse() error +} + +// Parse parses applicable fields of the passed-in option pointer and returns +// error during parsing. +func Parse(optsPtr interface{}) error { + return rangeFields(optsPtr, func(fp FlagParser) error { + return fp.Parse() + }) +} + +// rangeFields goes through all fields of ptr, optionally run fn if a field is +// public AND typed T. +func rangeFields[T any](ptr any, fn func(T) error) error { + v := reflect.ValueOf(ptr).Elem() + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if f.CanSet() { + iface := f.Addr().Interface() + if opts, ok := iface.(T); ok { + if err := fn(opts); err != nil { + return err + } + } + } + } + return nil +} diff --git a/cmd/oras/internal/option/parser_test.go b/cmd/oras/internal/option/parser_test.go new file mode 100644 index 000000000..f54a232e5 --- /dev/null +++ b/cmd/oras/internal/option/parser_test.go @@ -0,0 +1,88 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package option_test + +import ( + "errors" + "testing" + + "oras.land/oras/cmd/oras/internal/option" +) + +type Test struct { + CntPtr *int +} + +func (t *Test) Parse() error { + *t.CntPtr += 1 + if *t.CntPtr == 2 { + return errors.New("should not be tried twice") + } + return nil +} + +func TestParse_once(t *testing.T) { + cnt := 0 + type args struct { + Test + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"parse should be called once", args{Test{CntPtr: &cnt}}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := option.Parse(&tt.args); (err != nil) != tt.wantErr { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + + if cnt != 1 { + t.Errorf("Expect Parse() to be called once but got %v", cnt) + } + }) + } +} + +func TestParse_err(t *testing.T) { + cnt := 0 + type args struct { + Test1 Test + Test2 Test + Test3 Test + Test4 Test + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"parse should be called twice and aborted with error", args{Test{CntPtr: &cnt}, Test{CntPtr: &cnt}, Test{CntPtr: &cnt}, Test{CntPtr: &cnt}}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := option.Parse(&tt.args); (err != nil) != tt.wantErr { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + } + + if cnt != 2 { + t.Errorf("Expect Parse() to be called twice but got %v", cnt) + } + }) + } +} diff --git a/cmd/oras/internal/option/platform.go b/cmd/oras/internal/option/platform.go index 64736487f..fa0fdf363 100644 --- a/cmd/oras/internal/option/platform.go +++ b/cmd/oras/internal/option/platform.go @@ -26,25 +26,26 @@ import ( // Platform option struct. type Platform struct { - Platform string + platform string + Platform *ocispec.Platform } // ApplyFlags applies flags to a command flag set. func (opts *Platform) ApplyFlags(fs *pflag.FlagSet) { - fs.StringVarP(&opts.Platform, "platform", "", "", "request platform in the form of `os[/arch][/variant][:os_version]`") + fs.StringVarP(&opts.platform, "platform", "", "", "request platform in the form of `os[/arch][/variant][:os_version]`") } // parse parses the input platform flag to an oci platform type. -func (opts *Platform) Parse() (*ocispec.Platform, error) { - if opts.Platform == "" { - return nil, nil +func (opts *Platform) Parse() error { + if opts.platform == "" { + return nil } // OS[/Arch[/Variant]][:OSVersion] // If Arch is not provided, will use GOARCH instead var platformStr string var p ocispec.Platform - platformStr, p.OSVersion, _ = strings.Cut(opts.Platform, ":") + platformStr, p.OSVersion, _ = strings.Cut(opts.platform, ":") parts := strings.Split(platformStr, "/") switch len(parts) { case 3: @@ -55,14 +56,15 @@ func (opts *Platform) Parse() (*ocispec.Platform, error) { case 1: p.Architecture = runtime.GOARCH default: - return nil, fmt.Errorf("failed to parse platform %q: expected format os[/arch[/variant]]", opts.Platform) + return fmt.Errorf("failed to parse platform %q: expected format os[/arch[/variant]]", opts.platform) } p.OS = parts[0] if p.OS == "" { - return nil, fmt.Errorf("invalid platform: OS cannot be empty") + return fmt.Errorf("invalid platform: OS cannot be empty") } if p.Architecture == "" { - return nil, fmt.Errorf("invalid platform: Architecture cannot be empty") + return fmt.Errorf("invalid platform: Architecture cannot be empty") } - return &p, nil + opts.Platform = &p + return nil } diff --git a/cmd/oras/internal/option/platform_test.go b/cmd/oras/internal/option/platform_test.go index 246997654..8864bbede 100644 --- a/cmd/oras/internal/option/platform_test.go +++ b/cmd/oras/internal/option/platform_test.go @@ -3,9 +3,7 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,8 +25,8 @@ import ( func TestPlatform_ApplyFlags(t *testing.T) { var test struct{ Platform } ApplyFlags(&test, pflag.NewFlagSet("oras-test", pflag.ExitOnError)) - if test.Platform.Platform != "" { - t.Fatalf("expecting platform to be empty but got: %v", test.Platform.Platform) + if test.Platform.platform != "" { + t.Fatalf("expecting platform to be empty but got: %v", test.Platform.platform) } } @@ -37,15 +35,15 @@ func TestPlatform_Parse_err(t *testing.T) { name string opts *Platform }{ - {name: "empty arch 1", opts: &Platform{"os/"}}, - {name: "empty arch 2", opts: &Platform{"os//variant"}}, - {name: "empty os", opts: &Platform{"/arch"}}, - {name: "empty os with variant", opts: &Platform{"/arch/variant"}}, - {name: "trailing slash", opts: &Platform{"os/arch/variant/llama"}}, + {name: "empty arch 1", opts: &Platform{"os/", nil}}, + {name: "empty arch 2", opts: &Platform{"os//variant", nil}}, + {name: "empty os", opts: &Platform{"/arch", nil}}, + {name: "empty os with variant", opts: &Platform{"/arch/variant", nil}}, + {name: "trailing slash", opts: &Platform{"os/arch/variant/llama", nil}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := tt.opts.Parse() + err := tt.opts.Parse() if err == nil { t.Errorf("Platform.Parse() error = %v, wantErr %v", err, true) return @@ -60,21 +58,20 @@ func TestPlatform_Parse(t *testing.T) { opts *Platform want *ocispec.Platform }{ - {name: "empty", opts: &Platform{""}, want: nil}, - {name: "default arch", opts: &Platform{"os"}, want: &ocispec.Platform{OS: "os", Architecture: runtime.GOARCH}}, - {name: "os&arch", opts: &Platform{"os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, - {name: "empty variant", opts: &Platform{"os/aRcH/"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: ""}}, - {name: "os&arch&variant", opts: &Platform{"os/aRcH/vAriAnt"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt"}}, - {name: "os version", opts: &Platform{"os/aRcH/vAriAnt:osversion"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt", OSVersion: "osversion"}}, - {name: "long os version", opts: &Platform{"os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, + {name: "empty", opts: &Platform{platform: ""}, want: nil}, + {name: "default arch", opts: &Platform{platform: "os"}, want: &ocispec.Platform{OS: "os", Architecture: runtime.GOARCH}}, + {name: "os&arch", opts: &Platform{platform: "os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, + {name: "empty variant", opts: &Platform{platform: "os/aRcH/"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: ""}}, + {name: "os&arch&variant", opts: &Platform{platform: "os/aRcH/vAriAnt"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt"}}, + {name: "os version", opts: &Platform{platform: "os/aRcH/vAriAnt:osversion"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt", OSVersion: "osversion"}}, + {name: "long os version", opts: &Platform{platform: "os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.opts.Parse() - if err != nil { + if err := tt.opts.Parse(); err != nil { t.Errorf("Platform.Parse() error = %v", err) - return } + got := tt.opts.Platform if !reflect.DeepEqual(got, tt.want) { t.Errorf("Platform.Parse() = %v, want %v", got, tt.want) } diff --git a/cmd/oras/internal/option/remote.go b/cmd/oras/internal/option/remote.go index 118d75559..0391cb3fb 100644 --- a/cmd/oras/internal/option/remote.go +++ b/cmd/oras/internal/option/remote.go @@ -87,8 +87,13 @@ func (opts *Remote) ApplyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description } } -// ReadPassword tries to read password with optional cmd prompt. -func (opts *Remote) ReadPassword() (err error) { +// Parse tries to read password with optional cmd prompt. +func (opts *Remote) Parse() error { + return opts.readPassword() +} + +// readPassword tries to read password with optional cmd prompt. +func (opts *Remote) readPassword() (err error) { if opts.Password != "" { fmt.Fprintln(os.Stderr, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.") } else if opts.PasswordFromStdin { diff --git a/cmd/oras/login.go b/cmd/oras/login.go index e896d2252..b102444a8 100644 --- a/cmd/oras/login.go +++ b/cmd/oras/login.go @@ -61,7 +61,7 @@ Example - Log in with username and password in an interactive terminal and no TL `, Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.Hostname = args[0] @@ -130,9 +130,9 @@ func runLogin(opts loginOptions) (err error) { return nil } -func readLine(prompt string, slient bool) (string, error) { +func readLine(prompt string, silent bool) (string, error) { fmt.Print(prompt) - if slient { + if silent { fd := os.Stdin.Fd() state, err := term.SaveState(fd) if err != nil { @@ -147,7 +147,7 @@ func readLine(prompt string, slient bool) (string, error) { if err != nil { return "", err } - if slient { + if silent { fmt.Println() } diff --git a/cmd/oras/manifest/delete.go b/cmd/oras/manifest/delete.go index 568eefd91..09751eb2e 100644 --- a/cmd/oras/manifest/delete.go +++ b/cmd/oras/manifest/delete.go @@ -63,7 +63,7 @@ Example - Delete a manifest by digest 'sha256:99e4703fbf30916f549cd6bfa9cdbab614 if opts.OutputDescriptor && !opts.Confirmed { return errors.New("must apply --force to confirm the deletion if the descriptor is outputted") } - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(_ *cobra.Command, args []string) error { opts.targetRef = args[0] diff --git a/cmd/oras/manifest/fetch.go b/cmd/oras/manifest/fetch.go index 7173a5c42..5715141aa 100644 --- a/cmd/oras/manifest/fetch.go +++ b/cmd/oras/manifest/fetch.go @@ -69,8 +69,7 @@ Example - Fetch manifest with prettified json result: if opts.outputPath == "-" && opts.OutputDescriptor { return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } - - return opts.ReadPassword() + return option.Parse(&opts) }, Aliases: []string{"get"}, RunE: func(cmd *cobra.Command, args []string) error { @@ -98,11 +97,6 @@ func fetchManifest(opts fetchOptions) (fetchErr error) { } repo.ManifestMediaTypes = opts.mediaTypes - targetPlatform, err := opts.Parse() - if err != nil { - return err - } - src, err := opts.CachedTarget(repo) if err != nil { return err @@ -112,7 +106,7 @@ func fetchManifest(opts fetchOptions) (fetchErr error) { if opts.OutputDescriptor && opts.outputPath == "" { // fetch manifest descriptor only fetchOpts := oras.DefaultResolveOptions - fetchOpts.TargetPlatform = targetPlatform + fetchOpts.TargetPlatform = opts.Platform.Platform desc, err = oras.Resolve(ctx, src, opts.targetRef, fetchOpts) if err != nil { return err @@ -121,7 +115,7 @@ func fetchManifest(opts fetchOptions) (fetchErr error) { // fetch manifest content var content []byte fetchOpts := oras.DefaultFetchBytesOptions - fetchOpts.TargetPlatform = targetPlatform + fetchOpts.TargetPlatform = opts.Platform.Platform desc, content, err = oras.FetchBytes(ctx, src, opts.targetRef, fetchOpts) if err != nil { return err diff --git a/cmd/oras/manifest/fetch_config.go b/cmd/oras/manifest/fetch_config.go index d57fc62ff..542fa81e7 100644 --- a/cmd/oras/manifest/fetch_config.go +++ b/cmd/oras/manifest/fetch_config.go @@ -76,8 +76,7 @@ Example - Fetch and print the prettified descriptor of the config: if opts.outputPath == "-" && opts.OutputDescriptor { return errors.New("`--output -` cannot be used with `--descriptor` at the same time") } - - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] @@ -102,18 +101,13 @@ func fetchConfig(opts fetchConfigOptions) (fetchErr error) { return oerrors.NewErrInvalidReference(repo.Reference) } - targetPlatform, err := opts.Parse() - if err != nil { - return err - } - src, err := opts.CachedTarget(repo) if err != nil { return err } // fetch config descriptor - configDesc, err := fetchConfigDesc(ctx, src, opts.targetRef, targetPlatform) + configDesc, err := fetchConfigDesc(ctx, src, opts.targetRef, opts.Platform.Platform) if err != nil { return err } diff --git a/cmd/oras/manifest/push.go b/cmd/oras/manifest/push.go index 6249cef30..0c172ade5 100644 --- a/cmd/oras/manifest/push.go +++ b/cmd/oras/manifest/push.go @@ -81,7 +81,7 @@ Example - Push a manifest to repository 'locahost:5000/hello' and tag with 'tag1 if opts.fileRef == "-" && opts.PasswordFromStdin { return errors.New("`-` read file from input and `--password-stdin` read password from input cannot be both used") } - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(_ *cobra.Command, args []string) error { refs := strings.Split(args[0], ",") diff --git a/cmd/oras/pull.go b/cmd/oras/pull.go index c57cc6eb2..0288b28af 100644 --- a/cmd/oras/pull.go +++ b/cmd/oras/pull.go @@ -79,7 +79,7 @@ Example - Pull all files with concurrency level tuned: `, Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] @@ -99,10 +99,6 @@ Example - Pull all files with concurrency level tuned: func runPull(opts pullOptions) error { var printed sync.Map - targetPlatform, err := opts.Parse() - if err != nil { - return err - } repo, err := opts.NewRepository(opts.targetRef, opts.Common) if err != nil { return err @@ -125,8 +121,8 @@ func runPull(opts pullOptions) error { return err } } - if targetPlatform != nil { - copyOptions.WithTargetPlatform(targetPlatform) + if opts.Platform.Platform != nil { + copyOptions.WithTargetPlatform(opts.Platform.Platform) } var getConfigOnce sync.Once copyOptions.FindSuccessors = func(ctx context.Context, fetcher content.Fetcher, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { diff --git a/cmd/oras/push.go b/cmd/oras/push.go index e43c28d07..7a9f15a02 100644 --- a/cmd/oras/push.go +++ b/cmd/oras/push.go @@ -96,7 +96,7 @@ Example - Push file "hi.txt" with multiple tags and concurrency level tuned: if opts.artifactType != "" && opts.manifestConfigRef != "" { return errors.New("--artifact-type and --config cannot both be provided") } - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { refs := strings.Split(args[0], ",") diff --git a/cmd/oras/repository/ls.go b/cmd/oras/repository/ls.go index 40963b404..4355ba307 100644 --- a/cmd/oras/repository/ls.go +++ b/cmd/oras/repository/ls.go @@ -47,7 +47,7 @@ Example - List the repositories under the registry that include values lexically Args: cobra.ExactArgs(1), Aliases: []string{"list"}, PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.hostname = args[0] diff --git a/cmd/oras/repository/tags.go b/cmd/oras/repository/tags.go index abc6cc202..4cbf7287d 100644 --- a/cmd/oras/repository/tags.go +++ b/cmd/oras/repository/tags.go @@ -53,7 +53,7 @@ Example - Show tags of the target repository that include values lexically after Args: cobra.ExactArgs(1), Aliases: []string{"show-tags"}, PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] diff --git a/cmd/oras/tag/tag.go b/cmd/oras/tag/tag.go index c4fa8a336..bcad5f796 100644 --- a/cmd/oras/tag/tag.go +++ b/cmd/oras/tag/tag.go @@ -55,7 +55,7 @@ Example - Tag the manifest 'v1.0.1' in 'localhost:5000/hello' to 'v1.0.1', 'v1.0 `, Args: cobra.MinimumNArgs(2), PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.ReadPassword() + return option.Parse(&opts) }, RunE: func(_ *cobra.Command, args []string) error { opts.srcRef = args[0]