diff --git a/cmd/environment_stage_list.go b/cmd/environment_stage_list.go index a9e98b0e..25d221b7 100644 --- a/cmd/environment_stage_list.go +++ b/cmd/environment_stage_list.go @@ -3,7 +3,6 @@ package cmd import ( "context" "encoding/json" - "os" "strconv" "github.com/pterm/pterm" @@ -19,34 +18,45 @@ var environmentStageListCmd = &cobra.Command{ utils.Capture(cmd) tokenType, token, err := utils.GetAccessToken() - if err != nil { - utils.PrintlnError(err) - os.Exit(1) - panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 - } + utils.CheckError(err) client := utils.GetQoveryClient(tokenType, token) _, _, environmentId, err := getOrganizationProjectEnvironmentContextResourcesIds(client) - - if err != nil { - utils.PrintlnError(err) - os.Exit(1) - panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 - } + utils.CheckError(err) stages, _, err := client.DeploymentStageMainCallsAPI.ListEnvironmentDeploymentStage(context.Background(), environmentId).Execute() - - if err != nil { - utils.PrintlnError(err) - os.Exit(1) - panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 - } + utils.CheckError(err) if jsonFlag { utils.Println(getEnvironmentStageJsonOutput(*client, stages.GetResults())) return } + // Collect all skipped services across all stages + var skippedData [][]string + for _, stage := range stages.GetResults() { + for _, service := range stage.GetServices() { + if service.GetIsSkipped() { + skippedData = append(skippedData, []string{ + service.Id, + service.GetServiceType(), + utils.GetServiceNameByIdAndType(client, service.GetServiceId(), service.GetServiceType()), + stage.GetName(), + }) + } + } + } + + // Show skipped services section first + if len(skippedData) > 0 { + pterm.DefaultSection.WithBottomPadding(0).Println("Skipped services (excluded from environment-level deployments)") + utils.Println("") + err = utils.PrintTable([]string{"Id", "Type", "Name", "Stage"}, skippedData) + utils.Println("") + utils.CheckError(err) + } + + // Show each stage with only non-skipped services for _, stage := range stages.GetResults() { pterm.DefaultSection.WithBottomPadding(0).Println("deployment stage " + strconv.Itoa(int(stage.GetDeploymentOrder()+1)) + ": \"" + stage.GetName() + "\"") utils.Println("Stage id: " + stage.GetId()) @@ -58,42 +68,55 @@ var environmentStageListCmd = &cobra.Command{ var data [][]string for _, service := range stage.GetServices() { - data = append(data, []string{ - service.Id, - service.GetServiceType(), - utils.GetServiceNameByIdAndType(client, service.GetServiceId(), service.GetServiceType()), - }) + if !service.GetIsSkipped() { + data = append(data, []string{ + service.Id, + service.GetServiceType(), + utils.GetServiceNameByIdAndType(client, service.GetServiceId(), service.GetServiceType()), + }) + } } - if len(stage.GetServices()) == 0 { - utils.Println("") + if len(data) == 0 { + if len(stage.GetServices()) == 0 { + utils.Println("") + } else { + utils.Println("") + } } else { err = utils.PrintTable([]string{"Id", "Type", "Name"}, data) + utils.CheckError(err) } utils.Println("") - - if err != nil { - utils.PrintlnError(err) - os.Exit(1) - panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 - } } }, } func getEnvironmentStageJsonOutput(client qovery.APIClient, stages []qovery.DeploymentStageResponse) string { + var skippedServices []interface{} var results []interface{} for idx, stage := range stages { var services []interface{} for _, service := range stage.Services { - services = append(services, map[string]interface{}{ - "id": service.ServiceId, - "type": service.ServiceType, - "name": utils.GetServiceNameByIdAndType(&client, service.GetServiceId(), service.GetServiceType()), - }) + entry := map[string]interface{}{ + "id": service.ServiceId, + "type": service.ServiceType, + "name": utils.GetServiceNameByIdAndType(&client, service.GetServiceId(), service.GetServiceType()), + "is_skipped": service.GetIsSkipped(), + } + services = append(services, entry) + + if service.GetIsSkipped() { + skippedServices = append(skippedServices, map[string]interface{}{ + "id": service.ServiceId, + "type": service.ServiceType, + "name": utils.GetServiceNameByIdAndType(&client, service.GetServiceId(), service.GetServiceType()), + "stage": stage.Name, + }) + } } results = append(results, map[string]interface{}{ @@ -105,13 +128,11 @@ func getEnvironmentStageJsonOutput(client qovery.APIClient, stages []qovery.Depl }) } - j, err := json.Marshal(results) - - if err != nil { - utils.PrintlnError(err) - os.Exit(1) - panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 - } + j, err := json.Marshal(map[string]interface{}{ + "skipped_services": skippedServices, + "stages": results, + }) + utils.CheckError(err) return string(j) } diff --git a/cmd/environment_stage_skip.go b/cmd/environment_stage_skip.go new file mode 100644 index 00000000..ce2f383b --- /dev/null +++ b/cmd/environment_stage_skip.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "context" + "errors" + + "github.com/qovery/qovery-cli/utils" + "github.com/qovery/qovery-client-go" + "github.com/spf13/cobra" +) + +var environmentStageSkipCmd = &cobra.Command{ + Use: "skip", + Short: "Skip service from environment-level deployments", + Long: "Mark a service as skipped so it is excluded from environment-level bulk deployments while staying in its current stage. To reverse this, use 'environment stage unskip' or move the service to a different deployment stage, which automatically clears the skipped status.", + Run: func(cmd *cobra.Command, args []string) { + utils.Capture(cmd) + + tokenType, token, err := utils.GetAccessToken() + utils.CheckError(err) + + client := utils.GetQoveryClient(tokenType, token) + _, _, environmentId, err := getOrganizationProjectEnvironmentContextResourcesIds(client) + utils.CheckError(err) + + stages, _, err := client.DeploymentStageMainCallsAPI.ListEnvironmentDeploymentStage(context.Background(), environmentId).Execute() + utils.CheckError(err) + + var service *qovery.DeploymentStageServiceResponse + var currentStageId string + for _, stage := range stages.GetResults() { + service, _ = getServiceByName(client, stage.GetServices(), serviceName) + if service != nil { + currentStageId = stage.GetId() + break + } + } + + if service == nil { + utils.CheckError(errors.New("service not found")) + } + + req := qovery.AttachServiceToDeploymentStageRequest{} + req.SetIsSkipped(true) + + _, _, err = client.DeploymentStageMainCallsAPI. + AttachServiceToDeploymentStage(context.Background(), currentStageId, service.GetServiceId()). + AttachServiceToDeploymentStageRequest(req). + Execute() + utils.CheckError(err) + + utils.Println("Service \"" + serviceName + "\" is now skipped from environment-level deployments") + }, +} + +func init() { + environmentStageCmd.AddCommand(environmentStageSkipCmd) + environmentStageSkipCmd.Flags().StringVarP(&organizationName, "organization", "", "", "Organization Name") + environmentStageSkipCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") + environmentStageSkipCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") + environmentStageSkipCmd.Flags().StringVarP(&serviceName, "name", "n", "", "Service Name") + + _ = environmentStageSkipCmd.MarkFlagRequired("name") +} diff --git a/cmd/environment_stage_unskip.go b/cmd/environment_stage_unskip.go new file mode 100644 index 00000000..a875b269 --- /dev/null +++ b/cmd/environment_stage_unskip.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "context" + "errors" + + "github.com/qovery/qovery-cli/utils" + "github.com/qovery/qovery-client-go" + "github.com/spf13/cobra" +) + +var environmentStageUnskipCmd = &cobra.Command{ + Use: "unskip", + Short: "Unskip service from environment-level deployments", + Long: "Remove the skipped flag from a service so it is included again in environment-level bulk deployments.", + Run: func(cmd *cobra.Command, args []string) { + utils.Capture(cmd) + + tokenType, token, err := utils.GetAccessToken() + utils.CheckError(err) + + client := utils.GetQoveryClient(tokenType, token) + _, _, environmentId, err := getOrganizationProjectEnvironmentContextResourcesIds(client) + utils.CheckError(err) + + stages, _, err := client.DeploymentStageMainCallsAPI.ListEnvironmentDeploymentStage(context.Background(), environmentId).Execute() + utils.CheckError(err) + + var service *qovery.DeploymentStageServiceResponse + var currentStageId string + for _, stage := range stages.GetResults() { + service, _ = getServiceByName(client, stage.GetServices(), serviceName) + if service != nil { + currentStageId = stage.GetId() + break + } + } + + if service == nil { + utils.CheckError(errors.New("service not found")) + } + + req := qovery.AttachServiceToDeploymentStageRequest{} + req.SetIsSkipped(false) + + _, _, err = client.DeploymentStageMainCallsAPI. + AttachServiceToDeploymentStage(context.Background(), currentStageId, service.GetServiceId()). + AttachServiceToDeploymentStageRequest(req). + Execute() + utils.CheckError(err) + + utils.Println("Service \"" + serviceName + "\" is no longer skipped from environment-level deployments") + }, +} + +func init() { + environmentStageCmd.AddCommand(environmentStageUnskipCmd) + environmentStageUnskipCmd.Flags().StringVarP(&organizationName, "organization", "", "", "Organization Name") + environmentStageUnskipCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") + environmentStageUnskipCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") + environmentStageUnskipCmd.Flags().StringVarP(&serviceName, "name", "n", "", "Service Name") + + _ = environmentStageUnskipCmd.MarkFlagRequired("name") +}