A toolchain for working with Protocol Buffers in Go. It ships two buf-compatible
plugins:
protoc-gen-labset— aprotoccodegen plugin. It selects a generator bymodeparameter, so a single binary can host several code generators.labset-lint-plugin— abufplugincheck plugin that contributes custom lint rules tobuf lint.
- install with
go install
go install github.com/labset/protobuf-toolchain/cmd/protoc-gen-labset@latest
go install github.com/labset/protobuf-toolchain/cmd/labset-lint-plugin@latest- install with mise (via the
gobackend)
mise use "go:github.com/labset/protobuf-toolchain/cmd/protoc-gen-labset@latest"
mise use "go:github.com/labset/protobuf-toolchain/cmd/labset-lint-plugin@latest"or pin them in a project's mise.toml:
[tools]
"go:github.com/labset/protobuf-toolchain/cmd/protoc-gen-labset" = "latest"
"go:github.com/labset/protobuf-toolchain/cmd/labset-lint-plugin" = "latest"- codegen with
protoc-gen-labset, selecting a generator with themodeoption:
# buf.gen.yaml
version: v2
plugins:
- local: protoc-gen-labset
out: gen
opt: mode=proto-service- linting with
labset-lint-plugin:
# buf.yaml
version: v2
plugins:
- plugin: labset-lint-plugin
lint:
use:
- STANDARDprotoc-gen-labset hosts several generators behind the mode option. Most are
driven by the (labset.plugin.v1.message) annotation — a role plus the CRUD
operations to expose:
// projectmanagement/v1/project.proto
syntax = "proto3";
package projectmanagement.v1;
import "labset/plugin/v1/entity.proto";
import "labset/plugin/v1/options.proto";
message Project {
option (labset.plugin.v1.message) = {
role: ROLE_ENTITY
operations: [OPERATION_CREATE, OPERATION_READ, OPERATION_UPDATE, OPERATION_DELETE, OPERATION_LIST]
};
labset.plugin.v1.Entity entity = 1;
string name = 2;
}mode |
generates | docs |
|---|---|---|
proto-service |
a CRUD service split across proto files, per annotated entity | read the docs |
go-sqlc-atlas |
a Postgres schema, sqlc queries and the sqlc/Atlas config, per annotated entity | read the docs |
labset-lint-plugin contributes rules that keep entity annotations well-formed.
They are on by default once the plugin is wired into buf.yaml:
LABSET_ENTITY_ANNOTATION_ROOT_ONLY— the(labset.plugin.v1.message)role/operations annotation may only be applied to a top-level message. A nested message cannot be referenced by an unqualified name from generated files, so the codegen silently skips it; this rule surfaces the misplacement instead.LABSET_ENTITY_EMBEDDED_FIELD— aROLE_ENTITYmessage must embedlabset.plugin.v1.Entityat field number 1.Entitycarries the sharedidand thecreated_at/updated_at/deleted_atlifecycle timestamps:
message Project {
option (labset.plugin.v1.message) = {
role: ROLE_ENTITY
operations: [OPERATION_CREATE]
};
labset.plugin.v1.Entity entity = 1; // id + lifecycle timestamps
string name = 2;
}Local setup, project layout, how to add a generator or lint rule, and the release process live in the contributing guide.