diff --git a/constants/action.go b/constants/action.go index bcff84b7..f1e18c6e 100644 --- a/constants/action.go +++ b/constants/action.go @@ -24,4 +24,10 @@ const ( // ActionTransferred defines the action for transferring repository ownership. ActionTransferred = "transferred" + + // ActionBranch defines the action for deleting a branch. + ActionBranch = "branch" + + // ActionTag defines the action for deleting a tag. + ActionTag = "tag" ) diff --git a/constants/allow_events.go b/constants/allow_events.go index f5d2cdd9..3e00f2d2 100644 --- a/constants/allow_events.go +++ b/constants/allow_events.go @@ -21,4 +21,6 @@ const ( AllowCommentCreate AllowCommentEdit AllowSchedule + AllowDeleteBranch + AllowDeleteTag ) diff --git a/constants/event.go b/constants/event.go index cf72a089..9d59f7e5 100644 --- a/constants/event.go +++ b/constants/event.go @@ -7,6 +7,9 @@ const ( // EventComment defines the event type for comments added to a pull request. EventComment = "comment" + // EventDelete defines the event type for build and repo delete events. + EventDelete = "delete" + // EventDeploy defines the event type for build and repo deployment events. EventDeploy = "deployment" diff --git a/library/actions/comment.go b/library/actions/comment.go index 499cfed6..c0e04298 100644 --- a/library/actions/comment.go +++ b/library/actions/comment.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 - +// +//nolint:dupl // similar code to delete.go package actions import "github.com/go-vela/types/constants" diff --git a/library/actions/delete.go b/library/actions/delete.go new file mode 100644 index 00000000..fa88046d --- /dev/null +++ b/library/actions/delete.go @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +// +//nolint:dupl // similar code to push.go +package actions + +import "github.com/go-vela/types/constants" + +// Delete is the library representation of the various actions associated +// with the delete event webhook from the SCM. +type Delete struct { + Branch *bool `json:"branch"` + Tag *bool `json:"tag"` +} + +// FromMask returns the Delete type resulting from the provided integer mask. +func (a *Delete) FromMask(mask int64) *Delete { + a.SetBranch(mask&constants.AllowDeleteBranch > 0) + a.SetTag(mask&constants.AllowDeleteTag > 0) + + return a +} + +// ToMask returns the integer mask of the values for the Delete set. +func (a *Delete) ToMask() int64 { + mask := int64(0) + + if a.GetBranch() { + mask = mask | constants.AllowDeleteBranch + } + + if a.GetTag() { + mask = mask | constants.AllowDeleteTag + } + + return mask +} + +// GetBranch returns the Branch field from the provided Delete. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (a *Delete) GetBranch() bool { + // return zero value if Delete type or Branch field is nil + if a == nil || a.Branch == nil { + return false + } + + return *a.Branch +} + +// GetTag returns the Tag field from the provided Delete. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (a *Delete) GetTag() bool { + // return zero value if Delete type or Tag field is nil + if a == nil || a.Tag == nil { + return false + } + + return *a.Tag +} + +// SetBranch sets the Delete Branch field. +// +// When the provided Delete type is nil, it +// will set nothing and immediately return. +func (a *Delete) SetBranch(v bool) { + // return if Events type is nil + if a == nil { + return + } + + a.Branch = &v +} + +// SetTag sets the Delete Tag field. +// +// When the provided Delete type is nil, it +// will set nothing and immediately return. +func (a *Delete) SetTag(v bool) { + // return if Events type is nil + if a == nil { + return + } + + a.Tag = &v +} diff --git a/library/actions/delete_test.go b/library/actions/delete_test.go new file mode 100644 index 00000000..3c7cd539 --- /dev/null +++ b/library/actions/delete_test.go @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 + +package actions + +import ( + "reflect" + "testing" + + "github.com/go-vela/types/constants" +) + +func TestLibrary_Delete_Getters(t *testing.T) { + // setup tests + tests := []struct { + actions *Delete + want *Delete + }{ + { + actions: testDelete(), + want: testDelete(), + }, + { + actions: new(Delete), + want: new(Delete), + }, + } + + // run tests + for _, test := range tests { + if test.actions.GetBranch() != test.want.GetBranch() { + t.Errorf("GetBranch is %v, want %v", test.actions.GetBranch(), test.want.GetBranch()) + } + + if test.actions.GetTag() != test.want.GetTag() { + t.Errorf("GetTag is %v, want %v", test.actions.GetTag(), test.want.GetTag()) + } + } +} + +func TestLibrary_Delete_Setters(t *testing.T) { + // setup types + var a *Delete + + // setup tests + tests := []struct { + actions *Delete + want *Delete + }{ + { + actions: testDelete(), + want: testDelete(), + }, + { + actions: a, + want: new(Delete), + }, + } + + // run tests + for _, test := range tests { + test.actions.SetBranch(test.want.GetBranch()) + test.actions.SetTag(test.want.GetTag()) + + if test.actions.GetBranch() != test.want.GetBranch() { + t.Errorf("SetBranch is %v, want %v", test.actions.GetBranch(), test.want.GetBranch()) + } + + if test.actions.GetTag() != test.want.GetTag() { + t.Errorf("SetTag is %v, want %v", test.actions.GetTag(), test.want.GetTag()) + } + } +} + +func TestLibrary_Delete_FromMask(t *testing.T) { + // setup types + mask := testMask() + + want := testDelete() + + // run test + got := new(Delete).FromMask(mask) + + if !reflect.DeepEqual(got, want) { + t.Errorf("FromMask is %v, want %v", got, want) + } +} + +func TestLibrary_Delete_ToMask(t *testing.T) { + // setup types + actions := testDelete() + + want := int64(constants.AllowDeleteBranch | constants.AllowDeleteTag) + + // run test + got := actions.ToMask() + + if want != got { + t.Errorf("ToMask is %v, want %v", got, want) + } +} + +func testDelete() *Delete { + deletion := new(Delete) + deletion.SetBranch(true) + deletion.SetTag(true) + + return deletion +} diff --git a/library/actions/push.go b/library/actions/push.go index 5472af94..9853f454 100644 --- a/library/actions/push.go +++ b/library/actions/push.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 - +// +//nolint:dupl // similar code to comment.go package actions import "github.com/go-vela/types/constants" diff --git a/library/actions/push_test.go b/library/actions/push_test.go index fbdb3cb7..f8cd4c3b 100644 --- a/library/actions/push_test.go +++ b/library/actions/push_test.go @@ -116,6 +116,8 @@ func testMask() int64 { constants.AllowPullReopen | constants.AllowDeployCreate | constants.AllowCommentCreate | - constants.AllowSchedule, + constants.AllowSchedule | + constants.AllowDeleteBranch | + constants.AllowDeleteTag, ) } diff --git a/library/events.go b/library/events.go index 78ce5d8e..82eeba08 100644 --- a/library/events.go +++ b/library/events.go @@ -15,6 +15,7 @@ type Events struct { Deployment *actions.Deploy `json:"deployment"` Comment *actions.Comment `json:"comment"` Schedule *actions.Schedule `json:"schedule"` + Delete *actions.Delete `json:"delete"` } // NewEventsFromMask is an instatiation function for the Events type that @@ -25,6 +26,7 @@ func NewEventsFromMask(mask int64) *Events { deployActions := new(actions.Deploy).FromMask(mask) commentActions := new(actions.Comment).FromMask(mask) scheduleActions := new(actions.Schedule).FromMask(mask) + deleteActions := new(actions.Delete).FromMask(mask) e := new(Events) @@ -33,6 +35,7 @@ func NewEventsFromMask(mask int64) *Events { e.SetDeployment(deployActions) e.SetComment(commentActions) e.SetSchedule(scheduleActions) + e.SetDelete(deleteActions) return e } @@ -110,12 +113,20 @@ func (e *Events) List() []string { eventSlice = append(eventSlice, constants.EventSchedule) } + if e.GetDelete().GetBranch() { + eventSlice = append(eventSlice, constants.EventDelete+":"+constants.ActionBranch) + } + + if e.GetDelete().GetTag() { + eventSlice = append(eventSlice, constants.EventDelete+":"+constants.ActionTag) + } + return eventSlice } // 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() + return 0 | e.GetPush().ToMask() | e.GetPullRequest().ToMask() | e.GetComment().ToMask() | e.GetDeployment().ToMask() | e.GetDelete().ToMask() } // GetPush returns the Push field from the provided Events. If the object is nil, @@ -173,6 +184,17 @@ func (e *Events) GetSchedule() *actions.Schedule { return e.Schedule } +// GetDelete returns the Delete field from the provided Events. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (e *Events) GetDelete() *actions.Delete { + // return zero value if Events type or Comment field is nil + if e == nil || e.Delete == nil { + return new(actions.Delete) + } + + return e.Delete +} + // SetPush sets the Events Push field. // // When the provided Events type is nil, it @@ -237,3 +259,16 @@ func (e *Events) SetSchedule(v *actions.Schedule) { e.Schedule = v } + +// SetDelete sets the Events Delete field. +// +// When the provided Events type is nil, it +// will set nothing and immediately return. +func (e *Events) SetDelete(v *actions.Delete) { + // return if Events type is nil + if e == nil { + return + } + + e.Delete = v +} diff --git a/library/events_test.go b/library/events_test.go index 30ffb963..4e3f1458 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -43,6 +43,10 @@ func TestLibrary_Events_Getters(t *testing.T) { if !reflect.DeepEqual(test.events.GetComment(), test.want.GetComment()) { t.Errorf("GetComment is %v, want %v", test.events.GetPush(), test.want.GetPush()) } + + if !reflect.DeepEqual(test.events.GetDelete(), test.want.GetDelete()) { + t.Errorf("GetDelete is %v, want %v", test.events.GetDelete(), test.want.GetDelete()) + } } } @@ -71,6 +75,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.SetDelete(test.want.GetDelete()) if !reflect.DeepEqual(test.events.GetPush(), test.want.GetPush()) { t.Errorf("SetPush is %v, want %v", test.events.GetPush(), test.want.GetPush()) @@ -87,6 +92,10 @@ func TestLibrary_Events_Setters(t *testing.T) { if !reflect.DeepEqual(test.events.GetComment(), test.want.GetComment()) { t.Errorf("SetComment is %v, want %v", test.events.GetComment(), test.want.GetComment()) } + + if !reflect.DeepEqual(test.events.GetDelete(), test.want.GetDelete()) { + t.Errorf("SetDelete is %v, want %v", test.events.GetDelete(), test.want.GetDelete()) + } } } @@ -94,7 +103,7 @@ func TestLibrary_Events_List(t *testing.T) { // setup types e := testEvents() - want := []string{"push", "pull_request:opened", "pull_request:synchronize", "tag"} + want := []string{"push", "pull_request:opened", "pull_request:synchronize", "tag", "delete:branch", "delete:tag"} // run test got := e.List() @@ -111,7 +120,9 @@ func TestLibrary_Events_NewEventsFromMask(t *testing.T) { constants.AllowPushTag | constants.AllowPullOpen | constants.AllowPullSync | - constants.AllowPullReopen, + constants.AllowPullReopen | + constants.AllowDeleteBranch | + constants.AllowDeleteTag, ) want := testEvents() @@ -182,11 +193,16 @@ func testEvents() *Events { schedule := new(actions.Schedule) schedule.SetRun(false) + deletion := new(actions.Delete) + deletion.SetBranch(true) + deletion.SetTag(true) + e.SetPush(push) e.SetPullRequest(pr) e.SetDeployment(deploy) e.SetComment(comment) e.SetSchedule(schedule) + e.SetDelete(deletion) return e } diff --git a/library/repo.go b/library/repo.go index 2b1bdb51..44c9bd58 100644 --- a/library/repo.go +++ b/library/repo.go @@ -5,6 +5,8 @@ package library import ( "fmt" "strings" + + "github.com/go-vela/types/constants" ) // Repo is the library representation of a repo. @@ -364,7 +366,7 @@ func (r *Repo) GetAllowTag() bool { // When the provided Repo type is nil, or the field within // the type is nil, it returns the zero value for the field. func (r *Repo) GetAllowComment() bool { - // return zero value if Repo type or AllowTag field is nil + // return zero value if Repo type or AllowComment field is nil if r == nil || r.AllowComment == nil { return false } @@ -762,6 +764,40 @@ 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 diff --git a/library/repo_test.go b/library/repo_test.go index e536a135..e897142b 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -19,7 +19,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", + "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,tag,delete:branch,delete:tag", "VELA_REPO_BRANCH": "main", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "10", @@ -40,7 +40,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", + "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,tag,delete:branch,delete:tag", "REPOSITORY_BRANCH": "main", "REPOSITORY_CLONE": "https://github.com/github/octocat.git", "REPOSITORY_FULL_NAME": "github/octocat",