Skip to main content

Overview

Pipelines-as-Code allows you to define your Tekton pipelines directly in your Git repository. Pipeline definitions are stored in the .tekton/ directory and automatically executed when Git events occur.
Pipelines-as-Code stays as close to standard Tekton templates as possible. Write your templates as .yaml files, and PAC will run them.

Directory Structure

The .tekton directory must be at the top level of your repository:
your-repo/
├── .tekton/
│   ├── pull-request.yaml
│   ├── push-main.yaml
│   └── tasks/
│       └── custom-task.yaml
├── src/
└── README.md
1
Create the .tekton directory
2
Create a .tekton directory at the root of your repository:
3
mkdir .tekton
4
Generate a basic PipelineRun
5
Use the tkn pac CLI to generate a template:
6
tkn pac generate
7
You’ll be prompted for:
8
  • Git event type: Pull Request or Push
  • Target branch: e.g., main
  • 9
    This creates a basic template in .tekton/pull-request.yaml.
    10
    Customize your PipelineRun
    11
    Edit the generated file to add your build/test logic:
    12
    apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: pr-build
      annotations:
        pipelinesascode.tekton.dev/on-event: "[pull_request]"
        pipelinesascode.tekton.dev/on-target-branch: "[main]"
    spec:
      params:
        - name: repo_url
          value: "{{ repo_url }}"
        - name: revision
          value: "{{ revision }}"
      pipelineSpec:
        params:
          - name: repo_url
          - name: revision
        tasks:
          - name: fetch-repository
            taskRef:
              name: git-clone
              resolver: hub
            workspaces:
              - name: output
                workspace: source
            params:
              - name: url
                value: "$(params.repo_url)"
              - name: revision
                value: "$(params.revision)"
          - name: run-tests
            runAfter: [fetch-repository]
            taskRef:
              name: golang-test
              resolver: hub
            workspaces:
              - name: source
                workspace: source
      workspaces:
        - name: source
          emptyDir: {}
    

    Dynamic Variables

    Pipelines-as-Code provides template variables that are replaced at runtime with values from the Git event.

    Essential Variables

    repo_url
    string
    The full URL of the repositoryExample: https://github.com/openshift-pipelines/pipelines-as-code
    revision
    string
    The commit SHA being testedExample: 1234567890abcdef
    target_branch
    string
    The branch the event targets (for PRs: base branch; for push: the branch pushed to)Example: main
    source_branch
    string
    The branch where the event originates (for PRs: head branch; for push: same as target_branch)Example: feature/my-feature

    All Available Variables

    VariableDescriptionExample
    bodyFull webhook payload{{ body.pull_request.user.email }}
    eventNormalized event typepull_request, push, or incoming
    event_typeProvider-specific event typepull_request (GitHub), Merge Request (GitLab)
    git_auth_secretAuto-generated secret for private repospac-gitauth-xkxkx
    headersRequest headers{{ headers['x-github-event'] }}
    pull_request_numberPR/MR number42
    repo_nameRepository namepipelines-as-code
    repo_ownerRepository owneropenshift-pipelines
    repo_urlFull repository URLhttps://github.com/openshift-pipelines/pipelines-as-code
    revisionCommit SHA1234567890abcdef
    senderUsername of the event senderjohndoe
    source_branchSource branch namefeature-branch
    git_tagGit tag (only for tag push events)v1.0.0
    source_urlSource repository URLhttps://github.com/fork/repo
    target_branchTarget branch namemain
    target_namespaceNamespace where Repository CR matchedmy-namespace
    trigger_commentComment that triggered the run (GitOps commands)/test my-pipeline
    pull_request_labelsPR labels (newline-separated)bug\nenhancement

    Using Variables

    Variables use double-brace syntax: {{ variable_name }}
    params:
      - name: git-url
        value: "{{ repo_url }}"
      - name: git-revision
        value: "{{ revision }}"
    

    Advanced Variable Usage

    Accessing Webhook Body

    You can access any field from the webhook payload:
    params:
      - name: pr-title
        value: "{{ body.pull_request.title }}"
      - name: author-email
        value: "{{ body.pull_request.user.email }}"
    

    Object Values in YAML

    When passing objects or multiline values, use block format:
    params:
      - name: body
        value: |-
          {{ body }}
      - name: pull_request
        value: >
          {{ body.pull_request }}
    

    CEL Expressions

    For complex expressions, use the cel: prefix:
    params:
      - name: environment
        value: "{{ cel: pac.target_branch == 'main' ? 'production' : 'staging' }}"
      - name: commit-type
        value: "{{ cel: has(body.head_commit) && body.head_commit.message.startsWith('Merge') ? 'merge' : 'regular' }}"
      - name: go-files-changed
        value: "{{ cel: files.all.exists(f, f.endsWith('.go')) ? 'true' : 'false' }}"
    
    CEL expressions have access to:
    • body - Full webhook payload
    • headers - HTTP headers
    • files - Changed files (files.all, files.added, files.deleted, files.modified, files.renamed)
    • pac - Standard PAC parameters (pac.revision, pac.target_branch, etc.)

    PipelineRun Structure

    Required Components

    1
    Unique Name
    2
    Each PipelineRun must have a unique name:
    3
    metadata:
      name: unique-pipeline-name  # Must be unique across all PipelineRuns
    
    4
    PipelineRuns with duplicate names will never be matched.
    5
    Event Matching Annotations
    6
    At minimum, specify which events trigger the pipeline:
    7
    annotations:
      pipelinesascode.tekton.dev/on-event: "[pull_request]"
      pipelinesascode.tekton.dev/on-target-branch: "[main]"
    
    8
    PipelineSpec or PipelineRef
    9
    Define your pipeline inline or reference an external one:
    10
    Inline PipelineSpec
    spec:
      pipelineSpec:
        tasks:
          - name: build
            taskSpec:
              steps:
                - name: build-step
                  image: golang:1.21
                  script: |
                    go build ./...
    
    PipelineRef
    spec:
      pipelineRef:
        name: my-pipeline
      # Pipeline definition in .tekton/pipeline.yaml
    

    Complete Examples

    Python CI Pipeline

    .tekton/python-ci.yaml
    apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: python-ci
      annotations:
        pipelinesascode.tekton.dev/on-event: "[pull_request, push]"
        pipelinesascode.tekton.dev/on-target-branch: "[main, develop]"
    spec:
      pipelineSpec:
        tasks:
          - name: fetch-source
            taskRef:
              name: git-clone
              resolver: hub
            workspaces:
              - name: output
                workspace: source
            params:
              - name: url
                value: "{{ repo_url }}"
              - name: revision
                value: "{{ revision }}"
          - name: python-test
            runAfter: [fetch-source]
            taskRef:
              name: python-test
              resolver: hub
            workspaces:
              - name: source
                workspace: source
            params:
              - name: requirements_file
                value: "requirements.txt"
              - name: python_version
                value: "3.11"
      workspaces:
        - name: source
          emptyDir: {}
    

    Container Build Pipeline

    .tekton/build-push.yaml
    apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: build-push
      annotations:
        pipelinesascode.tekton.dev/on-event: "[push]"
        pipelinesascode.tekton.dev/on-target-branch: "[main]"
    spec:
      pipelineSpec:
        tasks:
          - name: fetch-source
            taskRef:
              name: git-clone
              resolver: hub
            workspaces:
              - name: output
                workspace: source
            params:
              - name: url
                value: "{{ repo_url }}"
              - name: revision
                value: "{{ revision }}"
          - name: build-push
            runAfter: [fetch-source]
            taskRef:
              name: buildah
              resolver: hub
            workspaces:
              - name: source
                workspace: source
            params:
              - name: IMAGE
                value: "quay.io/myorg/myapp:{{ revision }}"
              - name: DOCKERFILE
                value: "./Dockerfile"
      workspaces:
        - name: source
          emptyDir: {}
    

    Conditional Execution

    .tekton/docs-validation.yaml
    apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: docs-validation
      annotations:
        pipelinesascode.tekton.dev/on-event: "[pull_request]"
        pipelinesascode.tekton.dev/on-target-branch: "[main]"
        pipelinesascode.tekton.dev/on-path-change: "[docs/**, **.md]"
    spec:
      pipelineSpec:
        tasks:
          - name: fetch-source
            taskRef:
              name: git-clone
              resolver: hub
            workspaces:
              - name: output
                workspace: source
            params:
              - name: url
                value: "{{ repo_url }}"
              - name: revision
                value: "{{ revision }}"
          - name: lint-docs
            runAfter: [fetch-source]
            taskRef:
              name: markdown-lint
              resolver: hub
            workspaces:
              - name: source
                workspace: source
      workspaces:
        - name: source
          emptyDir: {}
    

    Using GitHub App Token

    Access the GitHub API using the temporary installation token:
    tasks:
      - name: add-comment
        taskRef:
          name: github-add-comment
        params:
          - name: REQUEST_URL
            value: "{{ repo_url }}/pull/{{ pull_request_number }}"
          - name: COMMENT_OR_FILE
            value: "Build completed successfully!"
          - name: GITHUB_TOKEN_SECRET_NAME
            value: "{{ git_auth_secret }}"
          - name: GITHUB_TOKEN_SECRET_KEY
            value: "git-provider-token"
    
    Or as an environment variable:
    env:
      - name: GITHUB_TOKEN
        valueFrom:
          secretKeyRef:
            name: "{{ git_auth_secret }}"
            key: "git-provider-token"
    
    • GitHub App tokens are available for 8 hours
    • Tokens are scoped to the repository by default (configurable via settings)

    Best Practices

    1
    Always use dynamic variables
    2
    Never hardcode repository URLs or commit SHAs:
    3
    # ✅ Good
    params:
      - name: url
        value: "{{ repo_url }}"
    
    # ❌ Bad
    params:
      - name: url
        value: "https://github.com/myorg/myrepo"
    
    4
    Check out code first
    5
    Most pipelines start by cloning the repository:
    6
    tasks:
      - name: fetch-repository
        taskRef:
          name: git-clone
          resolver: hub
        params:
          - name: url
            value: "{{ repo_url }}"
          - name: revision
            value: "{{ revision }}"
    
    7
    Use unique PipelineRun names
    8
    Ensure each PipelineRun has a distinct name to avoid conflicts.
    9
    Leverage workspaces
    10
    Share data between tasks using workspaces:
    11
    workspaces:
      - name: source
        emptyDir: {}
    
    12
    Test locally
    13
    Use tkn pac resolve to test your PipelineRun before pushing:
    14
    tkn pac resolve -f .tekton/pull-request.yaml
    

    Next Steps

    Event Matching

    Learn how to match PipelineRuns to specific events, branches, and file changes

    Pipeline Resolution

    Understand how PAC resolves tasks and pipelines from remote sources

    Running Pipelines

    Configure execution, permissions, and monitoring

    Repository CRD

    Configure the Repository Custom Resource

    Build docs developers (and LLMs) love