diff --git a/cmd/oras/internal/option/target.go b/cmd/oras/internal/option/target.go index 95194f6ac..a2f0e6995 100644 --- a/cmd/oras/internal/option/target.go +++ b/cmd/oras/internal/option/target.go @@ -53,7 +53,7 @@ type Target struct { // - registry and repository for the remote target Path string - isOCILayout bool + IsOCILayout bool } // ApplyFlags applies flags to a command flag set for unary target @@ -78,7 +78,7 @@ func (opts *Target) AnnotatedReference() string { // the full form is not implemented until a new type comes in. func (opts *Target) applyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description string) { flagPrefix, notePrefix := applyPrefix(prefix, description) - fs.BoolVarP(&opts.isOCILayout, flagPrefix+"oci-layout", "", false, "set "+notePrefix+"target as an OCI image layout") + fs.BoolVarP(&opts.IsOCILayout, flagPrefix+"oci-layout", "", false, "set "+notePrefix+"target as an OCI image layout") } // ApplyFlagsWithPrefix applies flags to a command flag set with a prefix string. @@ -91,7 +91,7 @@ func (opts *Target) ApplyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description // Parse gets target options from user input. func (opts *Target) Parse() error { switch { - case opts.isOCILayout: + case opts.IsOCILayout: opts.Type = TargetTypeOCILayout if len(opts.headerFlags) != 0 { return errors.New("custom header flags cannot be used on an OCI image layout target") diff --git a/cmd/oras/internal/option/target_test.go b/cmd/oras/internal/option/target_test.go index d01eeadde..fd13221ae 100644 --- a/cmd/oras/internal/option/target_test.go +++ b/cmd/oras/internal/option/target_test.go @@ -20,7 +20,7 @@ import ( ) func TestTarget_Parse_oci(t *testing.T) { - opts := Target{isOCILayout: true} + opts := Target{IsOCILayout: true} if err := opts.Parse(); err != nil { t.Errorf("Target.Parse() error = %v", err) @@ -31,7 +31,7 @@ func TestTarget_Parse_oci(t *testing.T) { } func TestTarget_Parse_remote(t *testing.T) { - opts := Target{isOCILayout: false} + opts := Target{IsOCILayout: false} if err := opts.Parse(); err != nil { t.Errorf("Target.Parse() error = %v", err) } diff --git a/cmd/oras/root/tag.go b/cmd/oras/root/tag.go index e4f65f830..dcc857f1f 100644 --- a/cmd/oras/root/tag.go +++ b/cmd/oras/root/tag.go @@ -17,6 +17,7 @@ package root import ( "context" + "errors" "fmt" "github.com/spf13/cobra" @@ -58,7 +59,20 @@ Example - Tag the manifest 'v1.0.1' in 'localhost:5000/hello' to 'v1.0.1', 'v1.0 Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI image layout folder 'layout-dir': oras tag layout-dir:v1.0.1 v1.0.2 `, - Args: oerrors.CheckArgs(argument.AtLeast(1), "the to-be-retage artifact and the tags to be added"), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) > 0 && (args[0] == "list" || args[0] == "ls") { + container := "a repository" + if opts.IsOCILayout { + container = "an OCI image layout" + } + return &oerrors.Error{ + Err: errors.New(`there is no "list" sub-command for "oras tag" command`), + Usage: fmt.Sprintf("%s %s", cmd.CommandPath(), cmd.Use), + Recommendation: fmt.Sprintf(`If you want to list available tags in %s, use "oras repo tags"`, container), + } + } + return oerrors.CheckArgs(argument.AtLeast(1), "the artifact to be retagged and the tags to be added")(cmd, args) + }, PreRunE: func(cmd *cobra.Command, args []string) error { opts.RawReference = args[0] if _, err := registry.ParseReference(opts.RawReference); err != nil { diff --git a/test/e2e/suite/command/tag.go b/test/e2e/suite/command/tag.go index 0daf4e840..3c98dcea3 100644 --- a/test/e2e/suite/command/tag.go +++ b/test/e2e/suite/command/tag.go @@ -36,10 +36,6 @@ var _ = Describe("ORAS beginners:", func() { ORAS("tag", RegistryRef(ZOTHost, ImageRepo, "i-dont-think-this-tag-exists"), "tagged").ExpectFailure().MatchErrKeyWords("Error:").Exec() }) - It("should fail when provided invalid reference", func() { - ORAS("tag", "list", "tagged").ExpectFailure().MatchErrKeyWords("Error:", "'list'").Exec() - }) - It("should fail and show detailed error description if no argument provided", func() { err := ORAS("tag").ExpectFailure().Exec().Err gomega.Expect(err).Should(gbytes.Say("Error")) @@ -47,6 +43,11 @@ var _ = Describe("ORAS beginners:", func() { gomega.Expect(err).Should(gbytes.Say("\n")) gomega.Expect(err).Should(gbytes.Say(`Run "oras tag -h"`)) }) + + It("should fail with suggestion if calling with `tag list`", func() { + ORAS("tag", "list").ExpectFailure().MatchErrKeyWords("Error:", `there is no "list" sub-command for "oras tag" command`, "repository", "oras repo tags").Exec() + ORAS("tag", "list", Flags.Layout).ExpectFailure().MatchErrKeyWords("Error:", `there is no "list" sub-command for "oras tag" command`, "OCI Image Layout", "oras repo tags").Exec() + }) }) })