diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 112ec365..7433661c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,10 +35,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' @@ -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@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.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@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/autobuild@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.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@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/analyze@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8 diff --git a/.github/workflows/pr-title-validate.yml b/.github/workflows/pr-title-validate.yml index 223f7cdf..dc8c67be 100644 --- a/.github/workflows/pr-title-validate.yml +++ b/.github/workflows/pr-title-validate.yml @@ -13,5 +13,7 @@ jobs: steps: - name: validate title + env: + TITLE: ${{ github.event.pull_request.title }} run: | - echo "${{ github.event.pull_request.title }}" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)(\(.*\)|):\s.+$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file + echo "$TITLE" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)(\(.*\)|)!?:\s.+$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) \ No newline at end of file diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index b9eb09c0..68ea470a 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -12,10 +12,10 @@ jobs: steps: - name: clone - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' @@ -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" @@ -36,10 +36,10 @@ jobs: steps: - name: clone - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' @@ -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/schema.yml b/.github/workflows/schema.yml index 352fe0d4..57f62ac7 100644 --- a/.github/workflows/schema.yml +++ b/.github/workflows/schema.yml @@ -13,10 +13,10 @@ jobs: steps: - name: clone - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5970250f..60108803 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ jobs: steps: - name: clone - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' @@ -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@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.out diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 0f039bfa..700684ba 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -14,10 +14,10 @@ jobs: steps: - name: clone - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: install go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: # use version from go.mod file go-version-file: 'go.mod' @@ -33,4 +33,7 @@ jobs: # Check that go fmt ./... produces a zero diff; clean up any changes afterwards. go fmt ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) # Check that go fix ./... produces a zero diff; clean up any changes afterwards. - go fix ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) + # + # Renable this after https://github.com/golang/go/commit/7fd62ba821b1044e8e4077df052b0a1232672d57 + # has been released. + # go fix ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) diff --git a/.golangci.yml b/.golangci.yml index ef42aeda..4ae4c744 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,6 +32,18 @@ linters-settings: lines: 160 statements: 70 + # https://github.com/daixiang0/gci + # ensure import order is consistent + # gci write --custom-order -s standard -s default -s blank -s dot -s "prefix(github.com/go-vela)" . + gci: + custom-order: true + sections: + - standard + - default + - blank + - dot + - prefix(github.com/go-vela) + # https://github.com/denis-tingaikin/go-header goheader: template: |- @@ -60,45 +72,42 @@ linters: # enable a specific set of linters to run enable: - - bidichk # checks for dangerous unicode character sequences - - bodyclose # checks whether HTTP response body is closed successfully - - contextcheck # check the function whether use a non-inherited context - - deadcode # finds unused code - - dupl # code clone detection - - errcheck # checks for unchecked errors - - errorlint # find misuses of errors - - exportloopref # check for exported loop vars - - funlen # detects long functions - - goconst # finds repeated strings that could be replaced by a constant - - gocyclo # computes and checks the cyclomatic complexity of functions - - godot # checks if comments end in a period - - gofmt # checks whether code was gofmt-ed - - goheader # checks is file header matches to pattern - - goimports # fixes imports and formats code in same style as gofmt - - gomoddirectives # manage the use of 'replace', 'retract', and 'excludes' directives in go.mod - - goprintffuncname # checks that printf-like functions are named with f at the end - - gosec # inspects code for security problems - - gosimple # linter that specializes in simplifying a code - - govet # reports suspicious constructs, ex. Printf calls whose arguments don't align with the format string - - ineffassign # detects when assignments to existing variables aren't used - - makezero # finds slice declarations with non-zero initial length - - misspell # finds commonly misspelled English words in comments - - nakedret # finds naked returns in functions greater than a specified function length - - nilerr # finds the code that returns nil even if it checks that the error is not nil - - noctx # noctx finds sending http request without context.Context - - nolintlint # reports ill-formed or insufficient nolint directives - - revive # linter for go - - staticcheck # applies static analysis checks, go vet on steroids - - structcheck # finds unused struct fields - - stylecheck # replacement for golint - - tenv # analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - - typecheck # parses and type-checks go code, like the front-end of a go compiler - - unconvert # remove unnecessary type conversions - - unparam # reports unused function parameters - - unused # checks for unused constants, variables, functions and types - - varcheck # finds unused global variables and constants - - whitespace # detects leading and trailing whitespace - - wsl # forces code to use empty lines + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context + - dupl # code clone detection + - errcheck # checks for unchecked errors + - errorlint # find misuses of errors + - exportloopref # check for exported loop vars + - funlen # detects long functions + - gci # consistent import ordering + - goconst # finds repeated strings that could be replaced by a constant + - gocyclo # computes and checks the cyclomatic complexity of functions + - godot # checks if comments end in a period + - gofmt # checks whether code was gofmt-ed + - goheader # checks is file header matches to pattern + - gomoddirectives # manage the use of 'replace', 'retract', and 'excludes' directives in go.mod + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects code for security problems + - gosimple # linter that specializes in simplifying a code + - govet # reports suspicious constructs, ex. Printf calls whose arguments don't align with the format string + - ineffassign # detects when assignments to existing variables aren't used + - makezero # finds slice declarations with non-zero initial length + - misspell # finds commonly misspelled English words in comments + - nakedret # finds naked returns in functions greater than a specified function length + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - noctx # noctx finds sending http request without context.Context + - nolintlint # reports ill-formed or insufficient nolint directives + - revive # linter for go + - staticcheck # applies static analysis checks, go vet on steroids + - stylecheck # replacement for golint + - tenv # analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - typecheck # parses and type-checks go code, like the front-end of a go compiler + - unconvert # remove unnecessary type conversions + - unparam # reports unused function parameters + - unused # checks for unused constants, variables, functions and types + - whitespace # detects leading and trailing whitespace + - wsl # forces code to use empty lines # static list of linters we know golangci can run but we've # chosen to leave disabled for now @@ -112,13 +121,13 @@ linters: # - exhaustivestruct - style preference # - forbidigo - unused # - forcetypeassert - unused - # - gci - use goimports # - gochecknoinits - unused # - gochecknoglobals - global variables allowed # - gocognit - unused complexity metric # - gocritic - style preference # - godox - to be used in the future # - goerr113 - to be used in the future + # - goimports - use gci # - golint - archived, replaced with revive # - gofumpt - use gofmt # - gomnd - get too many false-positives diff --git a/Makefile b/Makefile index 373dbb88..bd4d3e59 100644 --- a/Makefile +++ b/Makefile @@ -142,3 +142,24 @@ schema: @go get github.com/alecthomas/jsonschema @go get github.com/iancoleman/orderedmap @go run cmd/schema/main.go > schema.json + +# The `lint` target is intended to lint the +# Go source code with golangci-lint. +# +# Usage: `make lint` +.PHONY: lint +lint: + @echo + @echo "### Linting Go Code" + @golangci-lint run ./... + +# The `lintfix` target is intended to lint the +# Go source code with golangci-lint and apply +# any fixes that can be automatically applied. +# +# Usage: `make lintfix` +.PHONY: lintfix +lintfix: + @echo + @echo "### Fixing Go code with linter" + @golangci-lint run ./... --fix \ No newline at end of file diff --git a/constants/action.go b/constants/action.go index f1e18c6e..67e4e835 100644 --- a/constants/action.go +++ b/constants/action.go @@ -7,7 +7,7 @@ const ( // ActionOpened defines the action for opening pull requests. ActionOpened = "opened" - // ActionCreated defines the action for creating issue comments. + // ActionCreated defines the action for creating deployments or issue comments. ActionCreated = "created" // ActionEdited defines the action for the editing of pull requests or issue comments. @@ -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" @@ -30,4 +36,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/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/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/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/deployment.go b/database/deployment.go index ba97ecdd..61a56136 100644 --- a/database/deployment.go +++ b/database/deployment.go @@ -7,10 +7,11 @@ import ( "errors" "fmt" + "github.com/lib/pq" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library" "github.com/go-vela/types/raw" - "github.com/lib/pq" ) var ( diff --git a/database/deployment_test.go b/database/deployment_test.go index 3a3c6f2f..e22b4481 100644 --- a/database/deployment_test.go +++ b/database/deployment_test.go @@ -6,10 +6,11 @@ import ( "database/sql" "testing" - "github.com/go-vela/types/library" - "github.com/go-vela/types/raw" "github.com/google/go-cmp/cmp" "github.com/lib/pq" + + "github.com/go-vela/types/library" + "github.com/go-vela/types/raw" ) func TestDatabase_Deployment_Nullify(t *testing.T) { diff --git a/database/repo.go b/database/repo.go index 6b560f6d..99ce6eb2 100644 --- a/database/repo.go +++ b/database/repo.go @@ -7,9 +7,10 @@ import ( "encoding/base64" "errors" + "github.com/lib/pq" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library" - "github.com/lib/pq" ) var ( @@ -61,11 +62,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"` @@ -241,11 +237,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) @@ -339,11 +330,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/schedule.go b/database/schedule.go index 4f26b278..bd678b79 100644 --- a/database/schedule.go +++ b/database/schedule.go @@ -7,6 +7,7 @@ import ( "errors" "github.com/adhocore/gronx" + "github.com/go-vela/types/library" ) 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/database/secret.go b/database/secret.go index f9dae86d..33dcd800 100644 --- a/database/secret.go +++ b/database/secret.go @@ -8,10 +8,10 @@ import ( "errors" "strings" + "github.com/lib/pq" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library" - - "github.com/lib/pq" ) var ( @@ -42,21 +42,21 @@ 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)"` + 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 @@ -193,9 +193,9 @@ 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) secret.SetCreatedAt(s.CreatedAt.Int64) secret.SetCreatedBy(s.CreatedBy.String) secret.SetUpdatedAt(s.UpdatedAt.Int64) @@ -259,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 } @@ -272,21 +266,21 @@ 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()), + 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..d44a1265 100644 --- a/database/secret_test.go +++ b/database/secret_test.go @@ -168,9 +168,9 @@ 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) want.SetCreatedAt(tsCreate) want.SetCreatedBy("octocat") want.SetUpdatedAt(tsUpdate) @@ -292,9 +292,9 @@ 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) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat") s.SetUpdatedAt(tsUpdate) @@ -314,20 +314,20 @@ 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"}, + 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/database/step.go b/database/step.go index 437d0a18..41f2808a 100644 --- a/database/step.go +++ b/database/step.go @@ -50,6 +50,7 @@ type Step struct { Runtime sql.NullString `sql:"runtime"` Distribution sql.NullString `sql:"distribution"` CheckID sql.NullInt64 `sql:"check_id"` + ReportAs sql.NullString `sql:"report_as"` } // Nullify ensures the valid flag for @@ -148,6 +149,11 @@ func (s *Step) Nullify() *Step { s.CheckID.Valid = false } + // check if the ReportAs field should be false + if len(s.ReportAs.String) == 0 { + s.ReportAs.Valid = false + } + return s } @@ -173,6 +179,7 @@ func (s *Step) ToLibrary() *library.Step { step.SetRuntime(s.Runtime.String) step.SetDistribution(s.Distribution.String) step.SetCheckID(s.CheckID.Int64) + step.SetReportAs(s.ReportAs.String) return step } @@ -216,6 +223,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 } @@ -241,6 +249,7 @@ func StepFromLibrary(s *library.Step) *Step { Runtime: sql.NullString{String: s.GetRuntime(), Valid: true}, Distribution: sql.NullString{String: s.GetDistribution(), Valid: true}, CheckID: sql.NullInt64{Int64: s.GetCheckID(), 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/database/user.go b/database/user.go index 1e312755..a5030bcc 100644 --- a/database/user.go +++ b/database/user.go @@ -8,9 +8,10 @@ import ( "errors" "regexp" + "github.com/lib/pq" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library" - "github.com/lib/pq" ) var ( diff --git a/database/worker.go b/database/worker.go index 59c2f343..45d76388 100644 --- a/database/worker.go +++ b/database/worker.go @@ -6,9 +6,10 @@ import ( "database/sql" "errors" + "github.com/lib/pq" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library" - "github.com/lib/pq" ) var ( diff --git a/go.mod b/go.mod index 1f0790df..496f3ce4 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/go-vela/types -go 1.21 +go 1.21.9 require ( - github.com/adhocore/gronx v1.6.7 + 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 @@ -16,7 +16,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/gorilla/css v1.0.0 // indirect github.com/kr/pretty v0.2.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.23.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index f2cbb47e..037bfbec 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.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= @@ -22,8 +22,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/actions/pull.go b/library/actions/pull.go index 609c6248..b7d8f93c 100644 --- a/library/actions/pull.go +++ b/library/actions/pull.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // similar code to push.go + package actions import "github.com/go-vela/types/constants" @@ -12,6 +11,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 +21,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 +47,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 +102,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 +175,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.go b/library/actions/push.go index ed19c48f..612b2cff 100644 --- a/library/actions/push.go +++ b/library/actions/push.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // similar code to comment.go + package actions import "github.com/go-vela/types/constants" diff --git a/library/actions/push_test.go b/library/actions/push_test.go index 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/build_test.go b/library/build_test.go index 5c82c7a3..d9b810d7 100644 --- a/library/build_test.go +++ b/library/build_test.go @@ -8,8 +8,9 @@ import ( "testing" "time" - "github.com/go-vela/types/raw" "github.com/google/go-cmp/cmp" + + "github.com/go-vela/types/raw" ) func TestLibrary_Build_Duration(t *testing.T) { diff --git a/library/events.go b/library/events.go index 88074d80..48351517 100644 --- a/library/events.go +++ b/library/events.go @@ -3,8 +3,12 @@ package library import ( + "errors" + "fmt" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library/actions" + "github.com/go-vela/types/raw" ) // Events is the library representation of the various events that generate a @@ -17,6 +21,29 @@ type Events struct { Schedule *actions.Schedule `json:"schedule"` } +// UnmarshalYAML implements the Unmarshaler interface for the Events type. +func (e *Events) UnmarshalYAML(unmarshal func(interface{}) error) error { + // string slice we try unmarshalling to + stringSlice := new(raw.StringSlice) + + // attempt to unmarshal as a string slice type + err := unmarshal(stringSlice) + if err == nil { + // create new events from string slice + evs, err := NewEventsFromSlice(*stringSlice) + if err != nil { + return err + } + + // overwrite existing Events + *e = *evs + + return nil + } + + return errors.New("failed to unmarshal Events") +} + // NewEventsFromMask is an instatiation function for the Events type that // takes in an event mask integer value and populates the nested Events struct. func NewEventsFromMask(mask int64) *Events { @@ -37,6 +64,66 @@ 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, error) { + 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 + 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: + 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 + + default: + return nil, fmt.Errorf("invalid event provided: %s", event) + } + } + + return NewEventsFromMask(mask), nil +} + // 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 { @@ -58,13 +145,17 @@ 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: allowed = e.GetComment().GetCreated() case constants.EventComment + ":" + constants.ActionEdited: allowed = e.GetComment().GetEdited() - case constants.EventDeploy: + case constants.EventDeploy + ":" + constants.ActionCreated: allowed = e.GetDeployment().GetCreated() case constants.EventSchedule: allowed = e.GetSchedule().GetRun() @@ -102,6 +193,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 a26cc4dc..c224f989 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -6,9 +6,10 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library/actions" - "github.com/google/go-cmp/cmp" ) func TestLibrary_Events_Getters(t *testing.T) { @@ -122,6 +123,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 +132,7 @@ func TestLibrary_Events_List(t *testing.T) { wantTwo := []string{ "pull_request:edited", + "pull_request:labeled", "deployment", "comment:edited", "delete:tag", @@ -158,6 +161,7 @@ func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { constants.AllowPullOpen | constants.AllowPullSync | constants.AllowPullReopen | + constants.AllowPullUnlabel | constants.AllowCommentCreate | constants.AllowSchedule, ) @@ -166,6 +170,7 @@ func TestLibrary_Events_NewEventsFromMask_ToDatabase(t *testing.T) { constants.AllowPushDeleteTag | constants.AllowPullEdit | constants.AllowCommentEdit | + constants.AllowPullLabel | constants.AllowDeployCreate, ) @@ -194,6 +199,129 @@ 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 + failure bool + }{ + { + 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", "pull_request:unlabeled"}, + want: e1, + failure: false, + }, + { + name: "action specific events to e2", + events: []string{"delete:tag", "pull_request:edited", "deployment:created", "comment:edited", "pull_request:labeled"}, + want: e2, + failure: false, + }, + { + 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, + Labeled: &fBool, + Unlabeled: &fBool, + }, + Deployment: &actions.Deploy{ + Created: &tBool, + }, + Comment: &actions.Comment{ + Created: &tBool, + Edited: &tBool, + }, + Schedule: &actions.Schedule{ + Run: &tBool, + }, + }, + failure: false, + }, + { + 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, + Labeled: &fBool, + Unlabeled: &fBool, + }, + Deployment: &actions.Deploy{ + Created: &fBool, + }, + Comment: &actions.Comment{ + Created: &fBool, + Edited: &fBool, + }, + Schedule: &actions.Schedule{ + Run: &fBool, + }, + }, + failure: false, + }, + { + name: "empty events", + events: []string{}, + want: NewEventsFromMask(0), + }, + { + name: "invalid events", + events: []string{"foo:bar"}, + want: nil, + failure: true, + }, + } + + // run tests + for _, test := range tests { + got, err := NewEventsFromSlice(test.events) + + if test.failure { + if err == nil { + t.Errorf("NewEventsFromSlice should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("NewEventsFromSlice returned err: %v", err) + } + + 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() @@ -210,7 +338,9 @@ 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: "deployment", want: false}, + {event: "pull_request", action: "labeled", want: false}, + {event: "pull_request", action: "unlabeled", want: true}, + {event: "deployment", action: "created", want: false}, {event: "comment", action: "created", want: true}, {event: "comment", action: "edited", want: false}, {event: "schedule", want: true}, @@ -249,6 +379,8 @@ func testEvents() (*Events, *Events) { Synchronize: &tBool, Edited: &fBool, Reopened: &tBool, + Labeled: &fBool, + Unlabeled: &tBool, }, Deployment: &actions.Deploy{ Created: &fBool, @@ -274,6 +406,8 @@ func testEvents() (*Events, *Events) { Synchronize: &fBool, Edited: &tBool, Reopened: &fBool, + Labeled: &tBool, + Unlabeled: &fBool, }, Deployment: &actions.Deploy{ Created: &tBool, 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/repo.go b/library/repo.go index 64e2d1ce..06cef929 100644 --- a/library/repo.go +++ b/library/repo.go @@ -28,28 +28,37 @@ 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"` + AllowEvents *Events `json:"allow_events,omitempty" yaml:"allow_events"` PipelineType *string `json:"pipeline_type,omitempty"` PreviousName *string `json:"previous_name,omitempty"` ApproveBuild *string `json:"approve_build,omitempty"` InstallID *int64 `json:"install_id,omitempty"` } +// UnmarshalYAML implements the Unmarshaler interface for the Repo type. +// This allows custom fields in the Repo type to be read from a YAML file, like AllowEvents. +func (r *Repo) UnmarshalYAML(unmarshal func(interface{}) error) error { + // create an alias to perform a normal unmarshal and avoid an infinite loop + type jsonRepo Repo + + tmp := &jsonRepo{} + + err := unmarshal(tmp) + if err != nil { + return err + } + + // overwrite existing Repo + *r = Repo(*tmp) + + return nil +} + // Environment returns a list of environment variables // provided from the fields of the Repo type. 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()[:], ","), @@ -68,23 +77,18 @@ func (r *Repo) Environment() map[string]string { "VELA_REPO_INSTALL_ID": ToString(r.GetInstallID()), // 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()), } } @@ -309,71 +313,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 @@ -660,71 +599,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 @@ -791,16 +665,9 @@ func (r *Repo) SetInstallID(v int64) { } // String implements the Stringer interface for the Repo type. -// -//nolint:dupl // ignore duplicate with test func 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, @@ -823,11 +690,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..e102e103 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -7,51 +7,42 @@ import ( "reflect" "testing" - "github.com/go-vela/types/constants" "github.com/google/go-cmp/cmp" + + "github.com/go-vela/types/constants" ) 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,pull_request:unlabeled,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,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", + "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 +135,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 +191,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 +260,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 +284,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 +305,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 +356,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/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/library/secret.go b/library/secret.go index 2566b4cd..099efb55 100644 --- a/library/secret.go +++ b/library/secret.go @@ -14,21 +14,40 @@ 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"` + AllowEvents *Events `json:"allow_events,omitempty" yaml:"allow_events"` + 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"` +} + +// UnmarshalYAML implements the Unmarshaler interface for the Secret type. +// This allows custom fields in the Secret type to be read from a YAML file, like AllowEvents. +func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { + // create an alias to perform a normal unmarshal and avoid an infinite loop + type jsonSecret Secret + + tmp := &jsonSecret{} + + err := unmarshal(tmp) + if err != nil { + return err + } + + // overwrite existing secret + *s = Secret(*tmp) + + return nil } // Sanitize creates a duplicate of the Secret without the value. @@ -39,21 +58,21 @@ 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, + AllowEvents: s.AllowEvents, + AllowCommand: s.AllowCommand, + AllowSubstitution: s.AllowSubstitution, + CreatedAt: s.CreatedAt, + CreatedBy: s.CreatedBy, + UpdatedAt: s.UpdatedAt, + UpdatedBy: s.UpdatedBy, } } @@ -69,6 +88,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"], @@ -198,19 +222,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 @@ -237,6 +248,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 @@ -393,43 +417,43 @@ func (s *Secret) SetImages(v []string) { s.Images = &v } -// SetEvents sets the Events field. +// SetAllowEvents sets the AllowEvents field. // // When the provided Secret type is nil, it // will set nothing and immediately return. -func (s *Secret) SetEvents(v []string) { +func (s *Secret) SetAllowEvents(v *Events) { // return if Secret type is nil if s == nil { return } - s.Events = &v + s.AllowEvents = v } -// SetAllowEvents sets the AllowEvents field. +// SetAllowCommand sets the AllowCommand field. // // When the provided Secret type is nil, it // will set nothing and immediately return. -func (s *Secret) SetAllowEvents(v *Events) { +func (s *Secret) SetAllowCommand(v bool) { // return if Secret type is nil if s == nil { return } - s.AllowEvents = v + s.AllowCommand = &v } -// SetAllowCommand sets the AllowCommand field. +// SetAllowSubstitution sets the AllowSubstitution field. // // When the provided Secret type is nil, it // will set nothing and immediately return. -func (s *Secret) SetAllowCommand(v bool) { +func (s *Secret) SetAllowSubstitution(v bool) { // return if Secret type is nil if s == nil { return } - s.AllowCommand = &v + s.AllowSubstitution = &v } // SetCreatedAt sets the CreatedAt field. @@ -489,7 +513,7 @@ func (s *Secret) String() string { return fmt.Sprintf(`{ AllowCommand: %t, AllowEvents: %s, - Events: %s, + AllowSubstitution: %t, ID: %d, Images: %s, Name: %s, @@ -505,7 +529,7 @@ func (s *Secret) String() string { }`, s.GetAllowCommand(), s.GetAllowEvents().List(), - s.GetEvents(), + s.GetAllowSubstitution(), s.GetID(), s.GetImages(), s.GetName(), diff --git a/library/secret_test.go b/library/secret_test.go index 591c1ac5..48655a7c 100644 --- a/library/secret_test.go +++ b/library/secret_test.go @@ -8,10 +8,11 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/go-vela/types/constants" "github.com/go-vela/types/library/actions" "github.com/go-vela/types/pipeline" - "github.com/google/go-cmp/cmp" ) func TestLibrary_Secret_Sanitize(t *testing.T) { @@ -112,8 +113,11 @@ func TestLibrary_Secret_Match(t *testing.T) { { name: "deployment", step: &pipeline.Container{ - Image: "alpine:latest", - Environment: map[string]string{"VELA_BUILD_EVENT": "deployment"}, + Image: "alpine:latest", + Environment: map[string]string{ + "VELA_BUILD_EVENT": "deployment", + "VELA_BUILD_EVENT_ACTION": "created", + }, }, sec: &Secret{ Name: &v, @@ -354,6 +358,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 @@ -416,10 +441,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()) } @@ -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()) } @@ -475,9 +500,9 @@ 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()) test.secret.SetCreatedAt(test.want.GetCreatedAt()) test.secret.SetCreatedBy(test.want.GetCreatedBy()) test.secret.SetUpdatedAt(test.want.GetUpdatedAt()) @@ -515,10 +540,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()) } @@ -527,6 +548,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,7 +577,7 @@ func TestLibrary_Secret_String(t *testing.T) { want := fmt.Sprintf(`{ AllowCommand: %t, AllowEvents: %v, - Events: %s, + AllowSubstitution: %t, ID: %d, Images: %s, Name: %s, @@ -568,7 +593,7 @@ func TestLibrary_Secret_String(t *testing.T) { }`, s.GetAllowCommand(), s.GetAllowEvents().List(), - s.GetEvents(), + s.GetAllowSubstitution(), s.GetID(), s.GetImages(), s.GetName(), @@ -607,9 +632,9 @@ 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) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat") s.SetUpdatedAt(tsUpdate) diff --git a/library/step.go b/library/step.go index d9f4961a..293efd36 100644 --- a/library/step.go +++ b/library/step.go @@ -32,7 +32,7 @@ type Step struct { Runtime *string `json:"runtime,omitempty"` Distribution *string `json:"distribution,omitempty"` CheckID *int64 `json:"check_id,omitempty"` - Report *Report `json:"report,omitempty"` + ReportAs *string `json:"report_as,omitempty"` } // Duration calculates and returns the total amount of @@ -81,6 +81,7 @@ func (s *Step) Environment() map[string]string { "VELA_STEP_STARTED": ToString(s.GetStarted()), "VELA_STEP_STATUS": ToString(s.GetStatus()), "VELA_STEP_CHECK_ID": ToString(s.GetCheckID()), + "VELA_STEP_REPORT_AS": ToString(s.GetReportAs()), } } @@ -305,17 +306,17 @@ func (s *Step) GetCheckID() int64 { return *s.CheckID } -// GetReport returns the Report field. +// 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) GetReport() *Report { - // return zero value if Report type is nil - if s == nil { - return nil +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.Report + return *s.ReportAs } // SetID sets the ID field. @@ -513,7 +514,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. @@ -539,17 +540,17 @@ func (s *Step) SetCheckID(v int64) { s.CheckID = &v } -// SetReport sets the Report field. +// SetReportAs sets the ReportAs field. // // When the provided Step type is nil, it // will set nothing and immediately return. -func (s *Step) SetReport(v *Report) { - // return if Report type is nil +func (s *Step) SetReportAs(v string) { + // return if Step type is nil if s == nil { return } - s.Report = v + s.ReportAs = &v } // String implements the Stringer interface for the Step type. @@ -568,6 +569,7 @@ func (s *Step) String() string { Number: %d, RepoID: %d, CheckID: %d, + ReportAs: %s, Runtime: %s, Stage: %s, Started: %d, @@ -586,6 +588,7 @@ func (s *Step) String() string { s.GetNumber(), s.GetRepoID(), s.GetCheckID(), + s.GetReportAs(), s.GetRuntime(), s.GetStage(), s.GetStarted(), @@ -615,6 +618,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"] @@ -666,6 +670,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 602e2575..3d427cb0 100644 --- a/pipeline/container.go +++ b/pipeline/container.go @@ -31,28 +31,28 @@ type ( // // swagger:model PipelineContainer Container struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Commands []string `json:"commands,omitempty" yaml:"commands,omitempty"` - Detach bool `json:"detach,omitempty" yaml:"detach,omitempty"` - Directory string `json:"directory,omitempty" yaml:"directory,omitempty"` - Entrypoint []string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"` - Environment map[string]string `json:"environment,omitempty" yaml:"environment,omitempty"` - ExitCode int `json:"exit_code,omitempty" yaml:"exit_code,omitempty"` - Image string `json:"image,omitempty" yaml:"image,omitempty"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Needs []string `json:"needs,omitempty" yaml:"needs,omitempty"` - Networks []string `json:"networks,omitempty" yaml:"networks,omitempty"` - Number int `json:"number,omitempty" yaml:"number,omitempty"` - Ports []string `json:"ports,omitempty" yaml:"ports,omitempty"` - Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"` - Pull string `json:"pull,omitempty" yaml:"pull,omitempty"` - Ruleset Ruleset `json:"ruleset,omitempty" yaml:"ruleset,omitempty"` - Secrets StepSecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` - Ulimits UlimitSlice `json:"ulimits,omitempty" yaml:"ulimits,omitempty"` - Volumes VolumeSlice `json:"volumes,omitempty" yaml:"volumes,omitempty"` - User string `json:"user,omitempty" yaml:"user,omitempty"` - ReportStatus bool `json:"report_status,omitempty" yaml:"report_status,omitempty"` - ReportPath string `json:"report_path,omitempty" yaml:"report_path,omitempty"` + ID string `json:"id,omitempty" yaml:"id,omitempty"` + Commands []string `json:"commands,omitempty" yaml:"commands,omitempty"` + Detach bool `json:"detach,omitempty" yaml:"detach,omitempty"` + Directory string `json:"directory,omitempty" yaml:"directory,omitempty"` + Entrypoint []string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"` + Environment map[string]string `json:"environment,omitempty" yaml:"environment,omitempty"` + ExitCode int `json:"exit_code,omitempty" yaml:"exit_code,omitempty"` + Image string `json:"image,omitempty" yaml:"image,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Needs []string `json:"needs,omitempty" yaml:"needs,omitempty"` + Networks []string `json:"networks,omitempty" yaml:"networks,omitempty"` + Number int `json:"number,omitempty" yaml:"number,omitempty"` + Ports []string `json:"ports,omitempty" yaml:"ports,omitempty"` + Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"` + Pull string `json:"pull,omitempty" yaml:"pull,omitempty"` + Ruleset Ruleset `json:"ruleset,omitempty" yaml:"ruleset,omitempty"` + Secrets StepSecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` + 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"` + IDRequest string `json:"id_request,omitempty" yaml:"id_request,omitempty"` } ) @@ -135,7 +135,9 @@ 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 && + len(c.IDRequest) == 0 { return true } diff --git a/pipeline/container_test.go b/pipeline/container_test.go index 2f814ba7..3f5e3a45 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{ @@ -907,6 +932,7 @@ func testContainers() *ContainerSlice { Name: "clone", Number: 2, Pull: "always", + IDRequest: "yes", }, { ID: "step_github/octocat._1_echo", @@ -917,6 +943,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/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/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) } 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/build_test.go b/yaml/build_test.go index 8da97c90..a1ca6cc3 100644 --- a/yaml/build_test.go +++ b/yaml/build_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/library" "github.com/go-vela/types/raw" ) diff --git a/yaml/ruleset.go b/yaml/ruleset.go index 7b8fc615..4a6d87f7 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{} @@ -154,6 +159,9 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { constants.EventPull+":"+constants.ActionOpened, constants.EventPull+":"+constants.ActionSynchronize, constants.EventPull+":"+constants.ActionReopened) + case constants.EventDeploy: + events = append(events, + constants.EventDeploy+":"+constants.ActionCreated) case constants.EventComment: events = append(events, constants.EventComment+":"+constants.ActionCreated, diff --git a/yaml/ruleset_test.go b/yaml/ruleset_test.go index 762bb904..9b95f304 100644 --- a/yaml/ruleset_test.go +++ b/yaml/ruleset_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" ) @@ -22,12 +23,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 +49,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"}, @@ -114,7 +117,7 @@ func TestYaml_Ruleset_UnmarshalYAML(t *testing.T) { Tag: []string{"^refs/tags/(\\d+\\.)+\\d+$"}, }, Unless: Rules{ - Event: []string{"deployment", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "comment:edited", "schedule"}, + Event: []string{"deployment:created", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "comment:edited", "schedule"}, Path: []string{"foo.txt", "/foo/bar.txt"}, }, Matcher: "regexp", @@ -167,22 +170,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"}, }, }, } diff --git a/yaml/secret_test.go b/yaml/secret_test.go index f70637e6..9918b8cf 100644 --- a/yaml/secret_test.go +++ b/yaml/secret_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" ) diff --git a/yaml/service_test.go b/yaml/service_test.go index d0404eef..1289095c 100644 --- a/yaml/service_test.go +++ b/yaml/service_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" "github.com/go-vela/types/raw" ) diff --git a/yaml/stage.go b/yaml/stage.go index bfc0dffb..65943bd9 100644 --- a/yaml/stage.go +++ b/yaml/stage.go @@ -5,10 +5,10 @@ package yaml import ( "fmt" + "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" "github.com/go-vela/types/raw" - - "github.com/buildkite/yaml" ) type ( diff --git a/yaml/stage_test.go b/yaml/stage_test.go index cf90ba48..5cad3cb7 100644 --- a/yaml/stage_test.go +++ b/yaml/stage_test.go @@ -7,9 +7,9 @@ import ( "reflect" "testing" - "github.com/go-vela/types/pipeline" - "github.com/buildkite/yaml" + + "github.com/go-vela/types/pipeline" ) func TestYaml_StageSlice_ToPipeline(t *testing.T) { diff --git a/yaml/step.go b/yaml/step.go index fe4bad3c..feadb8a9 100644 --- a/yaml/step.go +++ b/yaml/step.go @@ -19,23 +19,23 @@ type ( // Step is the yaml representation of a step // from the steps block for a pipeline. Step struct { - Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` - Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-tag"` - Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-tag"` - Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-tag"` - Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-tag"` - Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-tag"` - Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-tag"` - Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-tag"` - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-tag"` - Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-tag"` - Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-tag"` - Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for a plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-tag"` - 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"` - ReportStatus bool `yaml:"report_status,omitempty" json:"report_status,omitempty" jsonschema:"description=Report the status of the container to the SCM.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_status-tag"` - ReportPath string `yaml:"report_path,omitempty" json:"report_path,omitempty" jsonschema:"description=Path to the file containing the status report.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_path-tag"` + Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-tag"` + Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-tag"` + Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-tag"` + Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-tag"` + Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-tag"` + Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-tag"` + Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-tag"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-tag"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-tag"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-tag"` + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-tag"` + Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for a plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-tag"` + 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"` + IDRequest string `yaml:"id_request,omitempty" json:"id_request,omitempty" jsonschema:"description=Request ID Request Token for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-id_request-tag"` } ) @@ -49,21 +49,21 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { for _, step := range *s { // append the element to the pipeline container slice *stepSlice = append(*stepSlice, &pipeline.Container{ - Commands: step.Commands, - Detach: step.Detach, - Entrypoint: step.Entrypoint, - Environment: step.Environment, - Image: step.Image, - Name: step.Name, - Privileged: step.Privileged, - Pull: step.Pull, - Ruleset: *step.Ruleset.ToPipeline(), - Secrets: *step.Secrets.ToPipeline(), - Ulimits: *step.Ulimits.ToPipeline(), - Volumes: *step.Volumes.ToPipeline(), - User: step.User, - ReportStatus: step.ReportStatus, - ReportPath: step.ReportPath, + Commands: step.Commands, + Detach: step.Detach, + Entrypoint: step.Entrypoint, + Environment: step.Environment, + Image: step.Image, + Name: step.Name, + Privileged: step.Privileged, + Pull: step.Pull, + Ruleset: *step.Ruleset.ToPipeline(), + Secrets: *step.Secrets.ToPipeline(), + Ulimits: *step.Ulimits.ToPipeline(), + Volumes: *step.Volumes.ToPipeline(), + User: step.User, + ReportAs: step.ReportAs, + IDRequest: step.IDRequest, }) } @@ -112,10 +112,6 @@ func (s *StepSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { if strings.EqualFold(step.Pull, "false") { step.Pull = constants.PullNotPresent } - - if len(step.ReportPath) > 0 && !strings.HasSuffix(step.ReportPath, ".json") && !strings.HasSuffix(step.ReportPath, ".JSON") { - step.ReportPath = fmt.Sprintf("%s.json", step.ReportPath) - } } // overwrite existing StepSlice diff --git a/yaml/step_test.go b/yaml/step_test.go index e1cead4c..686631a6 100644 --- a/yaml/step_test.go +++ b/yaml/step_test.go @@ -7,10 +7,10 @@ import ( "reflect" "testing" + "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" "github.com/go-vela/types/raw" - - "github.com/buildkite/yaml" ) func TestYaml_StepSlice_ToPipeline(t *testing.T) { @@ -30,6 +30,8 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { Name: "echo", Privileged: false, Pull: "not_present", + ReportAs: "my-step", + IDRequest: "yes", Ruleset: Ruleset{ If: Rules{ Branch: []string{"main"}, @@ -86,6 +88,8 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { Name: "echo", Privileged: false, Pull: "not_present", + ReportAs: "my-step", + IDRequest: "yes", Ruleset: pipeline.Ruleset{ If: pipeline.Rules{ Branch: []string{"main"}, @@ -187,9 +191,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/template_test.go b/yaml/template_test.go index 39827760..e7f760cb 100644 --- a/yaml/template_test.go +++ b/yaml/template_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/library" ) 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 diff --git a/yaml/ulimit_test.go b/yaml/ulimit_test.go index 46d28bde..dc6fdac6 100644 --- a/yaml/ulimit_test.go +++ b/yaml/ulimit_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" ) diff --git a/yaml/volume_test.go b/yaml/volume_test.go index dc7d9513..837703ad 100644 --- a/yaml/volume_test.go +++ b/yaml/volume_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/go-vela/types/pipeline" )