CEL Expressions

Every action and notifier accepts a when expression in CEL. The expression must return a boolean; the handler only runs when it evaluates to true. An empty/absent when means “always”.

when: 'event.Namespace == "production" && stateIn("failure", "error")'

Expressions are compiled at startup (and on hot reload) — an invalid expression fails fast, never at event time.

The event object

All fields of the neutral event are available under event.:

FieldTypeNotes
event.Resourcestringtaskrun, pipelinerun, customrun, eventlistener
event.Statestringpending, running, success, failure, error, canceled, done
event.ProviderstringFrom the scm.provider annotation
event.RunName / event.RunIDstringResource name / UID
event.NamespacestringKubernetes namespace
event.PipelineName / event.TaskNamestringReferenced Pipeline/Task spec names
event.PipelineTaskNamestringTask name within the pipeline
event.PipelineDisplayName / event.TaskDisplayNamestringDisplay names, if set
event.TriggerName / event.EventListenerNamestringTekton Triggers metadata
event.TaskCountintChild tasks of a PipelineRun
event.CommitSHAstring
event.Context / event.Description / event.TargetURLstringCheck name / human message / dashboard link
event.APIBaseURLstringPer-run API override
event.Repo.Owner / .Name / .ID / .Workspace / .Project / .OrgstringRepository identifiers
event.PRNumber / event.IssueNumber / event.DiscussionNumberint0 when absent
event.IsFinallyTaskboolTask belongs to a finally block
event.SCMEventTypestringOriginating webhook type (push, pull_request, …) when known
event.Resultsmap[string]stringTekton results by name, e.g. event.Results["IMAGE_DIGEST"]
event.StartedAt / event.FinishedAttimestampComparable: event.FinishedAt > event.StartedAt

Plus the CEL standard library: startsWith(), endsWith(), contains(), matches() (RE2), in, arithmetic, ternaries, etc.

Built-in macros

Shorthand helpers that expand to common checks:

MacroExpands to
isTaskRun() / isPipelineRun() / isCustomRun() / isEventListener()event.Resource == "…"
isPR()event.PRNumber != 0
isIssue()issue number set, PR and discussion unset
isDiscussion()event.DiscussionNumber != 0
isFinallyTask()event.IsFinallyTask == true
isPushEvent() / isPREvent() / isIssueEvent() / isCommentEvent()event.SCMEventType == "push" / "pull_request" / "issues" / "issue_comment"
stateIn("a", "b", …)event.State in ["a", "b", …]

Recipes

Only production failures:

when: 'event.Namespace == "production" && stateIn("failure", "error")'

Terminal states of pipeline runs (skip per-task noise):

when: 'isPipelineRun() && stateIn("success", "failure", "error", "canceled")'

PRs of one team’s repos:

when: 'isPR() && (event.Repo.Owner == "payments" || event.Repo.Name.startsWith("pay-"))'

Deploy pipelines only:

when: 'isPipelineRun() && event.PipelineName.matches("^deploy-.*")'

Skip finally cleanup tasks:

when: 'isTaskRun() && !isFinallyTask()'

Only when the pipeline produced an image:

when: 'event.Results["IMAGE_DIGEST"] != ""'

Per-team Slack routing (one notifier instance per team):

notifiers:
  slack:
    - name: payments-alerts
      channel: "#payments-alerts"
      when: 'event.Repo.Owner == "payments" && stateIn("failure", "error")'
    - name: platform-alerts
      channel: "#platform-alerts"
      when: 'event.Repo.Owner == "platform" && stateIn("failure", "error")'

Pitfalls