Published on

BufbuildとProtocについて

Authors

ここに記載されていること一覧

  • buf とは何か
  • Directory 構成
  • 秘伝の Makefile
  • コンパイル用の Dockerfile
  • Go file の生成
  • JS, TS file の生成
  • Doc file の生成

詰まる点は、コンパイルをするために、tools/tools.go を生成したり、buf から吐き出した image を protoc にどう渡すかだと思います。

buf とは

Bufbuild/bufとは、Uber が作成していたuber/prototoolなどの Protobuf の問題を解決しようとしているツールです。また、prototool などの protoc サポートツールなどを、Bufbuild/bufへ移行するためのドキュメントが作成されています。

以下、 uber/prototoolから転用

Update: We recommend checking out Buf, which is under active development.

There are a ton of docs for getting started, including for pmigration from Prototool.

自動でファイル検出したり、lint が強いです。

また、コンパイルも高速で、protoc のプロトコルプラグインとして使用していきます。

buf を使うモチベーションとして、Makefile のシェル芸に記載していますが、protoc 用の FileDescriptorSets を生成して、それを元に protoc が go に置換するところです。

つまり、proto だけではなく protobuf の FileDescriptorSets を介することによって proto のバージョンに依存しない schema の管理が出来、また buf を介した上での protoc が保証されています。

Prototool との違いは?

Migration Prototoolに buf との違いが記載されています。

一番大きいところでいうと、buf は現在、フォーマッターがありません。

Prototool では、 prototool format でフォーマットされます。

しかしながら、prototool には大きな欠点があります。

Prototool はサードパーティの Protobuf Parser を使用する一方で、ファイルが有効であるかを確認するために、さらにシェル化します。

ユースケースとしては、特定の proto ファイルだけが変更されて配布されてしまった場合に全体的な API で不整合が起こったりするのを buf で多少防げるようになります。

上記に記載しましたが、FileDescriptorSets を介することによって proto のバージョンに依存しない schema の管理が出来、また buf を介した上での protoc が保証されているのが、buf のメリットの一部です。

buf の導入

日本語の資料が少なく、公式ドキュメントも十分でないため、参考文献を交えて、話を進めていきます。

Homebrew でインストールが可能です。

brew tap bufbuild/buf
brew install buf

Directory 構成

/
├── Makefile     秘伝のMakefile
├── Dockerfile   コンパイル用のDockerfile
├── doc          protoについてのドキュメント
├── go           grpcやmock, validateのGoファイル群
├── js           grpc実装用のJS, TSファイル群
├── src          proto定義ファイル群
└──/ tools       コンパイルに必要なツール類
   ├── tools.go  コンパイルに必要なmodule定義
   ├── go.mod    go module
   └── go.sum    go moduleのsum

秘伝の Makefile

root 直下に Makefile を作成します。

docker-build: ## docker build
	docker build . -t protogen

protoc: ## wrapping buf-gen to use Docker
	docker run -it --rm -v $$(pwd):$$(pwd) -w $$(pwd) protogen make buf-gen

buf-doc: ## generate doc files
	buf image build -o - | \
	protoc --descriptor_set_in=/dev/stdin \
	--plugin=protoc-gen-doc=${GOPATH}/bin/protoc-gen-doc \
    --doc_out=doc \
    --doc_opt=markdown,README.md \
	$$(buf image build -o - | buf ls-files --input -)

buf-gen: ## generate go files
	buf image build -o - | \
	protoc --descriptor_set_in=/dev/stdin \
	--go_out=go --go-grpc_out=go --validate_out="lang=go:./go" --mock_out=go \
	$$(buf image build -o - | buf ls-files --input -)
	# cp -r go/github.com/abema/yoshikawa/research-buf/go ./
	# rm -r go/github.com

buf-js: ## generate js ts files
	buf image build -o - | \
	protoc --descriptor_set_in=/dev/stdin \
	--grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:js \
    --js_out="import_style=commonjs,binary:js" \
	$$(buf image build -o - | buf ls-files --input -)

buf-lint: ## buf lint proto files
	buf check lint

help: ## Display this help screen
	@grep -E '^[a-zA-Z/_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

コンパイル用の Dockerfile

root 直下に Dockerfile を作成します。

PROTOC のバージョンを固定したいときは、定数を変えてください。

あと、WORKDIR もプロジェクトに合わせて変更が必要です。

FROM golang:1.15

# バージョン固定
ENV PROTOC_TAG "3.13.0"

RUN apt-get update && \
    apt-get install -y unzip clang-format && \
    mkdir protoc && cd protoc && \
    wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_TAG}/protoc-${PROTOC_TAG}-linux-x86_64.zip -O protoc.zip && \
    unzip protoc.zip && \
    mv bin/protoc /usr/bin/protoc

# ここも変更おねがいします
WORKDIR $GOPATH/src/github.com/yoshikawa/research-buf

COPY tools/go.mod .
COPY tools/go.sum .
COPY tools/tools.go .
COPY Makefile .
COPY protoc-wrapper.sh .

RUN go mod download

RUN go install google.golang.org/protobuf/cmd/protoc-gen-go && \
    go install github.com/mwitkow/go-proto-validators/protoc-gen-govalidators && \
    go install github.com/envoyproxy/protoc-gen-validate && \
    go install github.com/srikrsna/protoc-gen-mock && \
    go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc && \
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc && \
    go install github.com/bufbuild/buf/cmd/buf && \
    go install github.com/bufbuild/buf/cmd/protoc-gen-buf-check-breaking && \
    go install github.com/bufbuild/buf/cmd/protoc-gen-buf-check-lint

ENV PATH $PATH:$GOROOT:$GOPATH:$GOBIN:$GOPATH/protoc/bin:/go/bin

buf.yaml の書き方

上記のように、build の roots をエントリーポイント(proto が定義されている箇所)として設定します。

# buf.yaml
build:
  roots:
    - src
lint:
  use:
    - STYLE_BASIC
  except:
    - ONEOF_LOWER_SNAKE_CASE
    - PACKAGE_LOWER_SNAKE_CASE

Go ファイルの生成

/
├── Makefile     秘伝のMakefile
├── Dockerfile   コンパイル用のDockerfile
├── doc          protoについてのドキュメント
├── go           grpcやmock, validateのGoファイル群
├── js           grpc実装用のJS, TSファイル群
├── src          proto定義ファイル群
└──/ tools       コンパイルに必要なツール類
   ├── tools.go  コンパイルに必要なmodule定義
   ├── go.mod    go module
   └── go.sum    go moduleのsum

Makefile と Dockerfile を設置して生成していきましょう。

また、tools/tools.go を作成して、 go.mod などを作成することで、コンパイルを可能にします。

// tools/tools.go
package tools

import (
	_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
	_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
	_ "github.com/mwitkow/go-proto-validators"
	_ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc"
	_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
	_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)

make docker-build && make protoc または Docker を使用しない場合は、make buf-gen で Go ファイルが生成されます。

生成される Go ファイル

/
└──/ go                   grpcやmock, validateのGoファイル群
   ├── ○○.pb.go
   ├── ○○.pb.mc.go
   ├── ○○.pb.validate.go
   └── ○○_grpc.pb.go

JavaScript TypeScript ファイルの生成

JavaScript, TypeScript ファイルの生成をしたい場合は、root 直下に package.json を作成します。

// package.json
{
  "name": "proto",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "@improbable-eng/grpc-web": "^0.13.0",
    "google-protobuf": "^3.13.0",
    "grpc-web": "^1.2.1"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

make buf-js で JavaScript, TypeScript ファイルが生成されます。

生成される JS, TS ファイル

/
└──/ js                     grpc実装用のJS, TSファイル群
   ├── ○○_grpc_web_pb.d.ts
   ├── ○○_grpc_web_pb.js
   ├── ○○_pb.d.ts
   └── ○○_pb.js

Document(Markdown)の生成

make buf-docdoc/README.md が生成されます。

Lint

buf で lint に Google Style Guide を導入します。

すでに、buf.yaml に記載されているので、割愛させていただきます。

詳しくは、Migration Prototoolで確認できます。