From 87efd8fe0b7cd7c89595b8e9d83e56d9c0775520 Mon Sep 17 00:00:00 2001 From: Davide Bianchi <10374360+davidebianchi@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:46:17 +0200 Subject: [PATCH] feat: add deploy trigger and deploy add status commands (#215) --- CHANGELOG.md | 9 + docs/30_commands.md | 36 +++- internal/clioptions/clioptions.go | 5 + internal/cmd/deploy/deploy.go | 144 ++------------- internal/cmd/deploy/status.go | 115 ++++++++++++ internal/cmd/deploy/status_test.go | 109 +++++++++++ internal/cmd/deploy/trigger.go | 174 ++++++++++++++++++ .../{deploy_test.go => trigger_test.go} | 30 ++- internal/resources/deploy/triggerstatus.go | 20 ++ 9 files changed, 509 insertions(+), 133 deletions(-) create mode 100644 internal/cmd/deploy/status.go create mode 100644 internal/cmd/deploy/status_test.go create mode 100644 internal/cmd/deploy/trigger.go rename internal/cmd/deploy/{deploy_test.go => trigger_test.go} (68%) create mode 100644 internal/resources/deploy/triggerstatus.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ae672..523fc99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `deploy add status` command +- `deploy trigger` command + +### Changed + +- deprecate `deploy` command + ## [v0.14.0] - 2024-07-25 ### Added diff --git a/docs/30_commands.md b/docs/30_commands.md index c1d0af9..2bebade 100644 --- a/docs/30_commands.md +++ b/docs/30_commands.md @@ -542,12 +542,23 @@ Available flags for the command: ## deploy +The `deploy` command allows you to manage the deployment of your Projects. + +Available subcommands are the following ones: + +```sh + trigger Trigger a deploy pipeline + add status Add a new deploy status +``` + +### trigger + This command allows you to trigger the deploy pipeline for the selected Project. Usage: ```sh -miactl deploy ENVIRONMENT [flags] +miactl deploy trigger ENVIRONMENT [flags] ``` Available flags for the command: @@ -562,6 +573,29 @@ Available flags for the command: - `--no-semver`, to force the deploy without `semver` - `--revision`, to specify the revision of the commit to deploy +### add status + +This command allows you to add a new deploy status for the selected trigger id pipelines of the Project, +only for those integration which trigger the pipeline with a trigger id (e.g. Jenkins integration). + +Usage: + +```sh +miactl deploy add status STATUS [flags] +``` + +where `STATUS` must be one of: `success`, `failed`, `canceled`, `skipped`. + +Available flags for the command: + +- `--endpoint`, to set the Console endpoint +- `--certificate-authority`, to provide the path to a custom CA certificate +- `--insecure-skip-tls-verify`, to disallow the check the validity of the certificate of the remote endpoint +- `--context`, to specify a different context from the currently selected one +- `--company-id`, to set the ID of the desired Company +- `--project-id`, to set the ID of the desired Project +- `--trigger-id`, to specify the trigger id to update + ## extensions The `extensions` command allows you to manage Company extensions. diff --git a/internal/clioptions/clioptions.go b/internal/clioptions/clioptions.go index de6e00a..ad5bb12 100644 --- a/internal/clioptions/clioptions.go +++ b/internal/clioptions/clioptions.go @@ -45,6 +45,7 @@ type CLIOptions struct { Revision string DeployType string NoSemVer bool + TriggerID string IAMRole string ProjectIAMRole string @@ -141,6 +142,10 @@ func (o *CLIOptions) AddDeployFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.NoSemVer, "no-semver", false, "force the deploy wihout semver") } +func (o *CLIOptions) AddDeployAddStatusFlags(flags *pflag.FlagSet) { + flags.StringVar(&o.TriggerID, "trigger-id", "", "trigger-id of the pipeline to update") +} + 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") diff --git a/internal/cmd/deploy/deploy.go b/internal/cmd/deploy/deploy.go index 76ff7b8..97dae05 100644 --- a/internal/cmd/deploy/deploy.go +++ b/internal/cmd/deploy/deploy.go @@ -16,155 +16,45 @@ package deploy import ( - "context" - "fmt" - "time" - - "github.com/mia-platform/miactl/internal/client" "github.com/mia-platform/miactl/internal/clioptions" - "github.com/mia-platform/miactl/internal/resources" "github.com/spf13/cobra" ) -const ( - deployProjectEndpointTemplate = "/api/deploy/projects/%s/trigger/pipeline/" - pipelineStatusEndpointTemplate = "/api/deploy/projects/%s/pipelines/%d/status/" -) - func NewDeployCmd(options *clioptions.CLIOptions) *cobra.Command { cmd := &cobra.Command{ Use: "deploy ENVIRONMENT", Short: "Deploy the target environment.", - Long: `Trigger the deploy of the target environment in the selected project. + Long: `Deprecation Warning: This command is deprecated. Use 'deploy trigger' instead. + +Trigger the deploy of the target environment in the selected project. The deploy will be performed by the pipeline setup in project, the command will then keep listening on updates of the status for keep the user informed on the updates. The command will exit with error if the pipeline will not end with a success.`, - Args: cobra.ExactArgs(1), + Args: cobra.ExactArgs(1), + Deprecated: "use 'deploy trigger' instead", RunE: func(cmd *cobra.Command, args []string) error { environmentName := args[0] - return run(cmd.Context(), environmentName, options) + return runDeployTrigger(cmd.Context(), environmentName, options) }, } - // set flags - flags := cmd.Flags() - options.AddConnectionFlags(flags) - options.AddContextFlags(flags) - options.AddCompanyFlags(flags) - options.AddProjectFlags(flags) - options.AddDeployFlags(flags) - if err := cmd.MarkFlagRequired("revision"); err != nil { - // if there is an error something very wrong is happening, panic - panic(err) - } - - return cmd -} + deployTriggerOptions(cmd, options) -func run(ctx context.Context, environmentName string, options *clioptions.CLIOptions) error { - restConfig, err := options.ToRESTConfig() - if err != nil { - return err - } + cmd.AddCommand( + triggerCmd(options), + addCmd(options), + ) - projectID := restConfig.ProjectID - if len(projectID) == 0 { - return fmt.Errorf("projectId is required to start a deploy") - } - - client, err := client.APIClientForConfig(restConfig) - if err != nil { - return err - } - - resp, err := triggerPipeline(ctx, client, environmentName, projectID, options) - if err != nil { - return fmt.Errorf("error executing the deploy request: %w", err) - } - fmt.Printf("Deploying project %s in the environment '%s'\n", projectID, environmentName) - - status, err := waitStatus(ctx, client, projectID, resp.ID, environmentName) - if err != nil { - return fmt.Errorf("error retrieving the pipeline status: %w", err) - } - - fmt.Printf("Pipeline ended with %s\n", status) - return nil + return cmd } -func triggerPipeline(ctx context.Context, client *client.APIClient, environmentName, projectID string, options *clioptions.CLIOptions) (*resources.DeployProject, error) { - request := resources.DeployProjectRequest{ - Environment: environmentName, - Revision: options.Revision, - Type: options.DeployType, - ForceDeploy: options.NoSemVer, - } - - if options.DeployType == "deploy_all" { - request.ForceDeploy = true - } - - requestBody, err := resources.EncodeResourceToJSON(request) - if err != nil { - return nil, fmt.Errorf("error mashalling body: %w", err) - } - - resp, err := client. - Post(). - APIPath(fmt.Sprintf(deployProjectEndpointTemplate, projectID)). - Body(requestBody). - Do(ctx) - if err != nil { - return nil, fmt.Errorf("error executing request: %w", err) +func addCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "add", } - if err := resp.Error(); err != nil { - return nil, err - } + cmd.AddCommand(newStatusAddCmd(options)) - body := new(resources.DeployProject) - err = resp.ParseResponse(body) - if err != nil { - return nil, err - } - - return body, nil -} - -// one and a half second is the time we wait between calls to the status endpoint. -// Declared here to override it during tests -var sleepDuration = (1 * time.Second) + (500 * time.Millisecond) - -func waitStatus(ctx context.Context, client *client.APIClient, projectID string, deployID int, environmentName string) (string, error) { - var outStatus *resources.PipelineStatus - for { - time.Sleep(sleepDuration) - resp, err := client. - Get(). - APIPath(fmt.Sprintf(pipelineStatusEndpointTemplate, projectID, deployID)). - SetParam("environment", environmentName). - Do(ctx) - - if err != nil { - return "", err - } - if err := resp.Error(); err != nil { - return "", err - } - - status := new(resources.PipelineStatus) - if err := resp.ParseResponse(status); err != nil { - return "", err - } - - if status.Status != "running" && status.Status != "pending" { - outStatus = status - break - } - - fmt.Printf("The pipeline is %s..\n", status.Status) - } - - return outStatus.Status, nil + return cmd } diff --git a/internal/cmd/deploy/status.go b/internal/cmd/deploy/status.go new file mode 100644 index 0000000..91680db --- /dev/null +++ b/internal/cmd/deploy/status.go @@ -0,0 +1,115 @@ +// 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" + "fmt" + "strings" + + "github.com/mia-platform/miactl/internal/client" + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" + "github.com/mia-platform/miactl/internal/resources/deploy" + "github.com/spf13/cobra" +) + +const ( + deployStatusTriggerEndpointTemplate = "/api/deploy/webhooks/projects/%s/pipelines/triggers/%s/status/" + deployStatusErrorRequiredTemplate = "%s is required to update the deploy trigger status" +) + +var allowedArgs = []string{"success", "failed", "canceled", "skipped"} + +func newStatusAddCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "status" + " [" + strings.Join(allowedArgs, "|") + "]", + Short: "Add status to deploy history record.", + Long: `This command is used to add a status to a deploy history record. + +The status can be updated only once, using the trigger ID provided in the 'deploy trigger' command +to the pipeline. + +At the moment, the only deploy trigger which creates a trigger ID is the integration with the Jenkins provider.`, + ValidArgs: allowedArgs, + Args: cobra.MatchAll( + cobra.ExactArgs(1), + cobra.OnlyValidArgs, + ), + RunE: func(cmd *cobra.Command, args []string) error { + return runAddDeployStatus(cmd.Context(), options, args[0]) + }, + } + + flags := cmd.Flags() + options.AddConnectionFlags(flags) + options.AddContextFlags(flags) + options.AddCompanyFlags(flags) + options.AddProjectFlags(flags) + options.AddDeployAddStatusFlags(flags) + if err := cmd.MarkFlagRequired("trigger-id"); err != nil { + // if there is an error something very wrong is happening, panic + panic(err) + } + + return cmd +} + +func runAddDeployStatus(ctx context.Context, options *clioptions.CLIOptions, status string) error { + restConfig, err := options.ToRESTConfig() + if err != nil { + return err + } + + projectID := restConfig.ProjectID + if len(projectID) == 0 { + return fmt.Errorf(deployStatusErrorRequiredTemplate, "projectId") + } + + client, err := client.APIClientForConfig(restConfig) + if err != nil { + return err + } + + triggerID := options.TriggerID + if len(triggerID) == 0 { + return fmt.Errorf(deployStatusErrorRequiredTemplate, "triggerId") + } + + requestBody := deploy.AddPipelineStatusRequest{ + Status: status, + } + payload, err := resources.EncodeResourceToJSON(requestBody) + if err != nil { + return err + } + + resp, err := client.Post(). + APIPath(fmt.Sprintf(deployStatusTriggerEndpointTemplate, projectID, triggerID)). + Body(payload). + Do(ctx) + if err != nil { + return err + } + + if err := resp.Error(); err != nil { + return err + } + + fmt.Printf("Deploy status updated for pipeline with triggerId %s to %s\n", triggerID, status) + + return nil +} diff --git a/internal/cmd/deploy/status_test.go b/internal/cmd/deploy/status_test.go new file mode 100644 index 0000000..5fed7ca --- /dev/null +++ b/internal/cmd/deploy/status_test.go @@ -0,0 +1,109 @@ +// 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" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAddStatus(t *testing.T) { + sleepDuration = 0 + + testCases := map[string]struct { + server *httptest.Server + status string + triggerID string + projectID string + expectErr bool + expectedErrMessage string + }{ + "add status succeed": { + server: testAddStatusServer(t), + triggerID: "trigger-id", + projectID: "project-id", + }, + "add status fails": { + server: testAddStatusServer(t), + triggerID: "fails-bad-request", + projectID: "project-id", + expectErr: true, + expectedErrMessage: "some bad request", + }, + "without project id": { + server: testAddStatusServer(t), + projectID: "", + triggerID: "trigger-id", + expectErr: true, + expectedErrMessage: fmt.Sprintf(deployStatusErrorRequiredTemplate, "projectId"), + }, + "without trigger id": { + server: testAddStatusServer(t), + projectID: "project-id", + triggerID: "", + expectErr: true, + expectedErrMessage: fmt.Sprintf(deployStatusErrorRequiredTemplate, "triggerId"), + }, + } + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + server := testCase.server + defer server.Close() + options := &clioptions.CLIOptions{ + Endpoint: server.URL, + TriggerID: testCase.triggerID, + ProjectID: testCase.projectID, + } + err := runAddDeployStatus(context.TODO(), options, testCase.status) + if testCase.expectErr { + require.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} + +func testAddStatusServer(t *testing.T) *httptest.Server { + t.Helper() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("request URL: %s\n", r.URL) + t.Helper() + switch { + case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(deployStatusTriggerEndpointTemplate, "project-id", "trigger-id"): + w.WriteHeader(http.StatusAccepted) + w.Write(nil) + case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(deployStatusTriggerEndpointTemplate, "project-id", "fails-bad-request"): + w.WriteHeader(http.StatusBadRequest) + + respBody := `{"error": "Bad Request","message": "some bad request"}` + w.Write([]byte(respBody)) + default: + w.WriteHeader(http.StatusNotFound) + require.FailNowf(t, "unknown http request", "request method: %s request URL: %s", r.Method, r.URL) + } + })) + + return server +} diff --git a/internal/cmd/deploy/trigger.go b/internal/cmd/deploy/trigger.go new file mode 100644 index 0000000..6678308 --- /dev/null +++ b/internal/cmd/deploy/trigger.go @@ -0,0 +1,174 @@ +// 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" + "fmt" + "time" + + "github.com/mia-platform/miactl/internal/client" + "github.com/mia-platform/miactl/internal/clioptions" + "github.com/mia-platform/miactl/internal/resources" + "github.com/spf13/cobra" +) + +const ( + deployProjectEndpointTemplate = "/api/deploy/projects/%s/trigger/pipeline/" + pipelineStatusEndpointTemplate = "/api/deploy/projects/%s/pipelines/%d/status/" +) + +func triggerCmd(options *clioptions.CLIOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "trigger ENVIRONMENT", + Short: "Deploy the target environment.", + Long: `Trigger the deploy of the target environment in the selected project. + +The deploy will be performed by the pipeline setup in project, the command will then keep +listening on updates of the status for keep the user informed on the updates. The command +will exit with error if the pipeline will not end with a success.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + environmentName := args[0] + return runDeployTrigger(cmd.Context(), environmentName, options) + }, + } + + deployTriggerOptions(cmd, options) + + return cmd +} + +func deployTriggerOptions(cmd *cobra.Command, options *clioptions.CLIOptions) { + // set flags + flags := cmd.Flags() + options.AddConnectionFlags(flags) + options.AddContextFlags(flags) + options.AddCompanyFlags(flags) + options.AddProjectFlags(flags) + options.AddDeployFlags(flags) + if err := cmd.MarkFlagRequired("revision"); err != nil { + // if there is an error something very wrong is happening, panic + panic(err) + } +} + +func runDeployTrigger(ctx context.Context, environmentName string, options *clioptions.CLIOptions) error { + restConfig, err := options.ToRESTConfig() + if err != nil { + return err + } + + projectID := restConfig.ProjectID + if len(projectID) == 0 { + return fmt.Errorf("projectId is required to start a deploy") + } + + client, err := client.APIClientForConfig(restConfig) + if err != nil { + return err + } + + resp, err := triggerPipeline(ctx, client, environmentName, projectID, options) + if err != nil { + return fmt.Errorf("error executing the deploy request: %w", err) + } + fmt.Printf("Deploying project %s in the environment '%s'\n", projectID, environmentName) + + status, err := waitStatus(ctx, client, projectID, resp.ID, environmentName) + if err != nil { + return fmt.Errorf("error retrieving the pipeline status: %w", err) + } + + fmt.Printf("Pipeline ended with %s\n", status) + return nil +} + +func triggerPipeline(ctx context.Context, client *client.APIClient, environmentName, projectID string, options *clioptions.CLIOptions) (*resources.DeployProject, error) { + request := resources.DeployProjectRequest{ + Environment: environmentName, + Revision: options.Revision, + Type: options.DeployType, + ForceDeploy: options.NoSemVer, + } + + if options.DeployType == "deploy_all" { + request.ForceDeploy = true + } + + requestBody, err := resources.EncodeResourceToJSON(request) + if err != nil { + return nil, fmt.Errorf("error mashalling body: %w", err) + } + + resp, err := client. + Post(). + APIPath(fmt.Sprintf(deployProjectEndpointTemplate, projectID)). + Body(requestBody). + Do(ctx) + if err != nil { + return nil, fmt.Errorf("error executing request: %w", err) + } + + if err := resp.Error(); err != nil { + return nil, err + } + + body := new(resources.DeployProject) + err = resp.ParseResponse(body) + if err != nil { + return nil, err + } + + return body, nil +} + +// one and a half second is the time we wait between calls to the status endpoint. +// Declared here to override it during tests +var sleepDuration = (1 * time.Second) + (500 * time.Millisecond) + +func waitStatus(ctx context.Context, client *client.APIClient, projectID string, deployID int, environmentName string) (string, error) { + var outStatus *resources.PipelineStatus + for { + time.Sleep(sleepDuration) + resp, err := client. + Get(). + APIPath(fmt.Sprintf(pipelineStatusEndpointTemplate, projectID, deployID)). + SetParam("environment", environmentName). + Do(ctx) + + if err != nil { + return "", err + } + if err := resp.Error(); err != nil { + return "", err + } + + status := new(resources.PipelineStatus) + if err := resp.ParseResponse(status); err != nil { + return "", err + } + + if status.Status != "running" && status.Status != "pending" { + outStatus = status + break + } + + fmt.Printf("The pipeline is %s..\n", status.Status) + } + + return outStatus.Status, nil +} diff --git a/internal/cmd/deploy/deploy_test.go b/internal/cmd/deploy/trigger_test.go similarity index 68% rename from internal/cmd/deploy/deploy_test.go rename to internal/cmd/deploy/trigger_test.go index e29385b..a251e75 100644 --- a/internal/cmd/deploy/deploy_test.go +++ b/internal/cmd/deploy/trigger_test.go @@ -38,11 +38,21 @@ func TestDeploy(t *testing.T) { expectErr bool }{ "pipeline succeed": { - server: testServer(t), + server: testTriggerServer(t), projectID: "correct", }, + "pipeline fails": { + server: testTriggerServer(t), + projectID: "fails-bad-request", + expectErr: true, + }, + "wait status fails": { + server: testTriggerServer(t), + projectID: "fails-wait-status", + expectErr: true, + }, "missing project ID": { - server: testServer(t), + server: testTriggerServer(t), projectID: "", expectErr: true, }, @@ -57,7 +67,7 @@ func TestDeploy(t *testing.T) { ProjectID: testCase.projectID, MiactlConfig: filepath.Join(t.TempDir(), "nofile"), } - err := run(context.TODO(), "environmentName", options) + err := runDeployTrigger(context.TODO(), "environmentName", options) if testCase.expectErr { require.Error(t, err) return @@ -67,12 +77,12 @@ func TestDeploy(t *testing.T) { } } -func testServer(t *testing.T) *httptest.Server { +func testTriggerServer(t *testing.T) *httptest.Server { t.Helper() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Helper() switch { - case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(deployProjectEndpointTemplate, "correct"): + case r.Method == http.MethodPost && (r.URL.Path == fmt.Sprintf(deployProjectEndpointTemplate, "correct") || r.URL.Path == fmt.Sprintf(deployProjectEndpointTemplate, "fails-wait-status")): data, err := resources.EncodeResourceToJSON(&resources.DeployProject{ ID: 1, URL: "http://example.com", @@ -87,6 +97,16 @@ func testServer(t *testing.T) *httptest.Server { }) require.NoError(t, err) w.Write(data) + case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(deployProjectEndpointTemplate, "fails-bad-request"): + respBody := `{"error": "Bad Request","message":"some bad request"}` + + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(respBody)) + case r.Method == http.MethodGet && r.URL.Path == fmt.Sprintf(pipelineStatusEndpointTemplate, "fails-wait-status", 1) && r.URL.Query().Get("environment") == "environmentName": + respBody := `{"error": "Internal Server Error","message":"some error"}` + + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(respBody)) default: w.WriteHeader(http.StatusNotFound) require.FailNowf(t, "unknown http request", "request method: %s request URL: %s", r.Method, r.URL) diff --git a/internal/resources/deploy/triggerstatus.go b/internal/resources/deploy/triggerstatus.go new file mode 100644 index 0000000..ae6b34e --- /dev/null +++ b/internal/resources/deploy/triggerstatus.go @@ -0,0 +1,20 @@ +// 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 + +type AddPipelineStatusRequest struct { + Status string `json:"status"` +}