From f8be8829d56bb66b43a3ec189cd6d50694a6471c Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 29 Dec 2023 15:01:52 +0800 Subject: [PATCH] chore(ux): improve error output for unknown spec flag (#1221) Signed-off-by: Billy Zha --- cmd/oras/internal/option/remote.go | 5 +- cmd/oras/internal/option/spec.go | 98 +++++++++++++++++++++++------- test/e2e/internal/utils/flags.go | 10 ++- test/e2e/suite/command/attach.go | 12 ++++ test/e2e/suite/command/push.go | 51 +++++++++++++++- 5 files changed, 144 insertions(+), 32 deletions(-) diff --git a/cmd/oras/internal/option/remote.go b/cmd/oras/internal/option/remote.go index 26c01fc90..5547569e5 100644 --- a/cmd/oras/internal/option/remote.go +++ b/cmd/oras/internal/option/remote.go @@ -116,10 +116,7 @@ func (opts *Remote) Parse() error { if err := opts.parseCustomHeaders(); err != nil { return err } - if err := opts.readPassword(); err != nil { - return err - } - return opts.DistributionSpec.Parse() + return opts.readPassword() } // readPassword tries to read password with optional cmd prompt. diff --git a/cmd/oras/internal/option/spec.go b/cmd/oras/internal/option/spec.go index 965eb929b..4a51a820d 100644 --- a/cmd/oras/internal/option/spec.go +++ b/cmd/oras/internal/option/spec.go @@ -17,9 +17,11 @@ package option import ( "fmt" + "strings" "github.com/spf13/pflag" "oras.land/oras-go/v2" + oerrors "oras.land/oras/cmd/oras/internal/errors" ) const ( @@ -27,59 +29,109 @@ const ( ImageSpecV1_0 = "v1.0" ) -// ImageSpec option struct. +const ( + DistributionSpecReferrersTagV1_1 = "v1.1-referrers-tag" + DistributionSpecReferrersAPIV1_1 = "v1.1-referrers-api" +) + +// ImageSpec option struct which implements pflag.Value interface. type ImageSpec struct { flag string PackVersion oras.PackManifestVersion } -// Parse parses flags into the option. -func (opts *ImageSpec) Parse() error { - switch opts.flag { +// Set validates and sets the flag value from a string argument. +func (is *ImageSpec) Set(value string) error { + is.flag = value + switch value { case ImageSpecV1_1: - opts.PackVersion = oras.PackManifestVersion1_1_RC4 + is.PackVersion = oras.PackManifestVersion1_1_RC4 case ImageSpecV1_0: - opts.PackVersion = oras.PackManifestVersion1_0 + is.PackVersion = oras.PackManifestVersion1_0 default: - return fmt.Errorf("unknown image specification flag: %q", opts.flag) + return &oerrors.Error{ + Err: fmt.Errorf("unknown image specification flag: %s", value), + Recommendation: fmt.Sprintf("Available options: %s", is.Options()), + } } return nil } +// Type returns the string value of the inner flag. +func (is *ImageSpec) Type() string { + return "string" +} + +// Options returns the string of usable options for the flag. +func (is *ImageSpec) Options() string { + return strings.Join([]string{ + ImageSpecV1_1, + ImageSpecV1_0, + }, ", ") +} + +// String returns the string representation of the flag. +func (is *ImageSpec) String() string { + return is.flag +} + // ApplyFlags applies flags to a command flag set. -func (opts *ImageSpec) ApplyFlags(fs *pflag.FlagSet) { - fs.StringVar(&opts.flag, "image-spec", ImageSpecV1_1, fmt.Sprintf("[Experimental] specify manifest type for building artifact. options: %s, %s", ImageSpecV1_1, ImageSpecV1_0)) +func (is *ImageSpec) ApplyFlags(fs *pflag.FlagSet) { + // default to v1.1-rc.4 + is.PackVersion = oras.PackManifestVersion1_1_RC4 + defaultFlag := ImageSpecV1_1 + fs.Var(is, "image-spec", fmt.Sprintf(`[Experimental] specify manifest type for building artifact. Options: %s (default %q)`, is.Options(), defaultFlag)) } -// DistributionSpec option struct. +// DistributionSpec option struct which implements pflag.Value interface. type DistributionSpec struct { // ReferrersAPI indicates the preference of the implementation of the Referrers API. // Set to true for referrers API, false for referrers tag scheme, and nil for auto fallback. ReferrersAPI *bool // specFlag should be provided in form of`--