Skip to main content

GitHub Actions

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, please 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 the following .github/workflows/pull-request.yaml file to your repository:

.github/workflows/pull-request.yaml
name: buf-pull-requeston: pull_requestjobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: bufbuild/buf-setup-action@v0.5.0

This will ensure buf is installed with the latest release version and is available for all subsequent steps within the current job.

If you'd like to pin the buf CLI to a specific version, update your setup step to include a version like so:

- uses: bufbuild/buf-setup-action@v0.5.0  with:    version: '1.0.0-rc1'

If you'd like 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@v0.5.0    with:      version: 'latest'

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 the following after your buf-setup step:

.github/workflows/pull-request.yaml
name: buf-pull-requeston: pull_requestjobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: bufbuild/buf-setup-action@v0.5.0      - 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 the following after your buf-lint step, as well as make the following adjustments to your previous steps.

.github/workflows/pull-request.yaml
name: buf-pull-requeston: pull_requestjobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: bufbuild/buf-setup-action@v0.5.0      - 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 will add in-line comments to your pull request to indicate these changes.

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 the following .github/workflows/push.yaml file alongside your pull request workflow configuration.

.github/workflows/push.yaml
name: buf-pushon:  push:    branches:      - mainjobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: bufbuild/buf-setup-action@v0.5.0      - 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 will only push 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 will work.

The buf-push step will also tag 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 like so:

.github/workflows/push.yaml
name: buf-pushon:  push:    branches:      - mainjobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: bufbuild/buf-setup-action@v0.5.0      - 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 will automatically be kept in-sync with the BSR, so you don't have to manually push your API updates!