From 2e046fceb8fe56fb7170495962f24475cee78d46 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:36:32 -0400 Subject: [PATCH 1/8] Merge pull request from GHSA-7v38-w32m-wx4m * fix(secrets): add substitution field and honor allow_commands for entrypoint * ditch substring detection in favor of strict byte replace for log masking * replace all over replace -1 --- database/secret.go | 63 ++++++++++++++------------- database/secret_test.go | 33 +++++++------- library/log.go | 27 ++++-------- library/log_test.go | 82 ++++++++++++++++++++++------------- library/secret.go | 95 ++++++++++++++++++++++++++++------------- library/secret_test.go | 33 ++++++++++++++ 6 files changed, 210 insertions(+), 123 deletions(-) diff --git a/database/secret.go b/database/secret.go index f9dae86d..a8e6c0e7 100644 --- a/database/secret.go +++ b/database/secret.go @@ -42,21 +42,22 @@ var ( // Secret is the database representation of a secret. type Secret struct { - ID sql.NullInt64 `sql:"id"` - Org sql.NullString `sql:"org"` - Repo sql.NullString `sql:"repo"` - Team sql.NullString `sql:"team"` - Name sql.NullString `sql:"name"` - Value sql.NullString `sql:"value"` - Type sql.NullString `sql:"type"` - Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` - Events pq.StringArray `sql:"events" gorm:"type:varchar(1000)"` - AllowEvents sql.NullInt64 `sql:"allow_events"` - AllowCommand sql.NullBool `sql:"allow_command"` - CreatedAt sql.NullInt64 `sql:"created_at"` - CreatedBy sql.NullString `sql:"created_by"` - UpdatedAt sql.NullInt64 `sql:"updated_at"` - UpdatedBy sql.NullString `sql:"updated_by"` + ID sql.NullInt64 `sql:"id"` + Org sql.NullString `sql:"org"` + Repo sql.NullString `sql:"repo"` + Team sql.NullString `sql:"team"` + Name sql.NullString `sql:"name"` + Value sql.NullString `sql:"value"` + Type sql.NullString `sql:"type"` + Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` + Events pq.StringArray `sql:"events" gorm:"type:varchar(1000)"` + AllowEvents sql.NullInt64 `sql:"allow_events"` + AllowCommand sql.NullBool `sql:"allow_command"` + AllowSubstitution sql.NullBool `sql:"allow_substitution"` + CreatedAt sql.NullInt64 `sql:"created_at"` + CreatedBy sql.NullString `sql:"created_by"` + UpdatedAt sql.NullInt64 `sql:"updated_at"` + UpdatedBy sql.NullString `sql:"updated_by"` } // Decrypt will manipulate the existing secret value by @@ -196,6 +197,7 @@ func (s *Secret) ToLibrary() *library.Secret { secret.SetEvents(s.Events) secret.SetAllowEvents(library.NewEventsFromMask(s.AllowEvents.Int64)) secret.SetAllowCommand(s.AllowCommand.Bool) + secret.SetAllowSubstitution(s.AllowSubstitution.Bool) secret.SetCreatedAt(s.CreatedAt.Int64) secret.SetCreatedBy(s.CreatedBy.String) secret.SetUpdatedAt(s.UpdatedAt.Int64) @@ -272,21 +274,22 @@ func (s *Secret) Validate() error { // to a database Secret type. func SecretFromLibrary(s *library.Secret) *Secret { secret := &Secret{ - ID: sql.NullInt64{Int64: s.GetID(), Valid: true}, - Org: sql.NullString{String: s.GetOrg(), Valid: true}, - Repo: sql.NullString{String: s.GetRepo(), Valid: true}, - Team: sql.NullString{String: s.GetTeam(), Valid: true}, - Name: sql.NullString{String: s.GetName(), Valid: true}, - Value: sql.NullString{String: s.GetValue(), Valid: true}, - Type: sql.NullString{String: s.GetType(), Valid: true}, - Images: pq.StringArray(s.GetImages()), - Events: pq.StringArray(s.GetEvents()), - AllowEvents: sql.NullInt64{Int64: s.GetAllowEvents().ToDatabase(), Valid: true}, - AllowCommand: sql.NullBool{Bool: s.GetAllowCommand(), Valid: true}, - CreatedAt: sql.NullInt64{Int64: s.GetCreatedAt(), Valid: true}, - CreatedBy: sql.NullString{String: s.GetCreatedBy(), Valid: true}, - UpdatedAt: sql.NullInt64{Int64: s.GetUpdatedAt(), Valid: true}, - UpdatedBy: sql.NullString{String: s.GetUpdatedBy(), Valid: true}, + ID: sql.NullInt64{Int64: s.GetID(), Valid: true}, + Org: sql.NullString{String: s.GetOrg(), Valid: true}, + Repo: sql.NullString{String: s.GetRepo(), Valid: true}, + Team: sql.NullString{String: s.GetTeam(), Valid: true}, + Name: sql.NullString{String: s.GetName(), Valid: true}, + Value: sql.NullString{String: s.GetValue(), Valid: true}, + Type: sql.NullString{String: s.GetType(), Valid: true}, + Images: pq.StringArray(s.GetImages()), + Events: pq.StringArray(s.GetEvents()), + AllowEvents: sql.NullInt64{Int64: s.GetAllowEvents().ToDatabase(), Valid: true}, + AllowCommand: sql.NullBool{Bool: s.GetAllowCommand(), Valid: true}, + AllowSubstitution: sql.NullBool{Bool: s.GetAllowSubstitution(), Valid: true}, + CreatedAt: sql.NullInt64{Int64: s.GetCreatedAt(), Valid: true}, + CreatedBy: sql.NullString{String: s.GetCreatedBy(), Valid: true}, + UpdatedAt: sql.NullInt64{Int64: s.GetUpdatedAt(), Valid: true}, + UpdatedBy: sql.NullString{String: s.GetUpdatedBy(), Valid: true}, } return secret.Nullify() diff --git a/database/secret_test.go b/database/secret_test.go index f4c6d23c..6c61ec44 100644 --- a/database/secret_test.go +++ b/database/secret_test.go @@ -171,6 +171,7 @@ func TestDatabase_Secret_ToLibrary(t *testing.T) { want.SetEvents([]string{"push", "tag", "deployment"}) want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(true) + want.SetAllowSubstitution(true) want.SetCreatedAt(tsCreate) want.SetCreatedBy("octocat") want.SetUpdatedAt(tsUpdate) @@ -295,6 +296,7 @@ func TestDatabase_SecretFromLibrary(t *testing.T) { s.SetEvents([]string{"push", "tag", "deployment"}) s.SetAllowEvents(library.NewEventsFromMask(1)) s.SetAllowCommand(true) + s.SetAllowSubstitution(true) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat") s.SetUpdatedAt(tsUpdate) @@ -314,20 +316,21 @@ func TestDatabase_SecretFromLibrary(t *testing.T) { // type with all fields set to a fake value. func testSecret() *Secret { return &Secret{ - ID: sql.NullInt64{Int64: 1, Valid: true}, - Org: sql.NullString{String: "github", Valid: true}, - Repo: sql.NullString{String: "octocat", Valid: true}, - Team: sql.NullString{String: "octokitties", Valid: true}, - Name: sql.NullString{String: "foo", Valid: true}, - Value: sql.NullString{String: "bar", Valid: true}, - Type: sql.NullString{String: "repo", Valid: true}, - Images: []string{"alpine"}, - Events: []string{"push", "tag", "deployment"}, - AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, - AllowCommand: sql.NullBool{Bool: true, Valid: true}, - CreatedAt: sql.NullInt64{Int64: tsCreate, Valid: true}, - CreatedBy: sql.NullString{String: "octocat", Valid: true}, - UpdatedAt: sql.NullInt64{Int64: tsUpdate, Valid: true}, - UpdatedBy: sql.NullString{String: "octocat2", Valid: true}, + ID: sql.NullInt64{Int64: 1, Valid: true}, + Org: sql.NullString{String: "github", Valid: true}, + Repo: sql.NullString{String: "octocat", Valid: true}, + Team: sql.NullString{String: "octokitties", Valid: true}, + Name: sql.NullString{String: "foo", Valid: true}, + Value: sql.NullString{String: "bar", Valid: true}, + Type: sql.NullString{String: "repo", Valid: true}, + Images: []string{"alpine"}, + Events: []string{"push", "tag", "deployment"}, + AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, + AllowCommand: sql.NullBool{Bool: true, Valid: true}, + AllowSubstitution: sql.NullBool{Bool: true, Valid: true}, + CreatedAt: sql.NullInt64{Int64: tsCreate, Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + UpdatedAt: sql.NullInt64{Int64: tsUpdate, Valid: true}, + UpdatedBy: sql.NullString{String: "octocat2", Valid: true}, } } diff --git a/library/log.go b/library/log.go index 9c2ce146..3c41f429 100644 --- a/library/log.go +++ b/library/log.go @@ -3,8 +3,8 @@ package library import ( + "bytes" "fmt" - "regexp" "github.com/go-vela/types/constants" ) @@ -45,25 +45,14 @@ func (l *Log) AppendData(data []byte) { func (l *Log) MaskData(secrets []string) { data := l.GetData() + // early exit on empty log or secret list + if len(data) == 0 || len(secrets) == 0 { + return + } + + // byte replace data with masked logs for _, secret := range secrets { - // escape regexp meta characters if they exist within value of secret - // - // https://pkg.go.dev/regexp#QuoteMeta - escaped := regexp.QuoteMeta(secret) - - // create regexp to match secrets in the log data surrounded by regexp metacharacters - // - // https://pkg.go.dev/regexp#MustCompile - buffer := `(\s|^|=|"|\?|:|'|\.|,|&|$|;|\[|\])` - re := regexp.MustCompile((buffer + escaped + buffer)) - - // create a mask for the secret - mask := fmt.Sprintf("$1%s$2", constants.SecretLogMask) - - // replace all regexp matches of secret with mask - // - // https://pkg.go.dev/regexp#Regexp.ReplaceAll - data = re.ReplaceAll(data, []byte(mask)) + data = bytes.ReplaceAll(data, []byte(secret), []byte(constants.SecretLogMask)) } // update data field to masked logs diff --git a/library/log_test.go b/library/log_test.go index 5ea64669..e0125507 100644 --- a/library/log_test.go +++ b/library/log_test.go @@ -42,59 +42,83 @@ func TestLibrary_Log_AppendData(t *testing.T) { func TestLibrary_Log_MaskData(t *testing.T) { // set up test secrets - sVals := []string{"secret", "((%.YY245***pP.><@@}}", "littlesecret", "extrasecret"} - - // set up test logs - s1 := "$ echo $NO_SECRET\nnosecret\n" - s2 := "((%.YY245***pP.><@@}}" - s2Masked := "***" - s3 := "$ echo $SECRET1\n((%.YY245***pP.><@@}}\n$ echo $SECRET2\nlittlesecret\n" - s3Masked := "$ echo $SECRET1\n***\n$ echo $SECRET2\n***\n" - s4 := "SOME_SECRET=((%.YY245***pP.><@@}}" - s4Masked := "SOME_SECRET=***" - s5 := "www.example.com?username=secret&password=extrasecret" - s5Masked := "www.example.com?username=***&password=***" - s6 := "[token: extrasecret]" - s6Masked := "[token: ***]" + sVals := []string{"gh_abc123def456", "((%.YY245***pP.><@@}}", "quick-bear-fox-squid", "SUPERSECRETVALUE"} tests := []struct { - want []byte log []byte + want []byte secrets []string }{ { // no secrets in log - want: []byte(s1), - log: []byte(s1), + log: []byte( + "$ echo hello\nhello\n", + ), + want: []byte( + "$ echo hello\nhello\n", + ), secrets: sVals, }, { // one secret in log - want: []byte(s2Masked), - log: []byte(s2), + log: []byte( + "((%.YY245***pP.><@@}}", + ), + want: []byte( + "***", + ), secrets: sVals, }, { // multiple secrets in log - want: []byte(s3Masked), - log: []byte(s3), + log: []byte( + "$ echo $SECRET1\n((%.YY245***pP.><@@}}\n$ echo $SECRET2\nquick-bear-fox-squid\n", + ), + want: []byte( + "$ echo $SECRET1\n***\n$ echo $SECRET2\n***\n", + ), secrets: sVals, }, { // secret with leading = - want: []byte(s4Masked), - log: []byte(s4), + log: []byte( + "SOME_SECRET=((%.YY245***pP.><@@}}", + ), + want: []byte( + "SOME_SECRET=***", + ), secrets: sVals, }, { // secret baked in URL query params - want: []byte(s5Masked), - log: []byte(s5), + log: []byte( + "www.example.com?username=quick-bear-fox-squid&password=SUPERSECRETVALUE", + ), + want: []byte( + "www.example.com?username=***&password=***", + ), secrets: sVals, }, { // secret in verbose brackets - want: []byte(s6Masked), - log: []byte(s6), + log: []byte( + "[token: gh_abc123def456]", + ), + want: []byte( + "[token: ***]", + ), + secrets: sVals, + }, + { // double secret + log: []byte( + "echo ${GITHUB_TOKEN}${SUPER_SECRET}\ngh_abc123def456SUPERSECRETVALUE\n", + ), + want: []byte( + "echo ${GITHUB_TOKEN}${SUPER_SECRET}\n******\n", + ), secrets: sVals, }, { // empty secrets slice - want: []byte(s3), - log: []byte(s3), + log: []byte( + "echo hello\nhello\n", + ), + want: []byte( + "echo hello\nhello\n", + ), secrets: []string{}, }, } diff --git a/library/secret.go b/library/secret.go index 2566b4cd..04e12497 100644 --- a/library/secret.go +++ b/library/secret.go @@ -14,21 +14,22 @@ import ( // // swagger:model Secret type Secret struct { - ID *int64 `json:"id,omitempty"` - Org *string `json:"org,omitempty"` - Repo *string `json:"repo,omitempty"` - Team *string `json:"team,omitempty"` - Name *string `json:"name,omitempty"` - Value *string `json:"value,omitempty"` - Type *string `json:"type,omitempty"` - Images *[]string `json:"images,omitempty"` - Events *[]string `json:"events,omitempty"` - AllowEvents *Events `json:"allow_events,omitempty"` - AllowCommand *bool `json:"allow_command,omitempty"` - CreatedAt *int64 `json:"created_at,omitempty"` - CreatedBy *string `json:"created_by,omitempty"` - UpdatedAt *int64 `json:"updated_at,omitempty"` - UpdatedBy *string `json:"updated_by,omitempty"` + ID *int64 `json:"id,omitempty"` + Org *string `json:"org,omitempty"` + Repo *string `json:"repo,omitempty"` + Team *string `json:"team,omitempty"` + Name *string `json:"name,omitempty"` + Value *string `json:"value,omitempty"` + Type *string `json:"type,omitempty"` + Images *[]string `json:"images,omitempty"` + Events *[]string `json:"events,omitempty"` + AllowEvents *Events `json:"allow_events,omitempty"` + AllowCommand *bool `json:"allow_command,omitempty"` + AllowSubstitution *bool `json:"allow_substitution,omitempty"` + CreatedAt *int64 `json:"created_at,omitempty"` + CreatedBy *string `json:"created_by,omitempty"` + UpdatedAt *int64 `json:"updated_at,omitempty"` + UpdatedBy *string `json:"updated_by,omitempty"` } // Sanitize creates a duplicate of the Secret without the value. @@ -39,21 +40,22 @@ func (s *Secret) Sanitize() *Secret { value := constants.SecretMask return &Secret{ - ID: s.ID, - Org: s.Org, - Repo: s.Repo, - Team: s.Team, - Name: s.Name, - Value: &value, - Type: s.Type, - Images: s.Images, - Events: s.Events, - AllowEvents: s.AllowEvents, - AllowCommand: s.AllowCommand, - CreatedAt: s.CreatedAt, - CreatedBy: s.CreatedBy, - UpdatedAt: s.UpdatedAt, - UpdatedBy: s.UpdatedBy, + ID: s.ID, + Org: s.Org, + Repo: s.Repo, + Team: s.Team, + Name: s.Name, + Value: &value, + Type: s.Type, + Images: s.Images, + Events: s.Events, + AllowEvents: s.AllowEvents, + AllowCommand: s.AllowCommand, + AllowSubstitution: s.AllowSubstitution, + CreatedAt: s.CreatedAt, + CreatedBy: s.CreatedBy, + UpdatedAt: s.UpdatedAt, + UpdatedBy: s.UpdatedBy, } } @@ -69,6 +71,11 @@ func (s *Secret) Match(from *pipeline.Container) bool { return false } + // check if a custom entrypoint is utilized when not allowed + if !commands && len(from.Commands) == 0 && len(from.Entrypoint) > 0 { + return false + } + eACL = s.GetAllowEvents().Allowed( from.Environment["VELA_BUILD_EVENT"], from.Environment["VELA_BUILD_EVENT_ACTION"], @@ -237,6 +244,19 @@ func (s *Secret) GetAllowCommand() bool { return *s.AllowCommand } +// GetAllowSubstitution returns the AllowSubstitution field. +// +// When the provided Secret type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Secret) GetAllowSubstitution() bool { + // return zero value if Secret type or AllowSubstitution field is nil + if s == nil || s.AllowSubstitution == nil { + return false + } + + return *s.AllowSubstitution +} + // GetCreatedAt returns the CreatedAt field. // // When the provided Secret type is nil, or the field within @@ -432,6 +452,19 @@ func (s *Secret) SetAllowCommand(v bool) { s.AllowCommand = &v } +// SetAllowSubstitution sets the AllowSubstitution field. +// +// When the provided Secret type is nil, it +// will set nothing and immediately return. +func (s *Secret) SetAllowSubstitution(v bool) { + // return if Secret type is nil + if s == nil { + return + } + + s.AllowSubstitution = &v +} + // SetCreatedAt sets the CreatedAt field. // // When the provided Secret type is nil, it @@ -489,6 +522,7 @@ func (s *Secret) String() string { return fmt.Sprintf(`{ AllowCommand: %t, AllowEvents: %s, + AllowSubstitution: %t, Events: %s, ID: %d, Images: %s, @@ -505,6 +539,7 @@ func (s *Secret) String() string { }`, s.GetAllowCommand(), s.GetAllowEvents().List(), + s.GetAllowSubstitution(), s.GetEvents(), s.GetID(), s.GetImages(), diff --git a/library/secret_test.go b/library/secret_test.go index 591c1ac5..ebe14549 100644 --- a/library/secret_test.go +++ b/library/secret_test.go @@ -354,6 +354,27 @@ func TestLibrary_Secret_Match(t *testing.T) { }, want: false, }, + { + name: "no commands allowed - entrypoint provided", + step: &pipeline.Container{ + Image: "alpine:latest", + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Event: []string{"push"}, + }, + }, + Entrypoint: []string{"sh", "-c", "echo hi"}, + }, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine"}, + AllowEvents: testEvents, + AllowCommand: &fBool, + }, + want: false, + }, } // run tests @@ -428,6 +449,10 @@ func TestLibrary_Secret_Getters(t *testing.T) { t.Errorf("GetAllowCommand is %v, want %v", test.secret.GetAllowCommand(), test.want.GetAllowCommand()) } + if test.secret.GetAllowSubstitution() != test.want.GetAllowSubstitution() { + t.Errorf("GetAllowSubstitution is %v, want %v", test.secret.GetAllowSubstitution(), test.want.GetAllowSubstitution()) + } + if test.secret.GetCreatedAt() != test.want.GetCreatedAt() { t.Errorf("GetCreatedAt is %v, want %v", test.secret.GetCreatedAt(), test.want.GetCreatedAt()) } @@ -478,6 +503,7 @@ func TestLibrary_Secret_Setters(t *testing.T) { test.secret.SetEvents(test.want.GetEvents()) test.secret.SetAllowEvents(test.want.GetAllowEvents()) test.secret.SetAllowCommand(test.want.GetAllowCommand()) + test.secret.SetAllowSubstitution(test.want.GetAllowSubstitution()) test.secret.SetCreatedAt(test.want.GetCreatedAt()) test.secret.SetCreatedBy(test.want.GetCreatedBy()) test.secret.SetUpdatedAt(test.want.GetUpdatedAt()) @@ -527,6 +553,10 @@ func TestLibrary_Secret_Setters(t *testing.T) { t.Errorf("SetAllowCommand is %v, want %v", test.secret.GetAllowCommand(), test.want.GetAllowCommand()) } + if test.secret.GetAllowSubstitution() != test.want.GetAllowSubstitution() { + t.Errorf("SetAllowSubstitution is %v, want %v", test.secret.GetAllowSubstitution(), test.want.GetAllowSubstitution()) + } + if test.secret.GetCreatedAt() != test.want.GetCreatedAt() { t.Errorf("SetCreatedAt is %v, want %v", test.secret.GetCreatedAt(), test.want.GetCreatedAt()) } @@ -552,6 +582,7 @@ func TestLibrary_Secret_String(t *testing.T) { want := fmt.Sprintf(`{ AllowCommand: %t, AllowEvents: %v, + AllowSubstitution: %t, Events: %s, ID: %d, Images: %s, @@ -568,6 +599,7 @@ func TestLibrary_Secret_String(t *testing.T) { }`, s.GetAllowCommand(), s.GetAllowEvents().List(), + s.GetAllowSubstitution(), s.GetEvents(), s.GetID(), s.GetImages(), @@ -610,6 +642,7 @@ func testSecret() *Secret { s.SetEvents([]string{"push", "tag", "deployment"}) s.SetAllowEvents(NewEventsFromMask(1)) s.SetAllowCommand(true) + s.SetAllowSubstitution(true) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat") s.SetUpdatedAt(tsUpdate) From 09a256b4c64941ce2144998d526533f346cc7a14 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:18:26 -0500 Subject: [PATCH 2/8] chore(deps): update all non-major dependencies (#359) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/reviewdog.yml | 4 ++-- .github/workflows/test.yml | 2 +- go.mod | 2 +- go.sum | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 112ec365..0cd1c7b9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -72,4 +72,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index b9eb09c0..5ccd8653 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -23,7 +23,7 @@ jobs: check-latest: true - name: golangci-lint - uses: reviewdog/action-golangci-lint@8e1117c7d327bbfb1eb7ec8dc2d895d13e6e17c3 # v2.6.0 + uses: reviewdog/action-golangci-lint@00311c26a97213f93f2fd3a3524d66762e956ae0 # v2.6.1 with: github_token: ${{ secrets.github_token }} golangci_lint_flags: "--config=.golangci.yml" @@ -47,7 +47,7 @@ jobs: check-latest: true - name: golangci-lint - uses: reviewdog/action-golangci-lint@8e1117c7d327bbfb1eb7ec8dc2d895d13e6e17c3 # v2.6.0 + uses: reviewdog/action-golangci-lint@00311c26a97213f93f2fd3a3524d66762e956ae0 # v2.6.1 with: github_token: ${{ secrets.github_token }} golangci_lint_flags: "--config=.golangci.yml" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5970250f..ff6dc9b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: go test -race -covermode=atomic -coverprofile=coverage.out ./... - name: coverage - uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1 + uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.out diff --git a/go.mod b/go.mod index 1f0790df..fe0ced70 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/go-vela/types go 1.21 require ( - github.com/adhocore/gronx v1.6.7 + github.com/adhocore/gronx v1.8.0 github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/drone/envsubst v1.0.3 github.com/ghodss/yaml v1.0.0 diff --git a/go.sum b/go.sum index f2cbb47e..bfcde40c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/adhocore/gronx v1.6.7 h1:yE/AKQP/yhjMRqV943XiPqBdmUwIF8VHJwm6KZhnk48= -github.com/adhocore/gronx v1.6.7/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/adhocore/gronx v1.8.0 h1:BHgzaGyS7zPmuMVqiIxyAwvKpwAX+bR7bCxDVacfhuo= +github.com/adhocore/gronx v1.8.0/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 h1:q+sMKdA6L8LyGVudTkpGoC73h6ak2iWSPFiFo/pFOU8= From b1970b932c9aabdc8ff1a29f002f177bd425d3af Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:16:20 -0400 Subject: [PATCH 3/8] fix(ci): address all outstanding linter feedback (#363) --- database/schedule_test.go | 2 ++ library/schedule_test.go | 41 +++++++++++++++++++++++++++++---------- raw/map_test.go | 1 + 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/database/schedule_test.go b/database/schedule_test.go index d7ff78aa..068f324a 100644 --- a/database/schedule_test.go +++ b/database/schedule_test.go @@ -155,8 +155,10 @@ func TestDatabase_Schedule_Validate(t *testing.T) { if err == nil { t.Errorf("Validate should have returned err") } + return } + if err != nil { t.Errorf("Validate returned err: %v", err) } diff --git a/library/schedule_test.go b/library/schedule_test.go index b2f8db54..8e325c5b 100644 --- a/library/schedule_test.go +++ b/library/schedule_test.go @@ -37,33 +37,43 @@ func TestLibrary_Schedule_Getters(t *testing.T) { if test.schedule.GetID() != test.want.GetID() { t.Errorf("GetID is %v, want %v", test.schedule.GetID(), test.want.GetID()) } + if test.schedule.GetRepoID() != test.want.GetRepoID() { t.Errorf("GetRepoID is %v, want %v", test.schedule.GetRepoID(), test.want.GetRepoID()) } + if test.schedule.GetActive() != test.want.GetActive() { t.Errorf("GetActive is %v, want %v", test.schedule.GetActive(), test.want.GetActive()) } + if test.schedule.GetName() != test.want.GetName() { t.Errorf("GetName is %v, want %v", test.schedule.GetName(), test.want.GetName()) } + if test.schedule.GetEntry() != test.want.GetEntry() { t.Errorf("GetEntry is %v, want %v", test.schedule.GetEntry(), test.want.GetEntry()) } + if test.schedule.GetCreatedAt() != test.want.GetCreatedAt() { t.Errorf("GetCreatedAt is %v, want %v", test.schedule.GetCreatedAt(), test.want.GetCreatedAt()) } + if test.schedule.GetCreatedBy() != test.want.GetCreatedBy() { t.Errorf("GetCreatedBy is %v, want %v", test.schedule.GetCreatedBy(), test.want.GetCreatedBy()) } + if test.schedule.GetUpdatedAt() != test.want.GetUpdatedAt() { t.Errorf("GetUpdatedAt is %v, want %v", test.schedule.GetUpdatedAt(), test.want.GetUpdatedAt()) } + if test.schedule.GetUpdatedBy() != test.want.GetUpdatedBy() { t.Errorf("GetUpdatedBy is %v, want %v", test.schedule.GetUpdatedBy(), test.want.GetUpdatedBy()) } + if test.schedule.GetScheduledAt() != test.want.GetScheduledAt() { t.Errorf("GetScheduledAt is %v, want %v", test.schedule.GetScheduledAt(), test.want.GetScheduledAt()) } + if test.schedule.GetBranch() != test.want.GetBranch() { t.Errorf("GetBranch is %v, want %v", test.schedule.GetBranch(), test.want.GetBranch()) } @@ -98,46 +108,57 @@ func TestLibrary_Schedule_Setters(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.schedule.SetID(test.want.GetID()) + test.schedule.SetRepoID(test.want.GetRepoID()) + test.schedule.SetActive(test.want.GetActive()) + test.schedule.SetName(test.want.GetName()) + test.schedule.SetEntry(test.want.GetEntry()) + test.schedule.SetCreatedAt(test.want.GetCreatedAt()) + test.schedule.SetCreatedBy(test.want.GetCreatedBy()) + test.schedule.SetUpdatedAt(test.want.GetUpdatedAt()) + test.schedule.SetUpdatedBy(test.want.GetUpdatedBy()) + test.schedule.SetScheduledAt(test.want.GetScheduledAt()) + test.schedule.SetBranch(test.want.GetBranch()) + if test.schedule.GetID() != test.want.GetID() { t.Errorf("SetID is %v, want %v", test.schedule.GetID(), test.want.GetID()) } - test.schedule.SetRepoID(test.want.GetRepoID()) + if test.schedule.GetRepoID() != test.want.GetRepoID() { t.Errorf("SetRepoID is %v, want %v", test.schedule.GetRepoID(), test.want.GetRepoID()) } - test.schedule.SetActive(test.want.GetActive()) + if test.schedule.GetActive() != test.want.GetActive() { t.Errorf("SetActive is %v, want %v", test.schedule.GetActive(), test.want.GetActive()) } - test.schedule.SetName(test.want.GetName()) + if test.schedule.GetName() != test.want.GetName() { t.Errorf("SetName is %v, want %v", test.schedule.GetName(), test.want.GetName()) } - test.schedule.SetEntry(test.want.GetEntry()) + if test.schedule.GetEntry() != test.want.GetEntry() { t.Errorf("SetEntry is %v, want %v", test.schedule.GetEntry(), test.want.GetEntry()) } - test.schedule.SetCreatedAt(test.want.GetCreatedAt()) + if test.schedule.GetCreatedAt() != test.want.GetCreatedAt() { t.Errorf("SetCreatedAt is %v, want %v", test.schedule.GetCreatedAt(), test.want.GetCreatedAt()) } - test.schedule.SetCreatedBy(test.want.GetCreatedBy()) + if test.schedule.GetCreatedBy() != test.want.GetCreatedBy() { t.Errorf("SetCreatedBy is %v, want %v", test.schedule.GetCreatedBy(), test.want.GetCreatedBy()) } - test.schedule.SetUpdatedAt(test.want.GetUpdatedAt()) + if test.schedule.GetUpdatedAt() != test.want.GetUpdatedAt() { t.Errorf("SetUpdatedAt is %v, want %v", test.schedule.GetUpdatedAt(), test.want.GetUpdatedAt()) } - test.schedule.SetUpdatedBy(test.want.GetUpdatedBy()) + if test.schedule.GetUpdatedBy() != test.want.GetUpdatedBy() { t.Errorf("SetUpdatedBy is %v, want %v", test.schedule.GetUpdatedBy(), test.want.GetUpdatedBy()) } - test.schedule.SetScheduledAt(test.want.GetScheduledAt()) + if test.schedule.GetScheduledAt() != test.want.GetScheduledAt() { t.Errorf("SetScheduledAt is %v, want %v", test.schedule.GetScheduledAt(), test.want.GetScheduledAt()) } - test.schedule.SetBranch(test.want.GetBranch()) + if test.schedule.GetBranch() != test.want.GetBranch() { t.Errorf("SetBranch is %v, want %v", test.schedule.GetBranch(), test.want.GetBranch()) } diff --git a/raw/map_test.go b/raw/map_test.go index aec603f4..0494c8ed 100644 --- a/raw/map_test.go +++ b/raw/map_test.go @@ -165,6 +165,7 @@ func TestStringSliceMap_Value(t *testing.T) { t.Errorf("StringSliceMap.Value() error = %v, wantErr %v", err, tt.wantErr) return } + if !reflect.DeepEqual(got, tt.want) { t.Errorf("StringSliceMap.Value() = %v, want %v", got, tt.want) } From e9cd33aaba610598fe582605048533cf78b224ea Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Thu, 21 Mar 2024 10:33:52 -0400 Subject: [PATCH 4/8] enhance(events): add NewEventsFromSlice method (#366) * enhance(events): add NewEventsFromSlice method * linter overlord --- constants/action.go | 3 ++ constants/event.go | 8 ++++ library/events.go | 53 +++++++++++++++++++++++ library/events_test.go | 96 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+) diff --git a/constants/action.go b/constants/action.go index f1e18c6e..802ddf79 100644 --- a/constants/action.go +++ b/constants/action.go @@ -30,4 +30,7 @@ const ( // ActionTag defines the action for deleting a tag. ActionTag = "tag" + + // ActionRun defines the action for running a schedule. + ActionRun = "run" ) diff --git a/constants/event.go b/constants/event.go index 9d59f7e5..c2ec26aa 100644 --- a/constants/event.go +++ b/constants/event.go @@ -27,4 +27,12 @@ const ( // EventTag defines the event type for build and repo tag events. EventTag = "tag" + + // Alternates for common user inputs that do not match our set constants. + + // EventPullAlternate defines the alternate event type for build and repo pull_request events. + EventPullAlternate = "pull" + + // EventDeployAlternate defines the alternate event type for build and repo deployment events. + EventDeployAlternate = "deploy" ) diff --git a/library/events.go b/library/events.go index 88074d80..45f1f908 100644 --- a/library/events.go +++ b/library/events.go @@ -37,6 +37,59 @@ func NewEventsFromMask(mask int64) *Events { return e } +// NewEventsFromSlice is an instantiation function for the Events type that +// takes in a slice of event strings and populates the nested Events struct. +func NewEventsFromSlice(events []string) *Events { + mask := int64(0) + + // iterate through all events provided + for _, event := range events { + switch event { + // push actions + case constants.EventPush, constants.EventPush + ":branch": + mask = mask | constants.AllowPushBranch + case constants.EventTag, constants.EventPush + ":" + constants.EventTag: + mask = mask | constants.AllowPushTag + case constants.EventDelete + ":" + constants.ActionBranch: + mask = mask | constants.AllowPushDeleteBranch + case constants.EventDelete + ":" + constants.ActionTag: + mask = mask | constants.AllowPushDeleteTag + case constants.EventDelete: + mask = mask | constants.AllowPushDeleteBranch | constants.AllowPushDeleteTag + + // pull_request actions + case constants.EventPull, constants.EventPullAlternate: + mask = mask | constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen + case constants.EventPull + ":" + constants.ActionOpened: + mask = mask | constants.AllowPullOpen + case constants.EventPull + ":" + constants.ActionEdited: + mask = mask | constants.AllowPullEdit + case constants.EventPull + ":" + constants.ActionSynchronize: + mask = mask | constants.AllowPullSync + case constants.EventPull + ":" + constants.ActionReopened: + mask = mask | constants.AllowPullReopen + + // deployment actions + case constants.EventDeploy, constants.EventDeployAlternate, constants.EventDeploy + ":" + constants.ActionCreated: + mask = mask | constants.AllowDeployCreate + + // comment actions + case constants.EventComment: + mask = mask | constants.AllowCommentCreate | constants.AllowCommentEdit + case constants.EventComment + ":" + constants.ActionCreated: + mask = mask | constants.AllowCommentCreate + case constants.EventComment + ":" + constants.ActionEdited: + mask = mask | constants.AllowCommentEdit + + // schedule actions + case constants.EventSchedule, constants.EventSchedule + ":" + constants.ActionRun: + mask = mask | constants.AllowSchedule + } + } + + return NewEventsFromMask(mask) +} + // 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 { diff --git a/library/events_test.go b/library/events_test.go index a26cc4dc..ef60257b 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -194,6 +194,102 @@ func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { } } +func Test_NewEventsFromSlice(t *testing.T) { + // setup types + tBool := true + fBool := false + + e1, e2 := testEvents() + + // setup tests + tests := []struct { + name string + events []string + want *Events + }{ + { + name: "action specific events to e1", + events: []string{"push:branch", "push:tag", "delete:branch", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "schedule:run"}, + want: e1, + }, + { + name: "action specific events to e2", + events: []string{"delete:tag", "pull_request:edited", "deployment:created", "comment:edited"}, + want: e2, + }, + { + name: "general events", + events: []string{"push", "pull", "deploy", "comment", "schedule", "tag", "delete"}, + want: &Events{ + Push: &actions.Push{ + Branch: &tBool, + Tag: &tBool, + DeleteBranch: &tBool, + DeleteTag: &tBool, + }, + PullRequest: &actions.Pull{ + Opened: &tBool, + Reopened: &tBool, + Edited: &fBool, + Synchronize: &tBool, + }, + Deployment: &actions.Deploy{ + Created: &tBool, + }, + Comment: &actions.Comment{ + Created: &tBool, + Edited: &tBool, + }, + Schedule: &actions.Schedule{ + Run: &tBool, + }, + }, + }, + { + name: "double events", + events: []string{"push", "push:branch", "pull_request", "pull_request:opened"}, + want: &Events{ + Push: &actions.Push{ + Branch: &tBool, + Tag: &fBool, + DeleteBranch: &fBool, + DeleteTag: &fBool, + }, + PullRequest: &actions.Pull{ + Opened: &tBool, + Reopened: &tBool, + Edited: &fBool, + Synchronize: &tBool, + }, + Deployment: &actions.Deploy{ + Created: &fBool, + }, + Comment: &actions.Comment{ + Created: &fBool, + Edited: &fBool, + }, + Schedule: &actions.Schedule{ + Run: &fBool, + }, + }, + }, + { + name: "empty events", + events: []string{}, + want: NewEventsFromMask(0), + }, + } + + // run tests + for _, test := range tests { + got := NewEventsFromSlice(test.events) + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("PopulateEvents failed for %s mismatch (-want +got):\n%s", test.name, diff) + } + } +} + func TestLibrary_Events_Allowed(t *testing.T) { // setup types eventsOne, eventsTwo := testEvents() From a7d60dfaa326f04f794cabf74131e83c8c02fe83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:48:07 -0500 Subject: [PATCH 5/8] fix(deps): update all non-major dependencies (#364) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0cd1c7b9..ca4a3929 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + uses: github/codeql-action/init@05963f47d870e2cb19a537396c1f668a348c7d8f # v3.24.8 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + uses: github/codeql-action/autobuild@05963f47d870e2cb19a537396c1f668a348c7d8f # v3.24.8 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -72,4 +72,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 + uses: github/codeql-action/analyze@05963f47d870e2cb19a537396c1f668a348c7d8f # v3.24.8 diff --git a/go.mod b/go.mod index fe0ced70..14587f80 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/go-vela/types go 1.21 require ( - github.com/adhocore/gronx v1.8.0 + github.com/adhocore/gronx v1.8.1 github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/drone/envsubst v1.0.3 github.com/ghodss/yaml v1.0.0 diff --git a/go.sum b/go.sum index bfcde40c..00c3fd42 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/adhocore/gronx v1.8.0 h1:BHgzaGyS7zPmuMVqiIxyAwvKpwAX+bR7bCxDVacfhuo= -github.com/adhocore/gronx v1.8.0/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A= +github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 h1:q+sMKdA6L8LyGVudTkpGoC73h6ak2iWSPFiFo/pFOU8= From 476edfa844f0dc306d1a4c8f78e2da71362969bc Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:15:42 -0400 Subject: [PATCH 6/8] adding some fields (#367) Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- constants/limit.go | 3 +++ database/step.go | 9 +++++++++ database/step_test.go | 4 ++++ library/step.go | 40 +++++++++++++++++++++++++++++++++++++- library/step_test.go | 21 +++++++++++++++++--- pipeline/container.go | 4 +++- pipeline/container_test.go | 1 + yaml/step.go | 2 ++ yaml/step_test.go | 9 ++++++--- yaml/testdata/step.yml | 1 + 10 files changed, 86 insertions(+), 8 deletions(-) diff --git a/constants/limit.go b/constants/limit.go index c6f2532c..c1a4ac95 100644 --- a/constants/limit.go +++ b/constants/limit.go @@ -33,4 +33,7 @@ const ( // DeployBuildsMaxSize defines the maximum size in characters for deployment builds. DeployBuildsMaxSize = 500 + + // ReportStepStatusLimit defines the maximum number of steps in a pipeline that may report their status to the SCM. + ReportStepStatusLimit = 10 ) diff --git a/database/step.go b/database/step.go index 318407e1..34304168 100644 --- a/database/step.go +++ b/database/step.go @@ -49,6 +49,7 @@ type Step struct { Host sql.NullString `sql:"host"` Runtime sql.NullString `sql:"runtime"` Distribution sql.NullString `sql:"distribution"` + ReportAs sql.NullString `sql:"report_as"` } // Nullify ensures the valid flag for @@ -142,6 +143,11 @@ func (s *Step) Nullify() *Step { s.Distribution.Valid = false } + // check if the ReportAs field should be false + if len(s.ReportAs.String) == 0 { + s.ReportAs.Valid = false + } + return s } @@ -166,6 +172,7 @@ func (s *Step) ToLibrary() *library.Step { step.SetHost(s.Host.String) step.SetRuntime(s.Runtime.String) step.SetDistribution(s.Distribution.String) + step.SetReportAs(s.ReportAs.String) return step } @@ -209,6 +216,7 @@ func (s *Step) Validate() error { s.Host = sql.NullString{String: sanitize(s.Host.String), Valid: s.Host.Valid} s.Runtime = sql.NullString{String: sanitize(s.Runtime.String), Valid: s.Runtime.Valid} s.Distribution = sql.NullString{String: sanitize(s.Distribution.String), Valid: s.Distribution.Valid} + s.ReportAs = sql.NullString{String: sanitize(s.ReportAs.String), Valid: s.ReportAs.Valid} return nil } @@ -233,6 +241,7 @@ func StepFromLibrary(s *library.Step) *Step { Host: sql.NullString{String: s.GetHost(), Valid: true}, Runtime: sql.NullString{String: s.GetRuntime(), Valid: true}, Distribution: sql.NullString{String: s.GetDistribution(), Valid: true}, + ReportAs: sql.NullString{String: s.GetReportAs(), Valid: true}, } return step.Nullify() diff --git a/database/step_test.go b/database/step_test.go index 90d10a51..270f939b 100644 --- a/database/step_test.go +++ b/database/step_test.go @@ -31,6 +31,7 @@ func TestDatabase_Step_Nullify(t *testing.T) { Host: sql.NullString{String: "", Valid: false}, Runtime: sql.NullString{String: "", Valid: false}, Distribution: sql.NullString{String: "", Valid: false}, + ReportAs: sql.NullString{String: "", Valid: false}, } // setup tests @@ -82,6 +83,7 @@ func TestDatabase_Step_ToLibrary(t *testing.T) { want.SetHost("example.company.com") want.SetRuntime("docker") want.SetDistribution("linux") + want.SetReportAs("test") // run test got := testStep().ToLibrary() @@ -191,6 +193,7 @@ func TestDatabase_StepFromLibrary(t *testing.T) { s.SetHost("example.company.com") s.SetRuntime("docker") s.SetDistribution("linux") + s.SetReportAs("test") want := testStep() @@ -222,5 +225,6 @@ func testStep() *Step { Host: sql.NullString{String: "example.company.com", Valid: true}, Runtime: sql.NullString{String: "docker", Valid: true}, Distribution: sql.NullString{String: "linux", Valid: true}, + ReportAs: sql.NullString{String: "test", Valid: true}, } } diff --git a/library/step.go b/library/step.go index 5dfa8aee..bf33ae5c 100644 --- a/library/step.go +++ b/library/step.go @@ -31,6 +31,7 @@ type Step struct { Host *string `json:"host,omitempty"` Runtime *string `json:"runtime,omitempty"` Distribution *string `json:"distribution,omitempty"` + ReportAs *string `json:"report_as,omitempty"` } // Duration calculates and returns the total amount of @@ -78,6 +79,7 @@ func (s *Step) Environment() map[string]string { "VELA_STEP_STAGE": ToString(s.GetStage()), "VELA_STEP_STARTED": ToString(s.GetStarted()), "VELA_STEP_STATUS": ToString(s.GetStatus()), + "VELA_STEP_REPORT_AS": ToString(s.GetReportAs()), } } @@ -289,6 +291,19 @@ func (s *Step) GetDistribution() string { return *s.Distribution } +// GetReportAs returns the ReportAs field. +// +// When the provided Step type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Step) GetReportAs() string { + // return zero value if Step type or ReportAs field is nil + if s == nil || s.ReportAs == nil { + return "" + } + + return *s.ReportAs +} + // SetID sets the ID field. // // When the provided Step type is nil, it @@ -484,7 +499,7 @@ func (s *Step) SetRuntime(v string) { s.Runtime = &v } -// SetDistribution sets the Runtime field. +// SetDistribution sets the Distribution field. // // When the provided Step type is nil, it // will set nothing and immediately return. @@ -497,6 +512,19 @@ func (s *Step) SetDistribution(v string) { s.Distribution = &v } +// SetReportAs sets the ReportAs field. +// +// When the provided Step type is nil, it +// will set nothing and immediately return. +func (s *Step) SetReportAs(v string) { + // return if Step type is nil + if s == nil { + return + } + + s.ReportAs = &v +} + // String implements the Stringer interface for the Step type. func (s *Step) String() string { return fmt.Sprintf(`{ @@ -512,6 +540,7 @@ func (s *Step) String() string { Name: %s, Number: %d, RepoID: %d, + ReportAs: %s, Runtime: %s, Stage: %s, Started: %d, @@ -529,6 +558,7 @@ func (s *Step) String() string { s.GetName(), s.GetNumber(), s.GetRepoID(), + s.GetReportAs(), s.GetRuntime(), s.GetStage(), s.GetStarted(), @@ -558,6 +588,7 @@ func StepFromBuildContainer(build *Build, ctn *pipeline.Container) *Step { s.SetName(ctn.Name) s.SetNumber(ctn.Number) s.SetImage(ctn.Image) + s.SetReportAs(ctn.ReportAs) // check if the VELA_STEP_STAGE environment variable exists value, ok := ctn.Environment["VELA_STEP_STAGE"] @@ -609,6 +640,13 @@ func StepFromContainerEnvironment(ctn *pipeline.Container) *Step { s.SetName(value) } + // check if the VELA_STEP_REPORT_AS environment variable exists + value, ok = ctn.Environment["VELA_STEP_REPORT_AS"] + if ok { + // set the ReportAs field to the value from environment variable + s.SetReportAs(value) + } + // check if the VELA_STEP_RUNTIME environment variable exists value, ok = ctn.Environment["VELA_STEP_RUNTIME"] if ok { diff --git a/library/step_test.go b/library/step_test.go index 60446d4e..ba73dc6e 100644 --- a/library/step_test.go +++ b/library/step_test.go @@ -55,6 +55,7 @@ func TestLibrary_Step_Environment(t *testing.T) { "VELA_STEP_IMAGE": "target/vela-git:v0.3.0", "VELA_STEP_NAME": "clone", "VELA_STEP_NUMBER": "1", + "VELA_STEP_REPORT_AS": "test", "VELA_STEP_RUNTIME": "docker", "VELA_STEP_STAGE": "", "VELA_STEP_STARTED": "1563474078", @@ -150,6 +151,10 @@ func TestLibrary_Step_Getters(t *testing.T) { if test.step.GetDistribution() != test.want.GetDistribution() { t.Errorf("GetDistribution is %v, want %v", test.step.GetDistribution(), test.want.GetDistribution()) } + + if test.step.GetReportAs() != test.want.GetReportAs() { + t.Errorf("GetReportAs is %v, want %v", test.step.GetReportAs(), test.want.GetReportAs()) + } } } @@ -190,6 +195,7 @@ func TestLibrary_Step_Setters(t *testing.T) { test.step.SetHost(test.want.GetHost()) test.step.SetRuntime(test.want.GetRuntime()) test.step.SetDistribution(test.want.GetDistribution()) + test.step.SetReportAs(test.want.GetReportAs()) if test.step.GetID() != test.want.GetID() { t.Errorf("SetID is %v, want %v", test.step.GetID(), test.want.GetID()) @@ -254,6 +260,10 @@ func TestLibrary_Step_Setters(t *testing.T) { if test.step.GetDistribution() != test.want.GetDistribution() { t.Errorf("SetDistribution is %v, want %v", test.step.GetDistribution(), test.want.GetDistribution()) } + + if test.step.GetReportAs() != test.want.GetReportAs() { + t.Errorf("SetReportAs is %v, want %v", test.step.GetReportAs(), test.want.GetReportAs()) + } } } @@ -274,6 +284,7 @@ func TestLibrary_Step_String(t *testing.T) { Name: %s, Number: %d, RepoID: %d, + ReportAs: %s, Runtime: %s, Stage: %s, Started: %d, @@ -291,6 +302,7 @@ func TestLibrary_Step_String(t *testing.T) { s.GetName(), s.GetNumber(), s.GetRepoID(), + s.GetReportAs(), s.GetRuntime(), s.GetStage(), s.GetStarted(), @@ -363,9 +375,10 @@ func TestLibrary_StepFromBuildContainer(t *testing.T) { { name: "container with build", container: &pipeline.Container{ - Name: s.GetName(), - Number: s.GetNumber(), - Image: s.GetImage(), + Name: s.GetName(), + Number: s.GetNumber(), + Image: s.GetImage(), + ReportAs: s.GetReportAs(), Environment: map[string]string{ "VELA_STEP_STAGE": "clone", }, @@ -423,6 +436,7 @@ func TestLibrary_StepFromContainerEnvironment(t *testing.T) { "VELA_STEP_IMAGE": "target/vela-git:v0.3.0", "VELA_STEP_NAME": "clone", "VELA_STEP_NUMBER": "1", + "VELA_STEP_REPORT_AS": "test", "VELA_STEP_RUNTIME": "docker", "VELA_STEP_STAGE": "clone", "VELA_STEP_STARTED": "1563474078", @@ -462,6 +476,7 @@ func testStep() *Step { s.SetHost("example.company.com") s.SetRuntime("docker") s.SetDistribution("linux") + s.SetReportAs("test") return s } diff --git a/pipeline/container.go b/pipeline/container.go index 3ada27a0..fcd12080 100644 --- a/pipeline/container.go +++ b/pipeline/container.go @@ -51,6 +51,7 @@ type ( Ulimits UlimitSlice `json:"ulimits,omitempty" yaml:"ulimits,omitempty"` Volumes VolumeSlice `json:"volumes,omitempty" yaml:"volumes,omitempty"` User string `json:"user,omitempty" yaml:"user,omitempty"` + ReportAs string `json:"report_as,omitempty" yaml:"report_as,omitempty"` } ) @@ -133,7 +134,8 @@ func (c *Container) Empty() bool { len(c.Secrets) == 0 && len(c.Ulimits) == 0 && len(c.Volumes) == 0 && - len(c.User) == 0 { + len(c.User) == 0 && + len(c.ReportAs) == 0 { return true } diff --git a/pipeline/container_test.go b/pipeline/container_test.go index 2f814ba7..ba08cd2b 100644 --- a/pipeline/container_test.go +++ b/pipeline/container_test.go @@ -917,6 +917,7 @@ func testContainers() *ContainerSlice { Name: "echo", Number: 3, Pull: "always", + ReportAs: "echo-step", Ruleset: Ruleset{ If: Rules{Event: []string{"push"}}, Operator: "and", diff --git a/yaml/step.go b/yaml/step.go index 82232143..396a3b74 100644 --- a/yaml/step.go +++ b/yaml/step.go @@ -34,6 +34,7 @@ type ( Detach bool `yaml:"detach,omitempty" json:"detach,omitempty" jsonschema:"description=Run the container in a detached (headless) state.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-detach-tag"` Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty" jsonschema:"description=Run the container with extra privileges.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-privileged-tag"` User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-tag"` + ReportAs string `yaml:"report_as,omitempty" json:"report_as,omitempty" jsonschema:"description=Set the name of the step to report as.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_as-tag"` } ) @@ -60,6 +61,7 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { Ulimits: *step.Ulimits.ToPipeline(), Volumes: *step.Volumes.ToPipeline(), User: step.User, + ReportAs: step.ReportAs, }) } diff --git a/yaml/step_test.go b/yaml/step_test.go index e1cead4c..2d0b6034 100644 --- a/yaml/step_test.go +++ b/yaml/step_test.go @@ -30,6 +30,7 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { Name: "echo", Privileged: false, Pull: "not_present", + ReportAs: "my-step", Ruleset: Ruleset{ If: Rules{ Branch: []string{"main"}, @@ -86,6 +87,7 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { Name: "echo", Privileged: false, Pull: "not_present", + ReportAs: "my-step", Ruleset: pipeline.Ruleset{ If: pipeline.Rules{ Branch: []string{"main"}, @@ -187,9 +189,10 @@ func TestYaml_StepSlice_UnmarshalYAML(t *testing.T) { Pull: "always", }, { - Name: "docker_build", - Image: "plugins/docker:18.09", - Pull: "always", + Name: "docker_build", + Image: "plugins/docker:18.09", + Pull: "always", + ReportAs: "docker", Parameters: map[string]interface{}{ "registry": "index.docker.io", "repo": "github/octocat", diff --git a/yaml/testdata/step.yml b/yaml/testdata/step.yml index 5dd49e3e..1d6d9cc9 100644 --- a/yaml/testdata/step.yml +++ b/yaml/testdata/step.yml @@ -28,6 +28,7 @@ - name: docker_build image: plugins/docker:18.09 + report_as: docker parameters: registry: index.docker.io repo: github/octocat From 9b43c701ab32de45621c489ecc9afcc694f0cd2e Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 1 Apr 2024 09:22:28 -0400 Subject: [PATCH 7/8] chore: delete legacy allow event fields and methods (#362) --- database/repo.go | 15 ---- database/repo_test.go | 15 ---- database/secret.go | 9 -- database/secret_test.go | 3 - item_test.go | 8 -- library/repo.go | 179 +++------------------------------------- library/repo_test.go | 126 +++++++--------------------- library/secret.go | 30 ------- library/secret_test.go | 12 --- 9 files changed, 40 insertions(+), 357 deletions(-) diff --git a/database/repo.go b/database/repo.go index 8fde8286..03a3d22c 100644 --- a/database/repo.go +++ b/database/repo.go @@ -61,11 +61,6 @@ type Repo struct { Private sql.NullBool `sql:"private"` Trusted sql.NullBool `sql:"trusted"` Active sql.NullBool `sql:"active"` - AllowPull sql.NullBool `sql:"allow_pull"` - AllowPush sql.NullBool `sql:"allow_push"` - AllowDeploy sql.NullBool `sql:"allow_deploy"` - AllowTag sql.NullBool `sql:"allow_tag"` - AllowComment sql.NullBool `sql:"allow_comment"` AllowEvents sql.NullInt64 `sql:"allow_events"` PipelineType sql.NullString `sql:"pipeline_type"` PreviousName sql.NullString `sql:"previous_name"` @@ -235,11 +230,6 @@ func (r *Repo) ToLibrary() *library.Repo { repo.SetPrivate(r.Private.Bool) repo.SetTrusted(r.Trusted.Bool) repo.SetActive(r.Active.Bool) - repo.SetAllowPull(r.AllowPull.Bool) - repo.SetAllowPush(r.AllowPush.Bool) - repo.SetAllowDeploy(r.AllowDeploy.Bool) - repo.SetAllowTag(r.AllowTag.Bool) - repo.SetAllowComment(r.AllowComment.Bool) repo.SetAllowEvents(library.NewEventsFromMask(r.AllowEvents.Int64)) repo.SetPipelineType(r.PipelineType.String) repo.SetPreviousName(r.PreviousName.String) @@ -332,11 +322,6 @@ func RepoFromLibrary(r *library.Repo) *Repo { Private: sql.NullBool{Bool: r.GetPrivate(), Valid: true}, Trusted: sql.NullBool{Bool: r.GetTrusted(), Valid: true}, Active: sql.NullBool{Bool: r.GetActive(), Valid: true}, - AllowPull: sql.NullBool{Bool: r.GetAllowPull(), Valid: true}, - AllowPush: sql.NullBool{Bool: r.GetAllowPush(), Valid: true}, - AllowDeploy: sql.NullBool{Bool: r.GetAllowDeploy(), Valid: true}, - AllowTag: sql.NullBool{Bool: r.GetAllowTag(), Valid: true}, - AllowComment: sql.NullBool{Bool: r.GetAllowComment(), Valid: true}, AllowEvents: sql.NullInt64{Int64: r.GetAllowEvents().ToDatabase(), Valid: true}, PipelineType: sql.NullString{String: r.GetPipelineType(), Valid: true}, PreviousName: sql.NullString{String: r.GetPreviousName(), Valid: true}, diff --git a/database/repo_test.go b/database/repo_test.go index 3aa63fca..22dbed0d 100644 --- a/database/repo_test.go +++ b/database/repo_test.go @@ -175,11 +175,6 @@ func TestDatabase_Repo_ToLibrary(t *testing.T) { want.SetPrivate(false) want.SetTrusted(false) want.SetActive(true) - want.SetAllowPull(false) - want.SetAllowPush(true) - want.SetAllowDeploy(false) - want.SetAllowTag(false) - want.SetAllowComment(false) want.SetAllowEvents(e) want.SetPipelineType("yaml") want.SetPreviousName("oldName") @@ -332,11 +327,6 @@ func TestDatabase_RepoFromLibrary(t *testing.T) { r.SetPrivate(false) r.SetTrusted(false) r.SetActive(true) - r.SetAllowPull(false) - r.SetAllowPush(true) - r.SetAllowDeploy(false) - r.SetAllowTag(false) - r.SetAllowComment(false) r.SetAllowEvents(e) r.SetPipelineType("yaml") r.SetPreviousName("oldName") @@ -373,11 +363,6 @@ func testRepo() *Repo { Private: sql.NullBool{Bool: false, Valid: true}, Trusted: sql.NullBool{Bool: false, Valid: true}, Active: sql.NullBool{Bool: true, Valid: true}, - AllowPull: sql.NullBool{Bool: false, Valid: true}, - AllowPush: sql.NullBool{Bool: true, Valid: true}, - AllowDeploy: sql.NullBool{Bool: false, Valid: true}, - AllowTag: sql.NullBool{Bool: false, Valid: true}, - AllowComment: sql.NullBool{Bool: false, Valid: true}, AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, PipelineType: sql.NullString{String: "yaml", Valid: true}, PreviousName: sql.NullString{String: "oldName", Valid: true}, diff --git a/database/secret.go b/database/secret.go index a8e6c0e7..1c93cf85 100644 --- a/database/secret.go +++ b/database/secret.go @@ -50,7 +50,6 @@ type Secret struct { Value sql.NullString `sql:"value"` Type sql.NullString `sql:"type"` Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` - Events pq.StringArray `sql:"events" gorm:"type:varchar(1000)"` AllowEvents sql.NullInt64 `sql:"allow_events"` AllowCommand sql.NullBool `sql:"allow_command"` AllowSubstitution sql.NullBool `sql:"allow_substitution"` @@ -194,7 +193,6 @@ func (s *Secret) ToLibrary() *library.Secret { secret.SetValue(s.Value.String) secret.SetType(s.Type.String) secret.SetImages(s.Images) - secret.SetEvents(s.Events) secret.SetAllowEvents(library.NewEventsFromMask(s.AllowEvents.Int64)) secret.SetAllowCommand(s.AllowCommand.Bool) secret.SetAllowSubstitution(s.AllowSubstitution.Bool) @@ -261,12 +259,6 @@ func (s *Secret) Validate() error { s.Images[i] = sanitize(v) } - // ensure that all Events are sanitized - // to avoid unsafe HTML content - for i, v := range s.Events { - s.Events[i] = sanitize(v) - } - return nil } @@ -282,7 +274,6 @@ func SecretFromLibrary(s *library.Secret) *Secret { Value: sql.NullString{String: s.GetValue(), Valid: true}, Type: sql.NullString{String: s.GetType(), Valid: true}, Images: pq.StringArray(s.GetImages()), - Events: pq.StringArray(s.GetEvents()), AllowEvents: sql.NullInt64{Int64: s.GetAllowEvents().ToDatabase(), Valid: true}, AllowCommand: sql.NullBool{Bool: s.GetAllowCommand(), Valid: true}, AllowSubstitution: sql.NullBool{Bool: s.GetAllowSubstitution(), Valid: true}, diff --git a/database/secret_test.go b/database/secret_test.go index 6c61ec44..d44a1265 100644 --- a/database/secret_test.go +++ b/database/secret_test.go @@ -168,7 +168,6 @@ func TestDatabase_Secret_ToLibrary(t *testing.T) { want.SetValue("bar") want.SetType("repo") want.SetImages([]string{"alpine"}) - want.SetEvents([]string{"push", "tag", "deployment"}) want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(true) want.SetAllowSubstitution(true) @@ -293,7 +292,6 @@ func TestDatabase_SecretFromLibrary(t *testing.T) { s.SetValue("bar") s.SetType("repo") s.SetImages([]string{"alpine"}) - s.SetEvents([]string{"push", "tag", "deployment"}) s.SetAllowEvents(library.NewEventsFromMask(1)) s.SetAllowCommand(true) s.SetAllowSubstitution(true) @@ -324,7 +322,6 @@ func testSecret() *Secret { Value: sql.NullString{String: "bar", Valid: true}, Type: sql.NullString{String: "repo", Valid: true}, Images: []string{"alpine"}, - Events: []string{"push", "tag", "deployment"}, AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, AllowCommand: sql.NullBool{Bool: true, Valid: true}, AllowSubstitution: sql.NullBool{Bool: true, Valid: true}, diff --git a/item_test.go b/item_test.go index 8643f3e1..245ebf95 100644 --- a/item_test.go +++ b/item_test.go @@ -55,10 +55,6 @@ func TestTypes_ToItem(t *testing.T) { Private: &booL, Trusted: &booL, Active: &booL, - AllowPull: &booL, - AllowPush: &booL, - AllowDeploy: &booL, - AllowTag: &booL, AllowEvents: e, } u := &library.User{ @@ -107,10 +103,6 @@ func TestTypes_ToItem(t *testing.T) { Private: &booL, Trusted: &booL, Active: &booL, - AllowPull: &booL, - AllowPush: &booL, - AllowDeploy: &booL, - AllowTag: &booL, AllowEvents: e, }, User: &library.User{ diff --git a/library/repo.go b/library/repo.go index 845028b9..0f82207b 100644 --- a/library/repo.go +++ b/library/repo.go @@ -28,11 +28,6 @@ type Repo struct { Private *bool `json:"private,omitempty"` Trusted *bool `json:"trusted,omitempty"` Active *bool `json:"active,omitempty"` - AllowPull *bool `json:"allow_pull,omitempty"` - AllowPush *bool `json:"allow_push,omitempty"` - AllowDeploy *bool `json:"allow_deploy,omitempty"` - AllowTag *bool `json:"allow_tag,omitempty"` - AllowComment *bool `json:"allow_comment,omitempty"` AllowEvents *Events `json:"allow_events,omitempty"` PipelineType *string `json:"pipeline_type,omitempty"` PreviousName *string `json:"previous_name,omitempty"` @@ -44,11 +39,6 @@ type Repo struct { func (r *Repo) Environment() map[string]string { return map[string]string{ "VELA_REPO_ACTIVE": ToString(r.GetActive()), - "VELA_REPO_ALLOW_COMMENT": ToString(r.GetAllowComment()), - "VELA_REPO_ALLOW_DEPLOY": ToString(r.GetAllowDeploy()), - "VELA_REPO_ALLOW_PULL": ToString(r.GetAllowPull()), - "VELA_REPO_ALLOW_PUSH": ToString(r.GetAllowPush()), - "VELA_REPO_ALLOW_TAG": ToString(r.GetAllowTag()), "VELA_REPO_ALLOW_EVENTS": strings.Join(r.GetAllowEvents().List()[:], ","), "VELA_REPO_BRANCH": ToString(r.GetBranch()), "VELA_REPO_TOPICS": strings.Join(r.GetTopics()[:], ","), @@ -66,23 +56,18 @@ func (r *Repo) Environment() map[string]string { "VELA_REPO_APPROVE_BUILD": ToString(r.GetApproveBuild()), // deprecated environment variables - "REPOSITORY_ACTIVE": ToString(r.GetActive()), - "REPOSITORY_ALLOW_COMMENT": ToString(r.GetAllowComment()), - "REPOSITORY_ALLOW_DEPLOY": ToString(r.GetAllowDeploy()), - "REPOSITORY_ALLOW_PULL": ToString(r.GetAllowPull()), - "REPOSITORY_ALLOW_PUSH": ToString(r.GetAllowPush()), - "REPOSITORY_ALLOW_TAG": ToString(r.GetAllowTag()), - "REPOSITORY_ALLOW_EVENTS": strings.Join(r.GetAllowEvents().List()[:], ","), - "REPOSITORY_BRANCH": ToString(r.GetBranch()), - "REPOSITORY_CLONE": ToString(r.GetClone()), - "REPOSITORY_FULL_NAME": ToString(r.GetFullName()), - "REPOSITORY_LINK": ToString(r.GetLink()), - "REPOSITORY_NAME": ToString(r.GetName()), - "REPOSITORY_ORG": ToString(r.GetOrg()), - "REPOSITORY_PRIVATE": ToString(r.GetPrivate()), - "REPOSITORY_TIMEOUT": ToString(r.GetTimeout()), - "REPOSITORY_TRUSTED": ToString(r.GetTrusted()), - "REPOSITORY_VISIBILITY": ToString(r.GetVisibility()), + "REPOSITORY_ACTIVE": ToString(r.GetActive()), + "REPOSITORY_ALLOW_EVENTS": strings.Join(r.GetAllowEvents().List()[:], ","), + "REPOSITORY_BRANCH": ToString(r.GetBranch()), + "REPOSITORY_CLONE": ToString(r.GetClone()), + "REPOSITORY_FULL_NAME": ToString(r.GetFullName()), + "REPOSITORY_LINK": ToString(r.GetLink()), + "REPOSITORY_NAME": ToString(r.GetName()), + "REPOSITORY_ORG": ToString(r.GetOrg()), + "REPOSITORY_PRIVATE": ToString(r.GetPrivate()), + "REPOSITORY_TIMEOUT": ToString(r.GetTimeout()), + "REPOSITORY_TRUSTED": ToString(r.GetTrusted()), + "REPOSITORY_VISIBILITY": ToString(r.GetVisibility()), } } @@ -307,71 +292,6 @@ func (r *Repo) GetActive() bool { return *r.Active } -// GetAllowPull returns the AllowPull field. -// -// 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) GetAllowPull() bool { - // return zero value if Repo type or AllowPull field is nil - if r == nil || r.AllowPull == nil { - return false - } - - return *r.AllowPull -} - -// GetAllowPush returns the AllowPush field. -// -// 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) GetAllowPush() bool { - // return zero value if Repo type or AllowPush field is nil - if r == nil || r.AllowPush == nil { - return false - } - - return *r.AllowPush -} - -// GetAllowDeploy returns the AllowDeploy field. -// -// 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) GetAllowDeploy() bool { - // return zero value if Repo type or AllowDeploy field is nil - if r == nil || r.AllowDeploy == nil { - return false - } - - return *r.AllowDeploy -} - -// GetAllowTag returns the AllowTag field. -// -// 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) GetAllowTag() bool { - // return zero value if Repo type or AllowTag field is nil - if r == nil || r.AllowTag == nil { - return false - } - - return *r.AllowTag -} - -// GetAllowComment returns the AllowComment field. -// -// 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 AllowComment field is nil - if r == nil || r.AllowComment == nil { - return false - } - - return *r.AllowComment -} - // GetAllowEvents returns the AllowEvents field. // // When the provided Repo type is nil, or the field within @@ -645,71 +565,6 @@ func (r *Repo) SetActive(v bool) { r.Active = &v } -// SetAllowPull sets the AllowPull field. -// -// When the provided Repo type is nil, it -// will set nothing and immediately return. -func (r *Repo) SetAllowPull(v bool) { - // return if Repo type is nil - if r == nil { - return - } - - r.AllowPull = &v -} - -// SetAllowPush sets the AllowPush field. -// -// When the provided Repo type is nil, it -// will set nothing and immediately return. -func (r *Repo) SetAllowPush(v bool) { - // return if Repo type is nil - if r == nil { - return - } - - r.AllowPush = &v -} - -// SetAllowDeploy sets the AllowDeploy field. -// -// When the provided Repo type is nil, it -// will set nothing and immediately return. -func (r *Repo) SetAllowDeploy(v bool) { - // return if Repo type is nil - if r == nil { - return - } - - r.AllowDeploy = &v -} - -// SetAllowTag sets the AllowTag field. -// -// When the provided Repo type is nil, it -// will set nothing and immediately return. -func (r *Repo) SetAllowTag(v bool) { - // return if Repo type is nil - if r == nil { - return - } - - r.AllowTag = &v -} - -// SetAllowComment sets the AllowComment field. -// -// When the provided Repo type is nil, it -// will set nothing and immediately return. -func (r *Repo) SetAllowComment(v bool) { - // return if Repo type is nil - if r == nil { - return - } - - r.AllowComment = &v -} - // SetAllowEvents sets the AllowEvents field. // // When the provided Repo type is nil, it @@ -768,11 +623,6 @@ func (r *Repo) SetApproveBuild(v string) { func (r *Repo) String() string { return fmt.Sprintf(`{ Active: %t, - AllowComment: %t, - AllowDeploy: %t, - AllowPull: %t, - AllowPush: %t, - AllowTag: %t, AllowEvents: %s, ApproveBuild: %s, Branch: %s, @@ -794,11 +644,6 @@ func (r *Repo) String() string { Visibility: %s, }`, r.GetActive(), - r.GetAllowComment(), - r.GetAllowDeploy(), - r.GetAllowPull(), - r.GetAllowPush(), - r.GetAllowTag(), r.GetAllowEvents().List(), r.GetApproveBuild(), r.GetBranch(), diff --git a/library/repo_test.go b/library/repo_test.go index 2f2684cd..de256a0e 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -14,44 +14,34 @@ import ( func TestLibrary_Repo_Environment(t *testing.T) { // setup types want := map[string]string{ - "VELA_REPO_ACTIVE": "true", - "VELA_REPO_ALLOW_COMMENT": "false", - "VELA_REPO_ALLOW_DEPLOY": "false", - "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,pull_request:reopened,tag,comment:created,schedule,delete:branch", - "VELA_REPO_BRANCH": "main", - "VELA_REPO_TOPICS": "cloud,security", - "VELA_REPO_BUILD_LIMIT": "10", - "VELA_REPO_CLONE": "https://github.com/github/octocat.git", - "VELA_REPO_FULL_NAME": "github/octocat", - "VELA_REPO_LINK": "https://github.com/github/octocat", - "VELA_REPO_NAME": "octocat", - "VELA_REPO_ORG": "github", - "VELA_REPO_PRIVATE": "false", - "VELA_REPO_TIMEOUT": "30", - "VELA_REPO_TRUSTED": "false", - "VELA_REPO_VISIBILITY": "public", - "VELA_REPO_PIPELINE_TYPE": "", - "VELA_REPO_APPROVE_BUILD": "never", - "REPOSITORY_ACTIVE": "true", - "REPOSITORY_ALLOW_COMMENT": "false", - "REPOSITORY_ALLOW_DEPLOY": "false", - "REPOSITORY_ALLOW_PULL": "false", - "REPOSITORY_ALLOW_PUSH": "true", - "REPOSITORY_ALLOW_TAG": "false", - "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", - "REPOSITORY_LINK": "https://github.com/github/octocat", - "REPOSITORY_NAME": "octocat", - "REPOSITORY_ORG": "github", - "REPOSITORY_PRIVATE": "false", - "REPOSITORY_TIMEOUT": "30", - "REPOSITORY_TRUSTED": "false", - "REPOSITORY_VISIBILITY": "public", + "VELA_REPO_ACTIVE": "true", + "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", + "VELA_REPO_CLONE": "https://github.com/github/octocat.git", + "VELA_REPO_FULL_NAME": "github/octocat", + "VELA_REPO_LINK": "https://github.com/github/octocat", + "VELA_REPO_NAME": "octocat", + "VELA_REPO_ORG": "github", + "VELA_REPO_PRIVATE": "false", + "VELA_REPO_TIMEOUT": "30", + "VELA_REPO_TRUSTED": "false", + "VELA_REPO_VISIBILITY": "public", + "VELA_REPO_PIPELINE_TYPE": "", + "VELA_REPO_APPROVE_BUILD": "never", + "REPOSITORY_ACTIVE": "true", + "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", + "REPOSITORY_LINK": "https://github.com/github/octocat", + "REPOSITORY_NAME": "octocat", + "REPOSITORY_ORG": "github", + "REPOSITORY_PRIVATE": "false", + "REPOSITORY_TIMEOUT": "30", + "REPOSITORY_TRUSTED": "false", + "REPOSITORY_VISIBILITY": "public", } // run test @@ -144,26 +134,6 @@ func TestLibrary_Repo_Getters(t *testing.T) { t.Errorf("GetActive is %v, want %v", test.repo.GetActive(), test.want.GetActive()) } - if test.repo.GetAllowPull() != test.want.GetAllowPull() { - t.Errorf("GetAllowPull is %v, want %v", test.repo.GetAllowPull(), test.want.GetAllowPull()) - } - - if test.repo.GetAllowPush() != test.want.GetAllowPush() { - t.Errorf("GetAllowPush is %v, want %v", test.repo.GetAllowPush(), test.want.GetAllowPush()) - } - - if test.repo.GetAllowDeploy() != test.want.GetAllowDeploy() { - t.Errorf("GetAllowDeploy is %v, want %v", test.repo.GetAllowDeploy(), test.want.GetAllowDeploy()) - } - - if test.repo.GetAllowTag() != test.want.GetAllowTag() { - t.Errorf("GetAllowTag is %v, want %v", test.repo.GetAllowTag(), test.want.GetAllowTag()) - } - - if test.repo.GetAllowComment() != test.want.GetAllowComment() { - t.Errorf("GetAllowComment is %v, want %v", test.repo.GetAllowComment(), test.want.GetAllowComment()) - } - if !reflect.DeepEqual(test.repo.GetAllowEvents(), test.want.GetAllowEvents()) { t.Errorf("GetRepo is %v, want %v", test.repo.GetAllowEvents(), test.want.GetAllowEvents()) } @@ -220,11 +190,6 @@ func TestLibrary_Repo_Setters(t *testing.T) { test.repo.SetPrivate(test.want.GetPrivate()) test.repo.SetTrusted(test.want.GetTrusted()) test.repo.SetActive(test.want.GetActive()) - test.repo.SetAllowPull(test.want.GetAllowPull()) - test.repo.SetAllowPush(test.want.GetAllowPush()) - test.repo.SetAllowDeploy(test.want.GetAllowDeploy()) - test.repo.SetAllowTag(test.want.GetAllowTag()) - test.repo.SetAllowComment(test.want.GetAllowComment()) test.repo.SetAllowEvents(test.want.GetAllowEvents()) test.repo.SetPipelineType(test.want.GetPipelineType()) test.repo.SetPreviousName(test.want.GetPreviousName()) @@ -294,26 +259,6 @@ func TestLibrary_Repo_Setters(t *testing.T) { t.Errorf("SetActive is %v, want %v", test.repo.GetActive(), test.want.GetActive()) } - if test.repo.GetAllowPull() != test.want.GetAllowPull() { - t.Errorf("SetAllowPull is %v, want %v", test.repo.GetAllowPull(), test.want.GetAllowPull()) - } - - if test.repo.GetAllowPush() != test.want.GetAllowPush() { - t.Errorf("SetAllowPush is %v, want %v", test.repo.GetAllowPush(), test.want.GetAllowPush()) - } - - if test.repo.GetAllowDeploy() != test.want.GetAllowDeploy() { - t.Errorf("SetAllowDeploy is %v, want %v", test.repo.GetAllowDeploy(), test.want.GetAllowDeploy()) - } - - if test.repo.GetAllowTag() != test.want.GetAllowTag() { - t.Errorf("SetAllowTag is %v, want %v", test.repo.GetAllowTag(), test.want.GetAllowTag()) - } - - if test.repo.GetAllowComment() != test.want.GetAllowComment() { - t.Errorf("SetAllowComment is %v, want %v", test.repo.GetAllowComment(), test.want.GetAllowComment()) - } - if !reflect.DeepEqual(test.repo.GetAllowEvents(), test.want.GetAllowEvents()) { t.Errorf("GetRepo is %v, want %v", test.repo.GetAllowEvents(), test.want.GetAllowEvents()) } @@ -338,11 +283,6 @@ func TestLibrary_Repo_String(t *testing.T) { want := fmt.Sprintf(`{ Active: %t, - AllowComment: %t, - AllowDeploy: %t, - AllowPull: %t, - AllowPush: %t, - AllowTag: %t, AllowEvents: %s, ApproveBuild: %s, Branch: %s, @@ -364,11 +304,6 @@ func TestLibrary_Repo_String(t *testing.T) { Visibility: %s, }`, r.GetActive(), - r.GetAllowComment(), - r.GetAllowDeploy(), - r.GetAllowPull(), - r.GetAllowPush(), - r.GetAllowTag(), r.GetAllowEvents().List(), r.GetApproveBuild(), r.GetBranch(), @@ -420,11 +355,6 @@ func testRepo() *Repo { r.SetPrivate(false) r.SetTrusted(false) r.SetActive(true) - r.SetAllowPull(false) - r.SetAllowPush(true) - r.SetAllowDeploy(false) - r.SetAllowTag(false) - r.SetAllowComment(false) r.SetAllowEvents(e) r.SetPipelineType("") r.SetPreviousName("") diff --git a/library/secret.go b/library/secret.go index 04e12497..57aa3e84 100644 --- a/library/secret.go +++ b/library/secret.go @@ -22,7 +22,6 @@ type Secret struct { Value *string `json:"value,omitempty"` Type *string `json:"type,omitempty"` Images *[]string `json:"images,omitempty"` - Events *[]string `json:"events,omitempty"` AllowEvents *Events `json:"allow_events,omitempty"` AllowCommand *bool `json:"allow_command,omitempty"` AllowSubstitution *bool `json:"allow_substitution,omitempty"` @@ -48,7 +47,6 @@ func (s *Secret) Sanitize() *Secret { Value: &value, Type: s.Type, Images: s.Images, - Events: s.Events, AllowEvents: s.AllowEvents, AllowCommand: s.AllowCommand, AllowSubstitution: s.AllowSubstitution, @@ -205,19 +203,6 @@ func (s *Secret) GetImages() []string { return *s.Images } -// GetEvents returns the Events field. -// -// When the provided Secret type is nil, or the field within -// the type is nil, it returns the zero value for the field. -func (s *Secret) GetEvents() []string { - // return zero value if Secret type or Events field is nil - if s == nil || s.Events == nil { - return []string{} - } - - return *s.Events -} - // GetAllowEvents returns the AllowEvents field. // // When the provided Secret type is nil, or the field within @@ -413,19 +398,6 @@ func (s *Secret) SetImages(v []string) { s.Images = &v } -// SetEvents sets the Events field. -// -// When the provided Secret type is nil, it -// will set nothing and immediately return. -func (s *Secret) SetEvents(v []string) { - // return if Secret type is nil - if s == nil { - return - } - - s.Events = &v -} - // SetAllowEvents sets the AllowEvents field. // // When the provided Secret type is nil, it @@ -523,7 +495,6 @@ func (s *Secret) String() string { AllowCommand: %t, AllowEvents: %s, AllowSubstitution: %t, - Events: %s, ID: %d, Images: %s, Name: %s, @@ -540,7 +511,6 @@ func (s *Secret) String() string { s.GetAllowCommand(), s.GetAllowEvents().List(), s.GetAllowSubstitution(), - s.GetEvents(), s.GetID(), s.GetImages(), s.GetName(), diff --git a/library/secret_test.go b/library/secret_test.go index ebe14549..ba0e3047 100644 --- a/library/secret_test.go +++ b/library/secret_test.go @@ -437,10 +437,6 @@ func TestLibrary_Secret_Getters(t *testing.T) { t.Errorf("GetImages is %v, want %v", test.secret.GetImages(), test.want.GetImages()) } - if !reflect.DeepEqual(test.secret.GetEvents(), test.want.GetEvents()) { - t.Errorf("GetEvents is %v, want %v", test.secret.GetEvents(), test.want.GetEvents()) - } - if !reflect.DeepEqual(test.secret.GetAllowEvents(), test.want.GetAllowEvents()) { t.Errorf("GetAllowEvents is %v, want %v", test.secret.GetAllowEvents(), test.want.GetAllowEvents()) } @@ -500,7 +496,6 @@ func TestLibrary_Secret_Setters(t *testing.T) { test.secret.SetValue(test.want.GetValue()) test.secret.SetType(test.want.GetType()) test.secret.SetImages(test.want.GetImages()) - test.secret.SetEvents(test.want.GetEvents()) test.secret.SetAllowEvents(test.want.GetAllowEvents()) test.secret.SetAllowCommand(test.want.GetAllowCommand()) test.secret.SetAllowSubstitution(test.want.GetAllowSubstitution()) @@ -541,10 +536,6 @@ func TestLibrary_Secret_Setters(t *testing.T) { t.Errorf("SetImages is %v, want %v", test.secret.GetImages(), test.want.GetImages()) } - if !reflect.DeepEqual(test.secret.GetEvents(), test.want.GetEvents()) { - t.Errorf("SetEvents is %v, want %v", test.secret.GetEvents(), test.want.GetEvents()) - } - if !reflect.DeepEqual(test.secret.GetAllowEvents(), test.want.GetAllowEvents()) { t.Errorf("SetAllowEvents is %v, want %v", test.secret.GetAllowEvents(), test.want.GetAllowEvents()) } @@ -583,7 +574,6 @@ func TestLibrary_Secret_String(t *testing.T) { AllowCommand: %t, AllowEvents: %v, AllowSubstitution: %t, - Events: %s, ID: %d, Images: %s, Name: %s, @@ -600,7 +590,6 @@ func TestLibrary_Secret_String(t *testing.T) { s.GetAllowCommand(), s.GetAllowEvents().List(), s.GetAllowSubstitution(), - s.GetEvents(), s.GetID(), s.GetImages(), s.GetName(), @@ -639,7 +628,6 @@ func testSecret() *Secret { s.SetValue("bar") s.SetType("repo") s.SetImages([]string{"alpine"}) - s.SetEvents([]string{"push", "tag", "deployment"}) s.SetAllowEvents(NewEventsFromMask(1)) s.SetAllowCommand(true) s.SetAllowSubstitution(true) From f16c3e4cb5fbbd200c302772c26d55626a43d447 Mon Sep 17 00:00:00 2001 From: Win San Date: Tue, 2 Apr 2024 10:37:26 -0500 Subject: [PATCH 8/8] feat(rulesets): add support for PR labeled events (#361) * Add support for PR label event 1. Add new action to the Pull struct 2. Update constants to reflect new event for the integer mask 3. Update YAML package to add new label rule 4. Update ruleset matching logic * Update test files related to PR event * Add support for PR unlabeled event 1. Add new action to the Pull struct 2. Update constants to reflect new event for the integer mask 3. Update YAML package to add new unlabeled rule 4. Update tests * Extend label rule to include other PR events * Fix linter warning --------- Co-authored-by: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Co-authored-by: dave vader <48764154+plyr4@users.noreply.github.com> --- constants/action.go | 6 + constants/allow_events.go | 3 +- library/actions/pull.go | 60 ++++++++++ library/actions/pull_test.go | 22 +++- library/actions/push_test.go | 1 + library/events.go | 16 +++ library/events_test.go | 18 ++- library/repo_test.go | 4 +- pipeline/container_test.go | 25 +++++ pipeline/ruleset.go | 211 ++++++++++++++++++----------------- pipeline/ruleset_test.go | 94 ++++++++++++---- webhook.go | 1 + yaml/ruleset.go | 5 + yaml/ruleset_test.go | 12 +- 14 files changed, 341 insertions(+), 137 deletions(-) diff --git a/constants/action.go b/constants/action.go index 802ddf79..da0a4449 100644 --- a/constants/action.go +++ b/constants/action.go @@ -22,6 +22,12 @@ const ( // ActionSynchronize defines the action for the synchronizing of pull requests. ActionSynchronize = "synchronize" + // ActionLabeled defines the action for the labeling of pull requests. + ActionLabeled = "labeled" + + // ActionUnlabeled defines the action for the unlabeling of pull requests. + ActionUnlabeled = "unlabeled" + // ActionTransferred defines the action for transferring repository ownership. ActionTransferred = "transferred" diff --git a/constants/allow_events.go b/constants/allow_events.go index 4a163a73..5df53926 100644 --- a/constants/allow_events.go +++ b/constants/allow_events.go @@ -11,7 +11,7 @@ const ( AllowPullSync _ // AllowPullAssigned - Not Implemented _ // AllowPullMilestoned - Not Implemented - _ // AllowPullLabel - Not Implemented + AllowPullLabel _ // AllowPullLocked - Not Implemented _ // AllowPullReady - Not Implemented AllowPullReopen @@ -23,4 +23,5 @@ const ( AllowSchedule AllowPushDeleteBranch AllowPushDeleteTag + AllowPullUnlabel ) diff --git a/library/actions/pull.go b/library/actions/pull.go index 609c6248..8e0063b1 100644 --- a/library/actions/pull.go +++ b/library/actions/pull.go @@ -12,6 +12,8 @@ type Pull struct { Edited *bool `json:"edited"` Synchronize *bool `json:"synchronize"` Reopened *bool `json:"reopened"` + Labeled *bool `json:"labeled"` + Unlabeled *bool `json:"unlabeled"` } // FromMask returns the Pull type resulting from the provided integer mask. @@ -20,6 +22,8 @@ func (a *Pull) FromMask(mask int64) *Pull { a.SetSynchronize(mask&constants.AllowPullSync > 0) a.SetEdited(mask&constants.AllowPullEdit > 0) a.SetReopened(mask&constants.AllowPullReopen > 0) + a.SetLabeled(mask&constants.AllowPullLabel > 0) + a.SetUnlabeled(mask&constants.AllowPullUnlabel > 0) return a } @@ -44,6 +48,14 @@ func (a *Pull) ToMask() int64 { mask = mask | constants.AllowPullReopen } + if a.GetLabeled() { + mask = mask | constants.AllowPullLabel + } + + if a.GetUnlabeled() { + mask = mask | constants.AllowPullUnlabel + } + return mask } @@ -91,6 +103,28 @@ func (a *Pull) GetReopened() bool { return *a.Reopened } +// GetLabeled returns the Labeled field from the provided Pull. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (a *Pull) GetLabeled() bool { + // return zero value if Pull type or Labeled field is nil + if a == nil || a.Labeled == nil { + return false + } + + return *a.Labeled +} + +// GetUnlabeled returns the Unlabeled field from the provided Pull. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (a *Pull) GetUnlabeled() bool { + // return zero value if Pull type or Unlabeled field is nil + if a == nil || a.Unlabeled == nil { + return false + } + + return *a.Unlabeled +} + // SetOpened sets the Pull Opened field. // // When the provided Pull type is nil, it @@ -142,3 +176,29 @@ func (a *Pull) SetReopened(v bool) { a.Reopened = &v } + +// SetLabeled sets the Pull Labeled field. +// +// When the provided Pull type is nil, it +// will set nothing and immediately return. +func (a *Pull) SetLabeled(v bool) { + // return if Pull type is nil + if a == nil { + return + } + + a.Labeled = &v +} + +// SetUnlabeled sets the Pull Unlabeled field. +// +// When the provided Pull type is nil, it +// will set nothing and immediately return. +func (a *Pull) SetUnlabeled(v bool) { + // return if Pull type is nil + if a == nil { + return + } + + a.Unlabeled = &v +} diff --git a/library/actions/pull_test.go b/library/actions/pull_test.go index 30b5ed8c..f4c815b5 100644 --- a/library/actions/pull_test.go +++ b/library/actions/pull_test.go @@ -42,6 +42,14 @@ func TestLibrary_Pull_Getters(t *testing.T) { if test.actions.GetReopened() != test.want.GetReopened() { t.Errorf("GetReopened is %v, want %v", test.actions.GetReopened(), test.want.GetReopened()) } + + if test.actions.GetLabeled() != test.want.GetLabeled() { + t.Errorf("GetLabeled is %v, want %v", test.actions.GetLabeled(), test.want.GetLabeled()) + } + + if test.actions.GetUnlabeled() != test.want.GetUnlabeled() { + t.Errorf("GetUnlabeled is %v, want %v", test.actions.GetUnlabeled(), test.want.GetUnlabeled()) + } } } @@ -70,6 +78,8 @@ func TestLibrary_Pull_Setters(t *testing.T) { test.actions.SetSynchronize(test.want.GetSynchronize()) test.actions.SetEdited(test.want.GetEdited()) test.actions.SetReopened(test.want.GetReopened()) + test.actions.SetLabeled(test.want.GetLabeled()) + test.actions.SetUnlabeled(test.want.GetUnlabeled()) if test.actions.GetOpened() != test.want.GetOpened() { t.Errorf("SetOpened is %v, want %v", test.actions.GetOpened(), test.want.GetOpened()) @@ -86,6 +96,14 @@ func TestLibrary_Pull_Setters(t *testing.T) { if test.actions.GetReopened() != test.want.GetReopened() { t.Errorf("SetReopened is %v, want %v", test.actions.GetReopened(), test.want.GetReopened()) } + + if test.actions.GetLabeled() != test.want.GetLabeled() { + t.Errorf("SetLabeled is %v, want %v", test.actions.GetLabeled(), test.want.GetLabeled()) + } + + if test.actions.GetUnlabeled() != test.want.GetUnlabeled() { + t.Errorf("SetUnlabeled is %v, want %v", test.actions.GetUnlabeled(), test.want.GetUnlabeled()) + } } } @@ -107,7 +125,7 @@ func TestLibrary_Pull_ToMask(t *testing.T) { // setup types actions := testPull() - want := int64(constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen) + want := int64(constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen | constants.AllowPullUnlabel) // run test got := actions.ToMask() @@ -123,6 +141,8 @@ func testPull() *Pull { pr.SetSynchronize(true) pr.SetEdited(false) pr.SetReopened(true) + pr.SetLabeled(false) + pr.SetUnlabeled(true) return pr } diff --git a/library/actions/push_test.go b/library/actions/push_test.go index 330444ba..b0cfba47 100644 --- a/library/actions/push_test.go +++ b/library/actions/push_test.go @@ -128,6 +128,7 @@ func testMask() int64 { constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen | + constants.AllowPullUnlabel | constants.AllowDeployCreate | constants.AllowCommentCreate | constants.AllowSchedule, diff --git a/library/events.go b/library/events.go index 45f1f908..e93acb10 100644 --- a/library/events.go +++ b/library/events.go @@ -68,6 +68,10 @@ func NewEventsFromSlice(events []string) *Events { mask = mask | constants.AllowPullSync case constants.EventPull + ":" + constants.ActionReopened: mask = mask | constants.AllowPullReopen + case constants.EventPull + ":" + constants.ActionLabeled: + mask = mask | constants.AllowPullLabel + case constants.EventPull + ":" + constants.ActionUnlabeled: + mask = mask | constants.AllowPullUnlabel // deployment actions case constants.EventDeploy, constants.EventDeployAlternate, constants.EventDeploy + ":" + constants.ActionCreated: @@ -111,6 +115,10 @@ func (e *Events) Allowed(event, action string) bool { allowed = e.GetPullRequest().GetEdited() case constants.EventPull + ":" + constants.ActionReopened: allowed = e.GetPullRequest().GetReopened() + case constants.EventPull + ":" + constants.ActionLabeled: + allowed = e.GetPullRequest().GetLabeled() + case constants.EventPull + ":" + constants.ActionUnlabeled: + allowed = e.GetPullRequest().GetUnlabeled() case constants.EventTag: allowed = e.GetPush().GetTag() case constants.EventComment + ":" + constants.ActionCreated: @@ -155,6 +163,14 @@ func (e *Events) List() []string { eventSlice = append(eventSlice, constants.EventPull+":"+constants.ActionReopened) } + if e.GetPullRequest().GetLabeled() { + eventSlice = append(eventSlice, constants.EventPull+":"+constants.ActionLabeled) + } + + if e.GetPullRequest().GetUnlabeled() { + eventSlice = append(eventSlice, constants.EventPull+":"+constants.ActionUnlabeled) + } + if e.GetPush().GetTag() { eventSlice = append(eventSlice, constants.EventTag) } diff --git a/library/events_test.go b/library/events_test.go index ef60257b..79f6ee16 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -122,6 +122,7 @@ func TestLibrary_Events_List(t *testing.T) { "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", + "pull_request:unlabeled", "tag", "comment:created", "schedule", @@ -130,6 +131,7 @@ func TestLibrary_Events_List(t *testing.T) { wantTwo := []string{ "pull_request:edited", + "pull_request:labeled", "deployment", "comment:edited", "delete:tag", @@ -158,6 +160,7 @@ func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen | + constants.AllowPullUnlabel | constants.AllowCommentCreate | constants.AllowSchedule, ) @@ -166,6 +169,7 @@ func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { constants.AllowPushDeleteTag | constants.AllowPullEdit | constants.AllowCommentEdit | + constants.AllowPullLabel | constants.AllowDeployCreate, ) @@ -209,12 +213,12 @@ func Test_NewEventsFromSlice(t *testing.T) { }{ { name: "action specific events to e1", - events: []string{"push:branch", "push:tag", "delete:branch", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "schedule:run"}, + events: []string{"push:branch", "push:tag", "delete:branch", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "schedule:run", "pull_request:unlabeled"}, want: e1, }, { name: "action specific events to e2", - events: []string{"delete:tag", "pull_request:edited", "deployment:created", "comment:edited"}, + events: []string{"delete:tag", "pull_request:edited", "deployment:created", "comment:edited", "pull_request:labeled"}, want: e2, }, { @@ -232,6 +236,8 @@ func Test_NewEventsFromSlice(t *testing.T) { Reopened: &tBool, Edited: &fBool, Synchronize: &tBool, + Labeled: &fBool, + Unlabeled: &fBool, }, Deployment: &actions.Deploy{ Created: &tBool, @@ -260,6 +266,8 @@ func Test_NewEventsFromSlice(t *testing.T) { Reopened: &tBool, Edited: &fBool, Synchronize: &tBool, + Labeled: &fBool, + Unlabeled: &fBool, }, Deployment: &actions.Deploy{ Created: &fBool, @@ -306,6 +314,8 @@ func TestLibrary_Events_Allowed(t *testing.T) { {event: "pull_request", action: "synchronize", want: true}, {event: "pull_request", action: "edited", want: false}, {event: "pull_request", action: "reopened", want: true}, + {event: "pull_request", action: "labeled", want: false}, + {event: "pull_request", action: "unlabeled", want: true}, {event: "deployment", want: false}, {event: "comment", action: "created", want: true}, {event: "comment", action: "edited", want: false}, @@ -345,6 +355,8 @@ func testEvents() (*Events, *Events) { Synchronize: &tBool, Edited: &fBool, Reopened: &tBool, + Labeled: &fBool, + Unlabeled: &tBool, }, Deployment: &actions.Deploy{ Created: &fBool, @@ -370,6 +382,8 @@ func testEvents() (*Events, *Events) { Synchronize: &fBool, Edited: &tBool, Reopened: &fBool, + Labeled: &tBool, + Unlabeled: &fBool, }, Deployment: &actions.Deploy{ Created: &tBool, diff --git a/library/repo_test.go b/library/repo_test.go index de256a0e..f9eaba2b 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -15,7 +15,7 @@ func TestLibrary_Repo_Environment(t *testing.T) { // setup types want := map[string]string{ "VELA_REPO_ACTIVE": "true", - "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,tag,comment:created,schedule,delete:branch", + "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", "VELA_REPO_BRANCH": "main", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "10", @@ -31,7 +31,7 @@ func TestLibrary_Repo_Environment(t *testing.T) { "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_APPROVE_BUILD": "never", "REPOSITORY_ACTIVE": "true", - "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,tag,comment:created,schedule,delete:branch", + "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", "REPOSITORY_BRANCH": "main", "REPOSITORY_CLONE": "https://github.com/github/octocat.git", "REPOSITORY_FULL_NAME": "github/octocat", diff --git a/pipeline/container_test.go b/pipeline/container_test.go index ba08cd2b..1e959cb5 100644 --- a/pipeline/container_test.go +++ b/pipeline/container_test.go @@ -202,6 +202,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -222,6 +223,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -241,6 +243,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -260,6 +263,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess, constants.StatusFailure}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -279,6 +283,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess, constants.StatusFailure}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -298,6 +303,7 @@ func TestPipeline_Container_Execute(t *testing.T) { If: Rules{ Status: []string{constants.StatusSuccess, constants.StatusFailure}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -318,6 +324,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Branch: []string{"main"}, Event: []string{constants.EventPush}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -338,6 +345,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Branch: []string{"main"}, Event: []string{constants.EventPush}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -359,6 +367,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Path: []string{"README.md"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -380,6 +389,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Path: []string{"README.md"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -401,6 +411,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Path: []string{"README.md"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -422,6 +433,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventComment}, Comment: []string{"run vela"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -443,6 +455,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventComment}, Comment: []string{"run vela"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -464,6 +477,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventComment}, Comment: []string{"run vela"}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -485,6 +499,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -506,6 +521,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -527,6 +543,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -570,6 +587,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventTag}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -592,6 +610,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventTag}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -614,6 +633,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventTag}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -634,6 +654,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Unless: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -653,6 +674,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Unless: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -672,6 +694,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Unless: Rules{ Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -693,6 +716,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ @@ -714,6 +738,7 @@ func TestPipeline_Container_Execute(t *testing.T) { Event: []string{constants.EventPush}, Status: []string{constants.StatusSuccess}, }, + Operator: "and", }, }, ruleData: &RuleData{ diff --git a/pipeline/ruleset.go b/pipeline/ruleset.go index 04880d57..a27c8ea6 100644 --- a/pipeline/ruleset.go +++ b/pipeline/ruleset.go @@ -37,6 +37,7 @@ type ( Status Ruletype `json:"status,omitempty" yaml:"status,omitempty"` Tag Ruletype `json:"tag,omitempty" yaml:"tag,omitempty"` Target Ruletype `json:"target,omitempty" yaml:"target,omitempty"` + Label Ruletype `json:"label,omitempty" yaml:"label,omitempty"` Parallel bool `json:"-" yaml:"-"` } @@ -57,6 +58,7 @@ type ( Status string `json:"status,omitempty" yaml:"status,omitempty"` Tag string `json:"tag,omitempty" yaml:"tag,omitempty"` Target string `json:"target,omitempty" yaml:"target,omitempty"` + Label []string `json:"label,omitempty" yaml:"label,omitempty"` Parallel bool `json:"-" yaml:"-"` } ) @@ -111,7 +113,8 @@ func (r *Rules) Empty() bool { len(r.Repo) == 0 && len(r.Status) == 0 && len(r.Tag) == 0 && - len(r.Target) == 0 { + len(r.Target) == 0 && + len(r.Label) == 0 { return true } @@ -119,152 +122,152 @@ func (r *Rules) Empty() bool { return false } -// Match returns true for the or operator when one of the +// Match returns true for the `or` operator when one of the // ruletypes from the rules match the provided ruledata. -// Match returns true for the and operator when all of the +// Match returns true for the `and` operator when all of the // ruletypes from the rules match the provided ruledata. For // both operators, when none of the ruletypes from the rules // match the provided ruledata, the function returns false. func (r *Rules) Match(from *RuleData, matcher, op string) (bool, error) { - // if the path ruletype is provided - if len(from.Path) > 0 { - // if the "or" operator is provided in the ruleset - if strings.EqualFold(op, constants.OperatorOr) { - // iterate through each path in the ruletype - for _, p := range from.Path { - matches, err := matches(r, from, matcher, p, constants.OperatorOr) - if err != nil { - return false, err - } - - // return true if any ruletype matches the ruledata - if matches { - return true, nil - } - } - - // return false if no match is found - return false, nil - } - - // iterate through each path in the ruletype - for _, p := range from.Path { - matches, err := matches(r, from, matcher, p, constants.OperatorAnd) - if err != nil { - return false, err - } - - // return true if any ruletype matches the ruledata - if matches { - return true, nil - } - } - - // return false if no match is found - return false, nil - } - - // if the "or" operator is provided in the ruleset - if strings.EqualFold(op, constants.OperatorOr) { - // return true if any ruletype matches the ruledata - return matches(r, from, matcher, "", constants.OperatorOr) - } - - return matches(r, from, matcher, "", constants.OperatorAnd) -} - -// Match returns true when the provided ruletype -// matches the provided ruledata. When the provided -// ruletype is empty, the function returns true for -// the `and` operator and false for the `or` operator. -func (r *Ruletype) Match(data, matcher, logic string) (bool, error) { - // return true for `and`, false for `or` if an empty ruletype is provided - if len(*r) == 0 { - return strings.EqualFold(logic, constants.OperatorAnd), nil - } - - // iterate through each pattern in the ruletype - for _, pattern := range *r { - // handle the pattern based off the matcher provided - switch matcher { - case constants.MatcherRegex, "regex": - regExpPattern, err := regexp.Compile(pattern) - if err != nil { - return false, fmt.Errorf("error in regex pattern %s: %w", pattern, err) - } - - // return true if the regexp pattern matches the ruledata - if regExpPattern.MatchString(data) { - return true, nil - } - case constants.MatcherFilepath: - fallthrough - default: - // return true if the pattern matches the ruledata - ok, _ := filepath.Match(pattern, data) - if ok { - return true, nil - } - } - } - - // return false if no match is found - return false, nil -} - -// matches is a helper function which leverages the Match method for all rules -// and returns `true` if the ruleset is indeed a match. -func matches(r *Rules, from *RuleData, matcher, path, logic string) (bool, error) { status := true var err error if len(from.Status) != 0 { - status, err = r.Status.Match(from.Status, matcher, logic) + status, err = r.Status.MatchSingle(from.Status, matcher, op) if err != nil { return false, err } } - matchBranch, err := r.Branch.Match(from.Branch, matcher, logic) + matchBranch, err := r.Branch.MatchSingle(from.Branch, matcher, op) if err != nil { return false, err } - matchComment, err := r.Comment.Match(from.Comment, matcher, logic) + matchComment, err := r.Comment.MatchSingle(from.Comment, matcher, op) if err != nil { return false, err } - matchEvent, err := r.Event.Match(from.Event, matcher, logic) + matchEvent, err := r.Event.MatchSingle(from.Event, matcher, op) if err != nil { return false, err } - matchPath, err := r.Path.Match(path, matcher, logic) + matchPath, err := r.Path.MatchMultiple(from.Path, matcher, op) if err != nil { return false, err } - matchRepo, err := r.Repo.Match(from.Repo, matcher, logic) + matchRepo, err := r.Repo.MatchSingle(from.Repo, matcher, op) if err != nil { return false, err } - matchTag, err := r.Tag.Match(from.Tag, matcher, logic) + matchTag, err := r.Tag.MatchSingle(from.Tag, matcher, op) if err != nil { return false, err } - matchTarget, err := r.Target.Match(from.Target, matcher, logic) + matchTarget, err := r.Target.MatchSingle(from.Target, matcher, op) if err != nil { return false, err } - switch logic { - case constants.OperatorAnd: - return (matchBranch && matchComment && matchEvent && matchPath && matchRepo && matchTag && matchTarget && status), nil + matchLabel, err := r.Label.MatchMultiple(from.Label, matcher, op) + if err != nil { + return false, err + } + + switch op { + case constants.OperatorOr: + return (matchBranch || matchComment || matchEvent || matchPath || matchRepo || matchTag || matchTarget || matchLabel || status), nil + default: + return (matchBranch && matchComment && matchEvent && matchPath && matchRepo && matchTag && matchTarget && matchLabel && status), nil + } +} + +// MatchSingle returns true when the provided ruletype +// matches the provided ruledata. When the provided +// ruletype is empty, the function returns true for +// the `and` operator and false for the `or` operator. +func (r *Ruletype) MatchSingle(data, matcher, logic string) (bool, error) { + // return true for `and`, false for `or` if an empty ruletype is provided + if len(*r) == 0 { + return strings.EqualFold(logic, constants.OperatorAnd), nil + } + + // iterate through each pattern in the ruletype + for _, pattern := range *r { + match, err := match(data, matcher, pattern) + if err != nil { + return false, err + } + + if match { + return true, nil + } + } + + // return false if no match is found + return false, nil +} + +// MatchMultiple returns true when the provided ruletype +// matches the provided ruledata. When the provided +// ruletype is empty, the function returns true for +// the `and` operator and false for the `or` operator. +func (r *Ruletype) MatchMultiple(data []string, matcher, logic string) (bool, error) { + // return true for `and`, false for `or` if an empty ruletype is provided + if len(*r) == 0 { + return strings.EqualFold(logic, constants.OperatorAnd), nil + } + + // iterate through each pattern in the ruletype + for _, pattern := range *r { + for _, value := range data { + match, err := match(value, matcher, pattern) + if err != nil { + return false, err + } + + if match { + return true, nil + } + } + } + + // return false if no match is found + return false, nil +} + +// match is a helper function that compares data against a pattern +// and returns true if the data matches the pattern, depending on +// matcher specified. +func match(data, matcher, pattern string) (bool, error) { + // handle the pattern based off the matcher provided + switch matcher { + case constants.MatcherRegex, "regex": + regExpPattern, err := regexp.Compile(pattern) + if err != nil { + return false, fmt.Errorf("error in regex pattern %s: %w", pattern, err) + } + + // return true if the regexp pattern matches the ruledata + if regExpPattern.MatchString(data) { + return true, nil + } + case constants.MatcherFilepath: + fallthrough default: - return (matchBranch || matchComment || matchEvent || matchPath || matchRepo || matchTag || matchTarget || status), nil + // return true if the pattern matches the ruledata + ok, _ := filepath.Match(pattern, data) + if ok { + return true, nil + } } + + // return false if no match is found + return false, nil } diff --git a/pipeline/ruleset_test.go b/pipeline/ruleset_test.go index a310ed71..7ea87a31 100644 --- a/pipeline/ruleset_test.go +++ b/pipeline/ruleset_test.go @@ -20,62 +20,62 @@ func TestPipeline_Ruleset_Match(t *testing.T) { {ruleset: &Ruleset{}, data: &RuleData{Branch: "main"}, want: true}, // If with and operator { - ruleset: &Ruleset{If: Rules{Branch: []string{"main"}}}, + ruleset: &Ruleset{If: Rules{Branch: []string{"main"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, { - ruleset: &Ruleset{If: Rules{Branch: []string{"main"}}}, + ruleset: &Ruleset{If: Rules{Branch: []string{"main"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{If: Rules{Branch: []string{"main"}, Event: []string{"push"}}}, + ruleset: &Ruleset{If: Rules{Branch: []string{"main"}, Event: []string{"push"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, { - ruleset: &Ruleset{If: Rules{Branch: []string{"main"}, Event: []string{"push"}}}, + ruleset: &Ruleset{If: Rules{Branch: []string{"main"}, Event: []string{"push"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "pull_request", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{If: Rules{Path: []string{"foo.txt", "/foo/bar.txt"}}}, + ruleset: &Ruleset{If: Rules{Path: []string{"foo.txt", "/foo/bar.txt"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "pull_request", Path: []string{}, Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{If: Rules{Comment: []string{"rerun"}}}, + ruleset: &Ruleset{If: Rules{Comment: []string{"rerun"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, { - ruleset: &Ruleset{If: Rules{Comment: []string{"rerun"}}}, + ruleset: &Ruleset{If: Rules{Comment: []string{"rerun"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "ok to test", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{If: Rules{Event: []string{"deployment"}, Target: []string{"production"}}}, + ruleset: &Ruleset{If: Rules{Event: []string{"deployment"}, Target: []string{"production"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "", Event: "deployment", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: "production"}, want: true, }, { - ruleset: &Ruleset{If: Rules{Event: []string{"deployment"}, Target: []string{"production"}}}, + ruleset: &Ruleset{If: Rules{Event: []string{"deployment"}, Target: []string{"production"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "", Event: "deployment", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: "stage"}, want: false, }, { - ruleset: &Ruleset{If: Rules{Event: []string{"schedule"}, Target: []string{"weekly"}}}, + ruleset: &Ruleset{If: Rules{Event: []string{"schedule"}, Target: []string{"weekly"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "", Event: "schedule", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: "weekly"}, want: true, }, { - ruleset: &Ruleset{If: Rules{Event: []string{"schedule"}, Target: []string{"weekly"}}}, + ruleset: &Ruleset{If: Rules{Event: []string{"schedule"}, Target: []string{"weekly"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "", Event: "schedule", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: "nightly"}, want: false, }, { - ruleset: &Ruleset{If: Rules{Status: []string{"success", "failure"}}}, + ruleset: &Ruleset{If: Rules{Status: []string{"success", "failure"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "ok to test", Event: "push", Repo: "octocat/hello-world", Status: "failure", Tag: "refs/heads/main", Target: ""}, want: true, }, @@ -107,27 +107,27 @@ func TestPipeline_Ruleset_Match(t *testing.T) { }, // Unless with and operator { - ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}}}, + ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}}}, + ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}}, Operator: "and"}, data: &RuleData{Branch: "dev", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, { - ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}, Event: []string{"push"}}}, + ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}, Event: []string{"push"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: false, }, { - ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}, Event: []string{"push"}}}, + ruleset: &Ruleset{Unless: Rules{Branch: []string{"main"}, Event: []string{"push"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "pull_request", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, { - ruleset: &Ruleset{Unless: Rules{Path: []string{"foo.txt", "/foo/bar.txt"}}}, + ruleset: &Ruleset{Unless: Rules{Path: []string{"foo.txt", "/foo/bar.txt"}}, Operator: "and"}, data: &RuleData{Branch: "main", Comment: "rerun", Event: "pull_request", Path: []string{}, Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, want: true, }, @@ -425,16 +425,52 @@ func TestPipeline_Rules_Match(t *testing.T) { }, { rules: &Rules{Event: []string{"push", "pull_request"}, Tag: []string{"release/*"}}, - data: &RuleData{Branch: "main", Event: "push", Repo: "octocat/hello-world", Status: "pending", Tag: "release/*", Target: ""}, + data: &RuleData{Branch: "main", Event: "tag", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, operator: "or", + want: false, + }, + { + rules: &Rules{Event: []string{"pull_request:labeled"}, Label: []string{"enhancement", "documentation"}}, + data: &RuleData{Branch: "main", Event: "pull_request:labeled", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "and", want: true, }, { - rules: &Rules{Event: []string{"push", "pull_request"}, Tag: []string{"release/*"}}, - data: &RuleData{Branch: "main", Event: "tag", Repo: "octocat/hello-world", Status: "pending", Tag: "refs/heads/main", Target: ""}, - operator: "or", + rules: &Rules{Event: []string{"pull_request:labeled"}, Label: []string{"enhancement", "documentation"}}, + data: &RuleData{Branch: "main", Event: "pull_request:labeled", Repo: "octocat/hello-world", Status: "pending", Label: []string{"support"}}, + operator: "and", + want: false, + }, + { + rules: &Rules{Event: []string{"pull_request:unlabeled"}, Label: []string{"enhancement", "documentation"}}, + data: &RuleData{Branch: "main", Event: "pull_request:unlabeled", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "and", + want: true, + }, + { + rules: &Rules{Event: []string{"pull_request:unlabeled"}, Label: []string{"enhancement"}}, + data: &RuleData{Branch: "main", Event: "pull_request:unlabeled", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "and", want: false, }, + { + rules: &Rules{Event: []string{"push"}, Label: []string{"enhancement", "documentation"}}, + data: &RuleData{Branch: "main", Event: "push", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "and", + want: true, + }, + { + rules: &Rules{Event: []string{"push"}, Label: []string{"enhancement"}}, + data: &RuleData{Branch: "main", Event: "push", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "and", + want: false, + }, + { + rules: &Rules{Event: []string{"push"}, Label: []string{"enhancement"}}, + data: &RuleData{Branch: "main", Event: "push", Repo: "octocat/hello-world", Status: "pending", Label: []string{"documentation"}}, + operator: "or", + want: true, + }, } // run test @@ -490,6 +526,9 @@ func TestPipeline_Ruletype_MatchAnd(t *testing.T) { // Target with filepath matcher {matcher: "filepath", rule: []string{"production"}, pattern: "production", want: true}, {matcher: "filepath", rule: []string{"stage"}, pattern: "production", want: false}, + // Label with filepath matcher + {matcher: "filepath", rule: []string{"enhancement", "documentation"}, pattern: "documentation", want: true}, + {matcher: "filepath", rule: []string{"enhancement", "documentation"}, pattern: "question", want: false}, // Empty with regex matcher {matcher: "regexp", rule: []string{}, pattern: "main", want: true}, {matcher: "regexp", rule: []string{}, pattern: "push", want: true}, @@ -525,11 +564,14 @@ func TestPipeline_Ruletype_MatchAnd(t *testing.T) { // Target with regex matcher {matcher: "regexp", rule: []string{"production"}, pattern: "production", want: true}, {matcher: "regexp", rule: []string{"stage"}, pattern: "production", want: false}, + // Label with regexp matcher + {matcher: "regexp", rule: []string{"enhancement", "documentation"}, pattern: "documentation", want: true}, + {matcher: "regexp", rule: []string{"enhancement", "documentation"}, pattern: "question", want: false}, } // run test for _, test := range tests { - got, _ := test.rule.Match(test.pattern, test.matcher, constants.OperatorAnd) + got, _ := test.rule.MatchSingle(test.pattern, test.matcher, constants.OperatorAnd) if got != test.want { t.Errorf("MatchAnd for %s matcher is %v, want %v", test.matcher, got, test.want) @@ -572,6 +614,9 @@ func TestPipeline_Ruletype_MatchOr(t *testing.T) { // Target with filepath matcher {matcher: "filepath", rule: []string{"production"}, pattern: "production", want: true}, {matcher: "filepath", rule: []string{"stage"}, pattern: "production", want: false}, + // Label with filepath matcher + {matcher: "filepath", rule: []string{"enhancement", "documentation"}, pattern: "documentation", want: true}, + {matcher: "filepath", rule: []string{"enhancement", "documentation"}, pattern: "question", want: false}, // Empty with regexp matcher {matcher: "regexp", rule: []string{}, pattern: "main", want: false}, {matcher: "regexp", rule: []string{}, pattern: "push", want: false}, @@ -599,11 +644,14 @@ func TestPipeline_Ruletype_MatchOr(t *testing.T) { // Target with regexp matcher {matcher: "regexp", rule: []string{"production"}, pattern: "production", want: true}, {matcher: "regexp", rule: []string{"stage"}, pattern: "production", want: false}, + // Label with regexp matcher + {matcher: "regexp", rule: []string{"enhancement", "documentation"}, pattern: "documentation", want: true}, + {matcher: "regexp", rule: []string{"enhancement", "documentation"}, pattern: "question", want: false}, } // run test for _, test := range tests { - got, _ := test.rule.Match(test.pattern, test.matcher, constants.OperatorOr) + got, _ := test.rule.MatchSingle(test.pattern, test.matcher, constants.OperatorOr) if got != test.want { t.Errorf("MatchOr for %s matcher is %v, want %v", test.matcher, got, test.want) diff --git a/webhook.go b/webhook.go index 1941e38f..579e8230 100644 --- a/webhook.go +++ b/webhook.go @@ -19,6 +19,7 @@ type PullRequest struct { Comment string Number int IsFromFork bool + Labels []string } // Webhook defines a struct that is used to return diff --git a/yaml/ruleset.go b/yaml/ruleset.go index 7b8fc615..2fe23e16 100644 --- a/yaml/ruleset.go +++ b/yaml/ruleset.go @@ -30,6 +30,7 @@ type ( Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` + Label []string `yaml:"label,omitempty,flow" json:"label,omitempty" jsonschema:"description=Limits step execution to match on pull requests labels.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` } ) @@ -84,6 +85,7 @@ func (r *Ruleset) UnmarshalYAML(unmarshal func(interface{}) error) error { advanced.If.Status = append(advanced.If.Status, simple.Status...) advanced.If.Tag = append(advanced.If.Tag, simple.Tag...) advanced.If.Target = append(advanced.If.Target, simple.Target...) + advanced.If.Label = append(advanced.If.Label, simple.Label...) // set ruleset `if` to advanced `if` rules r.If = advanced.If @@ -113,6 +115,7 @@ func (r *Rules) ToPipeline() *pipeline.Rules { Status: r.Status, Tag: r.Tag, Target: r.Target, + Label: r.Label, } } @@ -128,6 +131,7 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { Status raw.StringSlice Tag raw.StringSlice Target raw.StringSlice + Label raw.StringSlice }) // attempt to unmarshal rules @@ -140,6 +144,7 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { r.Status = rules.Status r.Tag = rules.Tag r.Target = rules.Target + r.Label = rules.Label // account for users who use non-scoped pull_request event events := []string{} diff --git a/yaml/ruleset_test.go b/yaml/ruleset_test.go index 762bb904..559395ba 100644 --- a/yaml/ruleset_test.go +++ b/yaml/ruleset_test.go @@ -22,12 +22,13 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { If: Rules{ Branch: []string{"main"}, Comment: []string{"test comment"}, - Event: []string{"push"}, + Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, + Label: []string{"enhancement"}, }, Unless: Rules{ Branch: []string{"main"}, @@ -47,12 +48,13 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { If: pipeline.Rules{ Branch: []string{"main"}, Comment: []string{"test comment"}, - Event: []string{"push"}, + Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, + Label: []string{"enhancement"}, }, Unless: pipeline.Rules{ Branch: []string{"main"}, @@ -167,22 +169,24 @@ func TestYaml_Rules_ToPipeline(t *testing.T) { rules: &Rules{ Branch: []string{"main"}, Comment: []string{"test comment"}, - Event: []string{"push"}, + Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, + Label: []string{"enhancement"}, }, want: &pipeline.Rules{ Branch: []string{"main"}, Comment: []string{"test comment"}, - Event: []string{"push"}, + Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, + Label: []string{"enhancement"}, }, }, }