Skip to content

Commit 52e29ce

Browse files
committed
feat(cel): add direct custom param variable access
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 a8212d3 commit 52e29ce

File tree

6 files changed

+499
-6
lines changed

6 files changed

+499
-6
lines changed

docs/content/docs/guide/customparams.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,69 @@ 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+
spec:
170+
params:
171+
- name: api_key
172+
secret_ref:
173+
name: my-secret
174+
key: key
175+
```
176+
177+
```yaml
178+
179+
apiVersion: tekton.dev/v1
180+
kind: PipelineRun
181+
metadata:
182+
name: my-pipeline-with-secret
183+
annotations:
184+
pipelinesascode.tekton.dev/on-cel-expression: |
185+
event == "push" && api_key != ""
186+
spec:
187+
# ... pipeline spec
188+
```
189+
190+
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ The fields available are:
309309
| `headers` | The full set of headers as passed by the Git provider. Example: `headers['x-github-event']` retrieves the event type on GitHub. |
310310
| `.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.) |
311311
| `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. |
312+
| Custom params | Any [custom parameters]({{< relref "/docs/guide/customparams" >}}) defined in 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. |
312313

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

pkg/matcher/annotation_matcher.go

Lines changed: 64 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,9 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
210212
}
211213
logger.Info(infomsg)
212214

215+
// Cache for custom params resolution per repository (to avoid repeated resolution)
216+
customParamsCache := make(map[string]map[string]string)
217+
213218
celValidationErrors := []*pacerrors.PacYamlValidations{}
214219
for _, prun := range pruns {
215220
prMatch := Match{
@@ -280,7 +285,30 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
280285
if celExpr, ok := prun.GetObjectMeta().GetAnnotations()[keys.OnCelExpression]; ok {
281286
checkPipelineRunAnnotation(prun, eventEmitter, repo)
282287

283-
out, err := celEvaluate(ctx, celExpr, event, vcx)
288+
// Determine the effective repository for this PipelineRun
289+
effectiveRepo := repo
290+
if prMatch.Repo != nil {
291+
effectiveRepo = prMatch.Repo
292+
}
293+
294+
// Resolve custom params for the effective repository (with caching)
295+
var customParams map[string]string
296+
if effectiveRepo != nil {
297+
cacheKey := effectiveRepo.GetNamespace() + "/" + effectiveRepo.GetName()
298+
if cached, found := customParamsCache[cacheKey]; found {
299+
customParams = cached
300+
} else {
301+
customParams = resolveCustomParamsForCEL(ctx, effectiveRepo, event, cs, vcx, eventEmitter, logger)
302+
customParamsCache[cacheKey] = customParams
303+
if len(customParams) > 0 {
304+
logger.Debugf("resolved %d custom params from repo %s for CEL", len(customParams), cacheKey)
305+
}
306+
}
307+
} else {
308+
customParams = map[string]string{}
309+
}
310+
311+
out, err := celEvaluate(ctx, celExpr, event, vcx, customParams)
284312
if err != nil {
285313
logger.Errorf("there was an error evaluating the CEL expression, skipping: %v", err)
286314
if checkIfCELEvaluateError(err) {
@@ -508,3 +536,38 @@ func MatchRunningPipelineRunForIncomingWebhook(eventType, incomingPipelineRun st
508536
}
509537
return nil
510538
}
539+
540+
// resolveCustomParamsForCEL resolves custom parameters from the Repository CR for use in CEL expressions.
541+
// It returns a map of parameter names to values, excluding reserved keywords.
542+
// All parameters are returned as strings, including those from secret_ref.
543+
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 {
544+
if repo == nil || repo.Spec.Params == nil {
545+
return map[string]string{}
546+
}
547+
548+
// Create kubeinteraction interface
549+
kinteract, err := kubeinteraction.NewKubernetesInteraction(cs)
550+
if err != nil {
551+
logger.Warnf("failed to create kubernetes interaction for custom params: %s", err.Error())
552+
return map[string]string{}
553+
}
554+
555+
// Use existing customparams package to resolve all params
556+
cp := customparams.NewCustomParams(event, repo, cs, kinteract, eventEmitter, vcx)
557+
allParams, _, err := cp.GetParams(ctx)
558+
if err != nil {
559+
eventEmitter.EmitMessage(repo, zap.WarnLevel, "CustomParamsCELError",
560+
fmt.Sprintf("failed to resolve custom params for CEL: %s", err.Error()))
561+
return map[string]string{}
562+
}
563+
564+
// Filter to only include params defined in repo.Spec.Params (not standard PAC params)
565+
result := make(map[string]string)
566+
for _, param := range *repo.Spec.Params {
567+
if value, ok := allParams[param.Name]; ok {
568+
result[param.Name] = value
569+
}
570+
}
571+
572+
return result
573+
}

0 commit comments

Comments
 (0)