June 20, 2022
1 min read

Bazel rules

Today we're excited to release Bazel rules for buf, rules_buf.

Bazel is a scalable multi-language build tool for large projects that focuses on reproducibility and speed. This resonates strongly at Buf where we help teams develop robust, language agnostic, scalable APIs.

The rules work alongside the proto_library rule of rules_proto.

Managing dependencies

The buf_dependencies repository rule helps manage Protobuf dependencies using the Buf Schema Registry (BSR). Here's a small example that demonstrates using the rule to gather dependencies into a target called buf_deps.

load("@rules_buf//buf:defs.bzl", "buf_dependencies")

buf_dependencies(
    name = "buf_deps",
    modules = [
        "buf.build/envoyproxy/protoc-gen-validate:dc09a417d27241f7b069feae2cd74a0e",
        "buf.build/acme/petapis:84a33a06f0954823a6f2a089fb1bb82e",
    ],
)

This can now be used as part of the deps attribute in the proto_library rule like this,

load("@rules_proto//proto:defs.bzl", "proto_library")

proto_library(
    name = "foo_proto",
    srcs = [
        "pet.proto" # imports "validate/validate.proto"
    ],
    deps = [
        "@buf_deps//validate:validate_proto"
    ],
)

You can read more about this in the docs.

Lint and Breaking Checks

buf lint and buf breaking are widely used to ensure quality standards of Protobuf APIs are met. Now, you can use them within Bazel. Here's an example,

Breaking rule

load("@rules_buf//buf:defs.bzl", "buf_breaking_test")

exports_files(["buf.yaml"])

buf_breaking_test(
    name = "foo_proto_breaking",
    against = "//:image.bin", # The Image file to check against.
    targets = ["//foo/v1:foo_proto"], # The Protobuf library targets to check
    config = ":buf.yaml",
)

Running bazel test --test_output=errors //:foo_proto_breaking will print the errors,

...
Executing tests from //:foo_proto_breaking
-----------------------------------------------------------------------------
--buf-plugin_out: foo/v1/foo.proto:1:1:Field "3" with name "Bar" on message "Foo" changed option "json_name" from "bar" to "Bar".
foo/v1/foo.proto:1:1:Field "3" on message "Foo" changed name from "bar" to "Bar".

Lint rule

load("@rules_buf//buf:defs.bzl", "buf_lint_test")
load("@rules_proto//proto:defs.bzl", "proto_library")

proto_library(
    name = "foo_proto",
    srcs = ["foo.proto"],
)

buf_lint_test(
    name = "foo_proto_lint",
    targets = [":foo_proto"],
    config = "//:buf.yaml",
)

Running bazel test --test_output=errors //foo/v1:foo_proto_lint will print the errors,

...
Executing tests from //foo/v1:foo_proto_lint
-----------------------------------------------------------------------------
--buf-plugin_out: foo/v1/foo.proto:1:1:Field name "Bar" should be lower_snake_case, such as "bar".

For more on these rules check out the docs for buf_lint_test and buf_breaking_test

Generate rules

At Buf we are strong believers of automation and hence we couldn't resist developing a Gazelle extension. The extension can be used to generate all of above rules! It understands both buf.work.yaml and buf.yaml configuration files.

Check out the Gazelle section of the docs for setup and usage instructions.

Next steps

Get started by setting up rules_buf or check out the examples illustrating various scenarios.

Talk to our team about making your Protocol Buffers workflow easier.