关于 protolint
protolint 是一个可插件扩展的 Protocol Buffer 代码检查与自动修复工具,用于检查 proto2 和 proto3 文件是否符合官方风格指南。它无需编译器即可运行,并可通过注释禁用个别规则,适合需要保持 API 兼容性的项目。
主要功能
- 01检查并自动修复 proto 文件风格问题
- 02无需编译器即可运行
- 03支持通过注释禁用规则
- 04支持加载自定义检查规则插件
- 05提供 protoc 插件与多种编辑器和 CI 集成
适用场景
- 01在 CI 中检查 proto 文件的命名与格式
- 02在保持 API 兼容的前提下逐步修复风格问题
- 03在编辑器中实时检查 proto 文件
MCP 相关
protolint 通过 --mcp 参数提供 MCP 服务支持,使 AI 模型能够直接调用 protolint 检查和修复 proto 文件。
详细介绍
protolint
protolint 是一款适用于 Protocol Buffer 文件(proto2+proto3)的可插拔 lint 和修复工具:
- 无需编译器即可运行,因此速度很快。
- 轻松遵循官方风格指南。规则与风格指南一一对应。
- 修复器会自动修复所有可能的官方风格指南违规问题。
- 支持通过 Protocol Buffer 文件中的注释禁用规则。
- 适用于必须保持 API 兼容性,同时尽可能遵循风格指南的项目。
- 对于一些规则,可以通过在发现违规的位置插入注释来自动禁用。
- 加载插件以包含自定义 lint 规则。
- 所有规则均经过测试。
- 支持多种集成方式。
- protoc 插件
- 编辑器集成
- GitHub Action
- CI 集成
演示
配置好 MCP 服务后,你可以让 Claude Desktop 等 MCP 客户端 lint 并修复 Protocol Buffer 文件,如下所示:
此外,vim-protolint 的工作方式如下。
MCP 服务
protolint 现已支持 Model Context Protocol(MCP),使 AI 模型能够直接与 protolint 交互。
用法
protolint --mcp
有关如何使用和集成 protolint 的 MCP 服务功能的详细文档,请参阅 MCP 文档。
安装
通过 Homebrew
可通过 yoheimuta/protolint tap 使用 Homebrew 在 Mac 或 Linux 上安装 protolint。
brew tap yoheimuta/protolint
brew install protolint
由于 homebrew-core 中包含 protolint,,你也可以直接通过 brew install protolint. 安装。这是默认安装的 tap。这样更简单,但维护者并非同一作者。若要保持更新,建议先运行 brew tap yoheimuta/protolint。
通过 GitHub Releases
你也可以从此发布页面下载预构建二进制文件:
在每个版本的下载区中,你都可以找到 .tar.gz 包中的预构建二进制文件。
使用维护中的 Docker 镜像
protolint 提供 Docker 镜像 yoheimuta/protolint,让你可以在 Docker 工作流中使用 protolint。
❯❯❯ docker run --volume "$(pwd):/workspace" --workdir /workspace yoheimuta/protolint lint _example/proto
[_example/proto/invalidFileName.proto:1:1] File name should be lower_snake_case.proto.
[_example/proto/issue_88/oneof_options.proto:11:5] Found an incorrect indentation style " ". " " is correct.
[_example/proto/issue_88/oneof_options.proto:12:5] Found an incorrect indentation style " ". " " is correct.
从源代码安装
如果有 Go 环境,可以从源代码安装二进制文件。 不过,我建议使用预构建二进制文件,因为从源代码安装的版本不包含版本信息。
go install github.com/yoheimuta/protolint/cmd/protolint@latest
在 JavaScript / TypeScript 项目中
你可以使用 npm 或 yarn 等 Node.js 包管理器来使用 protolint。
$ npm install protolint --save-dev
这会在本地 package.json 中添加一项开发依赖。
安装期间会调用 postinstall.js 脚本。该脚本会从 github 下载匹配的 protolint。和 @electron/get 一样,你可以使用以下环境变量绕过下载:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
| PROTOLINT_MIRROR_HOST | https://github.com | 托管二进制文件的 HTTP/Web 服务器基础 URL |
| PROTOLINT_MIRROR_REMOTE_PATH | yoheimuta/protolint/releases/download | 远程主机上的归档文件路径 |
| PROTOLINT_MIRROR_USERNAME | HTTP Basic 身份验证用户名 | |
| PROTOLINT_MIRROR_PASSWORD | HTTP Basic 身份验证密码 | |
| PROTOLINT_PROXY | 可选带身份验证信息的 HTTP(S) 代理 |
远程路径下必须镜像 releases 页面中的归档文件。
之后,你可以在开发脚本中使用 npx protolint(并传入所有提供的 protolint 参数)。
{
...
"scripts": {
"protoc": "....",
"preprotoc": "npx protolint"
},
...
}
你可以在 package.json 中添加一个 protolint 节点,其中可以包含下方 protolint.yml 的内容,并将其放在 lint 节点下,也就是说,配置的根元素为 protolint。
如果你想获得与 TSC 编译器相匹配的输出,请使用 tsc reporter。
在 Python 项目中
你可以在 Python 项目中将 protolint 用作代码检查工具。pypi 上的 wheel protolint-bin 包含适用于各种平台的预编译二进制文件。只需将所需版本添加到
pyproject.toml 或 requirements.txt 中。
下载的 wheel 将包含编译好的 protolint 和 protoc-gen-protolint Go 二进制文件。你的平台必须
与受支持的二进制文件平台兼容。
你可以将代码检查工具配置添加到 pyproject.toml 中的 tools.protolint 包。
使用方法
protolint lint example.proto example2.proto # file mode, specify multiple specific files
protolint lint . # directory mode, search for all .proto files recursively
protolint . # same as "protolint lint ."
protolint lint -stdin_filename=v1/f.proto - # read from stdin and assume its path is v1/f.proto
protolint lint -config_path=path/to/your_protolint.yaml . # use path/to/your_protolint.yaml
protolint lint -config_dir_path=path/to . # search path/to for .protolint.yaml
protolint lint -fix . # automatically fix some of the problems reported by some rules
protolint lint -fix -auto_disable=next . # this is preferable when you want to fix problems while maintaining the compatibility. Automatically fix some problems and insert disable comments to the other problems. The available values are next and this.
protolint lint -auto_disable=next . # automatically insert disable comments to the other problems.
protolint lint -v . # with verbose output to investigate the parsing error
protolint lint -no-error-on-unmatched-pattern . # exits with success code even if no file is found (file & directory mode)
protolint lint -reporter junit . # output results in JUnit XML format
protolint lint -output_file=path/to/out.txt # output results to path/to/out.txt
protolint lint -plugin ./my_custom_rule1 -plugin ./my_custom_rule2 . # run custom lint rules.
protolint list # list all current lint rules being used
protolint version # print protolint version
protolint --version # print protolint version (global flag)
protolint -v # print protolint version (when used as the only argument)
protolint 默认无需配置,对于大多数项目,开箱即用。
从标准输入进行代码检查
你可以将 - 用作文件路径,从标准输入检查 Protobuf 内容。
这对与脚本或 IDE 集成尤其有用。
cat example.proto | protolint lint -
如果需要提供虚拟文件名以进行配置
解析,或生成更具描述性的错误报告,请使用 -stdin_filename
标志:
cat example.proto | protolint lint -stdin_filename=api/v1/user.proto -
版本控制集成
protolint 可用作 pre-commit 钩子。在仓库的 .pre-commit-config.yaml 中添加以下内容,即可使用 Go 运行 protolint:
repos:
- repo: https://github.com/yoheimuta/protolint
rev: v0.56.3 # Select a release here
hooks:
- id: protolint
如需基于 Docker 执行,请使用 protolint-docker 钩子:
repos:
- repo: https://github.com/yoheimuta/protolint
hooks:
- id: protolint-docker # Uses yoheimuta/protolint:latest by default
重要: 对于 Docker 钩子,根据 pre-commit 的设计,rev 参数不能用于指定版本。若要固定到特定的 protolint 版本,请在 entry 中指定 Docker 镜像标签:
repos:
- repo: https://github.com/yoheimuta/protolint
hooks:
- id: protolint-docker
entry: yoheimuta/protolint:v0.55.6 lint # Pin to specific version
编辑器集成
Visual Studio Code
JetBrains IntelliJ IDEA、GoLand、WebStorm、PHPStorm、PyCharm……
Vim(ALE engine)
Vim(Syntastic)
GitHub Action
一个可在工作流中运行 protolint 的 GitHub Action
CI 集成
Jenkins 插件
- warnings-ng 以及任何使用 violatons-lib 的插件
Linter 聚合器
- MegaLinter 开箱即用地运行 protolint,它还内置了许多其他 linter
特定环境的输出
可以根据 CI/CD 环境的格式设置 lint 输出。必须使用输出格式进行设置。目前支持以下输出:
| 环境 | 命令行值 | 描述 | 示例 |
|---|---|---|---|
| Github Actions | ci-gh | Github 帮助 | ::warning file=example.proto,line=10,col=20,title=ENUM_NAMES_UPPER_CAMEL_CASE::EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES |
| Azure DevOps | ci-az | Azure DevOps 帮助 | ##vso[task.logissue type=warning;sourcepath=example.proto;linenumber=10;columnnumber=20;code=ENUM_NAMES_UPPER_CAMEL_CASE;]EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES |
| Gitlab CI/CD | ci-glab | 根据示例反向推导 | WARNING: ENUM_NAMES_UPPER_CAMEL_CASE example.proto(10,20) : EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES |
也可以使用通用的 ci 格式化程序,它会创建一个通用的问题匹配器。
使用 ci-env 值时,可以通过以下环境变量指定模板:
| 环境变量 | 优先级 | 含义 |
|---|---|---|
| PROTOLINT_CIREPORTER_TEMPLATE_STRING | 1 | 包含 Go-template 的字符串 |
| PROTOLINT_CIREPORTER_TEMPLATE_FILE | 2 | 包含 Go-template 的文件路径 |
不得添加末尾换行符,因为系统会自动添加。
可用字段如下:
Severity
: 严重性字符串(note、warning 或 error)
File
: 包含错误的文件路径
Line
: 包含错误的 file 中的行(起始位置)
Column
: 包含错误的 file 中的列(起始位置)
Rule
: 出错规则的名称
Message
: 描述错误的错误消息
生成输出文件和 CI/CD 错误流
你可以创建与 CI/CD 环境匹配的特定输出,同时也可以生成输出文件,例如供 github CodeQL 或 SonarQube 等静态代码分析工具使用。
添加 --add-reporter 标志即可实现。
请注意,值必须采用 <reporter-name>:<output-file-path> 格式(不包括 < 和 >)。
$ protolint --reporter ci-gh --add-reporter sarif:/path/to/my/output.sarif.json proto/*.proto
用作 protoc 插件
protolint 还维护了一个二进制文件 protoc-gen-protolint,可作为 protoc 插件执行 lint 功能。 详情请参阅 cmd/protoc-gen-protolint/README.md。
如果你已经在使用 protoc 插件工作流,这会很有用。
在 Go 代码中调用
你也可以在 Go 代码中使用 protolint。 详情请参阅 Go 文档 和 lib/lint_test.go。
import (
"bytes"
"github.com/yoheimuta/protolint/lib"
)
func main() {
args := []string{"-config_path", "path/to/your_protolint.yaml", "."}
var stdout bytes.Buffer
var stderr bytes.Buffer
err := lib.Lint(args, &stdout, &stderr)
if err != nil {
// Handle error
}
// Process output in stdout and stderr
}
规则
详情请参阅 internal/addon/rules。
规则集遵循以下指南:
- 官方风格指南。默认启用。基本上,这些规则可以通过添加
-fix选项来修复违规项。 - 非官方风格指南。默认禁用。你可以通过
.protolint.yaml启用各项规则。
命令行中的 -fix 选项可以自动修复所有由可修复规则报告的问题。
请参阅下方的 Fixable 列。
命令行中的 -auto_disable 选项可以自动禁用所有由自动禁用规则报告的问题。
当修复现有违规项会破坏兼容性时,此功能很有用。
请参阅下方的 AutoDisable 列。
- *1:这些规则不支持 AutoDisable,因为修复不会破坏兼容性。你应该使用
-fix运行 protolint。 | 官方 | 可修复 | 自动禁用 | ID | 用途 | |----|----|---|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 是 | ✅ | ✅ | ENUM_FIELD_NAMES_PREFIX | 验证枚举字段名称是否以其 ENUM_NAME_UPPER_SNAKE_CASE 为前缀。 | | 是 | ✅ | ✅ | ENUM_FIELD_NAMES_UPPER_SNAKE_CASE | 验证所有枚举字段名称是否均为 CAPITALS_WITH_UNDERSCORES。 | | 是 | ✅ | ✅ | ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH | 验证零值枚举是否应带有指定后缀(例如 "UNSPECIFIED"、"INVALID")。默认后缀为 "UNSPECIFIED"。可通过.protolint.yaml配置具体后缀。 | | 是 | ✅ | ✅ | ENUM_NAMES_UPPER_CAMEL_CASE | 验证所有枚举名称是否均为 CamelCase(首字母大写)。 | | 是 | ✅ | *1 | FILE_NAMES_LOWER_SNAKE_CASE | 验证所有文件名是否均为 lower_snake_case.proto。可通过.protolint.yaml配置要排除的文件。 | | 是 | ✅ | ✅ | FIELD_NAMES_LOWER_SNAKE_CASE | 验证所有字段名称是否均为 underscore_separated_names。 | | 是 | ✅ | *1 | IMPORTS_SORTED | 验证所有导入项是否已排序。 | | 是 | ✅ | ✅ | MESSAGE_NAMES_UPPER_CAMEL_CASE | 验证所有消息名称是否均为 CamelCase(首字母大写)。 | | 是 | ✅ | *1 | ORDER | 验证所有文件是否均按指定方式排序。 | | 是 | ✅ | *1 | PACKAGE_NAME_LOWER_CASE | 验证包名称是否仅包含小写字母。 | | 是 | ✅ | ✅ | RPC_NAMES_UPPER_CAMEL_CASE | 验证所有 rpc 名称是否均为 CamelCase(首字母大写)。 | | 是 | ✅ | ✅ | SERVICE_NAMES_UPPER_CAMEL_CASE | 验证所有服务名称是否均为 CamelCase(首字母大写)。 | | 是 | ✅ | ✅ | REPEATED_FIELD_NAMES_PLURALIZED | 验证重复字段名称是否为复数形式。 | | 是 | ✅ | *1 | QUOTE_CONSISTENT | 验证字符串的引号用法是否一致。默认使用双引号。可通过.protolint.yaml配置具体引号。 | | 是 | ✅ | *1 | INDENT | 强制使用一致的缩进样式。默认样式为 2 个空格。默认情况下,也会强制插入适当的换行符。可通过.protolint.yaml配置详细信息。 | | 是 | ✅ | *1 | PROTO3_FIELDS_AVOID_REQUIRED | 验证 proto3 的所有字段是否均未使用 required。 | | 是 | _ | ✅ | PROTO3_GROUPS_AVOID | 验证 proto3 是否避免使用所有 group。 | | 是 | _ | *1 | MAX_LINE_LENGTH | 强制限制最大行长度。行长度定义为该行中的 Unicode 字符数。默认值为 80 个字符。可通过.protolint.yaml配置详细信息。 | | 否 | _ | - | SERVICE_NAMES_END_WITH | 强制服务名称使用一致的后缀。可通过.protolint.yaml配置具体后缀。 | | 否 | _ | - | FIELD_NAMES_EXCLUDE_PREPOSITIONS | 验证所有字段名称是否均不包含介词(例如 "for"、"during"、"at")。可通过.protolint.yaml配置具体的介词和排除的关键词。 | | 否 | _ | - | MESSAGE_NAMES_EXCLUDE_PREPOSITIONS | 验证所有消息名称是否均不包含介词(例如 "With"、"For")。可通过.protolint.yaml配置具体的介词和排除的关键词。 | | 否 | _ | - | RPC_NAMES_CASE | 验证所有 rpc 名称是否符合指定的命名约定。需要通过.protolint.yaml配置具体约定。 | | 否 | _ | - | MESSAGES_HAVE_COMMENT | 验证所有消息是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | SERVICES_HAVE_COMMENT | 验证所有服务是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | RPCS_HAVE_COMMENT | 验证所有 rps 是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | FIELDS_HAVE_COMMENT | 验证所有字段是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | ENUMS_HAVE_COMMENT | 验证所有枚举是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | ENUM_FIELDS_HAVE_COMMENT | 验证所有枚举字段是否都有注释。可通过配置强制使用 Golang 风格的注释。 | | 否 | _ | - | FILE_HAS_COMMENT | 验证文件是否以文档注释开头。 | | 否 | _ | - | SYNTAX_CONSISTENT | 验证语法是否为指定版本。默认版本为 proto3。可通过.protolint.yaml配置版本。 | | 否 | _ | - | FIELD_NUMBERS_ORDER_ASCENDING | 验证字段顺序是否为升序。对于枚举,遵循option allow_alias = true;,允许相邻字段使用相同编号(非递减)。 | 我建议你在.protolint.yaml中添加all_default: true,因为上面的所有 linter 都会自动启用,这样每当 protolint 更新时,你都能享受到最大的好处。
以下是一些示例,展示了默认启用的良好风格。
- 表示不良风格,+ 表示良好风格:
ENUM_FIELD_NAMES_PREFIX
enum FooBar {
- UNSPECIFIED = 0;
+ FOO_BAR_UNSPECIFIED = 0;
}
ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
enum Foo {
- firstValue = 0;
+ FIRST_VALUE = 0;
- second_value = 1;
+ SECOND_VALUE = 1;
}
ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
enum Foo {
- FOO_FIRST = 0;
+ FOO_UNSPECIFIED = 0;
}
ENUM_NAMES_UPPER_CAMEL_CASE
- enum foobar {
+ enum FooBar {
FIRST_VALUE = 0;
SECOND_VALUE = 1;
}
FIELD_NAMES_LOWER_SNAKE_CASE
message SongServerRequest {
- required string SongName = 1;
+ required string song_name = 1;
}
IMPORTS_SORTED
- import public "new.proto";
+ import "myproject/other_protos.proto";
- import "myproject/other_protos.proto";
+ import public "new.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
MESSAGE_NAMES_UPPER_CAMEL_CASE
- message song_server_request {
+ message SongServerRequest {
required string SongName = 1;
required string song_name = 1;
}
ORDER
- option java_package = "com.example.foo";
- syntax = "proto3";
- package examplePb;
- message song_server_request { }
- import "other.proto";
+ syntax = "proto3";
+ package examplePb;
+ import "other.proto";
+ option java_package = "com.example.foo";
+ message song_server_request { }
PACKAGE_NAME_LOWER_CASE
- package myPackage
+ package my.package
RPC_NAMES_UPPER_CAMEL_CASE
service FooService {
- rpc get_something(FooRequest) returns (FooResponse);
+ rpc GetSomething(FooRequest) returns (FooResponse);
}
RPC_NAMES_UPPER_CAMEL_CASE
- service foo_service {
+ service FooService {
rpc get_something(FooRequest) returns (FooResponse);
rpc GetSomething(FooRequest) returns (FooResponse);
}
REPEATED_FIELD_NAMES_PLURALIZED
- repeated string song_name = 1;
+ repeated string song_names = 1;
INDENT
enum enumAllowingAlias {
UNKNOWN = 0;
- option allow_alias = true;
+ option allow_alias = true;
STARTED = 1;
- RUNNING = 2 [(custom_option) = "hello world"];
+ RUNNING = 2 [(custom_option) = "hello world"];
- }
+}
- message TestMessage { string test_field = 1; }
+ message TestMessage {
+ string test_field = 1;
+}
QUOTE_CONSISTENT
option java_package = "com.example.foo";
- option go_package = 'example';
+ option go_package = "example";
创建自定义规则
protolint 是可插拔的 linter,你可以自由创建自定义 lint 规则。
此仓库在 _example/plugin 目录中提供了一个完整的示例项目(即插件)。
报告器
protolint 内置了多个报告器(即格式化程序),用于控制 lint 结果的显示方式。
你可以在命令行中使用 -reporter 标志指定报告器。例如,-reporter junit 使用 junit 报告器。
内置的报告器选项如下:
- plain(默认)
- junit
- json
- sarif
- sonar(SonarQube 通用问题格式)
- unix
- tsc(与 TypeScript 编译器兼容)
配置
在 Protocol Buffer 文件中禁用规则
可以在 Protocol Buffer 文件中使用以下格式的注释禁用规则。 规则会一直处于禁用状态,直到文件末尾,或 linter 遇到匹配的启用注释:
// protolint:disable <ruleID1> [<ruleID2> <ruleID3>...]
...
// protolint:enable <ruleID1> [<ruleID2> <ruleID3>...]
还可以在 disable 命令后附加 :next 或 :this,使该命令仅应用于当前行或下一行。
例如:
enum Foo {
// protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
firstValue = 0; // no error
second_value = 1; // protolint:disable:this ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
THIRD_VALUE = 2; // spits out an error
}
将命令行选项 -auto_disable 设置为 next 或 this 后,每当发现问题时都会插入禁用命令。
你也可以同时指定 -fix 选项。对于支持 auto_disable 的规则,如果修复违规会导致架构不兼容,它们会抑制违规,而不是进行修复。
配置文件
protolint 可以使用名为 .protolint.yaml 的配置文件运行。
配置文件规范请参阅 _example/config/.protolint.yaml。
默认情况下,protolint 会自动在当前工作目录中查找配置文件,
然后依次向上查找各级父目录,直到文件系统根目录。
你也可以通过 -config_dir_path 标志在指定目录中查找。
还可以通过 --config_path 标志指定要查找的文件。
退出码
lint 文件时,protolint 会返回以下退出码之一:
0:lint 成功,且没有 lint 错误。1:lint 成功,但至少存在一个 lint 错误。2:lint 未成功,原因是其他错误,例如解析错误、内部错误和运行时错误。
设计动机
截至 2018/12/20,已经存在类似的 protobuf linter。
一种是 Google Protocol Buffers 编译器的插件。
- 如果你只想 lint 文件,创建编译环境可能会很繁琐。
- 而且,与解析文件相比,编译文件通常需要花费大量时间。
另一种是用于 lint Protocol Buffer 文件的命令行工具。
- 虽然它除了 lint 之外还有很多功能,但对于只想使用 linter 的用户来说似乎过于繁杂。
- lint 规则倾向于带有强烈的主观性。
- 此外,规则集与官方风格指南并不完全对应。用户需要详细了解规则和指南,再准确地组合这些规则。
其他工具
我在 2019/12/17 撰写了一篇文章,对比了各种 Protocol Buffer Linter,其中包括 protolint。
- https://qiita.com/yoheimuta/items/da7678fcd046b93a2637
- 注意:这篇文章是用日语写的。
依赖项
开发
发布
为了简化发布流程并减少人为错误,仓库中包含一个 release.sh 脚本。此脚本会自动执行创建并推送新发布标签所需的步骤。
使用方法
运行以下命令以创建新发布:
bash release.sh <version> [message]
推送标签只是第一步 — CI 会据此发布到 GitHub Releases、npm、PyPI、Maven Central、Docker Hub 和 Homebrew。有关如何验证各个渠道,请参阅 CONTRIBUTING.md。
许可证
MIT 许可证(MIT)
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。