diff --git a/docs/content/docs/guide/authoringprs.md b/docs/content/docs/guide/authoringprs.md index 267e74eef8..ddc0937e8b 100644 --- a/docs/content/docs/guide/authoringprs.md +++ b/docs/content/docs/guide/authoringprs.md @@ -187,6 +187,77 @@ for example, this will show the GitHub event type for a GitHub event: and then you can do the same conditional or access as described above for the `body` keyword. +## Using the cel: prefix for advanced CEL expressions + +For more complex CEL expressions that go beyond simple property access, you can +use the `cel:` prefix. This allows you to write arbitrary CEL expressions with +access to all available data sources. + +The `cel:` prefix provides access to: + +- `body` - The full webhook payload +- `headers` - HTTP request headers +- `files` - Changed files information (`files.all`, `files.added`, `files.deleted`, `files.modified`, `files.renamed`) +- `pac` - Standard PAC parameters (`pac.revision`, `pac.target_branch`, `pac.source_branch`, etc.) + +### Examples + +**Conditional values based on event action:** + +```yaml +params: + - name: pr-status + value: "{{ cel: body.action == \"opened\" ? \"new-pr\" : \"updated-pr\" }}" +``` + +**Environment selection based on target branch:** + +```yaml +params: + - name: environment + value: "{{ cel: pac.target_branch == \"main\" ? \"production\" : \"staging\" }}" +``` + +**Safe field access with has() function:** + +Use the `has()` function to safely check if a field exists before accessing it: + +```yaml +params: + - name: commit-type + value: "{{ cel: has(body.head_commit) && body.head_commit.message.startsWith(\"Merge\") ? \"merge\" : \"regular\" }}" +``` + +**Check if Go files were modified:** + +```yaml +params: + - name: run-go-tests + value: "{{ cel: files.all.exists(f, f.endsWith(\".go\")) ? \"true\" : \"false\" }}" +``` + +**String concatenation:** + +```yaml +params: + - name: greeting + value: "{{ cel: \"Build for \" + pac.repo_name + \" on \" + pac.target_branch }}" +``` + +**Count changed files:** + +```yaml +params: + - name: file-count + value: "{{ cel: files.all.size() }}" +``` + +{{< hint info >}} +If a `cel:` expression has a syntax error or fails to evaluate, it returns an +empty string. This allows PipelineRuns to continue even if an optional dynamic +value cannot be computed. +{{< /hint >}} + ## Using the temporary GitHub APP Token for GitHub API operations You can use the temporary installation token that is generated by Pipelines as diff --git a/pkg/cel/cel.go b/pkg/cel/cel.go index 320a431528..8f8a4aa583 100644 --- a/pkg/cel/cel.go +++ b/pkg/cel/cel.go @@ -1,6 +1,7 @@ package cel import ( + "bytes" "encoding/json" "fmt" @@ -38,14 +39,29 @@ func evaluate(expr string, env *cel.Env, data map[string]any) (ref.Val, error) { // / pacParams, it will output a Cel value or an error if selectedjm. func Value(query string, body any, headers, pacParams map[string]string, changedFiles map[string]any) (ref.Val, error) { // Marshal/Unmarshal the body to a map[string]any so we can access it from the CEL - nbody, err := json.Marshal(body) - if err != nil { - return nil, err - } var jsonMap map[string]any - err = json.Unmarshal(nbody, &jsonMap) - if err != nil { - return nil, err + switch b := body.(type) { + case nil: + jsonMap = map[string]any{} + case map[string]any: + jsonMap = b + default: + nbody, err := json.Marshal(body) + if err != nil { + return nil, err + } + trimmed := bytes.TrimSpace(nbody) + if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) { + jsonMap = map[string]any{} + } else { + err = json.Unmarshal(nbody, &jsonMap) + if err != nil { + return nil, err + } + if jsonMap == nil { + jsonMap = map[string]any{} + } + } } mapStrDyn := types.NewMapType(types.StringType, types.DynType) diff --git a/pkg/cel/cel_test.go b/pkg/cel/cel_test.go index 1a9a86ff48..1159188af4 100644 --- a/pkg/cel/cel_test.go +++ b/pkg/cel/cel_test.go @@ -39,6 +39,11 @@ func TestValue(t *testing.T) { assert.NilError(t, err) assert.Equal(t, ref.Val(val), val) + // Test pac-only query with nil body + val, err = Value("pac.param", nil, headers, pacParams, changedFiles) + assert.NilError(t, err) + assert.Equal(t, val.Value(), "value") + // Test an invalid query _, err = Value("invalid query", body, headers, pacParams, changedFiles) assert.ErrorContains(t, err, "failed to parse expression") diff --git a/pkg/templates/templating.go b/pkg/templates/templating.go index 24c280c3cb..63c0cf8510 100644 --- a/pkg/templates/templating.go +++ b/pkg/templates/templating.go @@ -27,10 +27,12 @@ var ( // value. // // The function first checks if the key in the placeholder has a prefix of -// "body", "headers", or "files". If it does and both `rawEvent` and `headers` +// "body", "headers", "files", or "cel:". If it does and both `rawEvent` and `headers` // are not nil, it attempts to retrieve the value for the key using the // `cel.Value` function and returns the corresponding string -// representation. If the key does not have any of the mentioned prefixes, the +// representation. The "cel:" prefix allows evaluating arbitrary CEL expressions +// with access to body, headers, files, and pac (standard PAC parameters) namespaces. +// If the key does not have any of the mentioned prefixes, the // function checks if the key exists in the `dico` map. If it does, the // function replaces the placeholder with the corresponding value from the // `dico` map. @@ -52,10 +54,18 @@ func ReplacePlaceHoldersVariables(template string, dico map[string]string, rawEv return keys.ParamsRe.ReplaceAllStringFunc(template, func(s string) string { parts := keys.ParamsRe.FindStringSubmatch(s) key := strings.TrimSpace(parts[1]) - if strings.HasPrefix(key, "body") || strings.HasPrefix(key, "headers") || strings.HasPrefix(key, "files") { + + // Check for cel: prefix first - it allows arbitrary CEL expressions + isCelExpr := strings.HasPrefix(key, "cel:") + + if strings.HasPrefix(key, "body") || strings.HasPrefix(key, "headers") || strings.HasPrefix(key, "files") || isCelExpr { // Check specific requirements for each prefix canEvaluate := false + celExpr := key switch { + case isCelExpr: + canEvaluate = true + celExpr = strings.TrimSpace(strings.TrimPrefix(key, "cel:")) case strings.HasPrefix(key, "body") && rawEvent != nil: canEvaluate = true case strings.HasPrefix(key, "headers") && headers != nil: @@ -70,8 +80,17 @@ func ReplacePlaceHoldersVariables(template string, dico map[string]string, rawEv for k, v := range headers { headerMap[k] = v[0] } - val, err := cel.Value(key, rawEvent, headerMap, map[string]string{}, changedFiles) + // For cel: prefix, pass dico as pacParams so pac.* variables are available + pacParams := map[string]string{} + if isCelExpr { + pacParams = dico + } + val, err := cel.Value(celExpr, rawEvent, headerMap, pacParams, changedFiles) if err != nil { + // For cel: prefix, return empty string on error + if isCelExpr { + return "" + } return s } var raw any diff --git a/pkg/templates/templating_test.go b/pkg/templates/templating_test.go index 29770d0b49..00122c4208 100644 --- a/pkg/templates/templating_test.go +++ b/pkg/templates/templating_test.go @@ -286,6 +286,255 @@ func TestReplacePlaceHoldersVariablesJSONOutput(t *testing.T) { } } +func TestReplacePlaceHoldersVariablesCelPrefix(t *testing.T) { + tests := []struct { + name string + template string + expected string + dicto map[string]string + headers http.Header + changedFiles map[string]any + rawEvent any + }{ + { + name: "cel: prefix with simple body access", + template: `result: {{ cel: body.hello }}`, + expected: `result: world`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]string{ + "hello": "world", + }, + }, + { + name: "cel: prefix with ternary expression", + template: `status: {{ cel: body.action == "opened" ? "new-pr" : "updated-pr" }}`, + expected: `status: new-pr`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "action": "opened", + }, + }, + { + name: "cel: prefix with ternary expression - else branch", + template: `status: {{ cel: body.action == "opened" ? "new-pr" : "updated-pr" }}`, + expected: `status: updated-pr`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "action": "synchronize", + }, + }, + { + name: "cel: prefix with pac namespace access", + template: `branch: {{ cel: pac.target_branch }}`, + expected: `branch: main`, + dicto: map[string]string{ + "target_branch": "main", + }, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with pac namespace conditional", + template: `env: {{ cel: pac.target_branch == "main" ? "production" : "staging" }}`, + expected: `env: production`, + dicto: map[string]string{ + "target_branch": "main", + }, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with pac namespace - staging branch", + template: `env: {{ cel: pac.target_branch == "main" ? "production" : "staging" }}`, + expected: `env: staging`, + dicto: map[string]string{ + "target_branch": "develop", + }, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with has() function", + template: `has_field: {{ cel: has(body.optional_field) ? body.optional_field : "default" }}`, + expected: `has_field: custom_value`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "optional_field": "custom_value", + }, + }, + { + name: "cel: prefix with has() function - field missing", + template: `has_field: {{ cel: has(body.optional_field) ? body.optional_field : "default" }}`, + expected: `has_field: default`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with files access", + template: `go_files: {{ cel: files.all.exists(f, f.endsWith(".go")) ? "yes" : "no" }}`, + expected: `go_files: yes`, + dicto: map[string]string{}, + changedFiles: map[string]any{ + "all": []string{"main.go", "README.md"}, + }, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with files access - no go files", + template: `go_files: {{ cel: files.all.exists(f, f.endsWith(".go")) ? "yes" : "no" }}`, + expected: `go_files: no`, + dicto: map[string]string{}, + changedFiles: map[string]any{ + "all": []string{"README.md", "config.yaml"}, + }, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with headers access", + template: `event: {{ cel: headers["X-GitHub-Event"] }}`, + expected: `event: push`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{ + "X-GitHub-Event": []string{"push"}, + }, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with boolean result", + template: `is_draft: {{ cel: body.draft == true }}`, + expected: `is_draft: false`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "draft": false, + }, + }, + { + name: "cel: prefix with invalid expression returns empty string", + template: `invalid: {{ cel: invalid.syntax[ }}`, + expected: `invalid: `, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with evaluation error returns empty string", + template: `error: {{ cel: body.nonexistent.deep.field }}`, + expected: `error: `, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with extra whitespace", + template: `result: {{ cel: body.hello }}`, + expected: `result: world`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]string{ + "hello": "world", + }, + }, + { + name: "cel: prefix with string concatenation", + template: `greeting: {{ cel: "Hello, " + body.name + "!" }}`, + expected: `greeting: Hello, World!`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "name": "World", + }, + }, + { + name: "cel: prefix with size function", + template: `count: {{ cel: body.items.size() }}`, + expected: `count: 3`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "items": []string{"a", "b", "c"}, + }, + }, + { + name: "cel: prefix with complex nested conditional (merge commit detection)", + template: `commit_type: {{ cel: has(body.head_commit) && body.head_commit.message.startsWith("Merge") ? "merge" : "regular" }}`, + expected: `commit_type: merge`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "head_commit": map[string]any{ + "message": "Merge pull request #123", + }, + }, + }, + { + name: "cel: prefix with complex nested conditional - regular commit", + template: `commit_type: {{ cel: has(body.head_commit) && body.head_commit.message.startsWith("Merge") ? "merge" : "regular" }}`, + expected: `commit_type: regular`, + dicto: map[string]string{}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{ + "head_commit": map[string]any{ + "message": "Fix bug in parser", + }, + }, + }, + { + name: "cel: prefix mixed with regular placeholders", + template: `branch: {{ target_branch }}, check: {{ cel: pac.target_branch == "main" ? "prod" : "dev" }}`, + expected: `branch: main, check: prod`, + dicto: map[string]string{ + "target_branch": "main", + }, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: map[string]any{}, + }, + { + name: "cel: prefix with nil rawEvent still works", + template: `result: {{ cel: pac.revision }}`, + expected: `result: abc123`, + dicto: map[string]string{"revision": "abc123"}, + changedFiles: map[string]any{}, + headers: http.Header{}, + rawEvent: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ReplacePlaceHoldersVariables(tt.template, tt.dicto, tt.rawEvent, tt.headers, tt.changedFiles) + if d := cmp.Diff(got, tt.expected); d != "" { + t.Fatalf("-got, +want: %v", d) + } + }) + } +} + func TestReplacePlaceHoldersVariablesEdgeCases(t *testing.T) { tests := []struct { name string diff --git a/test/gitea_params_test.go b/test/gitea_params_test.go index ead963de31..c8f4143029 100644 --- a/test/gitea_params_test.go +++ b/test/gitea_params_test.go @@ -526,3 +526,45 @@ func TestGiteaParamsChangedFilesCEL(t *testing.T) { fmt.Sprintf("tekton.dev/pipelineRun=%s,tekton.dev/pipelineTask=changed-files-push-params", sortedstatus[0].PipelineRunName), "step-test-changed-files-params-push", strings.ReplaceAll(fmt.Sprintf("%s-changed-files-push-params-2.golden", t.Name()), "/", "-"), 2) } + +// TestGiteaParamsCelPrefix tests the cel: prefix for arbitrary CEL expressions. +// The cel: prefix allows evaluating full CEL expressions with access to body, headers, files, and pac namespaces. +func TestGiteaParamsCelPrefix(t *testing.T) { + topts := &tgitea.TestOpts{ + Regexp: successRegexp, + TargetEvent: "pull_request", + YAMLFiles: map[string]string{ + ".tekton/pr.yaml": "testdata/pipelinerun-cel-prefix-test.yaml", + }, + CheckForStatus: "success", + ExpectEvents: false, + } + _, f := tgitea.TestPR(t, topts) + defer f() + + // check the repos CR only one pr should have run + repo, err := topts.ParamsRun.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(topts.TargetNS).Get(context.Background(), topts.TargetNS, metav1.GetOptions{}) + assert.NilError(t, err) + assert.Equal(t, len(repo.Status), 1, repo.Status) + + // Verify cel: prefix expressions evaluated correctly + // Expected output: + // cel_ternary: new-pr (body.action == "opened" for a new PR) + // cel_pac_branch: matched (pac.target_branch matches the target branch) + // cel_has_function: has-pr (body.pull_request exists) + // cel_string_concat: Build on + // cel_files_check: has-files (files.all.size() > 0 since we have changed files) + // cel_error_handling: (empty string - cel: prefix returns empty on error) + // if this change you need to run that e2e test with -test.update-golden=true + err = twait.RegexpMatchingInPodLog( + context.Background(), + topts.ParamsRun, + topts.TargetNS, + fmt.Sprintf("tekton.dev/pipelineRun=%s,tekton.dev/pipelineTask=cel-prefix-test", repo.Status[0].PipelineRunName), + "step-test-cel-prefix-values", + regexp.Regexp{}, + t.Name(), + 2, + ) + assert.NilError(t, err) +} diff --git a/test/github_pullrequest_test.go b/test/github_pullrequest_test.go index 04241e6bf7..f51ec30dff 100644 --- a/test/github_pullrequest_test.go +++ b/test/github_pullrequest_test.go @@ -685,6 +685,51 @@ func TestGithubIgnoreTagPushCommitsFromSkipPushEventsSetting(t *testing.T) { g.Cnx.Clients.Log.Infof("Tag %s has been deleted", tag) } +// TestGithubPullRequestCelPrefix tests the cel: prefix for arbitrary CEL expressions. +// The cel: prefix allows evaluating full CEL expressions with access to body, headers, files, and pac namespaces. +func TestGithubPullRequestCelPrefix(t *testing.T) { + ctx := context.Background() + g := &tgithub.PRTest{ + Label: "Github CEL Prefix", + YamlFiles: []string{"testdata/pipelinerun-cel-prefix-github.yaml"}, + } + g.RunPullRequest(ctx, t) + defer g.TearDown(ctx, t) + + // Wait for repository status to be updated + waitOpts := twait.Opts{ + RepoName: g.TargetNamespace, + Namespace: g.TargetNamespace, + MinNumberStatus: 1, + PollTimeout: twait.DefaultTimeout, + TargetSHA: g.SHA, + } + repo, err := twait.UntilRepositoryUpdated(ctx, g.Cnx.Clients, waitOpts) + assert.NilError(t, err) + assert.Assert(t, len(repo.Status) >= 1, "Expected at least one status") + + // Verify cel: prefix expressions evaluated correctly using golden file + // Expected output: + // cel_ternary: new-pr (body.action == "opened" for a new PR) + // cel_pac_branch: matched (pac.target_branch matches the target branch) + // cel_has_function: has-pr (body.pull_request exists) + // cel_string_concat: Build on + // cel_files_check: has-files (files.all.size() > 0 since we have changed files) + // cel_github_header: pull_request (X-Github-Event header value) + // cel_error_handling: (empty string - cel: prefix returns empty on error) + err = twait.RegexpMatchingInPodLog( + ctx, + g.Cnx, + g.TargetNamespace, + fmt.Sprintf("tekton.dev/pipelineRun=%s,tekton.dev/pipelineTask=cel-prefix-test", repo.Status[0].PipelineRunName), + "step-test-cel-prefix-values", + regexp.Regexp{}, + t.Name(), + 2, + ) + assert.NilError(t, err) +} + // Local Variables: // compile-command: "go test -tags=e2e -v -info TestGithubPullRequest$ ." // End: diff --git a/test/gitlab_merge_request_test.go b/test/gitlab_merge_request_test.go index 07132667cc..9c65321a0c 100644 --- a/test/gitlab_merge_request_test.go +++ b/test/gitlab_merge_request_test.go @@ -951,6 +951,96 @@ func TestGitlabConsistentCommitStatusOnMR(t *testing.T) { } } +// TestGitlabMergeRequestCelPrefix tests the cel: prefix for arbitrary CEL expressions. +// The cel: prefix allows evaluating full CEL expressions with access to body, headers, files, and pac namespaces. +func TestGitlabMergeRequestCelPrefix(t *testing.T) { + targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns") + ctx := context.Background() + runcnx, opts, glprovider, err := tgitlab.Setup(ctx) + assert.NilError(t, err) + ctx, err = cctx.GetControllerCtxInfo(ctx, runcnx) + assert.NilError(t, err) + runcnx.Clients.Log.Info("Testing cel: prefix with GitLab") + + projectinfo, resp, err := glprovider.Client().Projects.GetProject(opts.ProjectID, nil) + assert.NilError(t, err) + if resp != nil && resp.StatusCode == http.StatusNotFound { + t.Errorf("Repository %s not found in %s", opts.Organization, opts.Repo) + } + + err = tgitlab.CreateCRD(ctx, projectinfo, runcnx, opts, targetNS, nil) + assert.NilError(t, err) + + entries, err := payload.GetEntries(map[string]string{ + ".tekton/pipelinerun.yaml": "testdata/pipelinerun-cel-prefix-gitlab.yaml", + }, targetNS, projectinfo.DefaultBranch, + triggertype.PullRequest.String(), map[string]string{}) + assert.NilError(t, err) + + targetRefName := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test") + + gitCloneURL, err := scm.MakeGitCloneURL(projectinfo.WebURL, opts.UserName, opts.Password) + assert.NilError(t, err) + commitTitle := "Testing cel: prefix on " + targetRefName + scmOpts := &scm.Opts{ + GitURL: gitCloneURL, + CommitTitle: commitTitle, + Log: runcnx.Clients.Log, + WebURL: projectinfo.WebURL, + TargetRefName: targetRefName, + BaseRefName: projectinfo.DefaultBranch, + } + _ = scm.PushFilesToRefGit(t, scmOpts, entries) + + runcnx.Clients.Log.Infof("Branch %s has been created and pushed with cel: prefix test files", targetRefName) + mrTitle := "TestCelPrefix - " + targetRefName + mrID, err := tgitlab.CreateMR(glprovider.Client(), opts.ProjectID, targetRefName, projectinfo.DefaultBranch, mrTitle) + assert.NilError(t, err) + runcnx.Clients.Log.Infof("MergeRequest %s/-/merge_requests/%d has been created", projectinfo.WebURL, mrID) + defer tgitlab.TearDown(ctx, t, runcnx, glprovider, mrID, targetRefName, targetNS, opts.ProjectID) + + sopt := twait.SuccessOpt{ + Title: commitTitle, + OnEvent: "Merge Request", + TargetNS: targetNS, + NumberofPRMatch: 1, + SHA: "", + } + twait.Succeeded(ctx, t, runcnx, opts, sopt) + + // Get the MR to fetch the SHA + mr, _, err := glprovider.Client().MergeRequests.GetMergeRequest(opts.ProjectID, mrID, nil) + assert.NilError(t, err) + + // Get repository to find the pipelinerun name + repo, err := runcnx.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(targetNS).Get(ctx, targetNS, metav1.GetOptions{}) + assert.NilError(t, err) + assert.Assert(t, len(repo.Status) >= 1, "Expected at least one status, got %d", len(repo.Status)) + + // Verify cel: prefix expressions evaluated correctly using golden file + // Expected output: + // cel_ternary: new-mr (body.object_attributes.action == "open" for a new MR) + // cel_pac_branch: matched (pac.target_branch matches the target branch) + // cel_has_function: has-mr (body.object_attributes exists) + // cel_string_concat: Build on + // cel_files_check: has-files (files.all.size() > 0 since we have changed files) + // cel_gitlab_header: Merge Request Hook (X-Gitlab-Event header value) + // cel_error_handling: (empty string - cel: prefix returns empty on error) + err = twait.RegexpMatchingInPodLog( + ctx, + runcnx, + targetNS, + fmt.Sprintf("tekton.dev/pipelineRun=%s,tekton.dev/pipelineTask=cel-prefix-test", repo.Status[0].PipelineRunName), + "step-test-cel-prefix-values", + regexp.Regexp{}, + t.Name(), + 2, + ) + assert.NilError(t, err) + + _ = mr // use mr variable to avoid unused variable error +} + // Local Variables: // compile-command: "go test -tags=e2e -v -run ^TestGitlabMergeRequest$" // End: diff --git a/test/testdata/TestGiteaParamsCelPrefix.golden b/test/testdata/TestGiteaParamsCelPrefix.golden new file mode 100644 index 0000000000..91fb9b047a --- /dev/null +++ b/test/testdata/TestGiteaParamsCelPrefix.golden @@ -0,0 +1,6 @@ +cel_ternary: new-pr +cel_pac_branch: matched +cel_has_function: has-pr +cel_string_concat: Build on main +cel_files_check: has-files +cel_error_handling: diff --git a/test/testdata/TestGithubPullRequestCelPrefix.golden b/test/testdata/TestGithubPullRequestCelPrefix.golden new file mode 100644 index 0000000000..93ca82c561 --- /dev/null +++ b/test/testdata/TestGithubPullRequestCelPrefix.golden @@ -0,0 +1,7 @@ +cel_ternary: new-pr +cel_pac_branch: matched +cel_has_function: has-pr +cel_string_concat: Build on main +cel_files_check: has-files +cel_github_header: pull_request +cel_error_handling: diff --git a/test/testdata/TestGitlabMergeRequestCelPrefix.golden b/test/testdata/TestGitlabMergeRequestCelPrefix.golden new file mode 100644 index 0000000000..e02367b404 --- /dev/null +++ b/test/testdata/TestGitlabMergeRequestCelPrefix.golden @@ -0,0 +1,7 @@ +cel_ternary: new-mr +cel_pac_branch: matched +cel_has_function: has-mr +cel_string_concat: Build on main +cel_files_check: has-files +cel_gitlab_header: Merge Request Hook +cel_error_handling: diff --git a/test/testdata/pipelinerun-cel-prefix-github.yaml b/test/testdata/pipelinerun-cel-prefix-github.yaml new file mode 100644 index 0000000000..95702b3f48 --- /dev/null +++ b/test/testdata/pipelinerun-cel-prefix-github.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: "\\ .PipelineName //" + annotations: + pipelinesascode.tekton.dev/target-namespace: "\\ .TargetNamespace //" + pipelinesascode.tekton.dev/on-cel-expression: | + target_branch == "\\ .TargetBranch //" && event == "\\ .TargetEvent //" +spec: + pipelineSpec: + tasks: + - name: cel-prefix-test + taskSpec: + steps: + - name: test-cel-prefix-values + image: registry.access.redhat.com/ubi9/ubi-micro + script: | + # Test cel: prefix with various expressions for GitHub + # Expected output: + # cel_ternary: new-pr + # cel_pac_branch: matched + # cel_has_function: has-pr + # cel_string_concat: Build on + # cel_files_check: has-files + # cel_github_header: pull_request + # cel_error_handling: (empty - error returns empty string) + cat < 0 ? "has-files" : "no-files" }} + cel_github_header: {{ cel: headers["X-Github-Event"] }} + cel_error_handling: {{ cel: nonexistent.field.access }} + EOF diff --git a/test/testdata/pipelinerun-cel-prefix-gitlab.yaml b/test/testdata/pipelinerun-cel-prefix-gitlab.yaml new file mode 100644 index 0000000000..ee58547ce0 --- /dev/null +++ b/test/testdata/pipelinerun-cel-prefix-gitlab.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: "\\ .PipelineName //" + annotations: + pipelinesascode.tekton.dev/target-namespace: "\\ .TargetNamespace //" + pipelinesascode.tekton.dev/on-cel-expression: | + target_branch == "\\ .TargetBranch //" && event == "\\ .TargetEvent //" +spec: + pipelineSpec: + tasks: + - name: cel-prefix-test + taskSpec: + steps: + - name: test-cel-prefix-values + image: registry.access.redhat.com/ubi9/ubi-micro + script: | + # Test cel: prefix with various expressions for GitLab + # Expected output: + # cel_ternary: new-mr + # cel_pac_branch: matched + # cel_has_function: has-mr + # cel_string_concat: Build on + # cel_files_check: has-files + # cel_gitlab_header: Merge Request Hook + # cel_error_handling: (empty - error returns empty string) + cat < 0 ? "has-files" : "no-files" }} + cel_gitlab_header: {{ cel: headers["X-Gitlab-Event"] }} + cel_error_handling: {{ cel: nonexistent.field.access }} + EOF diff --git a/test/testdata/pipelinerun-cel-prefix-test.yaml b/test/testdata/pipelinerun-cel-prefix-test.yaml new file mode 100644 index 0000000000..2d7bebe284 --- /dev/null +++ b/test/testdata/pipelinerun-cel-prefix-test.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: "\\ .PipelineName //" + annotations: + pipelinesascode.tekton.dev/target-namespace: "\\ .TargetNamespace //" + pipelinesascode.tekton.dev/on-cel-expression: | + target_branch == "\\ .TargetBranch //" && event == "\\ .TargetEvent //" +spec: + pipelineSpec: + tasks: + - name: cel-prefix-test + taskSpec: + steps: + - name: test-cel-prefix-values + image: alpine + script: | + # Test cel: prefix with various expressions + # Expected output: + # cel_ternary: new-pr + # cel_pac_branch: matched + # cel_has_function: has-pr + # cel_string_concat: Build on + # cel_files_check: has-files + # cel_error_handling: (empty - error returns empty string) + cat < 0 ? "has-files" : "no-files" }} + cel_error_handling: {{ cel: nonexistent.field.access }} + EOF