Skip to content

Commit 78e3f5a

Browse files
committed
feat(cel): add direct custom param variable access in expressions
Allow custom parameters from Repository CR to be accessed directly as CEL variables without template expansion. Parameters can now be used as: param_name == "value" on top of "{{param_name}}" == "value". Jira: https://issues.redhat.com/browse/SRVKP-9118 Signed-off-by: Akshay Pant <akshay.akshaypant@gmail.com>
1 parent 5b220d7 commit 78e3f5a

File tree

6 files changed

+446
-20
lines changed

6 files changed

+446
-20
lines changed

docs/content/docs/guide/customparams.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,73 @@ and a pull request event.
122122
- [GitHub Documentation for webhook events](https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads?actionType=auto_merge_disabled#pull_request)
123123
- [GitLab Documentation for webhook events](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html)
124124
{{< /hint >}}
125+
126+
### Using custom parameters in CEL matching expressions
127+
128+
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.
129+
130+
For example, with this Repository CR configuration:
131+
132+
```yaml
133+
apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
134+
kind: Repository
135+
metadata:
136+
name: my-repo
137+
spec:
138+
url: "https://github.com/owner/repo"
139+
params:
140+
- name: enable_ci
141+
value: "true"
142+
- name: environment
143+
value: "staging"
144+
```
145+
146+
You can use these parameters directly in your PipelineRun's CEL expression:
147+
148+
```yaml
149+
apiVersion: tekton.dev/v1
150+
kind: PipelineRun
151+
metadata:
152+
name: my-pipeline
153+
annotations:
154+
pipelinesascode.tekton.dev/on-cel-expression: |
155+
event == "push" && enable_ci == "true" && environment == "staging"
156+
spec:
157+
# ... pipeline spec
158+
```
159+
160+
This approach is particularly useful for:
161+
162+
- **Conditional CI**: Enable or disable CI for specific repositories without changing PipelineRun files
163+
- **Environment-specific matching**: Run different pipelines based on environment configuration
164+
- **Feature flags**: Control which pipelines run using repository-level feature flags
165+
166+
Custom parameters from secrets are also available:
167+
168+
```yaml
169+
apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
170+
kind: Repository
171+
metadata:
172+
name: my-repo
173+
spec:
174+
url: "https://github.com/owner/repo"
175+
params:
176+
- name: api_key
177+
secret_ref:
178+
name: my-secret
179+
key: key
180+
```
181+
182+
```yaml
183+
apiVersion: tekton.dev/v1
184+
kind: PipelineRun
185+
metadata:
186+
name: my-pipeline-with-secret
187+
annotations:
188+
pipelinesascode.tekton.dev/on-cel-expression: |
189+
event == "push" && api_key != ""
190+
spec:
191+
# ... pipeline spec
192+
```
193+
194+
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.

docs/content/docs/guide/matchingevents.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -301,18 +301,19 @@ pipelinesascode.tekton.dev/on-cel-expression: |
301301

302302
The fields available are:
303303

304-
| **Field** | **Description** |
305-
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
306-
| `event` | `push`, `pull_request` or `incoming`. |
307-
| `target_branch` | The branch we are targeting. |
308-
| `source_branch` | The branch where this pull_request comes from. (On `push`, this is the same as `target_branch`.) |
309-
| `target_url` | The URL of the repository we are targeting. |
310-
| `source_url` | The URL of the repository where this pull_request comes from. (On `push`, this is the same as `target_url`.) |
311-
| `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.) |
312-
| `body` | The full body as passed by the Git provider. Example: `body.pull_request.number` retrieves the pull request number on GitHub. |
313-
| `headers` | The full set of headers as passed by the Git provider. Example: `headers['x-github-event']` retrieves the event type on GitHub. |
314-
| `.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.) |
315-
| `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. |
304+
| **Field** | **Description** |
305+
| --- | --- |
306+
| `event` | `push`, `pull_request` or `incoming`. |
307+
| `target_branch` | The branch we are targeting. |
308+
| `source_branch` | The branch where this pull_request comes from. (On `push`, this is the same as `target_branch`.) |
309+
| `target_url` | The URL of the repository we are targeting. |
310+
| `source_url` | The URL of the repository where this pull_request comes from. (On `push`, this is the same as `target_url`.) |
311+
| `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.) |
312+
| `body` | The full body as passed by the Git provider. Example: `body.pull_request.number` retrieves the pull request number on GitHub. |
313+
| `headers` | The full set of headers as passed by the Git provider. Example: `headers['x-github-event']` retrieves the event type on GitHub. |
314+
| `.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.) |
315+
| `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. |
316+
| 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 matching expressions]({{< relref "/docs/guide/customparams#using-custom-parameters-in-cel-matching-expressions" >}}) for details. |
316317

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

pkg/matcher/annotation_matcher.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
1010
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
1111
apipac "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
12+
"github.com/openshift-pipelines/pipelines-as-code/pkg/customparams"
1213
pacerrors "github.com/openshift-pipelines/pipelines-as-code/pkg/errors"
1314
"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
1415
"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
16+
"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
1517
"github.com/openshift-pipelines/pipelines-as-code/pkg/opscomments"
1618
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
1719
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
@@ -210,6 +212,12 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
210212
}
211213
logger.Info(infomsg)
212214

215+
// Resolve custom params once for all PipelineRuns (for use in CEL expressions)
216+
customParams := resolveCustomParamsForCEL(ctx, repo, event, cs, vcx, eventEmitter, logger)
217+
if len(customParams) > 0 {
218+
logger.Debugf("resolved %d custom params from repo for CEL", len(customParams))
219+
}
220+
213221
celValidationErrors := []*pacerrors.PacYamlValidations{}
214222
for _, prun := range pruns {
215223
prMatch := Match{
@@ -280,7 +288,7 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
280288
if celExpr, ok := prun.GetObjectMeta().GetAnnotations()[keys.OnCelExpression]; ok {
281289
checkPipelineRunAnnotation(prun, eventEmitter, repo)
282290

283-
out, err := celEvaluate(ctx, celExpr, event, vcx)
291+
out, err := celEvaluate(ctx, celExpr, event, vcx, customParams)
284292
if err != nil {
285293
logger.Errorf("there was an error evaluating the CEL expression, skipping: %v", err)
286294
if checkIfCELEvaluateError(err) {
@@ -508,3 +516,38 @@ func MatchRunningPipelineRunForIncomingWebhook(eventType, incomingPipelineRun st
508516
}
509517
return nil
510518
}
519+
520+
// resolveCustomParamsForCEL resolves custom parameters from the Repository CR for use in CEL expressions.
521+
// It returns a map of parameter names to values, excluding reserved keywords.
522+
// All parameters are returned as strings, including those from secret_ref.
523+
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 {
524+
if repo == nil || repo.Spec.Params == nil {
525+
return map[string]string{}
526+
}
527+
528+
// Create kubeinteraction interface
529+
kinteract, err := kubeinteraction.NewKubernetesInteraction(cs)
530+
if err != nil {
531+
logger.Warnf("failed to create kubernetes interaction for custom params: %s", err.Error())
532+
return map[string]string{}
533+
}
534+
535+
// Use existing customparams package to resolve all params
536+
cp := customparams.NewCustomParams(event, repo, cs, kinteract, eventEmitter, vcx)
537+
allParams, _, err := cp.GetParams(ctx)
538+
if err != nil {
539+
eventEmitter.EmitMessage(repo, zap.WarnLevel, "CustomParamsCELError",
540+
fmt.Sprintf("failed to resolve custom params for CEL: %s", err.Error()))
541+
return map[string]string{}
542+
}
543+
544+
// Filter to only include params defined in repo.Spec.Params (not standard PAC params)
545+
result := make(map[string]string)
546+
for _, param := range *repo.Spec.Params {
547+
if value, ok := allParams[param.Name]; ok {
548+
result[param.Name] = value
549+
}
550+
}
551+
552+
return result
553+
}

0 commit comments

Comments
 (0)