BASE BANK 株式会社 Dev Division でSoftware Developer をしている清水( @budougumi0617 )です。
みなさんの開発現場でも社内ライブラリ・モジュールとして開発しているコード・GitHubリポジトリがあると思います。
そのようなリポジトリはパッケージ管理システムを経由して利用することがほとんどですが、そのためにはリリース作業を行う必要があるかと思います。
私のチームでは先日GitHubリポジトリのリリース作業をGitHub Actionsで自動化したので、本記事ではその内容を共有したいと思います。
TL;DR
今回はGitHub Actionsとrelease-it npmを使っています。
上記の技術を組み合わせることで次のような自動リリースのワークフローを構築しました。
- (Pull Requestがマージされるなどで)mainブランチにコミットがプッシュされたらタグを打ち、GitHubリリースを作成する。
- 前回リリースとの差分でコミットメッセージのリリースノートを作成する
- 特定のファイルまたはディレクトリが更新されていたときだけリリースする
- コミットメッセージに応じてセマンティックバージョンのパッチ/マイナー/メジャーアップデートを切り替える
- Actions上でしか使わないnpmパッケージなので、リポジトリにpackage.jsonを置かない
GitHub Actionsを使って自動化を行なうと、コミット内容に応じた操作が簡単に実現できます。
ただし、次の制約もありました。
- プロテクトブランチを利用している場合はGitHub Actions上からコミットをプッシュすることはできない
- リリース用のPRをつくるといった迂回策が必要
そのため、今回の自動リリースでは「リポジトリ内のversion
変数の値を更新してコミットしておく」のような操作は含んでいません。
なお、今すぐ試してみたい方は以下の2つのファイルを用意するだけで実現できます。
- .release-it.json
- .github/workflows/release.yml
.release-it.json
の内容は次のとおりです。GitHubリポジトリのルートディレクトリに配置します。
{ "requireUpstream": false, "requireCleanWorkingDir": false, "github": { "release": true }, "git": { "commit": false, "push": false, "requireUpstream": false, "requireCleanWorkingDir": false }, "npm": { "publish": false, "ignoreVersion": true } }
.github/workflows/release.yml
の内容は次のとおりです。
name: auto release demo on: push: # mainブランチにコミットがpushされたときに限定 branches: - main # 上記条件に加えてgenディレクトリ配下が変更されたときのみという条件を追加 paths: - gen/** jobs: auto-release: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_IT_VERSION: 14.2.1 steps: - name: Check out codes uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v1 with: node-version: '12' - name: Set releaser settings run: | git config --global user.name release-machine git config --global user.email email@example.com - name: Major release id: major if: contains(toJSON(github.event.commits.*.message), 'bump up version major') run: npx release-it@${RELEASE_IT_VERSION} -- major --ci - name: Minor release id: minor # メジャーバージョンアップをしていないときマイナーバージョンアップを行なうか if: steps.major.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bump up version minor') run: npx release-it@${RELEASE_IT_VERSION} -- minor --ci - name: Patch release # コミットメッセージに特に指定がない場合はマイナーバージョンを更新する if: "!(steps.major.conclusion == 'success' || steps.minor.conclusion == 'success')" run: npx release-it@${RELEASE_IT_VERSION} -- patch --ci
今回のサンプルYAMLの場合はmain ブランチにgen
ディレクトリ内への変更を含んだPRをマージすると自動でリリースが行なわれます。
また、bump up version major
といったメッセージが含まれていた場合はメジャーバージョンアップが行なわれます。

なお、本記事で利用している各ツールのバージョンは以下のとおりです。
ツール名 | バージョン |
---|---|
GitHub Actions | v2 |
release-it npm | 14.2.1 |
Node.js | 12.X系 |
以下のURLは実際にGitHub Actionsで何回か自動リリースをしてみたサンプルリポジトリです。
リリース作業を自動化したい
どんな言語を使っていても、業務で開発を行なっていると社内ライブラリを作成することがあると思います。
作成したライブラリはnpmやComposer、Go Modulesなどのパッケージ管理システムを経由して使うことになるのが大半だと思います。
そうなると一定の更新ごとにタグを設定し、バージョン管理する必要が出てきます。
とはいえ 「PRをマージしたらgit tag
コマンドを打って…」と各開発者が行なうのは億劫です。
そのため、mainブランチにPRがマージされたら(コミットがプッシュされたら)自動でタグ打ち、リリースするという自動化を試みました。
もちろんタグはリリースのたびにセマンティックバージョンがインクリメントされるようにします。
GitHub Actions上でrelease-it npmを実行してリリースをする
release-it npmはよしなにセマンティックバージョンをインクリメントしながらリリースノートも作ってGitHubリリースを作成してくれるコマンドです。
たとえば、現時点のバージョンが0.1.2
だったとき、次のように実行するとマイナーバージョンをインクリメントした0.2.0
バージョンのリリースを作成してくれます。
$ npm run release -- minor --ci
次のリンクはrelease-it npmで作成されたリリースノートです。

GitHub Actions上でNode.js環境を用意して実行するだけで終わりかと思いきや、いろいろ設定する必要があったので、ポイントを解説していきます。
GitHub Actions実行時にタグも取得しておく
Set fetch-depth: 0 to fetch all history for all branches and tags.
今回構築する自動リリースのワークフローでは既存のタグからリリースするセマンティックバージョンを決定します。
そのため、GitHub Actions実行時にタグも一緒にチェックアウトしておく必要があります。タグはGitHub Actionsを利用時にほぼ100%use
されているであろうactions/checkout
に対してfetch-depth: 0
オプションを渡すだけで取得可能です。
- name: Checkout codes uses: actions/checkout@v2 with: fetch-depth: 0
特定のパス配下が更新されたときのみリリースする
今回自動リリースしたいリポジトリはコードの自動生成を行なっていました。そのため、次のような事情がありました。
- PRがマージされるたびのリリースは (どんどんバージョンが上がってしまうので)してほしくない
- 自動生成したコードが配置されている
gen/
ディレクトリの内容が変更されたときだけリリースしたい
OpenAPIやgRPCなどを利用して同リポジトリ内でクライアントコードを自動生成したりしていると、同様のニーズが生まれると思います。
最初はCircleCIを利用して自動リリースを実現しようと思ったのですが、パスを使ってCIを制御するのはGitHub Actionsのほうが簡単だったので、GitHub Actionsで自動リリース作業を行なうことにしました。
GitHub Actionsのワークフローでは次のような制御をすることができます。
コミット内容を確認して特定ディレクトリに更新があったか確認する
GitHub Actionsでは、on.<push|pull_request>.paths
を使うことで、特定ディレクトリに更新があったときだけにジョブの実行を制限できます 1 。
次のサンプルコードは以下の2つの条件を満たしたときのみ実行される設定です。
- mainブランチにコミットがプッシュされた
- プッシュされたコミットの中に
gen/
ディレクトリ内の更新が含まれていた
on: push: branches: - main paths: - gen/**
ワークフローの制御にコミットメッセージを利用する
このデータ構造はおそらく公式ドキュメントに明示的に載っていないのですが、GitHub Actionsでブランチにpushされた一連のコミットの情報をジョブ実行中に利用可能です。
ワークフロー実行時の情報はgithub context
として参照できるのですが、この中のgithub.event
でpushされたコミットの情報を持っています2。
この情報をパースするとコミットの内容をワークフロー中に使うことができます。
次のコードは「コミットのメッセージに'bump up version major'
があったらtrue
になる」式です。
contains(toJSON(github.event.commits.*.message), 'bump up version major')
これと、jobs.<job_id>.steps.if
、を使うことで、「コミットメッセージによって実行されるstep」をワークフローに用意することができます。
jobs: sample: runs-on: ubuntu-latest steps: - name: teststep if: contains(toJSON(github.event.commits.*.message), 'コミットメッセージを確認') run: echo 'executed!!'
また、contextのsteps.<step id>.conclusion
を用いることで前ステップの実行結果を利用して else if
のような制御を行なうことも可能です。
jobs: ifelse-pattern: steps: - name: foo id: foo if: contains(toJSON(github.event.commits.*.message), 'foo') run: echo 'if step!' - name: bar id: bar # fooステップがスキップされた && コミットメッセージにbarを含む場合に実行する if: steps.foo.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bar') run: echo 'elseif step!'
次のリンクは実際にステップをいくつかスキップしているActionsの実行結果です。
ここまではGitHub Actionsの設定方法でしたが、次はrelease-it npmをGitHub Actions上で使うコツです。
タグの設定とリリースはするが、コミットはしない
release-it npmはGitHub Actions上でnpx
コマンドで実行しているのでpackage.json
は不要です。
が、release-it npm用の設定を用意する必要があります。
今回利用している設定は次のとおりです。
{ "requireUpstream": false, "requireCleanWorkingDir": false, "github": { "release": true }, "git": { "commit": false, "push": false, "requireUpstream": false, "requireCleanWorkingDir": false }, "npm": { "publish": false, "ignoreVersion": true } }
ざっくり説明すると、次のような設定です。
- GitHubリリースを作成する
- gitのコミットは作成しない
- gitのpushはしない
- npmの公開はしない
- バージョンを決定するために
package.json
内のバージョンを参照しない
鋭い方は「”pushしない”ってことはタグも公開されないんじゃないの?」と思うかもしれませんが、いいのか悪いのか、リリースを行なうときにタグはプッシュされるようです。
Actionsからプロテクトブランチにはコミットをpushできない
ここまで便利なGitHub Actionsでしたが、ひとつ制約があります。それはプロテクトブランチにコミットをプッシュすることができない点です。抜け道がないか探していたのですがなさそうなので諦めました。
なので、先ほどのrelease-it npm用の設定ファイルは「タグの設定とリリースはするけどコミットはプッシュしない」という内容になります。
「自動リリースするときはpackage.json
の中にあるversion
も更新したい(コミットプッシュしたい)んだけど!」というようなニーズももちろんあると思います。
しかし、今回のユースケースではリリースタグでバージョンが管理されていればファイルとしてバージョンが参照できる必要はなかったので、こちらも妥協しました。
あとは開発するだけ!
以上の設定を行うと、特定条件のコミットを作るだけで自動でリリースされるようになります。
それぞれの設定は独立しているので、お好みでカスタマイズしていただけばと思います。
- 特定のディレクトリに限定する必要はないので
on.<push|pull_request>.paths
の設定は削除する - マッチするコミットメッセージを変更する etc...
name: auto release demo on: push: # mainブランチにコミットがpushされたときに限定 branches: - main # 上記条件に加えてgenディレクトリ配下が変更されたときのみという条件を追加 paths: - gen/** jobs: auto-release: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_IT_VERSION: 14.2.1 steps: - name: Check out codes uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v1 with: node-version: '12' - name: Set releaser settings run: | git config --global user.name release-machine git config --global user.email email@example.com - name: Major release id: major if: contains(toJSON(github.event.commits.*.message), 'bump up version major') run: npx release-it@${RELEASE_IT_VERSION} -- major --ci - name: Minor release id: minor # メジャーバージョンアップをしていないときマイナーバージョンアップを行なうか if: steps.major.conclusion == 'skipped' && contains(toJSON(github.event.commits.*.message), 'bump up version minor') run: npx release-it@${RELEASE_IT_VERSION} -- minor --ci - name: Patch release # コミットメッセージに特に指定がない場合はマイナーバージョンを更新する if: "!(steps.major.conclusion == 'success' || steps.minor.conclusion == 'success')" run: npx release-it@${RELEASE_IT_VERSION} -- patch --ci
終わりに
「PRマージしたぞー!」と思っても、そのあとにポチポチリリース作業をするのは億劫でした。
これで少しでも生産性があがるといいなと思っています。
なお、GitHub Actionsからプロテクトブランチへの直接コミットプッシュはできないのですが、renovateはPRを作成、Appで自動承認、自動マージという迂回をしてプロテクトブランチへのプッシュを実現しているようです。
GitHub Apps - renovate-approve · GitHub
もっと突き詰めたくなったら同様の操作を実装してファイル更新も含めた自動リリースを実現したいなと思います。
最後に、BASE BANKでは新しくデザイナーとカスタマーサクセスの募集を開始したので、ぜひご応募お待ちしています。
参考リンク
- https://github.com/budougumi0617/autorelease-by-release-it-on-actions
- https://www.npmjs.com/package/release-it
- https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
- https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions
- https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101/5
-
「特定のディレクトリに更新があったときは無視する」という逆制御もできます。↩