Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions docs/content/docs/guide/customparams.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,73 @@ and a pull request event.
- [GitHub Documentation for webhook events](https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads?actionType=auto_merge_disabled#pull_request)
- [GitLab Documentation for webhook events](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html)
{{< /hint >}}

### Using custom parameters in CEL matching expressions

In addition to template expansion (`{{ param }}`), custom parameters defined in the Repository CR are available as CEL variables in the `on-cel-expression` annotation. This allows you to control which PipelineRuns are triggered based on repository-specific configuration.

For example, with this Repository CR configuration:

```yaml
apiVersion: pipelinesascode.tekton.dev/v1alpha1
kind: Repository
metadata:
name: my-repo
spec:
url: "https://github.com/owner/repo"
params:
- name: enable_ci
value: "true"
- name: environment
value: "staging"
```

You can use these parameters directly in your PipelineRun's CEL expression:

```yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: my-pipeline
annotations:
pipelinesascode.tekton.dev/on-cel-expression: |
event == "push" && enable_ci == "true" && environment == "staging"
spec:
# ... pipeline spec
```

This approach is particularly useful for:

- **Conditional CI**: Enable or disable CI for specific repositories without changing PipelineRun files
- **Environment-specific matching**: Run different pipelines based on environment configuration
- **Feature flags**: Control which pipelines run using repository-level feature flags

Custom parameters from secrets are also available:

```yaml
apiVersion: pipelinesascode.tekton.dev/v1alpha1
kind: Repository
metadata:
name: my-repo
spec:
url: "https://github.com/owner/repo"
params:
- name: api_key
secret_ref:
name: my-secret
key: key
```

```yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: my-pipeline-with-secret
annotations:
pipelinesascode.tekton.dev/on-cel-expression: |
event == "push" && api_key != ""
spec:
# ... pipeline spec
```

For more information on CEL expressions and event matching, see the [Advanced event matching using CEL]({{< relref "/docs/guide/matchingevents#advanced-event-matching-using-cel" >}}) documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to document that referencing a param with a filter will cause a CEL error if the filter is false on the event. E.g.:

kind: Repository
metadata:
  name: my-repo
spec:
  url: "https://github.com/owner/repo"
  params:
    - name: meaning_of_life
      value: "42"
      filter: pac.event_type == "pull_request"
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: my-pipeline
  annotations:
    pipelinesascode.tekton.dev/on-cel-expression: |
      meaning_of_life == "ohana"
spec:
  # ... pipeline spec

IIUC on a push event, the above pipelinerun's CEL expression won't be false, it will be an error.

Copy link
Member

@aThorp96 aThorp96 Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we should document that custom parameters here do not override standard cel variables- I have been asked several times if a custom-param could override standard variables like trigger_target and even target_branch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some more details in docs in 7afc027

86 changes: 74 additions & 12 deletions docs/content/docs/guide/matchingevents.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,20 @@ pipelinesascode.tekton.dev/on-cel-expression: |

The fields available are:

| **Field** | **Description** |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `event` | `push`, `pull_request` or `incoming`. |
| `target_branch` | The branch we are targeting. |
| `source_branch` | The branch where this pull_request comes from. (On `push`, this is the same as `target_branch`.) |
| `target_url` | The URL of the repository we are targeting. |
| `source_url` | The URL of the repository where this pull_request comes from. (On `push`, this is the same as `target_url`.) |
| `event_title` | Matches the title of the event. For `push`, it matches the commit title. For PR, it matches the Pull/Merge Request title. (Only supported for `GitHub`, `GitLab`, and `BitbucketCloud` providers.) |
| `body` | The full body as passed by the Git provider. Example: `body.pull_request.number` retrieves the pull request number on GitHub. |
| `headers` | The full set of headers as passed by the Git provider. Example: `headers['x-github-event']` retrieves the event type on GitHub. |
| `.pathChanged` | A suffix function to a string that can be a glob of a path to check if changed. (Supported only for `GitHub` and `GitLab` providers.) |
| `files` | The list of files that changed in the event (`all`, `added`, `deleted`, `modified`, and `renamed`). Example: `files.all` or `files.deleted`. For pull requests, every file belonging to the pull request will be listed. |
| **Field** | **Description** |
| --- | --- |
| `event` | `push`, `pull_request` or `incoming`. |
| `event_type` | The event type from the webhook payload header. Provider-specific (e.g., GitHub sends `pull_request`, GitLab is `Merge Request`, etc). |
| `target_branch` | The branch we are targeting. |
| `source_branch` | The branch where this pull_request comes from. (On `push`, this is the same as `target_branch`.) |
| `target_url` | The URL of the repository we are targeting. |
| `source_url` | The URL of the repository where this pull_request comes from. (On `push`, this is the same as `target_url`.) |
| `event_title` | Matches the title of the event. For `push`, it matches the commit title. For PR, it matches the Pull/Merge Request title. (Only supported for `GitHub`, `GitLab`, and `BitbucketCloud` providers.) |
| `body` | The full body as passed by the Git provider. Example: `body.pull_request.number` retrieves the pull request number on GitHub. |
| `headers` | The full set of headers as passed by the Git provider. Example: `headers['x-github-event']` retrieves the event type on GitHub. |
| `.pathChanged` | A suffix function to a string that can be a glob of a path to check if changed. (Supported only for `GitHub` and `GitLab` providers.) |
| `files` | The list of files that changed in the event (`all`, `added`, `deleted`, `modified`, and `renamed`). Example: `files.all` or `files.deleted`. For pull requests, every file belonging to the pull request will be listed. |
| Custom params | Any [custom parameters]({{< relref "/docs/guide/customparams" >}}) provided from the Repository CR `spec.params` are available as CEL variables. Example: `enable_ci == "true"`. See [Using custom parameters in CEL expressions: limitations](#using-custom-parameters-in-cel-expressions-limitations) below for important details. |

CEL expressions let you do more complex filtering compared to the simple `on-target` annotation matching and enable more advanced scenarios.

Expand All @@ -330,6 +332,66 @@ You can find more information about the CEL language spec here:
<https://github.com/google/cel-spec/blob/master/doc/langdef.md>
{{< /hint >}}

### Using custom parameters in CEL expressions: limitations

#### Filtered custom parameters and CEL evaluation

When using a custom parameter with a `filter` in a CEL expression, be aware that if the filter condition
is **not met**, the parameter will be **undefined**, causing a CEL evaluation error rather than evaluating to false.

For example, consider this Repository CR:

```yaml
apiVersion: pipelinesascode.tekton.dev/v1alpha1
kind: Repository
metadata:
name: my-repo
spec:
url: "https://github.com/owner/repo"
params:
- name: docker_registry
value: "registry.staging.example.com"
filter: pac.event_type == "pull_request"
```

And this PipelineRun:

```yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: my-pipeline
annotations:
pipelinesascode.tekton.dev/on-cel-expression: |
docker_registry == "registry.staging.example.com"
spec:
# ... pipeline spec
```

On a **push event**, the `docker_registry` parameter will not be defined (since the filter only matches pull
requests), and the CEL expression will produce an **error**, not `false`. The PipelineRun will not be
evaluated and an error will be reported.

To avoid undefined parameter errors, ensure your CEL expressions only reference custom parameters when their
filter conditions match, or use parameters without filters for CEL matching. We recommend testing your CEL
expressions with different event types using the [tkn pac cel]({{< relref "/docs/guide/cli#tkn-pac-cel" >}})
command to verify they work correctly across all scenarios

#### Custom parameters do not override standard CEL variables

Custom parameters defined in the Repository CR cannot override the built-in CEL variables provided by
Pipelines-as-Code, such as:

* `event` (or `event_type`)
* `target_branch`
* `source_branch`
* `trigger_target`
* And other default variables documented in the table above

If you define a custom parameter with the same name as a standard CEL variable, the standard variable will
take precedence in CEL expressions. Custom parameters should use unique names that don't conflict with
built-in variables.

### Matching a PipelineRun to a branch with a regex

In a CEL expression, you can match a field name using a regular expression. For
Expand Down
45 changes: 44 additions & 1 deletion pkg/matcher/annotation_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
apipac "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
"github.com/openshift-pipelines/pipelines-as-code/pkg/customparams"
pacerrors "github.com/openshift-pipelines/pipelines-as-code/pkg/errors"
"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
"github.com/openshift-pipelines/pipelines-as-code/pkg/opscomments"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
Expand Down Expand Up @@ -210,6 +212,12 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
}
logger.Info(infomsg)

// Resolve custom params once for all PipelineRuns (for use in CEL expressions)
customParams := resolveCustomParamsForCEL(ctx, repo, event, cs, vcx, eventEmitter, logger)
if len(customParams) > 0 {
logger.Debugf("resolved %d custom params from repo for CEL", len(customParams))
}

celValidationErrors := []*pacerrors.PacYamlValidations{}
for _, prun := range pruns {
prMatch := Match{
Expand Down Expand Up @@ -280,7 +288,7 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
if celExpr, ok := prun.GetObjectMeta().GetAnnotations()[keys.OnCelExpression]; ok {
checkPipelineRunAnnotation(prun, eventEmitter, repo)

out, err := celEvaluate(ctx, celExpr, event, vcx)
out, err := celEvaluate(ctx, celExpr, event, vcx, customParams)
if err != nil {
logger.Errorf("there was an error evaluating the CEL expression, skipping: %v", err)
if checkIfCELEvaluateError(err) {
Expand Down Expand Up @@ -508,3 +516,38 @@ func MatchRunningPipelineRunForIncomingWebhook(eventType, incomingPipelineRun st
}
return nil
}

// resolveCustomParamsForCEL resolves custom parameters from the Repository CR for use in CEL expressions.
// It returns a map of parameter names to values, excluding reserved keywords.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is slightly inaccurate. The function does not exclude reserved keywords; that logic is handled in celEvaluate to prevent overwriting standard variables. To improve clarity, I suggest updating the comment to more accurately describe what the function does.

Suggested change
// It returns a map of parameter names to values, excluding reserved keywords.
// It returns a map of parameter names to values.

// All parameters are returned as strings, including those from secret_ref.
func resolveCustomParamsForCEL(ctx context.Context, repo *apipac.Repository, event *info.Event, cs *params.Run, vcx provider.Interface, eventEmitter *events.EventEmitter, logger *zap.SugaredLogger) map[string]string {
if repo == nil || repo.Spec.Params == nil {
return map[string]string{}
}

// Create kubeinteraction interface
kinteract, err := kubeinteraction.NewKubernetesInteraction(cs)
if err != nil {
logger.Warnf("failed to create kubernetes interaction for custom params: %s", err.Error())
return map[string]string{}
}
Comment on lines +529 to +533
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't you have kubeinteraction in params.Run?


// Use existing customparams package to resolve all params
cp := customparams.NewCustomParams(event, repo, cs, kinteract, eventEmitter, vcx)
allParams, _, err := cp.GetParams(ctx)
if err != nil {
eventEmitter.EmitMessage(repo, zap.WarnLevel, "CustomParamsCELError",
fmt.Sprintf("failed to resolve custom params for CEL: %s", err.Error()))
return map[string]string{}
}

// Filter to only include params defined in repo.Spec.Params (not standard PAC params)
result := make(map[string]string)
for _, param := range *repo.Spec.Params {
if value, ok := allParams[param.Name]; ok {
result[param.Name] = value
}
}

return result
}
Loading