こんにちは・こんばんは・おはようございます 開発支援部基盤バックエンドチームのid:aerealです。
今回は小粒でピリリと辛いちょっとしたテクニックをお届けします。
Goとコード生成
Goとコード生成は密接な関係にあります。
なにしろ go generate
というサブコマンドが提供されているほどです。
Classiが開発しているGoを採用したミドルウェア・dronにおいてもgomockによるモック実装の生成やOpenAPI仕様からGoの型の生成などで利用しています。
参考: dron: クラウドネイティブなcron代替の紹介 - Classi開発者ブログ
コード生成とRenovate
コード生成結果に係る変数は主に2つあります。
- 入力となるソースコード
- 生成するツール
ソースコードは言うまでもありません。リポジトリで管理されるソースコードは常に変更される可能性があります。
忘れられがちなのがコードを生成するツールです。
Go製の開発用ツールもバージョン管理している場合、dependabotやRenovateで他のライブラリ同様に更新がPull Requestで送られます。
アプリケーション内から使用されるライブラリであれば、十分なカバレッジを伴うテストコードがあればマージ前に非互換な挙動を発見できます。
ではコード生成に使うツールはどうでしょうか。ツールの更新に伴い生成結果が変わることもあります。 当然、テスト実行時にはコード生成を伴わないので発見できません。
性質が悪いことにコード生成 (go generate
) はそう頻繁に実行するものではないので発見が遅れがちです。
その結果、入力となるソースコードも変更されてしまい一体どんな結果があるべきなのかわからなくなる……ということが考えられます。
では生成するツールを古いバージョンで固定するしかないのでしょうか。
生成したコードの差分をCIで検出する
前述の通りアプリケーション内から利用しているライブラリをある程度の自信をもって更新できるのは自動テストがあるからでした。 コード生成の結果を検証するステップをCIに組み込めばツールの更新が破壊的変更を含まないことを確かめられ、安心してツールの更新ができるというものです。
上記のdronを例にとると以下のstepを記述しています。
go generate
を実行する- その後のワークツリーで
git diff --exit-code
を実行する
go generate
は自明として、 git diff --exit-code
について補足します。
--exit-code Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and 0 means no differences.
以上、git-diffのmanより。
差分があれば1を、無ければ0を終了ステータスに設定します。
シェルにおいて非ゼロの終了ステータスは異常を指すので「差分があれば失敗する」という挙動になります。
GitHub Actionsでは非ゼロの終了ステータスを補足するとデフォルトでそのジョブは失敗と報告されるので「go generate
して差分があればジョブを失敗させる」ことができます。
おまけ: 自動でコミット・プッシュするというアイデア
GitHub ActionsはGitHubのAPIへアクセスする権限を持ったトークンが特別の設定なく与えられるという強みを持っているので、単純に差分が生じただけであれば git commit && git push
させることも可能です。
マーケットプレイスにはAdd & commitのようなワークフローも公開されていますし、似たようなstepを書いてもよいです。
なおオーガニゼーションの設定や将来的なGitHub全体の変更によりトークンにデフォルトで付与される権限が狭められる可能性を考慮し、明示的に permissions
を指定するのが望ましいでしょう。
参考: Automatic token authentication - GitHub Docs
むすび
コード生成した結果に差分が生じていないかをCIで検証する方法について紹介しました。
コード生成を活用するGoならではのモチベーションではありますが、一般的なアプリケーション開発に応用できるトピックではないでしょうか。
ClassiではPull Requestのコメント欄でよく見かける指摘を自動化させる気概のあるエンジニアを歓迎します!