Examples
Real-world configurations, ready to adapt. The full annotated config shows every field.
The standard PR feedback loop
One self-updating summary comment + granular required checks + lifecycle labels:
scm:
github:
- name: github
enabled: true
auth:
secretRef:
name: github-token
actions:
- name: task-checks
type: commit_status
enabled: true
context_per_task: true # tekton/ci/build, tekton/ci/test, ...
when: 'isTaskRun() && !isFinallyTask()'
- name: pipeline-check
type: commit_status
enabled: true
when: 'isPipelineRun()'
- name: pr-summary
type: pr_comment
enabled: true
mode: upsert # one comment, updated in place
when: 'isPR() && isPipelineRun() && stateIn("running", "success", "failure")'
template: |
### {{ ternary "π" (ternary "β
" "β" (eq .State "success")) (eq .State "running") }} Pipeline `{{.PipelineName}}` β {{.State}}
**Commit:** `{{ .CommitSHA | trunc 8 }}` Β· {{ if .TargetURL }}[logs]({{.TargetURL}}){{ end }}
- name: ci-labels
type: label
enabled: true
when: 'isPR() && isPipelineRun() && stateIn("running", "success", "failure", "error")'
labels:
add: ["ci::{{.State}}"]
remove: ["ci::running", "ci::success", "ci::failure", "ci::error"]
Production failures β Slack, everything else silent
notifiers:
slack:
- name: prod-alerts
enabled: true
secretRef:
name: slack-webhook
channel: "#prod-alerts"
when: 'event.Namespace == "production" && isPipelineRun() && stateIn("failure", "error")'
template: |
:rotating_light: *{{.PipelineName}}* failed in *{{.Namespace}}*
Run `{{.RunName}}` Β· Commit `{{ .CommitSHA | trunc 8 }}`
{{if .TargetURL}}<{{.TargetURL}}|View logs>{{end}}
Deploy visibility: Environments + Grafana + Sentry
scm:
github:
- name: github
enabled: true
auth:
secretRef:
name: github-token
actions:
- name: deployments
type: deployment_status
enabled: true
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-")'
notifiers:
grafana:
- name: deploy-markers
enabled: true
url: https://grafana.company.example.com
token:
secretRef:
name: grafana-token
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-") && stateIn("success", "failure")'
sentry:
- name: sentry
enabled: true
org: acme
projects: ["api"]
token:
secretRef:
name: sentry-token
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-")'
Set the environment per run with the tekton.dev/tekton-events-relay.scm.context annotation (staging, production, β¦).
OAuth2 client credentials (webhook & Jira)
The generic webhook notifier and Jira support OAuth2 under auth.oauth2 β the relay fetches the access token and refreshes it before expiry, so the pod never 401s on a stale token. (Webhook works against any OAuth2-protected endpoint; Jira supports it natively β Cloud service accounts via https://auth.atlassian.com/oauth/token, Data Center via its OAuth2 provider. Grafana/Sentry are not included β their APIs use a service-account / auth token, not OAuth2 client credentials.)
notifiers:
webhook:
- name: oauth2-endpoint
enabled: true
url:
secretRef:
name: endpoint-url
auth:
type: oauth2
oauth2:
# grant_type: client_credentials # default
client_id:
secretRef:
name: endpoint-oauth2 # key: client_id
client_secret:
secretRef:
name: endpoint-oauth2 # key: client_secret
token_url: https://auth.company.example.com/oauth/token
when: 'isPipelineRun() && stateIn("success", "failure")'
grant_type defaults to client_credentials. The relay exposes no ingress/redirect, so it cannot run the interactive authorization_code flow; for providers that only bootstrap that way, obtain a refresh_token out of band (one-time consent) and let the relay rotate access tokens with grant_type: refresh_token:
auth:
type: oauth2
oauth2:
grant_type: refresh_token
client_id:
secretRef:
name: endpoint-oauth2
client_secret:
secretRef:
name: endpoint-oauth2
refresh_token:
secretRef:
name: endpoint-oauth2 # key: refresh_token (seeded out of band)
token_url: https://auth.company.example.com/oauth/token
Not using OAuth2? The static token/credential files for the webhook, grafana, sentry and jira notifiers are re-read on every request, so rotating the Kubernetes Secret takes effect without restarting the pod.
Exporting to Apache DevLake
Engineering metrics (DORA & friends) belong in DevLake β feed it with the webhook notifier and a gojq transform matching DevLakeβs deployments webhook schema:
notifiers:
webhook:
- name: devlake-deployments
enabled: true
url:
secretRef:
name: devlake-webhook # https://devlake/.../plugins/webhook/<id>/deployments
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-") && stateIn("success", "failure")'
transform: |
{
deploymentCommits: [{
repoUrl: ("https://github.com/" + .repo.owner + "/" + .repo.name),
refName: .commit_sha,
startedDate: .started_at,
finishedDate: .finished_at
}],
id: .run_id,
result: (if .state == "success" then "SUCCESS" else "FAILURE" end),
startedDate: .started_at,
finishedDate: .finished_at
}
Multi-platform simultaneously
Instances are independent β one event can update GitHub and a self-managed GitLab and page on-call:
scm:
github:
- name: github
enabled: true
auth:
secretRef:
name: github-token
actions: [{ name: status, type: commit_status, enabled: true }]
gitlab:
- name: gitlab-internal
variant: self-managed
enabled: true
base_url: https://gitlab.corp.example.com/api/v4
auth:
secretRef:
name: gitlab-token
actions: [{ name: status, type: commit_status, enabled: true }]
notifiers:
pagerduty:
- name: oncall
enabled: true
integration_key:
secretRef:
name: pd-key
severity: critical
when: 'event.Namespace == "production" && stateIn("failure", "error")'
Each run picks its target via the scm.provider annotation (github or gitlab-internal).
Pipeline summary with the accumulator
Batch all TaskRun results into a single PR comment posted when the PipelineRun finishes:
accumulator:
enabled: true
ttl: 5m
max_size: 200
provider:
name: github # a registered pr_comment-capable instance
scm:
github:
- name: github
enabled: true
auth:
secretRef:
name: github-token
actions:
- name: pr-comment
type: pr_comment
enabled: true
mode: upsert # summaries converge to one comment