GitHub Actions と semantic-releaseを組み合わせて Node.js パッケージのリリースフローを整備する試み

はじめに

オープンソースとして公開しているとある複数のパッケージでリリースフローが整備されいないという問題があって,昨年末は少々頭を悩ませていました.

そこで今回は GitHub Actions と semantic-release を組み合わせて npm へのリリース作業*1を自動化する,という方針で上記の問題を解決できないか?ということを試してみようと思います.

実験台にしたパッケージは krdlab/daab-session *2です.

先に軽く結論を述べますと,ライブラリ系のリポジトリでは導入する価値ありだ (というかした方が良い) と思いました.master ブランチにマージするだけで必要なものが自動的にリリースされるようになります.

GitHub Actions の設定

ここは公式が用意してくれているものをそのまま設定します.この段階では publish 関連を設定せずに,ビルドとテストが成功することだけを確認します.

後ほど少しだけ修正します.

semantic release の設定

semantic-release は Angular Commit Message Conventions にしたがってコミットするとパッケージのバージョニングと公開を自動化してくれます.導入手順は以下の通りです.

  1. 対象 GitHub リポジトリの Settings にある Secrets へ GitHub と npm のアクセストークンを追加
  2. semantic-release と必要な plugin を npm install
  3. Actions の設定で追加した .github/workflows/nodejs.yml に semantic-release 実行ステップを追加
  4. package.jsonrelease フィールドを追加

1. 対象 GitHub リポジトリの Settings にある Secrets へ GitHub と npm へのアクセストークンを追加

semantic-release が使うトークンとして以下の 2 つが必要になるので,発行 & 設定します.

  • GitHub で Personal access token を発行
    • repo 権限を与える
    • これを GH_TOKEN として Secrets に登録する
  • npm で Access Token を発行
    • publish 権限を与える
    • これを NPM_TOKEN として Secrets に登録する

2. semantic-release と必要な plugin を npm install

npm install --save-dev semantic-release @semantic-release/changelog @semantic-release/git

デフォルトでロードされるプラグインは以下の通りです.

https://semantic-release.gitbook.io/semantic-release/usage/plugins#default-plugins

Changelog を生成したい場合は @semantic-release/changelog が追加で必要になります.

また,更新された package.json や生成された CHANGELOG.md はデフォルトだと publish 先にしか含まれないため,リポジトリに反映するためには @semantic-release/git が必要です.

3. .github/workflows/nodejs.yml に semantic-release 実行ステップを追加

.github/workflows/nodejs.ymlsteps に以下を追加します.

    - name: semantic-release
      if: matrix.node-version == '12.x'
      run: |
        npm run semantic-release
      env:
        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        GH_TOKEN: ${{ secrets.GH_TOKEN }}

matrix build しているため if でリリースフローが複数回実行されないように絞り込んでおきます.

semantic-release はデフォルト設定だと master ブランチのみを対象としかつ npm を対象とするため,上記の追加だけで「PR を master ブランチにマージすると publish」されるようになります.ちなみに master ブランチ以外で実行した場合は以下のようなメッセージが出力されます.

This test run was triggered on the branch feature/add-github-actions, while semantic-release is configured to only publish from master, therefore a new version won’t be published.

4. package.jsonrelease フィールドを追加

package.json に以下の設定を追加します.といっても plugins のデフォルト値に changelog と git を追加しているだけです.

  "release": {
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      "@semantic-release/npm",
      "@semantic-release/github",
      "@semantic-release/git"
    ]
  }

注意点としては,リポジトリに付いている既存のバージョンタグが v1.0.1 のように v から始まる場合は問題ありませんが,そうでない場合は追加で tagFormat の設定が必要だということです.

semantic-release のリリースステップや dry-run の出力をみると Git タグを基に次のバージョン値を決めていることが分かります.この時タグからバージョンを抽出するフォーマットが tagFormat なのですが,このデフォルト値が v${version} になっているため,これを既存のタグフォーマットにあわせて修正する必要があります.

例えば単純にバージョン値を Git タグとして付けている場合は以下のような設定を追加します.

  "release": {
    "plugins": [
      ...
    ],
    "tagFormat": "${version}" <--- これ
  }

補足をいくつか

おわりに

以上のような設定を施すことで「master ブランチへマージしたら Release note や ChangeLog が生成されて npm に publish される」という一連の作業が自動化されます.

実行してみて思ったのは「想像以上に楽だ……」ってことです.パッケージ数が多いとより嬉しいんじゃないかなと思います.

リリース手順は実施頻度が低ければ忘れやすく,頻度が高ければ面倒なものになってしまいます.自動化しておくことでいずれの場合についても負担を軽減できる*3と感じました.

参考情報

*1:Release note/Changelog 作成 + npm publish

*2:自分以外誰も使っていないであろうパッケージ

*3:コミットメッセージ規約と仕組み自体の周知は必要