Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions api/models/project_v1_alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,18 @@ func (s *Scheduler) MarshalJSON() ([]byte, error) {
}

type Task struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Scheduled bool `json:"scheduled"`
Id string `json:"id,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"` // Deprecated: Use Reference field instead.
Reference *Reference `json:"reference,omitempty" yaml:"reference,omitempty"`
At string `json:"at,omitempty"`
PipelineFile string `json:"pipeline_file" yaml:"pipeline_file,omitempty"`
Status string `json:"status,omitempty" yaml:"status,omitempty"`
Parameters []TaskParameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Scheduled bool `json:"scheduled"`
Id string `json:"id,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"` // Deprecated: Use Reference field instead.
Reference *Reference `json:"reference,omitempty" yaml:"reference,omitempty"`
At string `json:"at,omitempty"`
PipelineFile string `json:"pipeline_file" yaml:"pipeline_file,omitempty"`
Status string `json:"status,omitempty" yaml:"status,omitempty"`
SkipScheduledRunNotifications bool `json:"skip_scheduled_run_notifications,omitempty" yaml:"skip_scheduled_run_notifications,omitempty"`
SkipManualRunNotifications bool `json:"skip_manual_run_notifications,omitempty" yaml:"skip_manual_run_notifications,omitempty"`
Parameters []TaskParameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
}

// UnmarshalJSON implements custom JSON unmarshaling for backward compatibility
Expand Down
28 changes: 15 additions & 13 deletions api/models/tasks_v1_alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@ type TaskParameterV1Alpha struct {
}

type TaskV1Alpha struct {
ID string `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
ProjectID string `json:"project_id" yaml:"project_id"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
At string `json:"at,omitempty" yaml:"at,omitempty"`
PipelineFile string `json:"pipeline_file" yaml:"pipeline_file"`
RequesterID string `json:"requester_id,omitempty" yaml:"requester_id,omitempty"`
UpdatedAt string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"`
Paused bool `json:"paused" yaml:"paused"`
Suspended bool `json:"suspended" yaml:"suspended"`
Recurring bool `json:"recurring" yaml:"recurring"`
Parameters []TaskParameterV1Alpha `json:"parameters,omitempty" yaml:"parameters,omitempty"`
ID string `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
ProjectID string `json:"project_id" yaml:"project_id"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
At string `json:"at,omitempty" yaml:"at,omitempty"`
PipelineFile string `json:"pipeline_file" yaml:"pipeline_file"`
RequesterID string `json:"requester_id,omitempty" yaml:"requester_id,omitempty"`
UpdatedAt string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"`
Paused bool `json:"paused" yaml:"paused"`
Suspended bool `json:"suspended" yaml:"suspended"`
Recurring bool `json:"recurring" yaml:"recurring"`
SkipScheduledRunNotifications bool `json:"skip_scheduled_run_notifications" yaml:"skip_scheduled_run_notifications"`
SkipManualRunNotifications bool `json:"skip_manual_run_notifications" yaml:"skip_manual_run_notifications"`
Parameters []TaskParameterV1Alpha `json:"parameters,omitempty" yaml:"parameters,omitempty"`
}

type TriggerV1Alpha struct {
Expand Down
64 changes: 64 additions & 0 deletions cmd/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,70 @@ spec:
assert.Equal(t, "", envParam.RegexPattern, "absent regex pattern should default to empty")
}

func Test__ApplyProject__FromYaml_TaskNotificationSkipFlags_Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

yaml_file := `
apiVersion: v1alpha
kind: Project
metadata:
name: Test
id: a13949b7-b2f6-4286-8f26-3962d7e97828
spec:
visibility: public
repository:
url: "git@github.com:/semaphoreci/cli.git"
integration_type: github_token
pipeline_file: ".semaphore/semaphore.yml"
run_on:
- branches
tasks:
- name: nightly
scheduled: true
branch: main
at: "0 3 * * *"
pipeline_file: ".semaphore/cron.yml"
skip_scheduled_run_notifications: true
skip_manual_run_notifications: false
- name: release
scheduled: false
branch: main
pipeline_file: ".semaphore/release.yml"
skip_manual_run_notifications: true
`

yaml_file_path := "/tmp/project_task_skip_flags.yaml"
ioutil.WriteFile(yaml_file_path, []byte(yaml_file), 0644)

var received *models.ProjectV1Alpha

httpmock.RegisterResponder("PATCH", "https://org.semaphoretext.xyz/api/v1alpha/projects/a13949b7-b2f6-4286-8f26-3962d7e97828",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)
received, _ = models.NewProjectV1AlphaFromJson(body)

return httpmock.NewStringResponse(200, string(body)), nil
},
)

RootCmd.SetArgs([]string{"apply", "-f", yaml_file_path})
RootCmd.Execute()

assert.NotNil(t, received)
assert.Len(t, received.Spec.Tasks, 2)

nightly := received.Spec.Tasks[0]
assert.Equal(t, "nightly", nightly.Name)
assert.Equal(t, true, nightly.SkipScheduledRunNotifications)
assert.Equal(t, false, nightly.SkipManualRunNotifications, "explicit false should stay false")

release := received.Spec.Tasks[1]
assert.Equal(t, "release", release.Name)
assert.Equal(t, false, release.SkipScheduledRunNotifications, "absent scheduled flag should default to false")
assert.Equal(t, true, release.SkipManualRunNotifications)
}

func Test__ApplyDeploymentTarget__FromYaml_Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
Expand Down
61 changes: 61 additions & 0 deletions cmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,67 @@ spec:
assert.Equal(t, `^[0-9]+\.[0-9]+\.[0-9]+$`, param.RegexPattern)
}

func Test__CreateProject__FromYaml_TaskNotificationSkipFlags_Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

yaml_file := `
apiVersion: v1alpha
kind: Project
metadata:
name: Test
spec:
visibility: public
repository:
url: "git@github.com:/semaphoreci/cli.git"
integration_type: github_token
pipeline_file: ".semaphore/semaphore.yml"
run_on:
- branches
tasks:
- name: nightly
scheduled: true
branch: main
at: "0 3 * * *"
pipeline_file: ".semaphore/cron.yml"
skip_scheduled_run_notifications: true
- name: release
scheduled: false
branch: main
pipeline_file: ".semaphore/release.yml"
`

yaml_file_path := "/tmp/project_task_skip_flags_create.yaml"
ioutil.WriteFile(yaml_file_path, []byte(yaml_file), 0644)

var received *models.ProjectV1Alpha

httpmock.RegisterResponder("POST", "https://org.semaphoretext.xyz/api/v1alpha/projects",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)
received, _ = models.NewProjectV1AlphaFromJson(body)

return httpmock.NewStringResponse(200, string(body)), nil
},
)

RootCmd.SetArgs([]string{"create", "-f", yaml_file_path})
RootCmd.Execute()

assert.NotNil(t, received)
assert.Len(t, received.Spec.Tasks, 2)

nightly := received.Spec.Tasks[0]
assert.Equal(t, "nightly", nightly.Name)
assert.Equal(t, true, nightly.SkipScheduledRunNotifications)
assert.Equal(t, false, nightly.SkipManualRunNotifications, "absent manual flag should default to false")

release := received.Spec.Tasks[1]
assert.Equal(t, "release", release.Name)
assert.Equal(t, false, release.SkipScheduledRunNotifications, "absent scheduled flag should default to false")
assert.Equal(t, false, release.SkipManualRunNotifications, "absent manual flag should default to false")
}

func Test__CreateNotification__FromYaml__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
Expand Down
4 changes: 4 additions & 0 deletions cmd/edit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ func Test__EditProject__WithTasks__Response200(t *testing.T) {
"scheduled":false,
"branch":"master",
"pipeline_file":".semaphore/cron.yml",
"skip_scheduled_run_notifications":true,
"skip_manual_run_notifications":false,
"parameters":[
{
"name":"param1",
Expand Down Expand Up @@ -365,6 +367,8 @@ func Test__EditProject__WithTasks__Response200(t *testing.T) {
assert.Equal(t, "", task.Branch) // Branch is cleared when auto-creating Reference
assert.Equal(t, task.Scheduled, false)
assert.Equal(t, task.PipelineFile, ".semaphore/cron.yml")
assert.Equal(t, true, task.SkipScheduledRunNotifications, "flag set on the server must survive the edit round-trip")
assert.Equal(t, false, task.SkipManualRunNotifications)

task_parameter := task.Parameters[0]

Expand Down
77 changes: 77 additions & 0 deletions cmd/get_tasks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,80 @@ func Test__ListTasks__WithParameterRegexValidation(t *testing.T) {
assert.True(t, param.ValidateInputFormat)
assert.Equal(t, "^(prod|staging|dev)$", param.RegexPattern)
}

func Test__DescribeTask__WithNotificationSkipFlags(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

taskID := "bb2ba294-d4b3-48bc-90a7-12dd56e9424c"

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/tasks/"+taskID,
func(req *http.Request) (*http.Response, error) {
body := `{
"schedule": {
"id": "bb2ba294-d4b3-48bc-90a7-12dd56e9424c",
"name": "nightly",
"project_id": "aa1ba294-d4b3-48bc-90a7-12dd56e9424a",
"branch": "main",
"pipeline_file": ".semaphore/cron.yml",
"recurring": true,
"skip_scheduled_run_notifications": true,
"skip_manual_run_notifications": false
}
}`
return httpmock.NewStringResponse(200, body), nil
},
)

c := client.NewTasksV1AlphaApi()
task, err := c.DescribeTask(taskID)

assert.NoError(t, err)
assert.True(t, task.Schedule.SkipScheduledRunNotifications, "expected skip_scheduled_run_notifications=true to be parsed")
assert.False(t, task.Schedule.SkipManualRunNotifications)
}

func Test__ListTasks__WithNotificationSkipFlags(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

projectID := "758cb945-7495-4e40-a9a1-4b3991c6a8fe"

httpmock.RegisterRegexpResponder("GET",
regexp.MustCompile(`https://org\.semaphoretext\.xyz/api/v1alpha/tasks\?.*project_id=`+projectID),
func(req *http.Request) (*http.Response, error) {
body := `[
{
"id": "bb2ba294-d4b3-48bc-90a7-12dd56e9424c",
"name": "nightly",
"project_id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe",
"branch": "main",
"pipeline_file": ".semaphore/cron.yml",
"recurring": true,
"skip_manual_run_notifications": true
},
{
"id": "cc3ba294-d4b3-48bc-90a7-12dd56e9424d",
"name": "deploy",
"project_id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe",
"branch": "main",
"pipeline_file": ".semaphore/deploy.yml",
"recurring": false
}
]`
return httpmock.NewStringResponse(200, body), nil
},
)

c := client.NewTasksV1AlphaApi()
tasks, err := c.ListTasks(projectID)

assert.NoError(t, err)
assert.Len(t, tasks, 2)

assert.False(t, tasks[0].SkipScheduledRunNotifications, "absent scheduled flag should default to false")
assert.True(t, tasks[0].SkipManualRunNotifications)

assert.False(t, tasks[1].SkipScheduledRunNotifications)
assert.False(t, tasks[1].SkipManualRunNotifications)
}