diff --git a/internal/cli/release.go b/internal/cli/release.go index fb8d678..56c5697 100644 --- a/internal/cli/release.go +++ b/internal/cli/release.go @@ -312,14 +312,36 @@ func releaseDeleteCmd() *cobra.Command { return cmd } +// uploadTagAndFile resolves the release tag and file for "release upload". The tag comes from --tag with the file as +// the only argument, or from the first of two positional arguments. +func uploadTagAndFile(flagTag string, args []string) (tag, file string, err error) { + if flagTag != "" { + if len(args) != 1 { + return "", "", fmt.Errorf("with --tag, expected one argument, got %d", len(args)) + } + return flagTag, args[0], nil + } + if len(args) != releaseAssetArgs { + return "", "", fmt.Errorf("expected (or --tag ), got %d argument(s)", len(args)) + } + return args[0], args[1], nil +} + func releaseUploadCmd() *cobra.Command { - return &cobra.Command{ - Use: "upload ", + var flagTag string + + cmd := &cobra.Command{ + Use: "upload ( | --tag )", Short: "Upload an asset to a release", - Args: cobra.ExactArgs(releaseAssetArgs), + Args: func(cmd *cobra.Command, args []string) error { + _, _, err := uploadTagAndFile(flagTag, args) + return err + }, RunE: func(cmd *cobra.Command, args []string) error { - tag := args[0] - filePath := args[1] + tag, filePath, err := uploadTagAndFile(flagTag, args) + if err != nil { + return err + } forge, owner, repoName, _, err := resolve.Repo(flagRepo, flagForgeType) if err != nil { @@ -349,6 +371,9 @@ func releaseUploadCmd() *cobra.Command { return nil }, } + + cmd.Flags().StringVar(&flagTag, "tag", "", "Tag name of the release (alternative to the positional )") + return cmd } func releaseDownloadCmd() *cobra.Command { diff --git a/internal/cli/release_test.go b/internal/cli/release_test.go index ee682ad..0565b8f 100644 --- a/internal/cli/release_test.go +++ b/internal/cli/release_test.go @@ -68,3 +68,47 @@ func TestReleaseSubcommands(t *testing.T) { } } } + +func TestReleaseUploadTagFlag(t *testing.T) { + cmd := releaseUploadCmd() + + f := cmd.Flags().Lookup("tag") + if f == nil { + t.Fatal("expected --tag flag to exist") + } + if f.Value.Type() != "string" { + t.Errorf("expected --tag to be string, got %s", f.Value.Type()) + } +} + +func TestUploadTagAndFile(t *testing.T) { + tests := []struct { + name string + flagTag string + args []string + wantTag string + wantFile string + wantErr bool + }{ + {name: "positional tag and file", args: []string{"v1.0.0", "app.zip"}, wantTag: "v1.0.0", wantFile: "app.zip"}, + {name: "tag flag and file", flagTag: "v1.0.0", args: []string{"app.zip"}, wantTag: "v1.0.0", wantFile: "app.zip"}, + {name: "tag flag with positional tag too", flagTag: "v1.0.0", args: []string{"v1.0.0", "app.zip"}, wantErr: true}, + {name: "no tag at all", args: []string{"app.zip"}, wantErr: true}, + {name: "no arguments", wantErr: true}, + {name: "tag flag without a file", flagTag: "v1.0.0", wantErr: true}, + {name: "too many positional arguments", args: []string{"v1.0.0", "a.zip", "b.zip"}, wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tag, file, err := uploadTagAndFile(tt.flagTag, tt.args) + if (err != nil) != tt.wantErr { + t.Fatalf("uploadTagAndFile(%q, %v) error = %v, wantErr %v", tt.flagTag, tt.args, err, tt.wantErr) + } + if tag != tt.wantTag || file != tt.wantFile { + t.Errorf("uploadTagAndFile(%q, %v) = (%q, %q), want (%q, %q)", + tt.flagTag, tt.args, tag, file, tt.wantTag, tt.wantFile) + } + }) + } +}