GitHub Actions is a CI/CD system supported by GitHub that runs workflows against your code based on an event. Buf has published a collection of GitHub Actions that work together to provide a fully featured CI/CD solution for Protobuf:
- buf-setup installs and
sets up
buf
, so that it can be used by other steps. - buf-lint lints Protobuf
files with
buf
, and comments in-line on pull requests. - buf-breaking verifies
backwards compatibility for your Protobuf files with
buf
, and comments in-line on pull requests. - buf-push pushes a module to the Buf Schema Registry (BSR). The module is pushed with a tag equal to the git commit SHA.
In this guide, you will configure these GitHub Actions so that buf lint
and
buf breaking
are run on all pull requests, and buf push
pushes your module
to the BSR when your pull request is merged.
Create a BSR token
The buf-push
step requires access to the BSR. For steps on obtaining a token,
see the Authentication page for more details. This
needs to be added as an encrypted
GitHub Secret.
In this guide, the API token is set to BUF_TOKEN
.
buf-setup
We will start with the buf-setup
action. All the other Buf GitHub Actions
require buf
to be installed on your GitHub Action runner, and buf-setup
will
handle that for us.
Add this .github/workflows/pull-request.yaml
file to your repository:
name: buf-pull-request
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
This ensures that buf
is installed with the latest release version and is
available for all subsequent steps within the current job.
To pin the buf
CLI to a specific version, update your setup step to include a
version:
- uses: bufbuild/buf-setup-action@v1
with:
version: "1.15.1"
To resolve the latest release from GitHub, you can specify latest
, but this is
not recommended:
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
with:
version: "latest"
To access your private Remote Packages in Buf Schema Registry(BSR), you may optionally supply with your Buf username and a Buf API Token:
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
with:
buf_user: ${{ secrets.BUF_USER }}
buf_api_token: ${{ secrets.BUF_API_TOKEN }}
Optionally, you can supply a github_token
input so that any GitHub API requests are authenticated. This may prevent rate
limit issues when running on GitHub hosted runners:
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ github.token }}
buf-lint
Now that you have installed buf
, let's configure lint. The buf-lint
action
lints your pull request and has the ability to provide in-line comments. Add
this after your buf-setup
step:
name: buf-pull-request
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-lint-action@v1
buf-breaking
We will do something similar for the breaking change detection. The
buf-breaking
action prevents breaking changes to your API based on a given
repository to check against, such as the HEAD
of the main
branch of your
repository.
Add this after your buf-lint
step and make these adjustments to your previous
steps.
name: buf-pull-request
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-lint-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
# The 'main' branch of the GitHub repository that defines the module.
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main"
If any breaking changes are detected against the provided remote, buf-breaking
adds inline comments to your pull request to indicate these changes. The results
will also be accessible from the following steps with
steps.<BUF_BREAKING_STEP_ID>.outputs.results
.
buf-push
Now that we've added steps for pull request workflow, let's add a second workflow to push to the BSR once the pull request has merged. We cannot use the same workflow since we do not want to be pushing to the BSR on each commit pushed to the pull request.
Add this .github/workflows/push.yaml
file alongside your pull request workflow
configuration.
name: buf-push
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-lint-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
# The 'main' branch of the GitHub repository that defines the module.
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,ref=HEAD~1"
- uses: bufbuild/buf-push-action@v1
with:
buf_token: ${{ secrets.BUF_TOKEN }}
This workflow is basically the same workflow as before, with an additional step
to push to the BSR when a push is made to the main
branch of your repository.
The buf-push
action only pushes to the BSR if contents have actually changed,
it otherwise succeeds silently.
When comparing against the same branch we also set ref=HEAD~1
to compare
against the previous commit on that branch.
Note, ref=HEAD~1
does not work well for
rebase and merge
operations, since buf
is comparing against the last commit there might be
older commits with breaking changes. If you're using Merge pull request
(GitHub default) or Squash and merge options then #ref=HEAD~1
should work.
The buf-push
step also tags the BSR commit with the git
commit SHA, so that
they are more easily associated with one another.
Inputs
Some repositories are structured so that their
buf.yaml
is defined in a sub-directory,
such as a ./proto
directory. In this case, you can specify the relative
sub-directory using the input
parameter (this is relevant for both
pull_request
and push
workflows). For example, consider the tree
for the
buf.build/acme/weather
module:
.
└── proto
├── acme
│ └── weather
│ └── v1
│ └── weather.proto
└── buf.yaml
You can adapt the push
workflow shown above so that it targets the ./proto
directory, as in this Action configuration:
name: buf-push
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-lint-action@v1
with:
input: "proto"
- uses: bufbuild/buf-breaking-action@v1
with:
input: "proto"
# The 'main' branch of the GitHub repository that defines the module.
# Note we specify the subdir to compare against.
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,ref=HEAD~1,subdir=proto"
- uses: bufbuild/buf-push-action@v1
with:
input: "proto"
buf_token: ${{ secrets.BUF_TOKEN }}
For more information on subdir
see the
Breaking Change Detection - Usage
section.
Wrapping up
Now that you've set up buf
to run lint checks and detect breaking changes in
your CI/CD environment, your APIs will always remain consistent, and you won't
need to waste any more time understanding the
complex backwards compatibility rules
to ensure that you never break your customers. Plus, the module defined in your
GitHub repository is automatically kept in sync with the BSR, so you don't have
to manually push your API updates!