# Tekton Events Relay - Complete Configuration Reference # This file demonstrates all available configuration options # # To use this file: # 1. Copy to /etc/tekton-events-relay/config.yaml # 2. Set required environment variables (tokens, passwords) # 3. Enable desired notifiers (enabled: false) # 4. Adjust templates and behaviors as needed # ============================================================================= # HTTP SERVER CONFIGURATION # ============================================================================= server: # HTTP server bind address (default: ":8080") # Format: "host:port" or ":port" for all interfaces addr: ":8080" # Separate metrics endpoint (optional, default: disabled) # Example: ":9090" serves metrics at http://localhost:9090/metrics # metrics_addr: ":9090" # HTTP read timeout in seconds (default: 10) read_timeout_sec: 10 # HTTP write timeout in seconds (default: 10) write_timeout_sec: 10 # Graceful shutdown timeout in seconds (default: 30) shutdown_timeout_sec: 30 # Maximum request body size in bytes (default: 1048576 = 1MB) max_body_size: 1048576 # Rate limiting (token bucket per source IP, optional) rate_limit: # Enable rate limiting (default: false) enabled: false # Sustained rate: requests per second (default: 100.0) # Examples: 10.0 (conservative), 100.0 (moderate), 1000.0 (high-volume) requests_per_second: 100.0 # Burst capacity: max requests per burst (default: 200) # Rule of thumb: 2-5x requests_per_second burst: 200 # Webhook authentication (optional, default: disabled) # Two types supported: # - hmac-sha256 (recommended): Validates X-Hub-Signature-256 header # - bearer: Validates Authorization: Bearer token header # # Security best practices: # - Store secrets in Kubernetes Secrets, mounted as files # - Use strong randomly-generated secrets (32+ characters) # - Use TLS/HTTPS to prevent secret interception # # Example: HMAC-SHA256 (GitHub-style) # auth: # enabled: false # type: "hmac-sha256" # secret_file: "/etc/secrets/server/auth/secret" # Generate: openssl rand -hex 32 # validate_timestamp: true # timestamp_tolerance: 5m # # Example: Bearer token # auth: # enabled: false # type: "bearer" # secret_file: "/etc/secrets/server/auth/secret" # Generate: openssl rand -base64 32 # ============================================================================= # OBSERVABILITY # ============================================================================= # Logging configuration # logging: # level: "info" # debug, info, warn, error # OpenTelemetry tracing (optional) # Set OTEL_EXPORTER_OTLP_ENDPOINT env var to enable # Example: OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4318 # If not set, tracing is disabled (noop tracer) # Prometheus metrics available at /metrics endpoint # ServiceMonitor can be enabled in Helm chart # ============================================================================= # GLOBAL SETTINGS # ============================================================================= # Tekton Dashboard URL to enrich result links # Optional dashboard_url: "https://tekton.example.com" # LRU cache size for event deduplication # Default: 10000 dedupe_size: 10000 # Per-handler execution deadline: one slow provider cannot stall the # dispatch of an event to the others (default: 10s) handler_timeout: 10s # ============================================================================= # OUTBOUND HTTP RETRY POLICY (all SCM clients and notifiers) # ============================================================================= # Exponential backoff with jitter. HTTP 429/503 Retry-After headers are # honored (capped at max_backoff). 4xx responses other than 429 never retry. retry: max_attempts: 4 # total attempts, including the first request initial_backoff: 250ms max_backoff: 30s # ============================================================================= # STATE BACKEND (deduplication + accumulator) # ============================================================================= # memory (default): per-pod state — lost on restart, NOT shared between # replicas. Run a single replica, or pick a shared backend below. # valkey: any RESP-compatible server (Valkey/KeyDB). A tiny instance with # no persistence is enough. # olric: embedded distributed cache — relay pods share state via gossip, # no extra deployment (the Helm chart wires the discovery service). store: backend: memory ttl: 1h # valkey: # address: valkey.tekton-events-relay.svc:6379 # password_file: /etc/secrets/valkey/password # db: 0 # key_prefix: tekton-events-relay # olric: # bind_port: 3320 # memberlist_port: 3322 # peers: [] # defaults to the chart's headless gossip service # ============================================================================= # DEAD LETTER QUEUE # ============================================================================= # Preserves events that failed with permanent errors (expired token, deleted # repo, ...) instead of silently dropping them. # Inspect: GET /api/v1/dlq # Replay: POST /api/v1/dlq/replay dlq: enabled: false # path: /var/lib/tekton-events-relay/dlq.jsonl # max_size_bytes: 10485760 # ============================================================================= # EVENT FILTER # ============================================================================= filter: # Process TaskRun events (default: false) # Enable for granular per-task notifications (warning: high-volume pipelines may flood channels) allow_taskrun: false # Process PipelineRun events (default: true) # Enable for pipeline-level success/failure notifications allow_pipelinerun: true # Process CustomRun events (default: false) # Enable for Custom resource event notifications allow_customrun: false # Process EventListener events (default: false) # Enable for EventListener event notifications allow_eventlistener: false # Silently ignore unknown event types (default: true) # Set to false during setup to detect misconfigured EventListeners ignore_unknown: true # Namespace filtering with path.Match wildcard support. # When allow_namespaces is empty, all namespaces are accepted (default). # Deny wins over allow when both are set. # Examples: # allow_namespaces: ["production", "staging"] # deny_namespaces: ["kube-*", "openshift-*"] allow_namespaces: [] deny_namespaces: [] # ============================================================================= # EVENT ACCUMULATOR (OPTIONAL) # ============================================================================= # Buffers events to batch multiple related events into a single notification. # Reduces notification noise when TaskRuns complete in quick succession. # # How it works: # 1. Events grouped by key (PipelineRun UID + Repo) # 2. Held in memory for `ttl` duration # 3. Released together as batch after TTL expires # 4. Safety limit: released immediately if buffer exceeds `max_size` accumulator: # Enable event accumulation (default: false) enabled: false # Time-to-live for buffered events (default: "30s") # Longer TTL = more batching but higher latency ttl: "30s" # Maximum buffer size per event key (default: 100) # Acts as safety valve during event storms max_size: 100 # ============================================================================= # LOGGING # ============================================================================= logging: # Log level: debug, info, warn, error # Default: "info" level: "info" # Verbose logging options verbose: # Include source file:line in logs (default: false) caller: false # Log HTTP request/response details to SCM APIs (default: false) http_calls: false # Log full webhook payloads (default: false) # Warning: May expose sensitive data in logs payloads: false # ============================================================================= # TRACING # ============================================================================= # OpenTelemetry tracing configuration. # Set endpoint to enable tracing; leave empty to disable (noop tracer). # Example: endpoint: "otel-collector:4318" tracing: # OTLP endpoint for trace export (empty = disabled) endpoint: "" # Service name reported in traces service_name: "tekton-events-relay" # ============================================================================= # SCM PROVIDERS # ============================================================================= # GitHub (Cloud or Enterprise Server) scm: github: - name: main-instance enabled: false # Personal access token (for commit_status, pr_comment, etc.) # Required scopes: repo:status, repo (if using actions) # File-based secret pattern (mount secret files in Kubernetes) auth: secret_file: "/etc/secrets/github-token" secret_key: "token" # Optional: key within JSON/YAML secret file # GitHub App authentication (alternative to token - required for check_run) # Uncomment to use GitHub App instead of personal token: # app_id: 123456 # installation_id: 789012 # # Private key must be mounted at /etc/github-app/private-key.pem # When using Helm, configure in values.yaml: # githubApp: # enabled: true # private_key: # secretRef: # name: github-app-credentials # key: private-key.pem # # Manual Kubernetes deployment: # kubectl create secret generic github-app-credentials \ # --from-file=private-key.pem=/path/to/key.pem # Then mount the Secret as a volume at /etc/github-app # API base URL (for GitHub Enterprise Server) base_url: "https://api.github.com" # Skip TLS certificate verification (testing only) # Default: false insecure_skip_verify: false # Actions (explicit opt-in, list format) actions: - name: commit-status type: commit_status enabled: false # CEL expression (optional) - available fields: Resource, State, RunName, TaskName, PipelineName, etc. when: 'event.Resource == "pipelinerun" && event.Repo.Owner == "myorg"' filter: tasks: allow: ["build", "test", "deploy"] deny: ["cleanup"] pipelines: allow: ["ci-pipeline", "release-pipeline"] event_listeners: allow: ["prod-listener"] # The action 'name' field IS the check run name displayed in GitHub UI - name: "Tekton CI Build" type: check_run enabled: false when: 'event.Resource == "pipelinerun" && event.Namespace == "production" && stateIn("running", "success", "failure")' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] template: | ## Pipeline: {{.PipelineName}} **Run:** {{.RunName}} **Namespace:** {{.Namespace}} **State:** {{.State}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View in Tekton Dashboard]({{.TargetURL}}){{end}} {{if eq .State "failure"}} ### Failure Details Task **{{.TaskName}}** failed. Review logs for error details. {{else if eq .State "success"}} ### Success All pipeline tasks completed successfully. {{end}} - name: deployment-status type: deployment_status enabled: false when: 'event.Resource == "pipelinerun" && event.PipelineName.startsWith("deploy-") && stateIn("running", "success", "failure")' filter: pipelines: allow: ["deploy-production", "deploy-staging"] # NOTE: Environment name is read from Event.Context, which is populated from # the Tekton annotation: tekton.dev/tekton-events-relay.scm.context # Add this annotation to your PipelineRun/TaskRun to specify environment: # metadata: # annotations: # tekton.dev/tekton-events-relay.scm.context: "production" # Defaults to "production" if not set. # The 'environment' and 'auto_merge' fields are NOT config fields - removed from example. template: | Pipeline: {{.PipelineName}} ({{.RunName}}) Commit: {{.CommitSHA}} Namespace: {{.Namespace}} - name: pr-comment type: pr_comment enabled: false when: 'event.Resource == "taskrun" && stateIn("success", "failure")' filter: tasks: allow: ["test"] template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} {{if eq .State "failure"}} Build failed. Check the logs for details. {{else if eq .State "success"}} Build passed successfully! ✅ {{end}} - name: issue-comment type: issue_comment enabled: false when: 'event.Namespace == "production" && stateIn("failure", "error")' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] template: | Pipeline {{.Context}} finished with state: **{{.State}}** **Run:** {{.RunName}} **Namespace:** {{.Namespace}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} {{if eq .State "failure"}} :x: Build failed in production namespace. {{end}} - name: discussion-comment type: discussion_comment enabled: false when: 'stateIn("success", "failure")' filter: pipelines: allow: ["release-pipeline"] template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} **Namespace:** {{.Namespace}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} {{if eq .State "success"}} :white_check_mark: Release pipeline completed successfully! {{else if eq .State "failure"}} :x: Release pipeline failed. Investigation required. {{end}} - name: label type: label enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] labels: add: ["ci:passed"] remove: ["ci:failed"] # --------------------------------------------------------------------------- # GitLab (Cloud or Self-hosted) # --------------------------------------------------------------------------- # Auth: Personal Access Token with api or write_repository scope # Supported actions: commit_status, pr_comment (MR note), issue_comment, label gitlab: - name: cloud-instance enabled: false base_url: "https://gitlab.com/api/v4" insecure_skip_verify: false auth: secret_file: "/etc/secrets/gitlab/cloud-instance/token" # secret_key: token # Optional: key within JSON/YAML secret file # oauth2: # OAuth2 alternative (create app at Settings > Applications) # client_id_file: "/etc/secrets/gitlab/cloud-instance/client_id" # client_secret_file: "/etc/secrets/gitlab/cloud-instance/client_secret" # token_url: "https://gitlab.com/oauth/token" actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] - name: mr-comment type: pr_comment enabled: false when: 'event.Resource == "pipelinerun" && stateIn("failure", "error")' template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} {{if eq .State "failure"}} :x: Build failed. Check the logs for details. {{end}} - name: issue-comment type: issue_comment enabled: false when: 'event.Namespace == "production" && stateIn("failure", "error")' template: | Pipeline {{.Context}} finished with state: **{{.State}}** {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} - name: discussion-comment type: discussion_comment enabled: false when: 'isPR() && stateIn("failure", "error")' template: | Pipeline {{.Context}} finished with state: **{{.State}}** {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} - name: commit-comment type: commit_comment enabled: false mode: upsert when: 'stateIn("success", "failure")' template: | Pipeline {{.State}} for `{{.CommitSHA}}` {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} - name: label type: label enabled: false labels: add: - name: "pipeline::success" color: "428bca" # GitLab blue remove: - name: "pipeline::failed" - name: server-instance enabled: false base_url: "https://gitlab.company.example.com/api/v4" insecure_skip_verify: false auth: secret_file: "/etc/secrets/gitlab/server-instance/token" # secret_key: token # Optional: key within JSON/YAML secret file actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' - name: label type: label enabled: false labels: add: - name: "pipeline::success" color: "428bca" # GitLab blue remove: - name: "pipeline::failed" # --------------------------------------------------------------------------- # Bitbucket (Cloud or Server) # --------------------------------------------------------------------------- # variant: "cloud" uses username + app_password; "server" uses personal access token # Supported actions: commit_status, pr_comment only bitbucket: - name: cloud-instance variant: cloud enabled: false base_url: "https://api.bitbucket.org/2.0" insecure_skip_verify: false auth: username_file: "/etc/secrets/bitbucket/cloud-instance/username" username_key: "username" # Optional: key within JSON/YAML secret file app_password_file: "/etc/secrets/bitbucket/cloud-instance/app_password" app_password_key: "password" # Optional: key within JSON/YAML secret file # oauth2: # Alternative: OAuth2 client credentials # client_id_file: "/etc/secrets/bitbucket/cloud-instance/client_id" # client_secret_file: "/etc/secrets/bitbucket/cloud-instance/client_secret" # token_url: "https://bitbucket.org/site/oauth2/access_token" actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] - name: pr-comment type: pr_comment enabled: false when: 'stateIn("success", "failure")' template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} - name: server-instance variant: server enabled: false base_url: "https://bitbucket.company.example.com" insecure_skip_verify: false auth: token_file: "/etc/secrets/bitbucket/server-instance/token" token_key: "token" # Optional: key within JSON/YAML secret file actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' - name: pr-comment type: pr_comment enabled: false when: 'stateIn("success", "failure")' template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} # --------------------------------------------------------------------------- # Azure DevOps # --------------------------------------------------------------------------- # Auth: Personal Access Token with Code (Status/PR) and Work Items scopes # Supported actions: commit_status, pr_comment, label # "genre" field groups commit statuses in the UI (e.g., "tekton-ci") azure_devops: - name: main-instance enabled: false secret_file: "/etc/secrets/azure-token" secret_key: "token" # Optional: key within JSON/YAML secret file base_url: "https://dev.azure.com" genre: "tekton-ci" insecure_skip_verify: false actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] - name: pr-comment type: pr_comment enabled: false mode: upsert when: 'stateIn("failure", "error")' template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} - name: label type: label enabled: false labels: add: - name: "build-passed" # Azure tags don't support colors, field ignored remove: - name: "build-failed" when: 'event.Resource == "pipelinerun" && event.Repo.Owner == "myorg"' # --------------------------------------------------------------------------- # Gitea # --------------------------------------------------------------------------- # Auth: Personal Access Token with repo and issue scopes # Supported actions: commit_status, pr_comment, issue_comment, label gitea: - name: main-instance enabled: false auth: secret_file: "/etc/secrets/gitea-token" secret_key: "token" # Optional: key within JSON/YAML secret file # oauth2: # OAuth2 alternative # client_id_file: "/etc/secrets/gitea/main-instance/client_id" # client_secret_file: "/etc/secrets/gitea/main-instance/client_secret" # token_url: "https://gitea.company.example.com/login/oauth/access_token" base_url: "https://gitea.company.example.com" insecure_skip_verify: false actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline", "release-pipeline"] - name: pr-comment type: pr_comment enabled: false when: 'event.RunName.startsWith("nightly-") && stateIn("success", "failure")' template: | ## Pipeline {{.State}} **Run:** {{.RunName}} **Commit:** {{.CommitSHA}} {{if .TargetURL}}[View Results]({{.TargetURL}}){{end}} {{if eq .State "failure"}} :x: Build failed. Check the logs for details. {{else if eq .State "success"}} :white_check_mark: Build passed successfully! {{end}} - name: issue-comment type: issue_comment enabled: false when: 'event.Namespace == "production" && stateIn("failure", "error")' template: | Pipeline {{.Context}} finished: **{{.State}}** {{if .TargetURL}}[Results]({{.TargetURL}}){{end}} - name: label type: label enabled: false labels: add: - name: "ci:passed" color: "0e8a16" # green remove: - name: "ci:failed" # --------------------------------------------------------------------------- # SourceHut # --------------------------------------------------------------------------- # Auth: OAuth 2.0 personal access token with builds:write scope # Supported actions: commit_status ONLY (email-based workflow) sourcehut: - name: main-instance enabled: false auth: secret_file: "/etc/secrets/sourcehut-token" secret_key: "token" # Optional: key within JSON/YAML secret file base_url: "https://builds.sr.ht" insecure_skip_verify: false actions: - name: commit-status type: commit_status enabled: false when: 'event.Resource == "pipelinerun"' filter: pipelines: allow: ["ci-pipeline"] # ============================================================================= # NOTIFIERS - CHAT & ALERTING # ============================================================================= # Send pipeline events to external chat/alerting systems. # Each notifier: name, enabled, when (CEL), template (Go template, optional) # # Template Variables: {{.RunName}}, {{.State}}, {{.CommitSHA}}, {{.TargetURL}}, # {{.PipelineName}}, {{.TaskName}}, {{.Namespace}}, {{.Repo.Owner}}, {{.Repo.Name}} # CEL Variables: event.Resource, event.State, event.RunName, event.Namespace, etc. # CEL Functions: startsWith(), endsWith(), contains(), matches() notifiers: # --------------------------------------------------------------------------- # Slack # --------------------------------------------------------------------------- # Setup: https://api.slack.com/apps > Enable Incoming Webhooks > Get URL # Supports Slack mrkdwn: *bold*, _italic_, `code`, , :emoji: slack: - name: main-channel enabled: false auth: webhook_url_file: "/etc/secrets/slack-webhook-url" # Webhook URL mode channel: "#ci-notifications" username: "Tekton CI" icon_emoji: ":robot_face:" when: 'event.State == "failure" || event.State == "error"' template: | :warning: *Pipeline {{.State}}* *Run:* `{{.RunName}}` *Pipeline:* {{.PipelineName}} *Namespace:* {{.Namespace}} *Commit:* `{{.CommitSHA}}` *Repo:* {{.Repo.Owner}}/{{.Repo.Name}} {{if .TargetURL}}<{{.TargetURL}}|View in Dashboard>{{end}} {{if eq .State "failure"}}:x: Build failed — check logs for details.{{end}} {{if eq .State "success"}}:white_check_mark: Build passed!{{end}} - name: production-alerts enabled: false auth: webhook_url_file: "/etc/secrets/slack-prod-webhook-url" # Webhook URL mode channel: "#production-alerts" username: "Tekton Prod" icon_emoji: ":rotating_light:" when: 'event.Namespace == "production" && event.State == "failure"' template: | :rotating_light: *PRODUCTION FAILURE* *Pipeline:* {{.PipelineName}} *Task:* {{.TaskName}} *Commit:* `{{.CommitSHA}}` *Repo:* {{.Repo.Owner}}/{{.Repo.Name}} {{if .TargetURL}}<{{.TargetURL}}|View Logs>{{end}} # Bot Token mode example (alternative to webhook URL) # - name: bot-token-channel # enabled: false # auth: # bot_token: # token_file: "/etc/secrets/slack-bot-token" # xoxb- token # channel_id: "C0123456789" # channel ID (not name) # mode: upsert # edit one message per run (bot token only) # thread_ts: "" # optional: post/update as a reply in this thread # when: 'event.State == "failure"' # --------------------------------------------------------------------------- # Microsoft Teams # --------------------------------------------------------------------------- # Setup: Channel > ... menu > Connectors > Incoming Webhook teams: - name: main-channel enabled: false auth: webhook_url_file: "/etc/secrets/teams-webhook-url" webhook_url_key: "webhook_url" # Optional: key within JSON/YAML secret file when: 'event.State == "success" || event.State == "failure" || event.State == "error"' template: | **Pipeline {{.State}}** | Field | Value | |-------|-------| | Run | {{.RunName}} | | Pipeline | {{.PipelineName}} | | Namespace | {{.Namespace}} | | Commit | {{.CommitSHA}} | | Repo | {{.Repo.Owner}}/{{.Repo.Name}} | {{if .TargetURL}}[View in Dashboard]({{.TargetURL}}){{end}} - name: release-notifications enabled: false auth: webhook_url_file: "/etc/secrets/teams-release-webhook-url" webhook_url_key: "webhook_url" # Optional: key within JSON/YAML secret file when: 'event.PipelineName.startsWith("release-") && (event.State == "success" || event.State == "failure")' # --------------------------------------------------------------------------- # Discord # --------------------------------------------------------------------------- # Setup: Server Settings > Integrations > Webhooks > New Webhook # Supports Discord markdown: **bold**, *italic*, `code`, ```code block```, > quote discord: - name: main-channel enabled: false auth: webhook_url_file: "/etc/secrets/discord-webhook-url" # Webhook URL mode username: "Tekton CI" when: 'event.Namespace == "staging" || event.Namespace == "production"' template: | **Pipeline {{.State}}** in `{{.Namespace}}` ``` Run: {{.RunName}} Pipeline: {{.PipelineName}} Task: {{.TaskName}} Commit: {{.CommitSHA}} Repo: {{.Repo.Owner}}/{{.Repo.Name}} ``` {{if .TargetURL}}[View in Dashboard]({{.TargetURL}}){{end}} {{if eq .State "failure"}}> :x: Build failed. Check the logs for details.{{end}} {{if eq .State "success"}}> :white_check_mark: Build passed!{{end}} - name: all-events enabled: false auth: webhook_url_file: "/etc/secrets/discord-all-webhook-url" # Webhook URL mode username: "Tekton Verbose" # Bot Token mode example (alternative to webhook URL) # - name: bot-token-channel # enabled: false # auth: # bot_token: # token_file: "/etc/secrets/discord-bot-token" # channel_id: "123456789012345678" # channel snowflake ID (quoted string) # mode: upsert # edit one message per run # when: 'event.State == "failure"' # --------------------------------------------------------------------------- # PagerDuty # --------------------------------------------------------------------------- # Setup: Service > Integrations > Events API V2 > Copy Integration Key # Severity: critical (P1), error (P2), warning (P3), info (P4) pagerduty: - name: main-service enabled: false auth: integration_key_file: "/etc/secrets/pagerduty-integration-key" integration_key_key: "integration_key" # Optional: key within JSON/YAML secret file severity: "critical" acknowledge_on_running: false # opt-in: send an acknowledge event while running when: 'event.State == "failure" || event.State == "error"' - name: staging-alerts enabled: false auth: integration_key_file: "/etc/secrets/pagerduty-staging-key" integration_key_key: "integration_key" # Optional: key within JSON/YAML secret file severity: "warning" when: 'event.Namespace == "staging" && event.State == "failure"' # --------------------------------------------------------------------------- # Datadog # --------------------------------------------------------------------------- # Setup: Organization Settings > API Keys > Create/Copy Key # Sites: datadoghq.com (US1), us3.datadoghq.com, datadoghq.eu, etc. # Tags: Use "key:value" format for faceted search datadog: - name: main-instance enabled: false auth: api_key_file: "/etc/secrets/datadog-api-key" api_key_key: "api_key" # Optional: key within JSON/YAML secret file site: "datadoghq.com" tags: - "env:production" - "team:platform" - "service:tekton-events-relay" when: 'event.State == "success" || event.State == "failure" || event.State == "error"' - name: staging-instance enabled: false auth: api_key_file: "/etc/secrets/datadog-api-key" api_key_key: "api_key" # Optional: key within JSON/YAML secret file site: "datadoghq.com" tags: - "env:staging" - "team:platform" when: 'event.Namespace == "staging"' # --------------------------------------------------------------------------- # Generic Webhook # --------------------------------------------------------------------------- # Sends events to any HTTP endpoint as JSON # Supports optional transform (gojq) and structured auth (bearer, basic, apikey, hmac) # # Transform: Optional gojq expression to reshape the payload before sending # Auth: Optional structured authentication (alternative to custom headers) # # Available auth types: # - bearer: Sets Authorization: Bearer {token} # - basic: Sets Authorization: Basic {base64(username:password)} # - apikey: Sets custom header with token value # - hmac: Computes HMAC-SHA256 of body, sets signature header # # Header priority: Base headers -> Auth headers -> Custom headers (wins on conflict) webhook: - name: main-webhook enabled: false url_file: "/etc/secrets/webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file when: 'event.State == "success" || event.State == "failure" || event.State == "error"' headers: X-Source: "tekton-events-relay" X-Environment: "production" Content-Type: "application/json" # Use auth block for bearer token instead of inline header auth: type: bearer token_file: "/etc/secrets/webhook-token" token_key: "token" # Optional: key within JSON/YAML secret file - name: transformed-webhook enabled: false url_file: "/etc/secrets/deployment-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file when: 'event.Resource == "pipelinerun" && event.State == "success"' # Transform the default payload to match consumer's API schema transform: | . | { id: .run_id, pipelineId: .pipeline_name, startedDate: .started_at, finishedDate: .finished_at, result: "SUCCESS", environment: "PRODUCTION", deploymentCommits: [{ repoUrl: .repo.owner + "/" + .repo.name, commitSha: .commit_sha }] } # Bearer token auth auth: type: bearer token_file: "/etc/secrets/deployment-api-token" token_key: "token" # Optional: key within JSON/YAML secret file - name: hmac-webhook enabled: false url_file: "/etc/secrets/hmac-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file when: 'event.State == "failure" || event.State == "error"' # HMAC-SHA256 signature for webhook verification auth: type: hmac secret_file: "/etc/secrets/webhook-hmac-secret" secret_key: "secret" # Optional: key within JSON/YAML secret file header: "X-Webhook-Signature" # Optional, defaults to X-Webhook-Signature - name: basic-auth-webhook enabled: false url_file: "/etc/secrets/basic-auth-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file # Basic authentication auth: type: basic username_file: "/etc/secrets/webhook-username" username_key: "username" # Optional: key within JSON/YAML secret file password_file: "/etc/secrets/webhook-password" password_key: "password" # Optional: key within JSON/YAML secret file - name: apikey-webhook enabled: false url_file: "/etc/secrets/apikey-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file # Custom API key header auth: type: apikey token_file: "/etc/secrets/api-key" token_key: "api_key" # Optional: key within JSON/YAML secret file header: "X-API-Key" - name: incident-webhook enabled: false url_file: "/etc/secrets/incident-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file when: 'event.Resource == "pipelinerun" && event.State == "failure"' headers: X-Priority: "high" auth: type: bearer token_file: "/etc/secrets/incident-api-token" token_key: "token" # Optional: key within JSON/YAML secret file - name: audit-log enabled: false url_file: "/etc/secrets/audit-webhook-url" url_key: "url" # Optional: key within JSON/YAML secret file auth: type: apikey token_file: "/etc/secrets/audit-api-key" token_key: "api_key" # Optional: key within JSON/YAML secret file header: "X-API-Key" # OAuth2 client_credentials (type: oauth2). The relay obtains an access # token from token_url and sends it as Authorization: Bearer, refreshing it # before expiry (so long-running pods never serve a stale token). Only # token_url is required; client_id/client_secret paths are inferred from # /etc/secrets/webhook//{client_id,client_secret} if omitted. - name: oauth2-webhook enabled: false url_file: "/etc/secrets/oauth2-webhook-url" auth: type: oauth2 oauth2: # grant_type: client_credentials # default client_id_file: "/etc/secrets/webhook/oauth2/client_id" client_secret_file: "/etc/secrets/webhook/oauth2/client_secret" token_url: "https://auth.example.com/oauth/token" # OAuth2 refresh_token grant. The relay performs NO interactive login (it # exposes no ingress/redirect): do the one-time authorization_code consent # out of band and seed the resulting refresh_token here; the relay rotates # access tokens automatically. - name: oauth2-refresh-webhook enabled: false url_file: "/etc/secrets/oauth2-refresh-webhook-url" auth: type: oauth2 oauth2: grant_type: refresh_token client_id_file: "/etc/secrets/webhook/oauth2/client_id" client_secret_file: "/etc/secrets/webhook/oauth2/client_secret" refresh_token_file: "/etc/secrets/webhook/oauth2/refresh_token" token_url: "https://auth.example.com/oauth/token" # --------------------------------------------------------------------------- # Grafana annotations — deployment markers on dashboards # --------------------------------------------------------------------------- grafana: - name: deploy-markers enabled: false url: https://grafana.example.com auth: # Grafana's API uses a service-account token (Bearer); it does not accept # OAuth2 client-credentials tokens. The token file is re-read on every # request, so rotating the Secret applies without restarting the pod. token_file: "/etc/secrets/grafana/token" # service-account token tags: ["deploy"] # dashboard_uid: "abc123" # optional: scope to a dashboard (org-wide when empty) # panel_id: 4 # optional: scope to a panel (requires dashboard_uid) when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-") && stateIn("success", "failure")' # template is required: inline string OR absolute /etc/templates path. # Inline: template: "{{.PipelineName}} {{.State}} ({{.RunName}})" # …or mount a ConfigMap and reference the file: # template: "/etc/templates/deploy-marker.tmpl" # --------------------------------------------------------------------------- # Sentry releases — release + deploy marker on successful runs # (version = CommitSHA, Sentry's recommended scheme) # --------------------------------------------------------------------------- sentry: - name: sentry enabled: false org: my-org projects: ["my-project"] auth: # Sentry's API uses an auth token (Bearer); it does not accept OAuth2 # client-credentials tokens. The token file is re-read per request, so # rotating the Secret applies without restarting the pod. token_file: "/etc/secrets/sentry/token" # base_url: https://sentry.company.example.com # self-hosted when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-")' # --------------------------------------------------------------------------- # Opsgenie — create alerts on failure/error, close on success # Alias "tekton-relay:{RunID}" ensures one alert per pipeline run. # --------------------------------------------------------------------------- opsgenie: - name: opsgenie enabled: false auth: api_key_file: "/etc/secrets/opsgenie/api_key" # team_name: "platform-team" # optional: assign team responder # priority: P3 # P1-P5, default P3 when: 'stateIn("failure", "error")' # --------------------------------------------------------------------------- # Email (SMTP) — subject and body are required templates # --------------------------------------------------------------------------- email: # Example A: inline templates - name: oncall-inline enabled: false host: smtp.example.com # port: 587 # 587 STARTTLS (default), 465 implicit TLS, 25 plain # encryption: starttls from: ci@example.com to: ["oncall@example.com"] # cc: ["leads@example.com"] # optional visible carbon-copy # bcc: ["audit@example.com"] # optional blind carbon-copy # reply_to: "noreply@example.com" # optional Reply-To override # auth (optional — omit for unauthenticated in-cluster relays): # username: smtp-user # password_file: "/etc/secrets/email/password" # XOAUTH2 (Gmail/M365) alternative to password — mutually exclusive: # auth: # username: ci@example.com # xoauth2: true # token_file: "/etc/secrets/email/token" subject: "[tekton] {{ if .PipelineName }}{{ .PipelineName }}{{ else }}{{ .RunName }}{{ end }} — {{ .State }}" template: | Pipeline {{ .State }}: {{ if .PipelineName }}{{ .PipelineName }}{{ else }}{{ .RunName }}{{ end }} Run: {{ .RunName }} {{- if .CommitSHA }} Commit: {{ printf "%.8s" .CommitSHA }} {{- end }} {{- if .TargetURL }} View logs: {{ .TargetURL }} {{- end }} html: false when: 'isPipelineRun() && stateIn("failure", "error")' # Example B: templates mounted from a ConfigMap at /etc/templates - name: oncall-file enabled: false host: smtp.example.com from: ci@example.com to: ["team@example.com"] subject: "/etc/templates/email-subject.tmpl" template: "/etc/templates/email-default.tmpl" when: 'isPipelineRun() && stateIn("failure", "error")'