diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-05-02 10:10:03 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-05-06 11:44:31 +0300 |
commit | 3f30f8999301a6e60b1c39f6bea739b943a5402f (patch) | |
tree | 1fb3c9f3aab22efa3aa7ec7ba63dec23902c0935 | |
parent | bd0172ed0bdbb8779aa1d44bc1076f39d8c4e953 (diff) |
protoc-gen-gitaly: Split up plugin by functionality
The `protoc-gen-gitaly` plugin provides two different functionalities:
- It lints our Protobuf definitions to make sure they conform to our
coding guidelines.
- It generates a `protolist.go` file, which contains a list of all
Protobuf files.
This is conflating two otherwise unrelated concerns, and thus makes the
divide between our `proto` and `lint-proto` Makefile targets a bit
blurry. Furthermore, it is creating a cyclic dependency: the linting
logic requires that the Protobuf files must have already been generated,
but we can only generate them when we have plugin compiled.
Prepare for a fix by splitting up functionality of this plugin into two
separate plugins: `protoc-gen-gitaly-lint` is responsible for linting
our definitions, while `protoc-gen-gitaly-protolist` will generate the
list of Protobuf files.
-rw-r--r-- | Makefile | 38 | ||||
-rw-r--r-- | proto/go/internal/cmd/protoc-gen-gitaly-lint/main.go | 108 | ||||
-rw-r--r-- | proto/go/internal/cmd/protoc-gen-gitaly-protolist/main.go (renamed from proto/go/internal/cmd/protoc-gen-gitaly/main.go) | 93 |
3 files changed, 138 insertions, 101 deletions
@@ -45,18 +45,19 @@ GIT_PREFIX ?= ${GIT_DEFAULT_PREFIX} FIPS_MODE ?= 0 # Tools -GIT := $(shell command -v git) -GOIMPORTS := ${TOOLS_DIR}/goimports -GOFUMPT := ${TOOLS_DIR}/gofumpt -GOLANGCI_LINT := ${TOOLS_DIR}/golangci-lint -PROTOLINT := ${TOOLS_DIR}/protolint -GO_LICENSES := ${TOOLS_DIR}/go-licenses -PROTOC := ${TOOLS_DIR}/protoc -PROTOC_GEN_GO := ${TOOLS_DIR}/protoc-gen-go -PROTOC_GEN_GO_GRPC:= ${TOOLS_DIR}/protoc-gen-go-grpc -PROTOC_GEN_GITALY := ${TOOLS_DIR}/protoc-gen-gitaly -GOTESTSUM := ${TOOLS_DIR}/gotestsum -GOCOVER_COBERTURA := ${TOOLS_DIR}/gocover-cobertura +GIT := $(shell command -v git) +GOIMPORTS := ${TOOLS_DIR}/goimports +GOFUMPT := ${TOOLS_DIR}/gofumpt +GOLANGCI_LINT := ${TOOLS_DIR}/golangci-lint +PROTOLINT := ${TOOLS_DIR}/protolint +GO_LICENSES := ${TOOLS_DIR}/go-licenses +PROTOC := ${TOOLS_DIR}/protoc +PROTOC_GEN_GO := ${TOOLS_DIR}/protoc-gen-go +PROTOC_GEN_GO_GRPC := ${TOOLS_DIR}/protoc-gen-go-grpc +PROTOC_GEN_GITALY_LINT := ${TOOLS_DIR}/protoc-gen-gitaly-lint +PROTOC_GEN_GITALY_PROTOLIST := ${TOOLS_DIR}/protoc-gen-gitaly-protolist +GOTESTSUM := ${TOOLS_DIR}/gotestsum +GOCOVER_COBERTURA := ${TOOLS_DIR}/gocover-cobertura # Tool options GOLANGCI_LINT_OPTIONS ?= @@ -439,11 +440,11 @@ cover: prepare-tests libgit2 ${GOCOVER_COBERTURA} .PHONY: proto ## Regenerate protobuf definitions. -proto: SHARED_PROTOC_OPTS = --plugin=${PROTOC_GEN_GO} --plugin=${PROTOC_GEN_GO_GRPC} --plugin=${PROTOC_GEN_GITALY} --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative -proto: ${PROTOC} ${PROTOC_GEN_GO} ${PROTOC_GEN_GO_GRPC} ${PROTOC_GEN_GITALY} ${SOURCE_DIR}/.ruby-bundle +proto: SHARED_PROTOC_OPTS = --plugin=${PROTOC_GEN_GO} --plugin=${PROTOC_GEN_GO_GRPC} --plugin=${PROTOC_GEN_GITALY_LINT} --plugin=${PROTOC_GEN_GITALY_PROTOLIST} --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative +proto: ${PROTOC} ${PROTOC_GEN_GO} ${PROTOC_GEN_GO_GRPC} ${PROTOC_GEN_GITALY_LINT} ${PROTOC_GEN_GITALY_PROTOLIST} ${SOURCE_DIR}/.ruby-bundle ${Q}mkdir -p ${SOURCE_DIR}/proto/go/gitalypb ${Q}rm -f ${SOURCE_DIR}/proto/go/gitalypb/*.pb.go - ${PROTOC} ${SHARED_PROTOC_OPTS} -I ${SOURCE_DIR}/proto -I ${PROTOC_INSTALL_DIR}/include --go_out=${SOURCE_DIR}/proto/go/gitalypb --gitaly_out=proto_dir=${SOURCE_DIR}/proto,gitalypb_dir=${SOURCE_DIR}/proto/go/gitalypb:${SOURCE_DIR} --go-grpc_out=${SOURCE_DIR}/proto/go/gitalypb ${SOURCE_DIR}/proto/*.proto + ${PROTOC} ${SHARED_PROTOC_OPTS} -I ${SOURCE_DIR}/proto -I ${PROTOC_INSTALL_DIR}/include --go_out=${SOURCE_DIR}/proto/go/gitalypb --gitaly-protolist_out=proto_dir=${SOURCE_DIR}/proto,gitalypb_dir=${SOURCE_DIR}/proto/go/gitalypb:${SOURCE_DIR} --gitaly-lint_out=${SOURCE_DIR} --go-grpc_out=${SOURCE_DIR}/proto/go/gitalypb ${SOURCE_DIR}/proto/*.proto ${SOURCE_DIR}/_support/generate-proto-ruby @ # this part is related to the generation of sources from testing proto files ${PROTOC} ${SHARED_PROTOC_OPTS} -I ${SOURCE_DIR}/internal --go_out=${SOURCE_DIR}/internal --go-grpc_out=${SOURCE_DIR}/internal ${SOURCE_DIR}/internal/praefect/grpc-proxy/testdata/test.proto @@ -605,8 +606,11 @@ ${TOOLS_DIR}/%: GOBIN = ${TOOLS_DIR} ${TOOLS_DIR}/%: ${TOOLS_DIR}/%.version ${Q}go install ${TOOL_PACKAGE}@${TOOL_VERSION} -${PROTOC_GEN_GITALY}: proto | ${TOOLS_DIR} - ${Q}go build -o $@ ${SOURCE_DIR}/proto/go/internal/cmd/protoc-gen-gitaly +${PROTOC_GEN_GITALY_LINT}: proto | ${TOOLS_DIR} + ${Q}go build -o $@ ${SOURCE_DIR}/proto/go/internal/cmd/protoc-gen-gitaly-lint + +${PROTOC_GEN_GITALY_PROTOLIST}: | ${TOOLS_DIR} + ${Q}go build -o $@ ${SOURCE_DIR}/proto/go/internal/cmd/protoc-gen-gitaly-protolist # External tools ${GOCOVER_COBERTURA}: TOOL_PACKAGE = github.com/t-yuki/gocover-cobertura diff --git a/proto/go/internal/cmd/protoc-gen-gitaly-lint/main.go b/proto/go/internal/cmd/protoc-gen-gitaly-lint/main.go new file mode 100644 index 000000000..e0756ceca --- /dev/null +++ b/proto/go/internal/cmd/protoc-gen-gitaly-lint/main.go @@ -0,0 +1,108 @@ +// Command protoc-gen-gitaly-lint is designed to be used as a protobuf compiler +// plugin to verify Gitaly processes are being followed when writing RPC's. +// +// Usage +// +// The protoc-gen-gitaly linter can be chained into any protoc workflow that +// requires verification that Gitaly RPC guidelines are followed. Typically +// this can be done by adding the following argument to an existing protoc +// command: +// +// --gitaly_lint_out=. +// +// For example, you may add the linter as an argument to the command responsible +// for generating Go code: +// +// protoc --go_out=. --gitaly_lint_out=. *.proto +// +// Or, you can run the Gitaly linter by itself. To try out, run the following +// command while in the project root: +// +// protoc --gitaly_lint_out=. ./go/internal/linter/testdata/incomplete.proto +// +// You should see some errors printed to screen for improperly written +// RPC's in the incomplete.proto file. +// +// Prerequisites +// +// The protobuf compiler (protoc) can be obtained from the GitHub page: +// https://github.com/protocolbuffers/protobuf/releases +// +// Background +// +// The protobuf compiler accepts plugins to analyze protobuf files and generate +// language specific code. +// +// These plugins require the following executable naming convention: +// +// protoc-gen-$NAME +// +// Where $NAME is the plugin name of the compiler desired. The protobuf compiler +// will search the PATH until an executable with that name is found for a +// desired plugin. For example, the following protoc command: +// +// protoc --gitaly_lint_out=. *.proto +// +// The above will search the PATH for an executable named protoc-gen-gitaly-lint +// +// The plugin accepts a protobuf message in STDIN that describes the parsed +// protobuf files. A response is sent back on STDOUT that contains any errors. +package main + +import ( + "fmt" + "io" + "log" + "os" + "strings" + + "gitlab.com/gitlab-org/gitaly/v14/proto/go/internal/linter" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/pluginpb" +) + +func main() { + data, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("reading input: %s", err) + } + + req := &pluginpb.CodeGeneratorRequest{} + + if err := proto.Unmarshal(data, req); err != nil { + log.Fatalf("parsing input proto: %s", err) + } + + if err := lintProtos(req); err != nil { + log.Fatal(err) + } +} + +func lintProtos(req *pluginpb.CodeGeneratorRequest) error { + var errMsgs []string + for _, pf := range req.GetProtoFile() { + errs := linter.LintFile(pf, req) + for _, err := range errs { + errMsgs = append(errMsgs, err.Error()) + } + } + + resp := &pluginpb.CodeGeneratorResponse{} + + if len(errMsgs) > 0 { + errMsg := strings.Join(errMsgs, "\n\t") + resp.Error = &errMsg + } + + // Send back the results. + data, err := proto.Marshal(resp) + if err != nil { + return fmt.Errorf("failed to marshal output proto: %s", err) + } + + _, err = os.Stdout.Write(data) + if err != nil { + return fmt.Errorf("failed to write output proto: %s", err) + } + return nil +} diff --git a/proto/go/internal/cmd/protoc-gen-gitaly/main.go b/proto/go/internal/cmd/protoc-gen-gitaly-protolist/main.go index efe8a312e..7e775360e 100644 --- a/proto/go/internal/cmd/protoc-gen-gitaly/main.go +++ b/proto/go/internal/cmd/protoc-gen-gitaly-protolist/main.go @@ -1,53 +1,12 @@ -/*Command protoc-gen-gitaly is designed to be used as a protobuf compiler -plugin to verify Gitaly processes are being followed when writing RPC's. - -Usage - -The protoc-gen-gitaly linter can be chained into any protoc workflow that -requires verification that Gitaly RPC guidelines are followed. Typically -this can be done by adding the following argument to an existing protoc -command: - - --gitaly_out=. - -For example, you may add the linter as an argument to the command responsible -for generating Go code: - - protoc --go_out=. --gitaly_out=. *.proto - -Or, you can run the Gitaly linter by itself. To try out, run the following -command while in the project root: - - protoc --gitaly_out=. ./go/internal/linter/testdata/incomplete.proto - -You should see some errors printed to screen for improperly written -RPC's in the incomplete.proto file. - -Prerequisites - -The protobuf compiler (protoc) can be obtained from the GitHub page: -https://github.com/protocolbuffers/protobuf/releases - -Background - -The protobuf compiler accepts plugins to analyze protobuf files and generate -language specific code. - -These plugins require the following executable naming convention: - - protoc-gen-$NAME - -Where $NAME is the plugin name of the compiler desired. The protobuf compiler -will search the PATH until an executable with that name is found for a -desired plugin. For example, the following protoc command: - - protoc --gitaly_out=. *.proto - -The above will search the PATH for an executable named protoc-gen-gitaly - -The plugin accepts a protobuf message in STDIN that describes the parsed -protobuf files. A response is sent back on STDOUT that contains any errors. -*/ +// Command protoc-gen-gitaly-protolist is designed to be used as a protobuf compiler to generate a +// list of protobuf files via a publicly accessible variable. +// +// This plugin can be accessed by invoking the protoc compiler with the following arguments: +// +// protoc --plugin=protoc-gen-gitaly-protolist --gitaly_protolist_out=. +// +// The plugin accepts a protobuf message in STDIN that describes the parsed protobuf files. A +// response is sent back on STDOUT that contains any errors. package main import ( @@ -61,7 +20,6 @@ import ( "strings" "text/template" - "gitlab.com/gitlab-org/gitaly/v14/proto/go/internal/linter" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/pluginpb" ) @@ -83,10 +41,6 @@ func main() { log.Fatalf("parsing input proto: %s", err) } - if err := lintProtos(req); err != nil { - log.Fatal(err) - } - if err := generateProtolistGo(req); err != nil { log.Fatal(err) } @@ -109,35 +63,6 @@ func parseArgs(argString string) (gitalyProtoDir string, gitalypbDir string) { return gitalyProtoDir, gitalypbDir } -func lintProtos(req *pluginpb.CodeGeneratorRequest) error { - var errMsgs []string - for _, pf := range req.GetProtoFile() { - errs := linter.LintFile(pf, req) - for _, err := range errs { - errMsgs = append(errMsgs, err.Error()) - } - } - - resp := &pluginpb.CodeGeneratorResponse{} - - if len(errMsgs) > 0 { - errMsg := strings.Join(errMsgs, "\n\t") - resp.Error = &errMsg - } - - // Send back the results. - data, err := proto.Marshal(resp) - if err != nil { - return fmt.Errorf("failed to marshal output proto: %s", err) - } - - _, err = os.Stdout.Write(data) - if err != nil { - return fmt.Errorf("failed to write output proto: %s", err) - } - return nil -} - func generateProtolistGo(req *pluginpb.CodeGeneratorRequest) error { var err error gitalyProtoDir, gitalypbDir := parseArgs(req.GetParameter()) |