kubell Creator's Note

ビジネスチャット「Chatwork」のエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

Github ActionsによるScala製ライブラリの依存ライブラリ更新と定期リリースの自動化

どうも、かとじゅん(@j5ik2o)です。

Scala製ライブラリでの依存ライブラリ更新と定期リリースをGithub Actionsを使って自動化するようにしたので、以下にその方法をまとめます。

具体的な例を見たい場合は以下のリポジトリを参照してください。

github.com

自動化した作業

  • 依存ライブラリのバージョンアップのPR作成とマージ
  • リリースノートの生成
  • JARファイルの公開

ツールの前提条件など

  • sbt-ci-release, sbt-scalafix, sbt-scalafmtのsbtプラグインは必須としています。CIでリリースするだけならsbt-ci-releaseの設定だけでOK
  • build.sbtlintコマンドエイリアスを追加。ここではコードフォーマットのチェックとscalafixのチェック処理を行います。
  • ライブラリの依存関係はproject/Dependecies.scalaに集約
  • 依存ライブラリの更新はscala-stewardを使う
    • Github Actionsから利用する場合は scala-steward-actionが便利
    • 事前にBOT用アカウントでPernal Access Tokenを発行し、GithubのSecretsにCW_SCALA_STEWARD_TOKENを登録(Github Appを使う場合は不要)

用意するGithub Actionsのワークフローは以下です。

  • CI
  • Snapshot
  • Bump Version
  • Release
  • Scala Steward

CI

CIのワークフローはmainブランチへのpush, pull_requestがトリガーでlintとtestを行います。加えて、pull requestのみ自動的にマージします。

scala-ulid/ci.yml at main · chatwork/scala-ulid · GitHub

自動マージはMergifyを使う方法もありますが、今回はPRを作ったauthorがBOTと同じ名前ならば自動的にマージするようにしています*1。自動マージの条件にauthor以外に変更されたファイルパスを指定する方法もあると思いますが、Github Actionsでうまくやる方法がわかりませんでした…。詳しい方、アドバイスもらえると助かります。

Snapshot

CIが成功したらsbt-ci-releaseを使ってスナップショット版のJARを公開します。sbt-ci-releaseの場合はタグ以外のコミットで起動した場合は自動的にスナップショット版のJARがデプロイされます。Release版のワークフローとほとんど変わりませんが、依存元のCIワークフローの成否判定やチェックアウトするコミットを合わせるなどの違いがあります。

scala-ulid/snapshot.yml at main · chatwork/scala-ulid · GitHub

Bump Version

sbt-ci-releaseではリリース版のJARをリリースするには、まずはタグを作成する必要があります。sbt-releaseの構成ではversion.sbtにバージョンを保存していましたが、sbt-ci-releaseではversion.sbtは削除しGit上のタグを利用することになります。このワークフローは手動実行もしくは1日一回自動的に実行されますが、15行目あたりでリリース済みの最新バージョン(タグ)からHEADまでに変更差分がないかチェックし、差分があればバージョンを上げたタグとGithubのリリースを自動的に作成します。

scala-ulid/bump-version.yml at main · chatwork/scala-ulid · GitHub

今回はバージョンを上げるためにmathieudutour/github-tag-actionを利用しています。タグ作成以外にconventional-changelogに対応していてリリースノートを自動生成できます(ただし、コミットメッセージにルールが必要になります。今回はAngularの規約を使いました)

github.com

リリースノートのためにコミットメッセージに規約が導入されるデメリットがあるので、一様にオススメできるものではないと思いますが…。

Release

リリース用にタグ作成されるとRelease用のワークフローが起動します。タグがトリガーになる+事前にテストするだけで、Snapshotのワークフローとほとんど違いはありません。

scala-ulid/release.yml at main · chatwork/scala-ulid · GitHub

ハマりどころがあります。Bump Versionでタグを作るとReleaseのワークフローが起動するはずですがGITHUB_TOKENだと起動せず、Personal Access Tokenだと起動します。要注意。

Scala Steward

最後がライブラリ依存関係を更新するPRを作成してくれるScala Stewardです。Scala StewardはScala専用のDependabotみたいなものです。Github Actionsから利用する場合は scala-steward-actionを使うととても簡単に導入できます。

scala-ulid/scala-steward.yml at main · chatwork/scala-ulid · GitHub

Scala Stewardによって作成されたPRはCIのフローがテストします。テストがパスすると自動的にマージされます。 .scala-steward.confのコミットメッセージにAngularの規約の一つであるbuild:を含めているので、Bump Versionのワークフローでリリース対象に含まれますので、依存ライブラリのバージョンアップだけであっても定期的にJARがリリースされるようになります。

https://github.com/chatwork/scala-ulid/blob/main/.scala-steward.conf#L2

FYI: Github ActionsでのScala Stewardの導入に関しては以下のブログ記事も見るとよいです。吉田さんのはGithub Appを使う例です。tibdex/github-app-tokenを使うと実行時にトークンが取得できるので、必要なアクションに渡してあげればよいです。前述したPull Requestの自動マージもGithub Appのトークンで実行できます*2

github.com xuwei-k.hatenablog.com xuwei-k.hatenablog.com scalapedia.com

まとめ

依存ライブラリのバージョンアップについては、Scala StewardがPRを作成→テストがパス→自動マージされます*3。JARファイルの公開とリリースノートの生成についても、一日一回最新のタグからの差分を検出して、新しい変更があれば自動的に実行されます。scala-steward-actionやリリースノートの自動生成については、ライブラリだけでなくアプリケーションでも役に立ちそうです。

ということで、溜まったPRをマージしていいですか?してくださいとか、JARの公開どうやったらいいですか?みたいなめんどくさい話はなくなって、そういう時間を別のことに当てられるようになります。参考になれば幸いです。

*1:BOT用に物理アカウントを一つ用意するか、Github Appを作りそのアプリケーションが実行時にトークンを取得する方法がありますが、今回はBOT用の物理アカウントを使っています

*2:実例はhttps://github.com/j5ik2o/akka-persistence-dynamodbを参考にしてください

*3:Mergifyを使う手もありますが、プライベートリポジトリを考慮するとGithub Actionsのほうがよいかもしれません