Skip to main content

GitHub Actions for Buf now available

Buf's new GitHub Actions make it easier than ever to instrument your CI/CD workflows with buf. Reduce friction and save developer time by automating these essential steps.

Buf recently published a collection of GitHub Actions that can be used to setup buf to build custom CI/CD workflows, automatically verify that your Protobuf files conform to lint rules, prevent backwards-incompatible changes, and push modules to the Buf Schema Registry (BSR) so that your GitHub repository is always in-sync with your published modules.

This guide will walk you through the steps required to create a GitHub workflow that runs buf-setup, buf-lint, buf-breaking, and buf-push for both pull requests and branch commits in general. Once you're done, your PRs will include in-line comments like the following:

image

If you haven't already signed up for the BSR beta, add yourself to the waitlist here!

TL;DR#

  • The buf-setup action installs buf for use in other custom actions that require the buf CLI.
  • The buf-lint and buf-breaking actions ensure that your Protobuf API changes always conform to your lint and breaking configurations. Lint failures and breaking changes are written as in-line comments.
  • The buf-push action automatically keeps your GitHub repository in-sync with the BSR.
  • Write your workflow configuration once, and leave the rest to buf.

Setup#

The buf-lint, buf-breaking, and buf-push actions require that buf is installed in the GitHub Action runner, so you can use the buf-setup action to install it.

Configuring the buf-setup action is easy - all you need to do is add the bufbuild/buf-setup-action job, and select a buf version to install.

For example, consider the following GitHub workflow configured in .github/workflows/pull_request.yaml:

name: buf
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v0.1.0
with:
version: '0.41.0'
- name: Print buf version
if: success()
run: |
buf --version

The buf-lint and buf-breaking actions require that you use at least version 0.41.0, so make sure to configure a version the satisfies ^0.41.0!

With this, you can create custom workflows that depend on buf without needing to worry about maintaing the buf installation yourself.

Lint#

Now that you've installed buf in setup, you can configure the buf-lint action so that changes to your Protobuf APIs are validated with the lint configuration found in your buf.yaml. We can configure the action so that buf-lint comments in-line like so:

name: buf
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v0.1.0
with:
version: '0.41.0'
- uses: bufbuild/buf-lint-action@v0.2.0
if: success()
with:
github_token: ${{ github.token }} # Required to write comments in-line.

For example, consider a buf.build/bufbuild/semver module that defines a Version message in the proto/bufbuild/semver/v1/semver.proto file shown below:

syntax = "proto3";
package bufbuild.semver.v1;
message Version {
uint64 major = 1;
uint64 minor = 2;
uint64 micro = 3;
string label = 4;
}

This module's buf.yaml configuration contains the following (notice the lint configuration):

version: v1beta1
name: buf.build/bufbuild/semver
build:
roots:
- .
lint:
use:
- DEFAULT
breaking:
use:
- FILE

Suppose a developer mistakenly updates the major field to Major, such that it violates the lint configuration (i.e. the updated field name doesn't conform to the FIELD_LOWER_SNAKE_CASE rule):

$ git diff
diff --git a/proto/bufbuild/semver/v1/semver.proto b/proto/bufbuild/semver/v1/semver.proto
index 1b9c90f..b488a66 100644
--- a/proto/bufbuild/semver/v1/semver.proto
+++ b/proto/bufbuild/semver/v1/semver.proto
@@ -3,7 +3,7 @@ syntax = "proto3";
package bufbuild.semver.v1;
message Version {
- uint64 major = 1;
+ uint64 Major = 1;
uint64 minor = 2;
uint64 micro = 3;
string label = 4;

The buf-lint action will detect the failure and comment in-line like so:

image

With this, the developer can quickly realize their mistake and update their change.

Breaking change detection#

Much like the lint section above, you can use the buf-breaking action to prevent breaking changes in your APIs according to the breaking configuration found in your buf.yaml. We can edit the workflow configuration so that buf-breaking comments in-line like so:

on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v0.1.0
id: setup
with:
version: '0.41.0'
- uses: bufbuild/buf-lint-action@v0.2.0
if: ${{ steps.setup.outcome == 'success' }}
with:
github_token: ${{ github.token }} # Required to write comments in-line.
- uses: bufbuild/buf-breaking-action@v0.2.0
if: ${{ steps.setup.outcome == 'success' }}
env:
BUF_INPUT_HTTPS_USERNAME: ${{ github.actor }}
BUF_INPUT_HTTPS_PASSWORD: ${{ github.token }}
with:
against: 'https://github.com/bufbuild/semver.git#branch=main' # The 'main' branch of the GitHub repository that defines the module.
github_token: ${{ github.token }} # Required to write comments in-line.

The if conditional is adapted so that both the buf-lint and buf-breaking actions are run if the buf-setup action succeeds. The simple success() check is insufficient here because a buf-lint failure would prevent the buf-breaking action from running (assuming the buf-lint action is configured before the buf-breaking job).

If the same scenario illustrated in the lint section above occurs, the buf-breaking action will detect the failure and comment in-line like so:

image

Push#

The buf-push action requires that you have access to the BSR beta. If you haven't already signed up, add yourself to the waitlist here!

Now that you've configured buf-setup, buf-lint and buf-breaking, you can adapt this workflow so that commits merged into the main branch will automatically push module updates to the BSR.

First, you'll need to create a buf API token so that you can write to the BSR. For details on creating a buf API token, please refer to the documentation. Once you've created a buf API token, you'll need to create an encrypted GitHub Secret for it. In the following example, the API token is set to BUF_TOKEN.

We don't want to push module updates to the BSR for every pull request commit, so you'll need to add a separate .github/workflows/push.yaml file that exists alongside the .github/workflows/pull_request.yaml file:

on:
push:
branches:
- main
steps:
- uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v0.1.0
id: setup
with:
version: '0.41.0'
- uses: bufbuild/buf-lint-action@v0.2.0
if: ${{ steps.setup.outcome == 'success' }}
with:
github_token: ${{ github.token }}
- uses: bufbuild/buf-breaking-action@v0.2.0
if: ${{ steps.setup.outcome == 'success' }}
env:
BUF_INPUT_HTTPS_USERNAME: ${{ github.actor }}
BUF_INPUT_HTTPS_PASSWORD: ${{ github.token }}
with:
against: 'https://github.com/bufbuild/semver.git#branch=main'
github_token: ${{ github.token }}
- uses: bufbuild/buf-push-action@v0.2.0
if: success() # Only trigger the 'buf-push-action' if all previous steps succeed.
with:
buf_token: ${{ secrets.BUF_TOKEN }}

With this, the buf-push action will only run if both the buf-lint and buf-breaking jobs are successful, but both buf-lint and buf-breaking will run as long as buf-setup is successful. The module pushed to the BSR is tagged with the git commit SHA so that it's easy to associate the two with one another.

Wrapping up#

Now that you have all of the Buf GitHub Actions configured in your CI/CD pipeline, you'll never have to worry about manual Protobuf maintenance again. If you have any questions or feedback, please reach out in our public Slack channel!