Basic setup
Add .github/workflows/buf-ci.yaml to the repo:
name: Buf CI
on:
push:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
delete:
permissions:
contents: read
pull-requests: write
jobs:
buf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bufbuild/buf-action@v1
with:
token: ${{ secrets.BUF_TOKEN }}This default config:
- Runs build, lint, format, and breaking change checks on every PR
- Pushes named modules to the BSR on git push/tag/branch
- Archives BSR labels when a branch or tag is deleted
BSR (Buf Schema Registry) — Buf’s hosted registry for sharing and versioning protobuf schemas, similar to npm for protos.
Skip breaking change detection
Via PR label
Add the label buf skip breaking to the PR. Requires labeled/unlabeled in the trigger (already in the default config above).
Via commit message
- uses: bufbuild/buf-action@v1
with:
breaking: |
contains(fromJSON('["push", "pull_request"]'), github.event_name) &&
!contains(github.event.head_commit.message, 'buf skip breaking')Disable label-skip entirely
breaking: ${{ github.event_name == 'pull_request' }}Disable individual steps
Each step has a boolean parameter:
- uses: bufbuild/buf-action@v1
with:
format: false # disable formatting
lint: false # disable lintingTrigger steps on specific events
- uses: bufbuild/buf-action@v1
with:
format: ${{ contains(fromJSON('["push", "pull_request"]'), github.event_name) }}Common values for github.event_name:
push— commit pushed to a branch or tagpull_request— PR opened, updated, or labeleddelete— branch or tag deletedrelease— GitHub release created/publishedworkflow_dispatch— manually triggered from the Actions UIschedule— cron-based trigger
Only push when proto files change
By default the workflow runs on every push regardless of what changed. Use paths to skip runs when no proto-related files were modified:
on:
push:
paths:
- '**.proto'
- '**/buf.yaml'
- '**/buf.lock'
- '**/buf.md'
- '**/README.md'
- '**/LICENSE'Non-root module
By default buf-action looks for buf.yaml at the repo root. If your protos live in a subdirectory (e.g. proto/), point input at it. You also need to tell the breaking change detector where to find the base version of that subdirectory.
Option 1 — clone the base on the fly (simpler, one checkout):
- uses: bufbuild/buf-action@v1
with:
input: proto/
breaking_against: ${{ github.event.repository.clone_url }}#format=git,commit=${{ github.event.pull_request.base.sha }},subdir=proto/breaking_against clones the repo at the PR’s base commit and points into the same subdirectory, so the detector compares proto/ on your branch against proto/ on the base branch.
Option 2 — check out both explicitly (more control, works in restricted network environments):
- uses: actions/checkout@v4
with:
path: head # current branch lands here
- uses: actions/checkout@v4
with:
path: base
ref: ${{ github.event.pull_request.base.sha }} # base branch lands here
- uses: bufbuild/buf-action@v1
with:
input: head/proto/
breaking_against: base/proto/Setup only (run buf manually after)
- uses: bufbuild/buf-action@v1
with:
setup_only: true
- run: buf build --error-format github-actionsPin buf version
- uses: bufbuild/buf-action@v1
with:
version: "1.68.4"If version is unset, the action resolves which buf binary to use in this order:
$BUF_VERSIONenv var — lets you override per-job without changing the workflow file- buf already installed on the runner — reuses whatever version the GitHub-hosted runner ships with
- Latest GitHub release — fetched at runtime, so builds are not reproducible without pinning
Disable PR summary comment
Remove pull-requests: write permission and set:
- uses: bufbuild/buf-action@v1
with:
pr_comment: falseSee also
- codegen-multi-service — generating Go code from protos across services, which this action validates and pushes
- field-numbers — protobuf field number rules; breaking change detection catches violations of these