diff --git a/cmd/cloud.go b/cmd/cloud.go index 02df3077..4beb803f 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -69,9 +69,12 @@ type startAgent struct { } type startRequest struct { - Agents []startAgent `json:"agents"` - Resources *Resources `json:"resources,omitempty"` - Metadata *deployer.Metadata `json:"metadata,omitempty"` + Agents []startAgent `json:"agents"` + Resources *Resources `json:"resources,omitempty"` + Metadata *deployer.Metadata `json:"metadata,omitempty"` + Tags []string `json:"tags,omitempty"` + TagDescription string `json:"description,omitempty"` + TagMessage string `json:"message,omitempty"` } func ShowNewProjectImport(ctx context.Context, logger logger.Logger, cmd *cobra.Command, apiUrl, apikey, projectId string, project *project.Project, dir string, isImport bool) { @@ -153,6 +156,18 @@ Examples: ciMessage, _ := cmd.Flags().GetString("ci-message") ciGitProvider, _ := cmd.Flags().GetString("ci-git-provider") ciLogsUrl, _ := cmd.Flags().GetString("ci-logs-url") + tags, _ := cmd.Flags().GetStringArray("tag") + description, _ := cmd.Flags().GetString("description") + message, _ := cmd.Flags().GetString("message") + + // remove duplicates and empty strings + tags = util.RemoveDuplicates(tags) + tags = util.RemoveEmpty(tags) + + // If no tags are provided, default to ["latest"] + if len(tags) == 0 { + tags = []string{"latest"} + } deploymentConfig := project.NewDeploymentConfig() client := util.NewAPIClient(ctx, logger, apiUrl, token) @@ -400,6 +415,10 @@ Examples: }, } + startRequest.Tags = tags + startRequest.TagDescription = description + startRequest.TagMessage = message + // Start deployment if err := client.Do("PUT", fmt.Sprintf("/cli/deploy/start/%s%s", theproject.ProjectId, deploymentId), startRequest, &startResponse); err != nil { errsystem.New(errsystem.ErrDeployProject, err, @@ -664,6 +683,9 @@ func init() { cloudDeployCmd.Flags().String("ci-message", "", "Used to set the commit message for your deployment metadata") cloudDeployCmd.Flags().String("ci-git-provider", "", "Used to set the git provider for your deployment metadata") cloudDeployCmd.Flags().String("ci-logs-url", "", "Used to set the CI logs URL for your deployment metadata") + cloudDeployCmd.Flags().StringArray("tag", nil, "Tag(s) to associate with this deployment (can be specified multiple times)") + cloudDeployCmd.Flags().String("description", "", "Description for the deployment") + cloudDeployCmd.Flags().String("message", "", "A shorter description for the deployment") cloudDeployCmd.Flags().MarkHidden("deploymentId") cloudDeployCmd.Flags().MarkHidden("ci") diff --git a/internal/mcp/deploy.go b/internal/mcp/deploy.go index 50610775..4cde417b 100644 --- a/internal/mcp/deploy.go +++ b/internal/mcp/deploy.go @@ -7,7 +7,10 @@ import ( ) type DeployArguments struct { - Directory string `json:"directory" jsonschema:"required,description=The directory where the project is located"` + Directory string `json:"directory" jsonschema:"required,description=The directory where the project is located"` + Tags []string `json:"tags,omitempty" jsonschema:"description=Tags to associate with this deployment"` + Description string `json:"description,omitempty" jsonschema:"description=Description for the deployment tag(s)"` + Message string `json:"message,omitempty" jsonschema:"description=Message for the deployment tag(s)"` } func init() { @@ -22,7 +25,17 @@ func init() { if resp := ensureProject(&c); resp != nil { return resp, nil } - result, err := execCommand(ctx, c.ProjectDir, "deploy", "--format", "json", "--dir", c.ProjectDir) + argsList := []string{"deploy", "--format", "json", "--dir", c.ProjectDir} + for _, tag := range args.Tags { + argsList = append(argsList, "--tag", tag) + } + if args.Description != "" { + argsList = append(argsList, "--description", args.Description) + } + if args.Message != "" { + argsList = append(argsList, "--message", args.Message) + } + result, err := execCommand(ctx, c.ProjectDir, argsList[0], argsList[1:]...) if err != nil { return nil, err } diff --git a/internal/util/slice.go b/internal/util/slice.go new file mode 100644 index 00000000..fa6fd987 --- /dev/null +++ b/internal/util/slice.go @@ -0,0 +1,24 @@ +package util + +func RemoveDuplicates[T comparable](slice []T) []T { + uniqueMap := make(map[T]bool) + uniqueSlice := []T{} + for _, item := range slice { + if !uniqueMap[item] { + uniqueMap[item] = true + uniqueSlice = append(uniqueSlice, item) + } + } + return uniqueSlice +} + +func RemoveEmpty[T comparable](slice []T) []T { + var zero T + nonEmptySlice := []T{} + for _, item := range slice { + if item != zero { + nonEmptySlice = append(nonEmptySlice, item) + } + } + return nonEmptySlice +} diff --git a/internal/util/slice_test.go b/internal/util/slice_test.go new file mode 100644 index 00000000..91ebe3ca --- /dev/null +++ b/internal/util/slice_test.go @@ -0,0 +1,60 @@ +package util + +import ( + "reflect" + "testing" +) + +func TestRemoveDuplicates_String(t *testing.T) { + input := []string{"a", "b", "a", "c", "b", "d"} + want := []string{"a", "b", "c", "d"} + got := RemoveDuplicates(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveDuplicates(strings) = %v, want %v", got, want) + } +} + +func TestRemoveDuplicates_Int(t *testing.T) { + input := []int{1, 2, 2, 3, 1, 4} + want := []int{1, 2, 3, 4} + got := RemoveDuplicates(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveDuplicates(ints) = %v, want %v", got, want) + } +} + +func TestRemoveEmpty_String(t *testing.T) { + input := []string{"a", "", "b", "", "c"} + want := []string{"a", "b", "c"} + got := RemoveEmpty(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveEmpty(strings) = %v, want %v", got, want) + } +} + +func TestRemoveEmpty_Int(t *testing.T) { + input := []int{0, 1, 2, 0, 3} + want := []int{1, 2, 3} + got := RemoveEmpty(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveEmpty(ints) = %v, want %v", got, want) + } +} + +func TestRemoveDuplicates_AlreadyUnique(t *testing.T) { + input := []string{"x", "y", "z"} + want := []string{"x", "y", "z"} + got := RemoveDuplicates(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveDuplicates(already unique) = %v, want %v", got, want) + } +} + +func TestRemoveEmpty_NoEmpty(t *testing.T) { + input := []int{1, 2, 3} + want := []int{1, 2, 3} + got := RemoveEmpty(input) + if !reflect.DeepEqual(got, want) { + t.Errorf("RemoveEmpty(no empty) = %v, want %v", got, want) + } +}