As more and more developers adopt Protobuf in their projects, it becomes increasingly important to ensure that the code is clean, consistent, and easy to read. This is where linting and formatting come in.
Linting and formatting tools help to maintain the quality of the code by enforcing a set of rules for style, syntax, and best practices. They can catch errors early, make the code easier to understand, and reduce the amount of manual code review required. In this how-to guide, we will explore how to set up linting and formatting for Protobuf in order to maintain code quality and consistency in your projects.
We recommend completing the Tour of Buf for an introduction to Protobuf linting with the
buf lint
command.
1. Define a module
To get started linting your Protobuf sources, create a
Buf module by adding a
buf.yaml
file to the root of the directory
that holds your Protobuf definitions. You can create the default buf.yaml
file
with this command:
$ buf mod init
That creates this file:
version: v1
breaking:
use:
- FILE
lint:
use:
- DEFAULT
As you can see, the default configuration applies the
DEFAULT
rules.
2. Run lint & format
You can run buf lint
on your module by specifying the filepath to the
directory containing the buf.yaml
. It uses the current directory by default,
so you can target the input defined in the current
directory with this command:
$ buf lint
The buf lint
command performs these actions in order:
- Discovers all of the Protobuf files per your
buf.yaml
configuration. - Copies them into memory.
- Compiles them
- Runs the compilation result against the configured lint rules.
For a more practical look at linting Protobuf sources with Buf, see the linting
example project.
Error syntax
Any lint errors discovered are printed out in this format:
Here's a full example output:
$ buf lint
Outputgoogle/type/datetime.proto:17:1:Package name "google.type" should be suffixed with a correctly formed version, such as "google.type.v1". pet/v1/pet.proto:42:10:Field name "petID" should be lower_snake_case, such as "pet_id". pet/v1/pet.proto:47:9:Service name "PetStore" should be suffixed with "Service".
2.1. JSON output
You can print lint output as JSON:
$ buf lint --error-format=json
Output{"path":"google/type/datetime.proto","start_line":17,"start_column":1,"end_line":17,"end_column":21,"type":"PACKAGE_VERSION_SUFFIX","message":"Package name \"google.type\" should be suffixed with a correctly formed version, such as \"google.type.v1\"."} {"path":"pet/v1/pet.proto","start_line":42,"start_column":10,"end_line":42,"end_column":15,"type":"FIELD_LOWER_SNAKE_CASE","message":"Field name \"petID\" should be lower_snake_case, such as \"pet_id\"."} {"path":"pet/v1/pet.proto","start_line":47,"start_column":9,"end_line":47,"end_column":17,"type":"SERVICE_SUFFIX","message":"Service name \"PetStore\" should be suffixed with \"Service\"."}
2.2. Copy errors into your configuration
We can output errors in a format that you can copy into your
buf.yaml
configuration file. This enables
you to ignore specific lint errors and gradually correct them over time:
$ buf lint --error-format=config-ignore-yaml
Outputversion: v1 lint: ignore_only: FIELD_LOWER_SNAKE_CASE: - pet/v1/pet.proto PACKAGE_VERSION_SUFFIX: - google/type/datetime.proto SERVICE_SUFFIX: - pet/v1/pet.proto
2.3. Automatically format your .proto
files
The buf format
command rewrites .proto
files in-place according to an opinionated style. Rewrite
the file(s) in-place with -w
. For example,
# Rewrite a single file in-place
$ buf format -w
3. Common use cases
buf
can lint inputs beyond your local Protobuf
files, such as Git repositories and
tarballs. This can be useful in a variety of
scenarios, such as using protoc output as buf
input. Here are some examples
script:
# Lint output from protoc passed to stdin.
protoc -I . --include_source_info $(find . -name '*.proto') -o /dev/stdout | buf lint -
# Lint a remote git repository on the fly and override the config to be your local config file.
buf lint 'https://github.com/googleapis/googleapis.git' --config buf.yaml
# Lint a module published to the Buf Schema Registry.
buf lint buf.build/acme/petapis
For remote locations that require authentication, see HTTPS Authentication and SSH Authentication.
4. Limit to specific files
By default, the buf
CLI builds all files under your
buf.yaml
configuration file. But you can
optionally lint only specific files or directories. This is an advanced feature
that's mostly intended to be used by other systems, like editors. In general,
it's better to let the buf
CLI discover all files and handle this for you. But
if you do need this, you can use the --path
flag:
$ buf lint \
--path path/to/foo.proto \
--path path/to/bar.proto
You can also combine this with an in-line configuration override:
$ buf lint \
--path path/to/foo.proto \
--path path/to/bar.proto \
--config '{"version":"v1","lint":{"use":["BASIC"]}}'
5. Docker
Buf ships a Docker image, bufbuild/buf
, that enables you to use buf
as part of your Docker workflow. Here's an example:
$ docker run \
--volume "$(pwd):/workspace" \
--workdir /workspace \
bufbuild/buf lint
Conclusion
In conclusion, linting your Protobuf sources with Buf is an important step in ensuring the consistency and correctness
of your Protobuf definitions. By defining a Buf module and running buf lint, you can quickly identify and correct issues
with your Protobuf files, such as field naming conventions, package name suffixes, and more. You can also use the buf
format command to automatically rewrite your .proto
files according to an opinionated style. By following the steps
outlined in this guide, you can improve the quality of your Protobuf definitions and avoid errors and inconsistencies
in your APIs.
Next steps
Check out our style guide for a greater understanding of Protobuf development best practices.