Linting tools help to maintain the quality of 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.
Buf offers linting for Protobuf files via the buf lint
command in the Buf CLI. In this tutorial, we'll explore how to
set up linting for Protobuf files to maintain code quality and consistency in your projects.
We recommend completing the Buf CLI tour for an
introduction to Protobuf linting with the buf lint
command.
1. Define a module
To lint your Protobuf sources, first initialize a Buf module. Modules identify a collection of Protobuf files for Buf to treat as a unit. Run the following command in the root of the directory that holds your Protobuf definitions.
$ buf mod init
That creates a buf.yaml
file with some basic defaults:
version: v1
breaking:
use:
- FILE
lint:
use:
- DEFAULT
As you can see, the lint configuration applies the DEFAULT
rules.
2. Run lint
Run buf lint
on your module by specifying the filepath to the
directory containing the buf.yaml
file. 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
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 example
scripts:
# 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
6. Editor integration
The machine readable error output integrates with IDEs, scripts, and other tools. Currently, we provide editor integration for Vim, Visual Studio Code, and JetBrains IDEs, and we may support other editors in the future.
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. 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.