From bb69db18a5453847cb5a5eb279e31f1fb7f9fac3 Mon Sep 17 00:00:00 2001 From: Sebastian Gumprich Date: Tue, 2 Jan 2024 20:31:50 +0000 Subject: [PATCH] feat(schedule): add option to create a scheduled pipeline with variables --- api/pipeline.go | 7 ++++-- api/schedule.go | 17 +++++++++++-- commands/ci/lint/lint.go | 12 ++++++++- commands/schedule/create/create.go | 25 +++++++++++++++++-- commands/schedule/create/create_test.go | 33 +++++++++++++++++++++++++ docs/source/ci/ci/lint.md | 8 ++++++ docs/source/ci/lint.md | 8 ++++++ docs/source/schedule/create.md | 3 ++- 8 files changed, 105 insertions(+), 8 deletions(-) diff --git a/api/pipeline.go b/api/pipeline.go index 74bebc99..6fc4fc9a 100644 --- a/api/pipeline.go +++ b/api/pipeline.go @@ -389,14 +389,17 @@ var PipelineJobsWithSha = func(client *gitlab.Client, pid interface{}, sha strin return PipelineJobsWithID(client, pid, pipelines[0].ID) } -var ProjectNamespaceLint = func(client *gitlab.Client, projectID int, content string) (*gitlab.ProjectLintResult, error) { +var ProjectNamespaceLint = func(client *gitlab.Client, projectID int, content string, ref string, dryRun bool, includeJobs bool) (*gitlab.ProjectLintResult, error) { if client == nil { client = apiClient.Lab() } c, _, err := client.Validate.ProjectNamespaceLint( projectID, &gitlab.ProjectNamespaceLintOptions{ - Content: &content, + Content: &content, + DryRun: &dryRun, + Ref: &ref, + IncludeJobs: &includeJobs, }, ) if err != nil { diff --git a/api/schedule.go b/api/schedule.go index 8f674365..cf45fa36 100644 --- a/api/schedule.go +++ b/api/schedule.go @@ -34,12 +34,25 @@ var RunSchedule = func(client *gitlab.Client, repo string, schedule int, opts .. return nil } -var CreateSchedule = func(client *gitlab.Client, repo string, scheduleOpts *gitlab.CreatePipelineScheduleOptions, opts ...gitlab.RequestOptionFunc) error { +var CreateSchedule = func(client *gitlab.Client, repo string, scheduleOpts *gitlab.CreatePipelineScheduleOptions, opts ...gitlab.RequestOptionFunc) (error, *gitlab.PipelineSchedule) { if client == nil { client = apiClient.Lab() } - _, _, err := client.PipelineSchedules.CreatePipelineSchedule(repo, scheduleOpts, opts...) + schedule, _, err := client.PipelineSchedules.CreatePipelineSchedule(repo, scheduleOpts, opts...) + if err != nil { + return fmt.Errorf("creating scheduled pipeline status: %w", err), nil + } + + return nil, schedule +} + +var CreateScheduleVariable = func(client *gitlab.Client, repo string, schedule *gitlab.PipelineSchedule, scheduleVarOpts *gitlab.CreatePipelineScheduleVariableOptions, opts ...gitlab.RequestOptionFunc) error { + if client == nil { + client = apiClient.Lab() + } + + _, _, err := client.PipelineSchedules.CreatePipelineScheduleVariable(repo, schedule.ID, scheduleVarOpts, opts...) if err != nil { return fmt.Errorf("creating scheduled pipeline status: %w", err) } diff --git a/commands/ci/lint/lint.go b/commands/ci/lint/lint.go index 6c74729c..b083e698 100644 --- a/commands/ci/lint/lint.go +++ b/commands/ci/lint/lint.go @@ -15,6 +15,12 @@ import ( "github.com/spf13/cobra" ) +var ( + ref string + dryRun bool + includeJobs bool +) + func NewCmdLint(f *cmdutils.Factory) *cobra.Command { pipelineCILintCmd := &cobra.Command{ Use: "lint", @@ -37,6 +43,10 @@ func NewCmdLint(f *cmdutils.Factory) *cobra.Command { }, } + pipelineCILintCmd.Flags().BoolVarP(&dryRun, "dry-run", "", false, "Run pipeline creation simulation.") + pipelineCILintCmd.Flags().BoolVarP(&includeJobs, "include-jobs", "", false, "The response should include the list of jobs that would exist in a static check or pipeline simulation.") + pipelineCILintCmd.Flags().StringVar(&ref, "ref", "", "When dry-run is true, sets the branch or tag context for validating the CI/CD YAML configuration.") + return pipelineCILintCmd } @@ -87,7 +97,7 @@ func lintRun(f *cmdutils.Factory, path string) error { fmt.Fprintln(f.IO.StdOut, "Validating...") - lint, err := api.ProjectNamespaceLint(apiClient, projectID, string(content)) + lint, err := api.ProjectNamespaceLint(apiClient, projectID, string(content), ref, dryRun, includeJobs) if err != nil { return err } diff --git a/commands/schedule/create/create.go b/commands/schedule/create/create.go index 9684e0a8..be9f5a29 100644 --- a/commands/schedule/create/create.go +++ b/commands/schedule/create/create.go @@ -2,6 +2,7 @@ package create import ( "fmt" + "strings" "gitlab.com/gitlab-org/cli/api" "gitlab.com/gitlab-org/cli/commands/cmdutils" @@ -11,12 +12,14 @@ import ( "github.com/xanzy/go-gitlab" ) +var variableList []string + func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { scheduleCreateCmd := &cobra.Command{ Use: "create [flags]", Short: `Schedule a new pipeline.`, Example: heredoc.Doc(` - glab schedule create --cron "0 * * * *" --description "Describe your pipeline here" --ref "main" + glab schedule create --cron "0 * * * *" --description "Describe your pipeline here" --ref "main" --variable "foo:bar" --variable "baz:baz" `), Long: ``, RunE: func(cmd *cobra.Command, args []string) error { @@ -32,11 +35,14 @@ func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { l := &gitlab.CreatePipelineScheduleOptions{} + variable := &gitlab.CreatePipelineScheduleVariableOptions{} + description, _ := cmd.Flags().GetString("description") ref, _ := cmd.Flags().GetString("ref") cron, _ := cmd.Flags().GetString("cron") cronTimeZone, _ := cmd.Flags().GetString("cronTimeZone") active, _ := cmd.Flags().GetBool("active") + variableList, _ = cmd.Flags().GetStringSlice("variable") l.Description = &description l.Ref = &ref @@ -44,10 +50,24 @@ func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { l.CronTimezone = &cronTimeZone l.Active = &active - err = api.CreateSchedule(apiClient, repo.FullName(), l) + err, schedule := api.CreateSchedule(apiClient, repo.FullName(), l) if err != nil { return err } + + for _, v := range variableList { + split := strings.SplitN(v, ":", 2) + if len(split) != 2 { + return fmt.Errorf("Invalid format for --variable: %s", v) + } + variable.Key = &split[0] + variable.Value = &split[1] + err = api.CreateScheduleVariable(apiClient, repo.FullName(), schedule, variable) + if err != nil { + return err + } + } + fmt.Fprintln(f.IO.StdOut, "Created schedule") return nil @@ -58,6 +78,7 @@ func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { scheduleCreateCmd.Flags().String("cron", "", "Cron interval pattern") scheduleCreateCmd.Flags().String("cronTimeZone", "UTC", "Cron timezone") scheduleCreateCmd.Flags().Bool("active", true, "Whether or not the schedule is active") + scheduleCreateCmd.Flags().StringSliceVar(&variableList, "variable", []string{}, "Pass variables to schedule in format :") _ = scheduleCreateCmd.MarkFlagRequired("ref") _ = scheduleCreateCmd.MarkFlagRequired("cron") diff --git a/commands/schedule/create/create_test.go b/commands/schedule/create/create_test.go index 580d33ac..5926a314 100644 --- a/commands/schedule/create/create_test.go +++ b/commands/schedule/create/create_test.go @@ -47,6 +47,39 @@ func Test_ScheduleCreate(t *testing.T) { ExpectedMsg: []string{""}, cli: "--cron '*0 * * * *' --description 'example pipeline'", }, + { + Name: "Schedule created but with skipped variable", + wantStderr: "Invalid format for --variable: foo", + wantErr: true, + cli: "--cron '*0 * * * *' --description 'example pipeline' --ref 'main' --variable 'foo'", + httpMocks: []httpMock{ + { + http.MethodPost, + "/api/v4/projects/OWNER/REPO/pipeline_schedules", + http.StatusCreated, + `{}`, + }, + }, + }, + { + Name: "Schedule created with variable", + ExpectedMsg: []string{"Created schedule"}, + cli: "--cron '*0 * * * *' --description 'example pipeline' --ref 'main' --variable 'foo:bar'", + httpMocks: []httpMock{ + { + http.MethodPost, + "/api/v4/projects/OWNER/REPO/pipeline_schedules", + http.StatusCreated, + `{}`, + }, + { + http.MethodPost, + "/api/v4/projects/OWNER/REPO/pipeline_schedules/0/variables", + http.StatusCreated, + `{}`, + }, + }, + }, } for _, tc := range testCases { diff --git a/docs/source/ci/ci/lint.md b/docs/source/ci/ci/lint.md index 5081ce5d..25a5e7b1 100644 --- a/docs/source/ci/ci/lint.md +++ b/docs/source/ci/ci/lint.md @@ -29,6 +29,14 @@ $ glab ci lint path/to/.gitlab-ci.yml ``` +## Options + +```plaintext + --dry-run Run pipeline creation simulation. + --include-jobs The response should include the list of jobs that would exist in a static check or pipeline simulation. + --ref string When dry-run is true, sets the branch or tag context for validating the CI/CD YAML configuration. +``` + ## Options inherited from parent commands ```plaintext diff --git a/docs/source/ci/lint.md b/docs/source/ci/lint.md index 15cc9a69..683f6cda 100644 --- a/docs/source/ci/lint.md +++ b/docs/source/ci/lint.md @@ -29,6 +29,14 @@ $ glab ci lint path/to/.gitlab-ci.yml ``` +## Options + +```plaintext + --dry-run Run pipeline creation simulation. + --include-jobs The response should include the list of jobs that would exist in a static check or pipeline simulation. + --ref string When dry-run is true, sets the branch or tag context for validating the CI/CD YAML configuration. +``` + ## Options inherited from parent commands ```plaintext diff --git a/docs/source/schedule/create.md b/docs/source/schedule/create.md index 15dd6249..026c7e9d 100644 --- a/docs/source/schedule/create.md +++ b/docs/source/schedule/create.md @@ -20,7 +20,7 @@ glab schedule create [flags] ## Examples ```plaintext -glab schedule create --cron "0 * * * *" --description "Describe your pipeline here" --ref "main" +glab schedule create --cron "0 * * * *" --description "Describe your pipeline here" --ref "main" --variable "foo:bar" --variable "baz:baz" ``` @@ -32,6 +32,7 @@ glab schedule create --cron "0 * * * *" --description "Describe your pipeline he --cronTimeZone string Cron timezone (default "UTC") --description string Description of the schedule --ref string Target branch or tag + --variable strings Pass variables to schedule in format : ``` ## Options inherited from parent commands