gRPCから少し離れ、GitHub Actionsを利用した「コード生成コマンドの実行の自動化」を行います。
実際の運用でgRPCを利用し始める場合に覚えておくと役に立つかも知れません。
gRPCから少し離れ、GitHub Actionsを利用した「コード生成コマンドの実行の自動化」を行います。
実際の運用でgRPCを利用し始める場合に覚えておくと役に立つかも知れません。
こんにちは。サーバーエンジニアのりつきです。
当記事は【基礎編】および【実装編】と題した記事の続きですのでまだ読んでいない方はそちらの記事を読んでからご覧ください。
今回はgRPCを実際にプロジェクトに利用するにあたりより開発効率を上げることができる手段として、
GitHub Actionsを利用しprotocコマンドを自動実行させる方法について紹介・解説する記事になります。
gRPCそのものからは離れた内容になりますが、是非読んでいただけると幸いです。
GitHub Actionsの利用には使用状況に応じて料金が発生します。
今回作成するワークフローは一度の実行で1分かからないので基本的には問題ありませんが、GitHub Freeプランだと本記事執筆時で月2,000分までが無料で利用できる範囲なのでご注意ください。
料金体系について詳しくはこちら。
【実装編】までの内容でProtocol Buffersを用いたgRPCサーバー・クライアントの開発についてはある程度解説しました。
しかし、このまま実務で利用しようとするといくつか問題点が発生するかと思います。
その問題点とは「毎回protocコマンドを実行して生成されたコードをgitに上げるのは結構手間」というものです。
より具体的に言うと下記のような問題などが起こり得ます。
そこで、GitHub Actionsを利用して.protoファイルが更新されたときに自動でprotocコマンドの実行および変更のコミットを行うことで上記の問題を以下のように解消できます。
このように手動で行った時に発生し得る問題を解決し、開発効率の向上を目指します。
早速GitHub Actionsに実行させるワークフローを記述していきます。
まずは examgrpc/以下に下記のファイルを用意してください。
- examgrpc/
- .github/
- workflows/
- generate-pb.yaml : このファイルを追加
- pkg/
- grpc/
- proto/
- message.proto
- service.proto
追加したファイルの中身は下記になります。
generate-pb.yaml
# grpcのコード生成を行うワークフロー
name: Generate gRPC codes
on:
push:
branches:
- main
jobs:
# protoの差分を確認するジョブ
check-proto-diff:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
changed_files_count: ${{steps.check_diff.outputs.changed_files_count}}
steps:
# gitユーザーの設定
- name: Setup Git User
shell: bash
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
# コードのチェックアウト
- name: Checkout codes
uses: actions/checkout@v4
with:
fetch-depth: 0
# protoの差分を確認
- name: check proto diff
id: check_diff
run: |
CHANGED_FILES_COUNT=$(git diff --name-only HEAD^ proto | wc -l)
echo "$CHANGED_FILES_COUNT file was changed"
echo "changed_files_count=$CHANGED_FILES_COUNT" >> "$GITHUB_OUTPUT"
# grpcのコード生成を行うジョブ
generate-grpc-codes:
needs: [check-proto-diff]
if: needs.check-proto-diff.outputs.changed_files_count != '0'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# gitユーザーの設定
- name: Setup Git User
shell: bash
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
# コードのチェックアウト
- name: Checkout codes
uses: actions/checkout@v4
# goのセットアップ
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
# protocコマンドのインストール
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: "29.3"
# コード生成
- name: Generate Codes
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5
export PATH="$PATH:$(go env GOPATH)/bin"
protoc ./proto/*.proto --go_out=. --go-grpc_out=. --proto_path=.
# コードのコミット
- name: Commit and push Generated Codes
id: commit_changes
run: |
git add pkg/grpc
git commit -m "gRPC code generate"
git push origin HEAD:main
このワークフローの要点は下記の三箇所です。
# protoの差分を確認
- name: check proto diff
id: check_diff
run: |
CHANGED_FILES_COUNT=$(git diff --name-only HEAD^ proto | wc -l)
echo "$CHANGED_FILES_COUNT file was changed"
echo "changed_files_count=$CHANGED_FILES_COUNT" >> "$GITHUB_OUTPUT"
このstepは直前のコミットと現在のコミットの、proto/以下のファイルの差分のみを比較して差分のあるファイル数をGITHUB_OUTPUTへと出力しています。
詳しい使い方は公式のリファレンスに記載されていますが、
GITHUB_OUTPUTにファイル数を出力してワークフロー上の別のjobから読み込むことで.protoファイルに変更がない場合(readmeやこのyaml自体の更新など)にコード生成が動かないように制御しています。
# protocコマンドのインストール
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: "29.3"
ローカル環境ではhomebrewを利用してコマンドをインストールしていましたが、
GitHub Actionsでprotocコマンドを利用するためのアクションが存在しており、
それを利用することで簡単にワークフロー内でコマンドを利用できます。
# コード生成
- name: Generate Codes
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5
export PATH="$PATH:$(go env GOPATH)/bin"
protoc ./proto/*.proto --go_out=. --go-grpc_out=. --proto_path=.
基礎編でも利用したパッケージのインストールとコマンドの実行を行います。
以上のワークフローをmainブランチにpushすると、
ワークフローは起動しますがコードの生成が行われないことが確認できるかと思います。
動作確認のため.protoファイルを更新してみましょう。
更新する内容はなんでも問題ありませんが、ここではHelloRequestにパラメータを一つ追加します。
// 型の定義
message HelloRequest {
string message = 1;
string name = 2; // このパラメータを追加
}
上記の更新をmainにpushするとワークフローが実行され、pkg/grpc/以下のコードが更新されることを確認できます。
また、server/やclient/直下で下記のコマンドを実行すると各プロジェクトでも追加したパラメータが利用できるようになっているはずです。
$ go get github.com/{GitHubアカウント名}/examgrpc
コードの生成の自動化は完了しましたが、このままでは一つ問題が残っています。
現状生成後のコードのバージョンがコミットハッシュでしか管理されていないため、別の機能の開発中など最新ではないgRPCのコードが必要な場合にわざわざコミットハッシュを調べる必要があります。
そこで、gRPCのコードが更新された時パッケージのタグバージョンを更新し、古いバージョンの取得を容易にしたいと思います。
まずは現在のバージョンを保持しておくファイルをプロジェクトに追加します。
- examgrpc/
- VERSION : このファイルを追加
- .github/
- workflows/
- generate-pb.yaml
- pkg/
- grpc/
- proto/
- message.proto
- service.proto
中身は下記のようにしておきます。patchバージョンは自動で更新されますがmajor,minorのバージョンは開発しながら必要に応じて手動で更新します。
VERSION
0.0.0
続けてgenerate-pb.yamlに以下の設定を追加します。
# grpcのコード生成とバージョン更新を行うワークフロー
name: Generate gRPC codes
on:
push:
branches:
- main
jobs:
# protoの差分を確認するジョブ
check-proto-diff:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
changed_files_count: ${{steps.check_diff.outputs.changed_files_count}}
steps:
# gitユーザーの設定
- name: Setup Git User
shell: bash
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
# コードのチェックアウト
- name: Checkout codes
uses: actions/checkout@v4
with:
fetch-depth: 0
# protoの差分を確認
- name: check proto diff
id: check_diff
run: |
CHANGED_FILES_COUNT=$(git diff --name-only HEAD^ proto | wc -l)
echo "$CHANGED_FILES_COUNT file was changed"
echo "changed_files_count=$CHANGED_FILES_COUNT" >> "$GITHUB_OUTPUT"
# grpcのコード生成とバージョン更新を行うジョブ
generate-grpc-codes:
needs: [check-proto-diff]
if: needs.check-proto-diff.outputs.changed_files_count != '0'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# gitユーザーの設定
- name: Setup Git User
shell: bash
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
# コードのチェックアウト
- name: Checkout codes
uses: actions/checkout@v4
# goのセットアップ
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
# protocコマンドのインストール
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: "29.3"
# コード生成
- name: Generate Codes
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5
export PATH="$PATH:$(go env GOPATH)/bin"
protoc ./proto/*.proto --go_out=. --go-grpc_out=. --proto_path=.
# コードのコミット
- name: Commit and push Generated Codes
id: commit_changes
run: |
git add pkg/grpc
git commit -m "gRPC code generate"
git push origin HEAD:main
# --- ここから下の部分を追加 ---------
# 現在のバージョンの読み込み
- name: Read current version
id: current_version
run: |
echo $(cat VERSION)
echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
# バージョンの自動更新
- name: Increment version
id: increment_version
run: |
current_version=${{steps.current_version.outputs.version}}
IFS='.' read -r -a version_parts <<< "${current_version}"
major=${version_parts[0]}
minor=${version_parts[1]}
patch=${version_parts[2]}
new_patch=$((patch + 1))
new_version="$major.$minor.$new_patch"
echo "new_version=v${new_version}" >> "$GITHUB_OUTPUT"
echo $new_version > VERSION
# バージョンを更新してブランチにコミット
- name: Commit and push updated VERSION
run: |
new_version=${{steps.increment_version.outputs.new_version}}
git add VERSION
git commit -m "Increment version to ${new_version}"
git push origin HEAD:main
# タグのリリース
- name: Create and push the tag
run: |
new_version=${{steps.increment_version.outputs.new_version}}
git tag $new_version
git push origin $new_version
この部分はgRPCの話とは別枠の小技のようなものなので簡単に解説すると下記のような動作をしています。
この変更をmainブランチに反映した後、再度proto/以下のファイルを更新してみます。
message.proto
// 型の定義
message HelloRequest {
string message = 1;
string name = 2;
}
message HelloResponse {
string resMessage = 1;
}
// --- 下記の型定義を追加
message GoodByeRequest {
string message = 1;
string name = 2;
}
message GoodByeResponse {
string resMessage = 1;
}
service.proto
// サービスの定義
service GreetingService {
// メソッドの定義
rpc Hello (HelloRequest) returns (HelloResponse);
rpc GoodBye (GoodByeRequest) returns (GoodByeResponse); // このメソッドを追加
}
proto/以下のファイルを更新しmainに取り込むとワークフローが起動します。
ワークフローが正常に実行された状態でmainブランチを確認するとVERSIONファイルの中身が"0.0.1"に更新されv0.0.1のタグが作成されていることが確認できます。
バージョンタグを設定することにより、生成したコードを利用するプロジェクトで下記のようにバージョンを指定して、必要な更新のみを利用して開発することが可能になります。
$ go get github.com/{GitHubアカウント名}/examgrpc@v0.0.1
以上のようにgRPCおよびProtocolBuffersを開発に利用する際はコード生成の自動化をできるようにしておくと、開発の効率をぐっと上げることが可能です。
ここまで全三記事を読んでいただき、ありがとうございました。
このブログを制作する中でも新たな発見があったので制作自体にも意義があったように思えます。
例えば最近のgRPCの開発ではprotocコマンドではなくbufというツールのCLIを利用するのがモダンらしく、キャッチアップが足りなかったなと思うこともありました。
自身の学習内容のアウトプットとして書き始めた記事ですが読んでいただいた皆様の開発や学習の一助となっていれば幸いです。
また、EMoshUでは一緒に開発できる仲間を募集しております。
ご興味を持たれましたら、ぜひ募集要項をご覧ください。カジュアル面談なども実施しております。