Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

Java屋がsemantic-releaseに思うこと

最近Java周りでもsemantic-releaseの利用機会が増えています。Gradle pluginMaven pluginが生まれ、特に後者はyarn*1で実行されるため既存のプラグインとも組み合わせやすく、JavaScriptと比較しても遜色ない状態と言えそうです。

2019年3月時点で、Java特にMavenがどのようにsemantic-releaseを活用できるのか、まとめてみます。

semantic-releaseとは

プロジェクトにおいて以下の制約を導入することで、リリース作業をより一段階自動化する仕組みです。

  1. Semantic Versioningを使ったバージョン番号の付け方
  2. Conventional Commit Messagesを使ったコミットコメントの書き方

すでにJavaコミュニティにおいてもSemantic Versioningは標準となっているため、実際に学ばなければならないのはConventional Commit Messageだけと言えます。

semantic-releaseは具体的なリリース作業を定義しません。このため使っているプロジェクト管理ツールやCIサービスを問わずに利用可能です。代わりに、リリース手順を構成するステップを定義しており、利用者はステップごとに必要な処理をフックできます。 これは、Mavenがビルドライフサイクルを構成するフェーズを定義しているのと近いです。Maven自身が各フェーズにおける具体的な処理を定義せずプラグインに委ねているように、semantic-releaseも具体的なリリース作業の定義をプラグインに委ねています。

各Stepの説明はsemantic-releaseのREADME.mdに書かれています:

Step名 説明
Verify Conditions リリースに必要な条件がすべて揃っていることを確認する
Get last release Git tagsから最新(前回)のリリースバージョンを取得する
Analyze commits 最新(前回)のリリースから今回のリリースまでに含まれるすべてのコミットを解析する
Verify release リリース可能な状態かどうか検証する
Generate notes 最新(前回)のリリースから今回のリリースまでに含まれるすべてのコミットからリリースノートを生成する
Create Git tag 新しくGitタグを作成する
Prepare リリースの準備をする
Publish リリースを出荷する
Notify 新しく作成したリリース、または発生したエラーについて通知する

GitHub Actionsを使えば、PR後にPrepare Stepまで処理を進め、承認を受けたらPublish & Notifyするという運用もできそうです。出荷承認プロセスを持つコミュニティでも活用できるでしょう。

Mavenとの併用

semantic-releaseをMavenと併用する場合、semantic-releaseがMavenを実行する形になります。Publish Stepでmvn deployする形です。

package.jsonは必須ではないですが、実際にはこのように作成してしまったほうがプロジェクトの見通しは良くなると思われます。これはpom.xmlをコミットするために@semantic-release/gitのカスタマイズが必要となるためです。またdevDependenciesにsemantic-releaseとそのプラグインが列記されるため、それらのバージョン管理がしやすくなる利点もあります。 プロジェクトにpom.xmlpackage.jsonが同居するので、CIツール側で設定ファイルの有無によるビルドツールの推定をしている場合は注意が必要かもしれません。

高速なリリースが行えるのが大きな利点

semantic-release最大の利点は、なんと言っても頻繁にリリースが行えることです。何も考えずPull Requestをマージするだけで、すべてのリリース作業が実施されMaven Cental等にデプロイを行うことができます。LeanやContinuous Deliveryが証明したように、高速かつ頻繁にユーザへ変更を届けることはプロジェクトの成功へダイレクトに効きます。導入可能なプロジェクトでは速やかに入れていくことが望ましいと思います。

他には、semantic-releaseを利用することでリリースマネージャの手作業、例えばタグ打ちやCHANGELOG作成を自動化できる点も嬉しいです。GitHub Releaseの更新のような従来から別の自動化手法があったものも、コミットコメントをSingle Sourceとして利用する手法に切り替えることで、透明性と正確性を確保できます。

考えられるデメリットはそこまで大きくない

一般に、頻繁なリリースはユーザに追随するための負担を強いる可能性がありますが、semantic-releaseはバージョン番号をSemantic Versionに従った形で決めるので、ユーザもバージョン更新の必要性と負担を簡単に見積ることができます。デプロイ回数が増えるのでMaven Repositoryの記憶容量が気になるかもしれませんが、メリットに比較して十分に安いコストではと感じます。OSS開発においてはここは特に気にならない問題でしょう。

Semantic Commit Messageをチームに普及するのが最初の難関か

デメリットが小さいならJavaコミュニティにもすぐに普及するでしょうか?個人的には懐疑的です。大きく分けて2つの問題があります。

まずSemantic Commit Message自体、そこまでクリアに定義されているものではありません。AngularJS Commit Message Conventionsから生まれたもので、多様なプロジェクトに適合できるものにはまだなっていません。

またSemantic Commit Messageに定義されていないコミット種別を利用することも禁止されていません。プロジェクトごとにコミット種別の亜種が生まれ、収集つかなくなる可能性がありそうです。
例えば依存ライブラリのバージョンアップはchore, build, fix, securityのいずれにするべきでしょう?実際にProbotプロジェクトではchore(package), chore(dependencies), build(package), fix(package)のすべてが使われています。1プロジェクト内でも統一されていないわけです。
今のところ周辺ツールもあまり賢くないので、PRレビュー時にレビュアーがコミット種別を確認する必要があります。プロジェクト導入時の最初の難関は、不明瞭な定義をローカルルールに落とし込んできちんと普及させる、このプロセスにあるでしょう。

次に、Semantic Commit Messageの利用を忘れたときに、コミットコメントの修正が面倒という問題があります。Gitはこれをサポートしていますが、コミット履歴の修正は基本的な操作と比べて難度が高いため、ある程度の慣れが必要です。 これについてはPRをSquash Commitしてしまうという、若干粗い解決方法があります。GitHubはSquashしてマージする時のコミットコメントをPRタイトルから取るので、PRタイトルをSemantic Commit Messageに準じたものにすればよいのです。PRタイトルはWeb画面から簡単に修正できるので、慣れていない人でも回しやすいでしょう。なおSemantic Commit Probotはこの運用を想定した実装になっていて、コミットコメントがSemantic Commit Messageとして不正な場合でもPRタイトルが問題なければパスしてくれます。

機能は十分なので運用経験と実績を積むべき時期

以上で、Java屋がsemantic-releaseに思うことを見てきました。

機能的には充分で、すぐにプロジェクトに適用することができます。nexus-staging-maven-pluginmaven-release-pluginなどの既存設定をそのまま使えるので、新規プロジェクトのみならず既存プロジェクトへの導入も簡単でしょう。

まだJavaコミュニティにおいてはさほど知名度がないため、コミュニティでの開発に浸透するのはまだ先だと思いますが、個人プロジェクトではぜひ使ってほしいです。ユーザが増え実績が積まれれば、Javaコミュニティにおいても利用が進むことでしょう。

*1:npmでも良いはずだが、semantic-release-pluginのドキュメントはyarnで統一されているので、長いものには巻かれておく