feat(schedule): Add commands to create and delete schedules

This commit is contained in:
Sebastian Gumprich 2023-06-09 02:40:11 +00:00 committed by Gary Holtz
parent a81a2ae527
commit afdcc8b053
9 changed files with 403 additions and 0 deletions

View File

@ -33,3 +33,28 @@ 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 {
if client == nil {
client = apiClient.Lab()
}
_, _, err := client.PipelineSchedules.CreatePipelineSchedule(repo, scheduleOpts, opts...)
if err != nil {
return fmt.Errorf("creating scheduled pipeline status: %w", err)
}
return nil
}
var DeleteSchedule = func(client *gitlab.Client, scheduleId int, repo string, opts ...gitlab.RequestOptionFunc) (err error) {
if client == nil {
client = apiClient.Lab()
}
_, err = client.PipelineSchedules.DeletePipelineSchedule(repo, scheduleId, opts...)
if err != nil {
return fmt.Errorf("deleting scheduled pipeline status: %w", err)
}
return nil
}

View File

@ -0,0 +1,67 @@
package create
import (
"fmt"
"gitlab.com/gitlab-org/cli/api"
"gitlab.com/gitlab-org/cli/commands/cmdutils"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
"github.com/xanzy/go-gitlab"
)
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"
`),
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
apiClient, err := f.HttpClient()
if err != nil {
return err
}
repo, err := f.BaseRepo()
if err != nil {
return err
}
l := &gitlab.CreatePipelineScheduleOptions{}
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")
l.Description = &description
l.Ref = &ref
l.Cron = &cron
l.CronTimezone = &cronTimeZone
l.Active = &active
err = api.CreateSchedule(apiClient, repo.FullName(), l)
if err != nil {
return err
}
fmt.Fprintln(f.IO.StdOut, "Created schedule")
return nil
},
}
scheduleCreateCmd.Flags().String("description", "", "Description of the schedule")
scheduleCreateCmd.Flags().String("ref", "", "Target branch or tag")
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.MarkFlagRequired("ref")
_ = scheduleCreateCmd.MarkFlagRequired("cron")
_ = scheduleCreateCmd.MarkFlagRequired("description")
return scheduleCreateCmd
}

View File

@ -0,0 +1,86 @@
package create
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/cli/commands/cmdtest"
"gitlab.com/gitlab-org/cli/pkg/httpmock"
"gitlab.com/gitlab-org/cli/test"
)
func Test_ScheduleCreate(t *testing.T) {
type httpMock struct {
method string
path string
status int
body string
}
testCases := []struct {
Name string
ExpectedMsg []string
wantErr bool
cli string
wantStderr string
httpMocks []httpMock
}{
{
Name: "Schedule created",
ExpectedMsg: []string{"Created schedule"},
cli: "--cron '*0 * * * *' --description 'example pipeline' --ref 'main'",
httpMocks: []httpMock{
{
http.MethodPost,
"/api/v4/projects/OWNER/REPO/pipeline_schedules",
http.StatusCreated,
`{}`,
},
},
},
{
Name: "Schedule not created because of missing ref",
wantStderr: "required flag(s) \"ref\" not set",
wantErr: true,
ExpectedMsg: []string{""},
cli: "--cron '*0 * * * *' --description 'example pipeline'",
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
fakeHTTP := &httpmock.Mocker{
MatchURL: httpmock.PathAndQuerystring,
}
defer fakeHTTP.Verify(t)
for _, mock := range tc.httpMocks {
fakeHTTP.RegisterResponder(mock.method, mock.path, httpmock.NewStringResponse(mock.status, mock.body))
}
out, err := runCommand(fakeHTTP, false, tc.cli)
for _, msg := range tc.ExpectedMsg {
require.Contains(t, out.String(), msg)
}
if err != nil {
if tc.wantErr == true {
if assert.Error(t, err) {
require.Equal(t, tc.wantStderr, err.Error())
}
return
}
}
})
}
}
func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
ios, _, stdout, stderr := cmdtest.InitIOStreams(isTTY, "")
factory := cmdtest.InitFactory(ios, rt)
_, _ = factory.HttpClient()
cmd := NewCmdCreate(factory)
return cmdtest.ExecuteCommand(cmd, cli, stdout, stderr)
}

View File

@ -0,0 +1,63 @@
package delete
import (
"fmt"
"strconv"
"gitlab.com/gitlab-org/cli/api"
"gitlab.com/gitlab-org/cli/commands/cmdutils"
"gitlab.com/gitlab-org/cli/pkg/iostreams"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
)
type RunOpts struct {
ScheduleId int
IO *iostreams.IOStreams
}
func NewCmdDelete(f *cmdutils.Factory) *cobra.Command {
opts := &RunOpts{
IO: f.IO,
}
scheduleDeleteCmd := &cobra.Command{
Use: "delete [flags]",
Short: `Delete schedule with the specified ID.`,
Example: heredoc.Doc(`
glab schedule delete 10
`),
Long: ``,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
apiClient, err := f.HttpClient()
if err != nil {
return err
}
repo, err := f.BaseRepo()
if err != nil {
return err
}
id, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
scheduleId := int(id)
if err != nil {
return err
}
err = api.DeleteSchedule(apiClient, scheduleId, repo.FullName())
if err != nil {
return err
}
fmt.Fprintln(opts.IO.StdOut, "Deleted schedule with ID", scheduleId)
return nil
},
}
return scheduleDeleteCmd
}

View File

@ -0,0 +1,82 @@
package delete
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/cli/commands/cmdtest"
"gitlab.com/gitlab-org/cli/pkg/httpmock"
"gitlab.com/gitlab-org/cli/test"
)
func Test_ScheduleDelete(t *testing.T) {
type httpMock struct {
method string
path string
status int
body string
}
testCases := []struct {
Name string
ExpectedMsg []string
wantErr bool
cli string
wantStderr string
httpMocks []httpMock
}{
{
Name: "Schedule deleted",
ExpectedMsg: []string{"Deleted schedule with ID 1"},
cli: "1",
httpMocks: []httpMock{
{
http.MethodDelete,
"/api/v4/projects/OWNER/REPO/pipeline_schedules/1",
http.StatusNoContent,
"",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
fakeHTTP := &httpmock.Mocker{
MatchURL: httpmock.PathAndQuerystring,
}
defer fakeHTTP.Verify(t)
for _, mock := range tc.httpMocks {
fakeHTTP.RegisterResponder(mock.method, mock.path, httpmock.NewStringResponse(mock.status, mock.body))
}
out, err := runCommand(fakeHTTP, false, tc.cli)
fmt.Print(err)
if err != nil {
if tc.wantErr == true {
if assert.Error(t, err) {
require.Equal(t, tc.wantStderr, err.Error())
}
return
}
}
for _, msg := range tc.ExpectedMsg {
fmt.Print(msg)
require.Contains(t, out.String(), msg)
}
})
}
}
func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
ios, _, stdout, stderr := cmdtest.InitIOStreams(isTTY, "")
factory := cmdtest.InitFactory(ios, rt)
_, _ = factory.HttpClient()
cmd := NewCmdDelete(factory)
return cmdtest.ExecuteCommand(cmd, cli, stdout, stderr)
}

View File

@ -1,6 +1,8 @@
package schedule
import (
scheduleCreateCmd "gitlab.com/gitlab-org/cli/commands/schedule/create"
scheduleDeleteCmd "gitlab.com/gitlab-org/cli/commands/schedule/delete"
scheduleListCmd "gitlab.com/gitlab-org/cli/commands/schedule/list"
scheduleRunCmd "gitlab.com/gitlab-org/cli/commands/schedule/run"
@ -21,6 +23,8 @@ func NewCmdSchedule(f *cmdutils.Factory) *cobra.Command {
scheduleCmd.AddCommand(scheduleListCmd.NewCmdList(f))
scheduleCmd.AddCommand(scheduleRunCmd.NewCmdRun(f))
scheduleCmd.AddCommand(scheduleCreateCmd.NewCmdCreate(f))
scheduleCmd.AddCommand(scheduleDeleteCmd.NewCmdDelete(f))
return scheduleCmd
}

View File

@ -0,0 +1,42 @@
---
stage: Create
group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
<!--
This documentation is auto generated by a script.
Please do not edit this file directly. Run `make gen-docs` instead.
-->
# `glab schedule create`
Schedule a new pipeline.
```plaintext
glab schedule create [flags]
```
## Examples
```plaintext
glab schedule create --cron "0 * * * *" --description "Describe your pipeline here" --ref "main"
```
## Options
```plaintext
--active Whether or not the schedule is active (default true)
--cron string Cron interval pattern
--cronTimeZone string Cron timezone (default "UTC")
--description string Description of the schedule
--ref string Target branch or tag
```
## Options inherited from parent commands
```plaintext
--help Show help for command
-R, --repo OWNER/REPO Select another repository using the OWNER/REPO or `GROUP/NAMESPACE/REPO` format or full URL or git URL
```

View File

@ -0,0 +1,32 @@
---
stage: Create
group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
<!--
This documentation is auto generated by a script.
Please do not edit this file directly. Run `make gen-docs` instead.
-->
# `glab schedule delete`
Delete schedule with the specified ID.
```plaintext
glab schedule delete [flags]
```
## Examples
```plaintext
glab schedule delete 10
```
## Options inherited from parent commands
```plaintext
--help Show help for command
-R, --repo OWNER/REPO Select another repository using the OWNER/REPO or `GROUP/NAMESPACE/REPO` format or full URL or git URL
```

View File

@ -34,5 +34,7 @@ skd
## Subcommands
- [create](create.md)
- [delete](delete.md)
- [list](list.md)
- [run](run.md)