From 483bff0c86934aa1143e33316ab0d4548e7bf356 Mon Sep 17 00:00:00 2001 From: claire1618 <55173466+claire1618@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:25:25 -0600 Subject: [PATCH 1/6] fix: enabling a delete event to be allowed (#342) Co-authored-by: Claire.Nicholas --- library/events.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/events.go b/library/events.go index 82eeba08..64ed8609 100644 --- a/library/events.go +++ b/library/events.go @@ -67,6 +67,10 @@ func (e *Events) Allowed(event, action string) bool { allowed = e.GetDeployment().GetCreated() case constants.EventSchedule: allowed = e.GetSchedule().GetRun() + case constants.EventDelete + ":" + constants.ActionBranch: + allowed = e.GetDelete().GetBranch() + case constants.EventDelete + ":" + constants.ActionTag: + allowed = e.GetDelete().GetTag() } return allowed From a4d640c8760ef5a6525a4941b1418635f867f981 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:34:21 -0500 Subject: [PATCH 2/6] fix(repo): remove dead code EventAllowed (#343) --- library/events.go | 4 +++- library/repo.go | 36 ------------------------------------ 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/library/events.go b/library/events.go index 64ed8609..34b1d710 100644 --- a/library/events.go +++ b/library/events.go @@ -40,10 +40,12 @@ func NewEventsFromMask(mask int64) *Events { return e } -// EventAllowed determines whether or not an event is allowed based on the repository settings. +// Allowed determines whether or not an event + action is allowed based on whether +// its event:action is set to true in the Events struct. func (e *Events) Allowed(event, action string) bool { allowed := false + // if there is an action, create `event:action` comparator string if len(action) > 0 { event = event + ":" + action } diff --git a/library/repo.go b/library/repo.go index 44c9bd58..845028b9 100644 --- a/library/repo.go +++ b/library/repo.go @@ -5,8 +5,6 @@ package library import ( "fmt" "strings" - - "github.com/go-vela/types/constants" ) // Repo is the library representation of a repo. @@ -764,40 +762,6 @@ func (r *Repo) SetApproveBuild(v string) { r.ApproveBuild = &v } -// EventAllowed determines whether or not an event is allowed based on the repository settings. -func (r *Repo) EventAllowed(event, action string) (allowed bool) { - allowed = false - - if len(action) > 0 { - event = event + ":" + action - } - - switch event { - case constants.EventPush: - allowed = r.GetAllowEvents().GetPush().GetBranch() - case constants.EventPull + ":" + constants.ActionOpened: - allowed = r.GetAllowEvents().GetPullRequest().GetOpened() - case constants.EventPull + ":" + constants.ActionSynchronize: - allowed = r.GetAllowEvents().GetPullRequest().GetSynchronize() - case constants.EventPull + ":" + constants.ActionEdited: - allowed = r.GetAllowEvents().GetPullRequest().GetEdited() - case constants.EventTag: - allowed = r.GetAllowEvents().GetPush().GetTag() - case constants.EventComment + ":" + constants.ActionCreated: - allowed = r.GetAllowEvents().GetComment().GetCreated() - case constants.EventComment + ":" + constants.ActionEdited: - allowed = r.GetAllowEvents().GetComment().GetEdited() - case constants.EventDeploy: - allowed = r.GetAllowEvents().GetDeployment().GetCreated() - case constants.EventDelete + ":" + constants.ActionBranch: - allowed = r.GetAllowEvents().GetDelete().GetBranch() - case constants.EventDelete + ":" + constants.ActionTag: - allowed = r.GetAllowEvents().GetDelete().GetTag() - } - - return allowed -} - // String implements the Stringer interface for the Repo type. // //nolint:dupl // ignore duplicate with test func From 00dcbc6ffa55754367daa69d920e94dad324d25d Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:10:36 -0500 Subject: [PATCH 3/6] enhance(ci): include PR title validation workflow (#344) * enhance(ci): include PR title validation workflow * update action name * include all necessary types * point to docs and add a couple extra options --- .github/workflows/pr-title-validate.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/pr-title-validate.yml diff --git a/.github/workflows/pr-title-validate.yml b/.github/workflows/pr-title-validate.yml new file mode 100644 index 00000000..f62dacf0 --- /dev/null +++ b/.github/workflows/pr-title-validate.yml @@ -0,0 +1,17 @@ +# name of the action +name: validate PR title + +# trigger on pull_request events of the opened & edited type. +on: + pull_request: + types: [ opened, synchronize, edited, reopened ] + +# pipeline to execute +jobs: + validate: + runs-on: ubuntu-latest + + steps: + - name: validate + run: | + echo "${{ github.event.pull_request.title }}" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)\(.*\):.*$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file From 0e2e18b383dedd61cefd7403e4b6fab458f5d9a2 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:19:28 -0500 Subject: [PATCH 4/6] fix(events): add schedule to ToDatabase and improve tests (#345) --- library/events.go | 14 ++- library/events_test.go | 232 +++++++++++++++++++++++++++++------------ library/repo_test.go | 11 +- 3 files changed, 183 insertions(+), 74 deletions(-) diff --git a/library/events.go b/library/events.go index 34b1d710..fbcb23d0 100644 --- a/library/events.go +++ b/library/events.go @@ -59,6 +59,8 @@ func (e *Events) Allowed(event, action string) bool { allowed = e.GetPullRequest().GetSynchronize() case constants.EventPull + ":" + constants.ActionEdited: allowed = e.GetPullRequest().GetEdited() + case constants.EventPull + ":" + constants.ActionReopened: + allowed = e.GetPullRequest().GetReopened() case constants.EventTag: allowed = e.GetPush().GetTag() case constants.EventComment + ":" + constants.ActionCreated: @@ -99,6 +101,10 @@ func (e *Events) List() []string { eventSlice = append(eventSlice, constants.EventPull+":"+constants.ActionEdited) } + if e.GetPullRequest().GetReopened() { + eventSlice = append(eventSlice, constants.EventPull+":"+constants.ActionReopened) + } + if e.GetPush().GetTag() { eventSlice = append(eventSlice, constants.EventTag) } @@ -132,7 +138,13 @@ func (e *Events) List() []string { // ToDatabase is an Events method that converts a nested Events struct into an integer event mask. func (e *Events) ToDatabase() int64 { - return 0 | e.GetPush().ToMask() | e.GetPullRequest().ToMask() | e.GetComment().ToMask() | e.GetDeployment().ToMask() | e.GetDelete().ToMask() + return 0 | + e.GetPush().ToMask() | + e.GetPullRequest().ToMask() | + e.GetComment().ToMask() | + e.GetDeployment().ToMask() | + e.GetSchedule().ToMask() | + e.GetDelete().ToMask() } // GetPush returns the Push field from the provided Events. If the object is nil, diff --git a/library/events_test.go b/library/events_test.go index 4e3f1458..c1bafe94 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -8,17 +8,25 @@ import ( "github.com/go-vela/types/constants" "github.com/go-vela/types/library/actions" + "github.com/google/go-cmp/cmp" ) func TestLibrary_Events_Getters(t *testing.T) { + // setup types + eventsOne, eventsTwo := testEvents() + // setup tests tests := []struct { events *Events want *Events }{ { - events: testEvents(), - want: testEvents(), + events: eventsOne, + want: eventsOne, + }, + { + events: eventsTwo, + want: eventsTwo, }, { events: new(Events), @@ -44,6 +52,10 @@ func TestLibrary_Events_Getters(t *testing.T) { t.Errorf("GetComment is %v, want %v", test.events.GetPush(), test.want.GetPush()) } + if !reflect.DeepEqual(test.events.GetSchedule(), test.want.GetSchedule()) { + t.Errorf("GetSchedule is %v, want %v", test.events.GetSchedule(), test.want.GetSchedule()) + } + if !reflect.DeepEqual(test.events.GetDelete(), test.want.GetDelete()) { t.Errorf("GetDelete is %v, want %v", test.events.GetDelete(), test.want.GetDelete()) } @@ -54,14 +66,20 @@ func TestLibrary_Events_Setters(t *testing.T) { // setup types var e *Events + eventsOne, eventsTwo := testEvents() + // setup tests tests := []struct { events *Events want *Events }{ { - events: testEvents(), - want: testEvents(), + events: eventsOne, + want: eventsOne, + }, + { + events: eventsTwo, + want: eventsTwo, }, { events: e, @@ -75,6 +93,7 @@ func TestLibrary_Events_Setters(t *testing.T) { test.events.SetPullRequest(test.want.GetPullRequest()) test.events.SetDeployment(test.want.GetDeployment()) test.events.SetComment(test.want.GetComment()) + test.events.SetSchedule(test.want.GetSchedule()) test.events.SetDelete(test.want.GetDelete()) if !reflect.DeepEqual(test.events.GetPush(), test.want.GetPush()) { @@ -93,6 +112,10 @@ func TestLibrary_Events_Setters(t *testing.T) { t.Errorf("SetComment is %v, want %v", test.events.GetComment(), test.want.GetComment()) } + if !reflect.DeepEqual(test.events.GetSchedule(), test.want.GetSchedule()) { + t.Errorf("SetSchedule is %v, want %v", test.events.GetSchedule(), test.want.GetSchedule()) + } + if !reflect.DeepEqual(test.events.GetDelete(), test.want.GetDelete()) { t.Errorf("SetDelete is %v, want %v", test.events.GetDelete(), test.want.GetDelete()) } @@ -101,108 +124,181 @@ func TestLibrary_Events_Setters(t *testing.T) { func TestLibrary_Events_List(t *testing.T) { // setup types - e := testEvents() + eventsOne, eventsTwo := testEvents() + + wantOne := []string{ + "push", + "pull_request:opened", + "pull_request:synchronize", + "pull_request:reopened", + "tag", + "comment:created", + "schedule", + "delete:branch", + } - want := []string{"push", "pull_request:opened", "pull_request:synchronize", "tag", "delete:branch", "delete:tag"} + wantTwo := []string{ + "pull_request:edited", + "deployment", + "comment:edited", + "delete:tag", + } // run test - got := e.List() + gotOne := eventsOne.List() + + if diff := cmp.Diff(wantOne, gotOne); diff != "" { + t.Errorf("(List: -want +got):\n%s", diff) + } + + gotTwo := eventsTwo.List() - if !reflect.DeepEqual(got, want) { - t.Errorf("List is %v, want %v", got, want) + if diff := cmp.Diff(wantTwo, gotTwo); diff != "" { + t.Errorf("(List Inverse: -want +got):\n%s", diff) } } -func TestLibrary_Events_NewEventsFromMask(t *testing.T) { +func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { // setup mask - mask := int64( + maskOne := int64( constants.AllowPushBranch | constants.AllowPushTag | constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen | - constants.AllowDeleteBranch | + constants.AllowCommentCreate | + constants.AllowSchedule | + constants.AllowDeleteBranch, + ) + + maskTwo := int64( + constants.AllowPullEdit | + constants.AllowCommentEdit | + constants.AllowDeployCreate | constants.AllowDeleteTag, ) - want := testEvents() + wantOne, wantTwo := testEvents() // run test - got := NewEventsFromMask(mask) + gotOne := NewEventsFromMask(maskOne) + + if diff := cmp.Diff(wantOne, gotOne); diff != "" { + t.Errorf("(NewEventsFromMask: -want +got):\n%s", diff) + } + + gotTwo := NewEventsFromMask(maskTwo) - if !reflect.DeepEqual(got, want) { - t.Errorf("NewEventsFromMask is %v, want %v", got, want) + if diff := cmp.Diff(wantTwo, gotTwo); diff != "" { + t.Errorf("(NewEventsFromMask Inverse: -want +got):\n%s", diff) + } + + // ensure ToDatabase maps back to masks + if gotOne.ToDatabase() != maskOne { + t.Errorf("ToDatabase returned %d, want %d", gotOne.ToDatabase(), maskOne) + } + + if gotTwo.ToDatabase() != maskTwo { + t.Errorf("ToDatabase returned %d, want %d", gotTwo.ToDatabase(), maskTwo) } } func TestLibrary_Events_Allowed(t *testing.T) { + // setup types + eventsOne, eventsTwo := testEvents() + // setup tests tests := []struct { - events *Events event string action string want bool }{ - { - events: testEvents(), - event: "pull_request", - action: "opened", - want: true, - }, - { - events: testEvents(), - event: "deployment", - want: false, - }, - { - events: testEvents(), - event: "push", - want: true, - }, + {event: "push", want: true}, + {event: "tag", want: true}, + {event: "pull_request", action: "opened", want: true}, + {event: "pull_request", action: "synchronize", want: true}, + {event: "pull_request", action: "edited", want: false}, + {event: "pull_request", action: "reopened", want: true}, + {event: "deployment", want: false}, + {event: "comment", action: "created", want: true}, + {event: "comment", action: "edited", want: false}, + {event: "schedule", want: true}, + {event: "delete", action: "branch", want: true}, + {event: "delete", action: "tag", want: false}, } for _, test := range tests { - got := test.events.Allowed(test.event, test.action) + gotOne := eventsOne.Allowed(test.event, test.action) + gotTwo := eventsTwo.Allowed(test.event, test.action) + + if gotOne != test.want { + t.Errorf("Allowed for %s/%s is %v, want %v", test.event, test.action, gotOne, test.want) + } - if got != test.want { - t.Errorf("Allowed is %v, want %v", got, test.want) + if gotTwo == test.want { + t.Errorf("Allowed Inverse for %s/%s is %v, want %v", test.event, test.action, gotTwo, !test.want) } } } -func testEvents() *Events { - e := new(Events) - - pr := new(actions.Pull) - pr.SetOpened(true) - pr.SetSynchronize(true) - pr.SetEdited(false) - pr.SetReopened(true) - - push := new(actions.Push) - push.SetBranch(true) - push.SetTag(true) - - deploy := new(actions.Deploy) - deploy.SetCreated(false) +// testEvents is a helper test function that returns an Events struct and its inverse for unit test coverage. +func testEvents() (*Events, *Events) { + tBool := true + fBool := false - comment := new(actions.Comment) - comment.SetCreated(false) - comment.SetEdited(false) - - schedule := new(actions.Schedule) - schedule.SetRun(false) - - deletion := new(actions.Delete) - deletion.SetBranch(true) - deletion.SetTag(true) + e1 := &Events{ + Push: &actions.Push{ + Branch: &tBool, + Tag: &tBool, + }, + PullRequest: &actions.Pull{ + Opened: &tBool, + Synchronize: &tBool, + Edited: &fBool, + Reopened: &tBool, + }, + Deployment: &actions.Deploy{ + Created: &fBool, + }, + Comment: &actions.Comment{ + Created: &tBool, + Edited: &fBool, + }, + Schedule: &actions.Schedule{ + Run: &tBool, + }, + Delete: &actions.Delete{ + Branch: &tBool, + Tag: &fBool, + }, + } - e.SetPush(push) - e.SetPullRequest(pr) - e.SetDeployment(deploy) - e.SetComment(comment) - e.SetSchedule(schedule) - e.SetDelete(deletion) + e2 := &Events{ + Push: &actions.Push{ + Branch: &fBool, + Tag: &fBool, + }, + PullRequest: &actions.Pull{ + Opened: &fBool, + Synchronize: &fBool, + Edited: &tBool, + Reopened: &fBool, + }, + Deployment: &actions.Deploy{ + Created: &tBool, + }, + Comment: &actions.Comment{ + Created: &fBool, + Edited: &tBool, + }, + Schedule: &actions.Schedule{ + Run: &fBool, + }, + Delete: &actions.Delete{ + Branch: &fBool, + Tag: &tBool, + }, + } - return e + return e1, e2 } diff --git a/library/repo_test.go b/library/repo_test.go index e897142b..2f2684cd 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/go-vela/types/constants" + "github.com/google/go-cmp/cmp" ) func TestLibrary_Repo_Environment(t *testing.T) { @@ -19,7 +20,7 @@ func TestLibrary_Repo_Environment(t *testing.T) { "VELA_REPO_ALLOW_PULL": "false", "VELA_REPO_ALLOW_PUSH": "true", "VELA_REPO_ALLOW_TAG": "false", - "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,tag,delete:branch,delete:tag", + "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,tag,comment:created,schedule,delete:branch", "VELA_REPO_BRANCH": "main", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "10", @@ -40,7 +41,7 @@ func TestLibrary_Repo_Environment(t *testing.T) { "REPOSITORY_ALLOW_PULL": "false", "REPOSITORY_ALLOW_PUSH": "true", "REPOSITORY_ALLOW_TAG": "false", - "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,tag,delete:branch,delete:tag", + "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,tag,comment:created,schedule,delete:branch", "REPOSITORY_BRANCH": "main", "REPOSITORY_CLONE": "https://github.com/github/octocat.git", "REPOSITORY_FULL_NAME": "github/octocat", @@ -56,8 +57,8 @@ func TestLibrary_Repo_Environment(t *testing.T) { // run test got := testRepo().Environment() - if !reflect.DeepEqual(got, want) { - t.Errorf("Environment is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(Environment: -want +got):\n%s", diff) } } @@ -402,7 +403,7 @@ func TestLibrary_Repo_String(t *testing.T) { func testRepo() *Repo { r := new(Repo) - e := testEvents() + e, _ := testEvents() r.SetID(1) r.SetOrg("github") From f40578eb2dac13c44adc43835a63c75c411b2f12 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:17:17 -0500 Subject: [PATCH 5/6] enhance(env): add VELA_PULL_REQUEST_SOURCE and _TARGET to comment type (#346) --- .github/workflows/pr-title-validate.yml | 2 +- library/build.go | 2 ++ library/build_test.go | 13 +++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-title-validate.yml b/.github/workflows/pr-title-validate.yml index f62dacf0..aa3588f6 100644 --- a/.github/workflows/pr-title-validate.yml +++ b/.github/workflows/pr-title-validate.yml @@ -12,6 +12,6 @@ jobs: runs-on: ubuntu-latest steps: - - name: validate + - name: validate title run: | echo "${{ github.event.pull_request.title }}" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)\(.*\):.*$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file diff --git a/library/build.go b/library/build.go index 5b919dfb..3c872933 100644 --- a/library/build.go +++ b/library/build.go @@ -148,6 +148,8 @@ func (b *Build) Environment(workspace, channel string) map[string]string { envs["BUILD_PULL_REQUEST_NUMBER"] = number envs["VELA_BUILD_PULL_REQUEST"] = number envs["VELA_PULL_REQUEST"] = number + envs["VELA_PULL_REQUEST_SOURCE"] = b.GetHeadRef() + envs["VELA_PULL_REQUEST_TARGET"] = b.GetBaseRef() } // check if the Build event is deployment diff --git a/library/build_test.go b/library/build_test.go index bd0b265c..5c82c7a3 100644 --- a/library/build_test.go +++ b/library/build_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-vela/types/raw" + "github.com/google/go-cmp/cmp" ) func TestLibrary_Build_Duration(t *testing.T) { @@ -51,6 +52,8 @@ func TestLibrary_Build_Environment(t *testing.T) { _comment.SetEvent("comment") _comment.SetEventAction("created") _comment.SetRef("refs/pulls/1/head") + _comment.SetHeadRef("dev") + _comment.SetBaseRef("main") _deploy := testBuild() _deploy.SetEvent("deployment") @@ -146,7 +149,7 @@ func TestLibrary_Build_Environment(t *testing.T) { "VELA_BUILD_APPROVED_BY": "OctoCat", "VELA_BUILD_AUTHOR": "OctoKitty", "VELA_BUILD_AUTHOR_EMAIL": "OctoKitty@github.com", - "VELA_BUILD_BASE_REF": "", + "VELA_BUILD_BASE_REF": "main", "VELA_BUILD_BRANCH": "main", "VELA_BUILD_CHANNEL": "TODO", "VELA_BUILD_CLONE": "https://github.com/github/octocat.git", @@ -171,9 +174,11 @@ func TestLibrary_Build_Environment(t *testing.T) { "VELA_BUILD_TITLE": "push received from https://github.com/github/octocat", "VELA_BUILD_WORKSPACE": "TODO", "VELA_PULL_REQUEST": "1", + "VELA_PULL_REQUEST_SOURCE": "dev", + "VELA_PULL_REQUEST_TARGET": "main", "BUILD_AUTHOR": "OctoKitty", "BUILD_AUTHOR_EMAIL": "OctoKitty@github.com", - "BUILD_BASE_REF": "", + "BUILD_BASE_REF": "main", "BUILD_BRANCH": "main", "BUILD_CHANNEL": "TODO", "BUILD_CLONE": "https://github.com/github/octocat.git", @@ -439,8 +444,8 @@ func TestLibrary_Build_Environment(t *testing.T) { for _, test := range tests { got := test.build.Environment("TODO", "TODO") - if !reflect.DeepEqual(got, test.want) { - t.Errorf("Environment is %v, want %v", got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("(Environment: -want +got):\n%s", diff) } } } From 8a6ef2dcdbe20b4135bf2b3094e278c7a741755c Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:01:59 -0500 Subject: [PATCH 6/6] fix(ci): title validator handle no parentheses + enforce whitespace (#347) --- .github/workflows/pr-title-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-title-validate.yml b/.github/workflows/pr-title-validate.yml index aa3588f6..223f7cdf 100644 --- a/.github/workflows/pr-title-validate.yml +++ b/.github/workflows/pr-title-validate.yml @@ -14,4 +14,4 @@ jobs: steps: - name: validate title run: | - echo "${{ github.event.pull_request.title }}" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)\(.*\):.*$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file + echo "${{ github.event.pull_request.title }}" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)(\(.*\)|):\s.+$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file