New to this? Prow for your organization is the guided path; this page is the reference.
One caller workflow runs the whole bot through the reusable workflow
cncf/prow-github-actions/.github/workflows/prow.yml.
The caller owns the triggers, the permissions and the concurrency group; the reusable
workflow runs the action at its own commit, so the two never drift.
- One repository
- Without
pull_request_target - An organization
- Upgrading
- Inputs and secrets
- What you get with zero config
- Using the action directly
| Step | Do |
|---|---|
| 1 | Copy templates/workflow-templates/prow.yml to .github/workflows/prow.yml. Replace $default-branch with your default branch (the organization installer below does this for you). |
| 2 | Optionally add a .github/prow.yaml (starter, reference). Without one the built-in labels and the organization's file, if any, apply. |
| 3 | Actions → Prow → Run workflow once: the label-sync job creates the labels the configuration describes. Label commands only apply labels that exist. |
The caller, in full:
name: Prow
on:
issues:
types: [opened, reopened, labeled, unlabeled]
issue_comment:
types: [created]
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
pull_request_review:
types: [submitted, dismissed]
check_suite:
types: [completed]
schedule:
- cron: '0 * * * *'
workflow_dispatch:
push:
branches: [main]
paths: [.github/prow.yaml]
permissions:
contents: write
issues: write
pull-requests: write
statuses: write
actions: write
concurrency:
group: prow-${{ github.event_name }}-${{ github.event.action }}-${{ github.event.comment.id || github.event.pull_request.number || github.event.issue.number || github.run_id }}
cancel-in-progress: false
jobs:
prow:
if: github.event_name != 'workflow_dispatch' && github.event_name != 'push'
uses: cncf/prow-github-actions/.github/workflows/prow.yml@v3.0.1
label-sync:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push'
uses: cncf/prow-github-actions/.github/workflows/prow.yml@v3.0.1
with:
jobs: label-sync| Trigger | Why |
|---|---|
pull_request_target |
Fork pull requests get a write token, so they are labeled and merged too. Safe because nothing checks out or runs pull request code: the reusable workflow only checks out cncf/prow-github-actions at its own commit (events). Forbidden by your policy? See without pull_request_target. |
schedule |
Backstop for merges the events missed (jobs). Hourly is plenty; drop it if you like. |
workflow_dispatch, push |
The label-sync job, on demand and whenever .github/prow.yaml changes. |
concurrency |
One group per comment, and per event and activity type for everything else (events). cancel-in-progress stays false: a run that is merging must not be cancelled. |
The permissions block is the ceiling: a reusable workflow's job can use at most what the
caller grants. The reusable job asks for exactly contents: write (merges, reading OWNERS
and configuration files), issues: write and pull-requests: write (labels, comments,
assignees, reviews) statuses: write (the prow/lgtm commit status that
binds lgtm to the reviewed commit) and
actions: write (/retest, /test and /ok-to-test re-run or approve workflow runs).
Grant less and GitHub refuses to start the called job, since a called
workflow may only downgrade, never elevate, the caller's permissions.
Some organizations forbid pull_request_target outright (zizmor's dangerous-triggers audit
flags it; a hash-pinning policy often comes with it). The second template,
templates/workflow-templates/prow-pull-request.yml
(with prow-pull-request.properties.json, sharing prow.svg), installs the same bot on
pull_request. The diff against the default caller is two lines of triggers and one job:
- pull_request_target:
+ pull_request:
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
schedule:
- - cron: '0 * * * *'
+ - cron: '*/5 * * * *'
jobs:
prow:
- if: github.event_name != 'workflow_dispatch' && github.event_name != 'push'
+ if: github.event_name != 'workflow_dispatch' && github.event_name != 'push' && github.event_name != 'schedule'
+ sweep:
+ if: github.event_name == 'schedule'
+ uses: cncf/prow-github-actions/.github/workflows/prow.yml@v3.0.1
+ with:
+ jobs: sweep lgtm| Pull request | Handled by | Latency |
|---|---|---|
| from the repository itself | the events, as with the default template | seconds |
| from a fork | the sweep job: needs-* labels, OWNERS labels and reviewers, approval, the merge. The pull_request/pull_request_review runs return at once: GitHub gives them a read-only token |
the cron interval; */5 is GitHub's shortest and slots are delayed under load, so minutes |
any, on a comment (/lgtm, /approve, ...) |
the issue_comment run, which has a write token on forks too |
seconds |
Caveat: an lgtm label applied by hand on a fork pull request cannot be
bound to the commit by the read-only run,
so the sweep strips it with a comment; use /lgtm. Organizations that hash-pin replace @v3.0.1
in the template with the release's commit sha and a # v3.0.1 comment; Dependabot keeps it
current.
| Put | At | Effect |
|---|---|---|
prow.yaml |
<org>/.github repository, prow.yaml |
Every repository of the organization inherits it; a repository's own .github/prow.yaml layers on top (tiers). |
prow.yml, prow.properties.json, prow.svg |
<org>/.github repository, workflow-templates/ |
Every repository sees Prow under Actions → New workflow → Workflows created by ; one click installs the caller with $default-branch filled in (GitHub docs). |
prow-pull-request.yml, prow-pull-request.properties.json |
same place, optional | Prow (pull_request, no pull_request_target), the pull_request install mode for organizations whose policy forbids pull_request_target. |
The loader looks for <org>/.project prow.yaml before <org>/.github prow.yaml. A
consuming repository's GITHUB_TOKEN cannot read another private repository, and the
loader treats an unreadable repository like a missing one: with a private .project the
organization tier silently falls through to <org>/.github. To use a private .project,
pass a token with read access to it:
jobs:
prow:
uses: cncf/prow-github-actions/.github/workflows/prow.yml@v3.0.1
secrets:
token: ${{ secrets.PROW_TOKEN }}Or name the source explicitly with the config input, which replaces the organization
lookup (the config input).
| Ref | Behaviour |
|---|---|
@v3.0.1 |
Exact release, what the templates ship. Dependabot's github-actions ecosystem proposes the next release (it also updates reusable-workflow uses: refs). |
@v3 |
Floats: release.yml moves it to every stable v3.x.y. Nothing to do; you get every fix and every minor change without review. |
@<sha> # v3.0.1 |
Hash pinning; the commit is on the release page. Dependabot keeps the comment and the sha in step. |
Whatever the ref, the reusable workflow checks out the action at the same commit as the workflow file, so a caller pinned to a sha runs exactly that action bundle and a floating tag moves both together.
The reusable workflow needs github.com: it reads the job.workflow_sha context, which is not
available on GitHub Enterprise Server. There, use the action directly.
Since lgtm is bound to the reviewed commit
the caller must grant statuses: write (the templates and the snippets on this page do), unless
prow.yaml sets lgtm.bind_to_commit: false. Without it /lgtm fails with
cannot bind lgtm to the commit: grant statuses: write ... and applies no label. Pull requests
already carrying lgtm when you upgrade are unbound: their next evaluation strips the label
once, with a comment saying why; re-apply with /lgtm.
Every input is optional. Each maps to the action.yml input of the same name.
| Input | Default | Meaning |
|---|---|---|
prow-commands |
every built-in command except /meow |
The /commands to enable on issue_comment. Setting it replaces the list: add /meow or a dynamic /<key> command here. |
jobs |
lgtm |
The jobs for schedule, workflow_dispatch and push, and the PR jobs for pull_request: lgtm merges on the schedule and strips lgtm from updated PRs; sweep evaluates recently updated PRs (fork PRs under pull_request). |
merge-method |
merge |
merge, squash or rebase; tide.merge_method in prow.yaml wins. Ignored on a branch that requires a merge queue: the queue's method wins (merge queues). |
config |
— | An explicit configuration source, owner/repo:path[@ref] or an https:// url (configuration). |
dry-run |
false |
label-sync logs what it would create or update and writes nothing. |
| Secret | Default | Meaning |
|---|---|---|
token |
the caller's github.token |
A token that can read a private <org>/.project, a bot user's token so merges trigger push workflows (events), or one with the workflows scope so fork pull requests whose merge involves workflow files can be merged (automatic merging). |
cat-api-key |
— | The thecatapi.com key for /meow. |
jobs:
prow:
uses: cncf/prow-github-actions/.github/workflows/prow.yml@v3.0.1
with:
prow-commands: /lgtm /approve /hold /kind /area /priority /meow
merge-method: squash
secrets:
token: ${{ secrets.PROW_TOKEN }}
cat-api-key: ${{ secrets.CAT_API_KEY }}secrets: inherit also works; the reusable workflow reads only token and cat-api-key.
With the caller alone and no prow.yaml in any tier:
| Feature | Docs |
|---|---|
Every built-in /command on issues and pull requests: assign, cc, approve, lgtm, hold, close, reopen, lock, retitle, milestone, help, good-first-issue, lifecycle, stage, status, check-required-labels, auto-cc, retest, test, ok-to-test, and the label commands once their labels exist |
commands |
| Reviewers requested and labels applied from OWNERS files | labeling, blunderbuss |
Automatic merging once a PR carries lgtm and no do-not-merge/*, needs-rebase or hold, on events and hourly; lgtm counts only for the commit it reviewed |
automatic merging |
lgtm removed when new commits are pushed |
PR jobs |
label-sync creating lgtm, approved, do-not-merge/hold, hold, help wanted, good first issue, ok-to-test and the lifecycle/*, stage/*, status/* labels |
jobs |
Label sections (/kind, /area, /priority, ...) and needs-* rules need a prow.yaml
(starter).
The action itself is still uses: cncf/prow-github-actions@v3.0.1 with the same inputs; it is the
way on GitHub Enterprise Server and for workflows that mix it with other steps. The
events page has the direct form of the caller above.