From 9429c0e7d8ab543029e7bf128673c41b6753c3ef Mon Sep 17 00:00:00 2001 From: Zachary Greenfield Date: Tue, 13 Jan 2026 14:01:12 -0600 Subject: [PATCH 1/4] feat: Add ability to get latest prod deployment --- internal/clioptions/clioptions.go | 4 + internal/cmd/runtime.go | 2 + internal/cmd/runtime/deployments.go | 114 +++++++++++++++++++++++ internal/cmd/runtime/deployments_test.go | 87 +++++++++++++++++ internal/resources/responses.go | 15 +++ 5 files changed, 222 insertions(+) create mode 100644 internal/cmd/runtime/deployments.go create mode 100644 internal/cmd/runtime/deployments_test.go diff --git a/internal/clioptions/clioptions.go b/internal/clioptions/clioptions.go index 53516a88..9ab224a1 100644 --- a/internal/clioptions/clioptions.go +++ b/internal/clioptions/clioptions.go @@ -329,6 +329,10 @@ func (o *CLIOptions) AddImportFlags(flags *pflag.FlagSet) { flags.StringVar(&o.InputFilePath, "filename", "", "file or folder containing the resources to import") } +func (o *CLIOptions) AddDeployLatestFlags(flags *pflag.FlagSet) { + flags.StringVar(&o.Environment, "environment", "", "the environment scope for the command") +} + func (o *CLIOptions) ToRESTConfig() (*client.Config, error) { locator := cliconfig.NewConfigPathLocator() locator.ExplicitPath = o.MiactlConfig diff --git a/internal/cmd/runtime.go b/internal/cmd/runtime.go index 5eefbd92..6cd441d9 100644 --- a/internal/cmd/runtime.go +++ b/internal/cmd/runtime.go @@ -23,6 +23,7 @@ import ( "github.com/mia-platform/miactl/internal/cmd/events" "github.com/mia-platform/miactl/internal/cmd/logs" runtimeresources "github.com/mia-platform/miactl/internal/cmd/resources" + "github.com/mia-platform/miactl/internal/cmd/runtime" ) func RuntimeCmd(o *clioptions.CLIOptions) *cobra.Command { @@ -51,6 +52,7 @@ the resources generated, like Pods, Cronjobs and logs. environments.EnvironmentCmd(o), events.Command(o), logs.Command(o), + runtime.NewDeploymentsCmd(o), ) return cmd diff --git a/internal/cmd/runtime/deployments.go b/internal/cmd/runtime/deployments.go new file mode 100644 index 00000000..b7df20a7 --- /dev/null +++ b/internal/cmd/runtime/deployments.go @@ -0,0 +1,114 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "context" + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/mia-platform/miactl/internal/client" + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" +) + +const ( + deploymentsLatestEndpointTemplate = "/api/deploy/projects/%s/deployment/" +) + +func NewDeploymentsCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "deployments", + Short: "Manage deployments of the project", + } + + cmd.AddCommand( + newLatestDeploymentCmd(options), + ) + + return cmd +} + +func newLatestDeploymentCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "latest", + Short: "Get the latest deployment for the project", + Long: "Get the latest deployment for the project in the specified environment.", + RunE: func(cmd *cobra.Command, args []string) error { + return runLatestDeployment(cmd.Context(), options) + }, + } + + flags := cmd.Flags() + options.AddDeployLatestFlags(flags) + + return cmd +} + +func runLatestDeployment(ctx context.Context, options *clioptions.CLIOptions) error { + restConfig, err := options.ToRESTConfig() + if err != nil { + return err + } + + projectID := restConfig.ProjectID + if len(projectID) == 0 { + return errors.New("projectId is required") + } + + client, err := client.APIClientForConfig(restConfig) + if err != nil { + return err + } + + resp, err := client. + Get(). + APIPath(fmt.Sprintf(deploymentsLatestEndpointTemplate, projectID)). + SetParam("page", "1"). + SetParam("per_page", "1"). + SetParam("scope", "success"). + SetParam("environment", options.Environment). + Do(ctx) + + if err != nil { + return fmt.Errorf("error executing request: %w", err) + } + + if err := resp.Error(); err != nil { + return err + } + + var deployments []resources.DeploymentHistory + if err := resp.ParseResponse(&deployments); err != nil { + return fmt.Errorf("cannot parse server response: %w", err) + } + + if len(deployments) == 0 { + fmt.Println("No successful deployments found") + return nil + } + + latest := deployments[0] + fmt.Printf("Latest deployment for environment %s:\n", latest.Environment) + fmt.Printf("ID: %s\n", latest.ID) + fmt.Printf("Ref: %s\n", latest.Ref) + fmt.Printf("Status: %s\n", latest.Status) + fmt.Printf("Finished At: %s\n", latest.FinishedAt) + + return nil +} diff --git a/internal/cmd/runtime/deployments_test.go b/internal/cmd/runtime/deployments_test.go new file mode 100644 index 00000000..0a784673 --- /dev/null +++ b/internal/cmd/runtime/deployments_test.go @@ -0,0 +1,87 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" +) + +func TestLatestDeployment(t *testing.T) { + testProjectID := "test-project-id" + testEnv := "dev" + now := time.Now().Truncate(time.Second) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/api/deploy/projects/%s/deployment/", testProjectID), r.URL.Path) + assert.Equal(t, "1", r.URL.Query().Get("page")) + assert.Equal(t, "1", r.URL.Query().Get("per_page")) + assert.Equal(t, "success", r.URL.Query().Get("scope")) + assert.Equal(t, testEnv, r.URL.Query().Get("environment")) + + resp := []resources.DeploymentHistory{ + { + ID: "deploy-123", + Ref: "main", + PipelineID: "pipe-123", + Status: "success", + FinishedAt: now, + Environment: testEnv, + }, + } + data, _ := resources.EncodeResourceToJSON(resp) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) + })) + defer server.Close() + + options := clioptions.NewCLIOptions() + options.Endpoint = server.URL + options.ProjectID = testProjectID + options.Environment = testEnv + + err := runLatestDeployment(t.Context(), options) + assert.NoError(t, err) +} + +func TestLatestDeploymentNoResults(t *testing.T) { + testProjectID := "test-project-id" + testEnv := "dev" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("[]")) + })) + defer server.Close() + + options := clioptions.NewCLIOptions() + options.Endpoint = server.URL + options.ProjectID = testProjectID + options.Environment = testEnv + + err := runLatestDeployment(t.Context(), options) + assert.NoError(t, err) +} diff --git a/internal/resources/responses.go b/internal/resources/responses.go index c0d4f4a2..57a22aa0 100644 --- a/internal/resources/responses.go +++ b/internal/resources/responses.go @@ -187,6 +187,21 @@ type Deployment struct { Age time.Time `json:"creationTimestamp"` //nolint: tagliatelle } +type DeploymentHistory struct { + ID string `json:"id"` + Ref string `json:"ref"` + PipelineID string `json:"pipelineId"` + Status string `json:"status"` + FinishedAt time.Time `json:"finishedAt"` + Environment string `json:"env"` //nolint:tagliatelle + EnvironmentInfo EnvironmentInfo `json:"environmentInfo"` +} + +type EnvironmentInfo struct { + EnvID string `json:"envId"` + Label string `json:"label"` +} + type Service struct { Name string `json:"name"` Type string `json:"type"` From ea95776ed523120aa8680f1f4281d6073528add6 Mon Sep 17 00:00:00 2001 From: Zachary Greenfield Date: Wed, 21 Jan 2026 11:03:55 -0600 Subject: [PATCH 2/4] refactor: Moved command from runtime to deploy, reorganized flags, changelog added --- CHANGELOG.md | 1 + internal/clioptions/clioptions.go | 8 +-- internal/cmd/deploy/deploy.go | 1 + internal/cmd/deploy/latest.go | 101 +++++++++++++++++++++++++++++ internal/cmd/deploy/latest_test.go | 87 +++++++++++++++++++++++++ internal/cmd/runtime.go | 2 - 6 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 internal/cmd/deploy/latest.go create mode 100644 internal/cmd/deploy/latest_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index dbad98e6..d136e66c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `miactl deploy latest` for getting the latest successful deployment for a project in a specified environment - `miactl runtime create job` has two additional new flags: - `--waitJobCompletion` (default `false`) - if enabled, the `miactl` will wait for the job completion - `--waitJobTimeoutSeconds` (default `600` seconds) - if `--waitJobCompletion` is enabled, the maximum wait timeout diff --git a/internal/clioptions/clioptions.go b/internal/clioptions/clioptions.go index 9ab224a1..a3a75b73 100644 --- a/internal/clioptions/clioptions.go +++ b/internal/clioptions/clioptions.go @@ -175,6 +175,10 @@ func (o *CLIOptions) AddDeployAddStatusFlags(flags *pflag.FlagSet) { flags.StringVar(&o.TriggerID, "trigger-id", "", "trigger-id of the pipeline to update") } +func (o *CLIOptions) AddDeployLatestFlags(flags *pflag.FlagSet) { + flags.StringVar(&o.Environment, "environment", "", "the environment scope for the command") +} + func (o *CLIOptions) AddContextAuthFlags(flags *pflag.FlagSet) { flags.StringVar(&o.BasicClientID, "client-id", "", "the client ID of the service account") flags.StringVar(&o.BasicClientSecret, "client-secret", "", "the client secret of the service account") @@ -329,10 +333,6 @@ func (o *CLIOptions) AddImportFlags(flags *pflag.FlagSet) { flags.StringVar(&o.InputFilePath, "filename", "", "file or folder containing the resources to import") } -func (o *CLIOptions) AddDeployLatestFlags(flags *pflag.FlagSet) { - flags.StringVar(&o.Environment, "environment", "", "the environment scope for the command") -} - func (o *CLIOptions) ToRESTConfig() (*client.Config, error) { locator := cliconfig.NewConfigPathLocator() locator.ExplicitPath = o.MiactlConfig diff --git a/internal/cmd/deploy/deploy.go b/internal/cmd/deploy/deploy.go index 72351174..46e2a94a 100644 --- a/internal/cmd/deploy/deploy.go +++ b/internal/cmd/deploy/deploy.go @@ -33,6 +33,7 @@ Can trigger deployments to specific environments and monitor their status.`, cmd.AddCommand( triggerCmd(options), newStatusAddCmd(options), + newLatestDeploymentCmd(options), ) return cmd diff --git a/internal/cmd/deploy/latest.go b/internal/cmd/deploy/latest.go new file mode 100644 index 00000000..0bb9fc90 --- /dev/null +++ b/internal/cmd/deploy/latest.go @@ -0,0 +1,101 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deploy + +import ( + "context" + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/mia-platform/miactl/internal/client" + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" +) + +const ( + deploymentsLatestEndpointTemplate = "/api/deploy/projects/%s/deployment/" +) + +func newLatestDeploymentCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "latest", + Short: "Get the latest deployment for the project", + Long: "Get the latest deployment for the project in the specified environment.", + RunE: func(cmd *cobra.Command, args []string) error { + return runLatestDeployment(cmd.Context(), options) + }, + } + + flags := cmd.Flags() + options.AddDeployLatestFlags(flags) + + return cmd +} + +func runLatestDeployment(ctx context.Context, options *clioptions.CLIOptions) error { + restConfig, err := options.ToRESTConfig() + if err != nil { + return err + } + + projectID := restConfig.ProjectID + if len(projectID) == 0 { + return errors.New("projectId is required") + } + + client, err := client.APIClientForConfig(restConfig) + if err != nil { + return err + } + + resp, err := client. + Get(). + APIPath(fmt.Sprintf(deploymentsLatestEndpointTemplate, projectID)). + SetParam("page", "1"). + SetParam("per_page", "1"). + SetParam("scope", "success"). + SetParam("environment", options.Environment). + Do(ctx) + + if err != nil { + return fmt.Errorf("error executing request: %w", err) + } + + if err := resp.Error(); err != nil { + return err + } + + var deployments []resources.DeploymentHistory + if err := resp.ParseResponse(&deployments); err != nil { + return fmt.Errorf("cannot parse server response: %w", err) + } + + if len(deployments) == 0 { + fmt.Println("No successful deployments found") + return nil + } + + latest := deployments[0] + fmt.Printf("Latest deployment for environment %s:\n", latest.Environment) + fmt.Printf("ID: %s\n", latest.ID) + fmt.Printf("Ref: %s\n", latest.Ref) + fmt.Printf("Status: %s\n", latest.Status) + fmt.Printf("Finished At: %s\n", latest.FinishedAt) + + return nil +} diff --git a/internal/cmd/deploy/latest_test.go b/internal/cmd/deploy/latest_test.go new file mode 100644 index 00000000..f1c19378 --- /dev/null +++ b/internal/cmd/deploy/latest_test.go @@ -0,0 +1,87 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deploy + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" +) + +func TestLatestDeployment(t *testing.T) { + testProjectID := "test-project-id" + testEnv := "dev" + now := time.Now().Truncate(time.Second) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/api/deploy/projects/%s/deployment/", testProjectID), r.URL.Path) + assert.Equal(t, "1", r.URL.Query().Get("page")) + assert.Equal(t, "1", r.URL.Query().Get("per_page")) + assert.Equal(t, "success", r.URL.Query().Get("scope")) + assert.Equal(t, testEnv, r.URL.Query().Get("environment")) + + resp := []resources.DeploymentHistory{ + { + ID: "deploy-123", + Ref: "main", + PipelineID: "pipe-123", + Status: "success", + FinishedAt: now, + Environment: testEnv, + }, + } + data, _ := resources.EncodeResourceToJSON(resp) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) + })) + defer server.Close() + + options := clioptions.NewCLIOptions() + options.Endpoint = server.URL + options.ProjectID = testProjectID + options.Environment = testEnv + + err := runLatestDeployment(t.Context(), options) + assert.NoError(t, err) +} + +func TestLatestDeploymentNoResults(t *testing.T) { + testProjectID := "test-project-id" + testEnv := "dev" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("[]")) + })) + defer server.Close() + + options := clioptions.NewCLIOptions() + options.Endpoint = server.URL + options.ProjectID = testProjectID + options.Environment = testEnv + + err := runLatestDeployment(t.Context(), options) + assert.NoError(t, err) +} diff --git a/internal/cmd/runtime.go b/internal/cmd/runtime.go index 6cd441d9..5eefbd92 100644 --- a/internal/cmd/runtime.go +++ b/internal/cmd/runtime.go @@ -23,7 +23,6 @@ import ( "github.com/mia-platform/miactl/internal/cmd/events" "github.com/mia-platform/miactl/internal/cmd/logs" runtimeresources "github.com/mia-platform/miactl/internal/cmd/resources" - "github.com/mia-platform/miactl/internal/cmd/runtime" ) func RuntimeCmd(o *clioptions.CLIOptions) *cobra.Command { @@ -52,7 +51,6 @@ the resources generated, like Pods, Cronjobs and logs. environments.EnvironmentCmd(o), events.Command(o), logs.Command(o), - runtime.NewDeploymentsCmd(o), ) return cmd From bfdb5028b0df822a1a321dbb935d7daee1c5fd84 Mon Sep 17 00:00:00 2001 From: Zachary Greenfield Date: Wed, 21 Jan 2026 11:13:32 -0600 Subject: [PATCH 3/4] refactor: removed unused files --- internal/cmd/runtime/deployments.go | 114 ----------------------- internal/cmd/runtime/deployments_test.go | 87 ----------------- 2 files changed, 201 deletions(-) delete mode 100644 internal/cmd/runtime/deployments.go delete mode 100644 internal/cmd/runtime/deployments_test.go diff --git a/internal/cmd/runtime/deployments.go b/internal/cmd/runtime/deployments.go deleted file mode 100644 index b7df20a7..00000000 --- a/internal/cmd/runtime/deployments.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Mia srl -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runtime - -import ( - "context" - "errors" - "fmt" - - "github.com/spf13/cobra" - - "github.com/mia-platform/miactl/internal/client" - "github.com/mia-platform/miactl/internal/clioptions" - "github.com/mia-platform/miactl/internal/resources" -) - -const ( - deploymentsLatestEndpointTemplate = "/api/deploy/projects/%s/deployment/" -) - -func NewDeploymentsCmd(options *clioptions.CLIOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "deployments", - Short: "Manage deployments of the project", - } - - cmd.AddCommand( - newLatestDeploymentCmd(options), - ) - - return cmd -} - -func newLatestDeploymentCmd(options *clioptions.CLIOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "latest", - Short: "Get the latest deployment for the project", - Long: "Get the latest deployment for the project in the specified environment.", - RunE: func(cmd *cobra.Command, args []string) error { - return runLatestDeployment(cmd.Context(), options) - }, - } - - flags := cmd.Flags() - options.AddDeployLatestFlags(flags) - - return cmd -} - -func runLatestDeployment(ctx context.Context, options *clioptions.CLIOptions) error { - restConfig, err := options.ToRESTConfig() - if err != nil { - return err - } - - projectID := restConfig.ProjectID - if len(projectID) == 0 { - return errors.New("projectId is required") - } - - client, err := client.APIClientForConfig(restConfig) - if err != nil { - return err - } - - resp, err := client. - Get(). - APIPath(fmt.Sprintf(deploymentsLatestEndpointTemplate, projectID)). - SetParam("page", "1"). - SetParam("per_page", "1"). - SetParam("scope", "success"). - SetParam("environment", options.Environment). - Do(ctx) - - if err != nil { - return fmt.Errorf("error executing request: %w", err) - } - - if err := resp.Error(); err != nil { - return err - } - - var deployments []resources.DeploymentHistory - if err := resp.ParseResponse(&deployments); err != nil { - return fmt.Errorf("cannot parse server response: %w", err) - } - - if len(deployments) == 0 { - fmt.Println("No successful deployments found") - return nil - } - - latest := deployments[0] - fmt.Printf("Latest deployment for environment %s:\n", latest.Environment) - fmt.Printf("ID: %s\n", latest.ID) - fmt.Printf("Ref: %s\n", latest.Ref) - fmt.Printf("Status: %s\n", latest.Status) - fmt.Printf("Finished At: %s\n", latest.FinishedAt) - - return nil -} diff --git a/internal/cmd/runtime/deployments_test.go b/internal/cmd/runtime/deployments_test.go deleted file mode 100644 index 0a784673..00000000 --- a/internal/cmd/runtime/deployments_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright Mia srl -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runtime - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/mia-platform/miactl/internal/clioptions" - "github.com/mia-platform/miactl/internal/resources" -) - -func TestLatestDeployment(t *testing.T) { - testProjectID := "test-project-id" - testEnv := "dev" - now := time.Now().Truncate(time.Second) - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, fmt.Sprintf("/api/deploy/projects/%s/deployment/", testProjectID), r.URL.Path) - assert.Equal(t, "1", r.URL.Query().Get("page")) - assert.Equal(t, "1", r.URL.Query().Get("per_page")) - assert.Equal(t, "success", r.URL.Query().Get("scope")) - assert.Equal(t, testEnv, r.URL.Query().Get("environment")) - - resp := []resources.DeploymentHistory{ - { - ID: "deploy-123", - Ref: "main", - PipelineID: "pipe-123", - Status: "success", - FinishedAt: now, - Environment: testEnv, - }, - } - data, _ := resources.EncodeResourceToJSON(resp) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write(data) - })) - defer server.Close() - - options := clioptions.NewCLIOptions() - options.Endpoint = server.URL - options.ProjectID = testProjectID - options.Environment = testEnv - - err := runLatestDeployment(t.Context(), options) - assert.NoError(t, err) -} - -func TestLatestDeploymentNoResults(t *testing.T) { - testProjectID := "test-project-id" - testEnv := "dev" - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("[]")) - })) - defer server.Close() - - options := clioptions.NewCLIOptions() - options.Endpoint = server.URL - options.ProjectID = testProjectID - options.Environment = testEnv - - err := runLatestDeployment(t.Context(), options) - assert.NoError(t, err) -} From ac849d6d7b9c73a435a2de82c616e5290fd2ceaa Mon Sep 17 00:00:00 2001 From: Zachary Greenfield Date: Wed, 21 Jan 2026 11:15:21 -0600 Subject: [PATCH 4/4] refactor: renaming command function name --- internal/cmd/deploy/deploy.go | 2 +- internal/cmd/deploy/latest.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/deploy/deploy.go b/internal/cmd/deploy/deploy.go index 46e2a94a..4231d2bf 100644 --- a/internal/cmd/deploy/deploy.go +++ b/internal/cmd/deploy/deploy.go @@ -33,7 +33,7 @@ Can trigger deployments to specific environments and monitor their status.`, cmd.AddCommand( triggerCmd(options), newStatusAddCmd(options), - newLatestDeploymentCmd(options), + latestDeploymentCmd(options), ) return cmd diff --git a/internal/cmd/deploy/latest.go b/internal/cmd/deploy/latest.go index 0bb9fc90..58974988 100644 --- a/internal/cmd/deploy/latest.go +++ b/internal/cmd/deploy/latest.go @@ -31,7 +31,7 @@ const ( deploymentsLatestEndpointTemplate = "/api/deploy/projects/%s/deployment/" ) -func newLatestDeploymentCmd(options *clioptions.CLIOptions) *cobra.Command { +func latestDeploymentCmd(options *clioptions.CLIOptions) *cobra.Command { cmd := &cobra.Command{ Use: "latest", Short: "Get the latest deployment for the project",