GitHub Actionsとsemantic-releaseでnpmパッケージのバージョン管理・リリースノート作成を自動化してみた
こんにちは、CX事業本部のうらわです。
本記事では最近触ったGitHub Actionsとsemantic-releaseの連携についてご紹介します。
semantic-releaseとは
npmパッケージのバージョン管理やnpm repositoryへのpublishを自動で行うためのツールです。GitHubと連携すると、タグとリリースノートを自動で作成してくれます。
semantic-releaseはパブリックに公開するnpmパッケージだけでなく、プライベートなnpmパッケージ・npm以外のライブラリやツールでも利用することができます。
今回はプライベートなnpmパッケージの想定で、semantic releaseとGitHub Actionsを組み合わせたバージョン管理・リリースノート作成の自動化を試してみます。
なお、本記事で作成するサンプルプロジェクトのソースコードは以下に格納してあります。
https://github.com/urawa72/semantic-release-sample
準備
以下の環境で実施します。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.6 BuildVersion: 19G2021 $ node -v v12.18.3 $ npm -v 6.14.6
まずはサンプルプロジェクトを作成します。
mkdir semantic-release-sample cd semantic-release-sample git init echo "node_modules" > .gitignore npm init -y
必要なnpmパッケージを追加します。各パッケージに関する設定は後述します。
npm i -D typescript semantic-release husky @commitlint/cli @commitlint/config-conventional
semantic-releaseを試すにあたりTypeScriptを使用する必要はないのですが、今回は私の好みでTypeScriptも導入しておきます。
npx tsc --init
README.mdとsrc/index.tsを追加しておきます。
echo "# semantic-release-sample" > README.md mkdir src echo "console.log('hello world')" > src/index.ts
各種設定
private: true
パッケージを非公開設定とするために、package.json
に"private: true"
を追記します。
ただし、今回はnpm resistoryへpublishするためのsemantic-releaseの設定は実施しないためnpm resistoryへ公開されることはありませんが、プライベートなパッケージはこの設定を追記しておく方が良いかと思います。
"private": true
semantic-release
package.json
に以下を追記します。これはmasterブランチにマージされたコミットのメッセージを分析してGitHubにタグとリリースノートを作成するための設定です。
"release": { "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/github" ], "branches": [ "master" ] },
ちなみに、"plugins"
の配列の末尾に@semantic-release/npm
を追加すると、npm publish関連のリリースステップが実行されます。今回はプライベートなnpmパッケージの想定なので、記述していません。
commitlint & husky
semantic-releaseではコミットメッセージが重要になるため、コミット時にメッセージが形式通りになっているかチェックするようにします。package.json
に以下を追記します。
"commitlint": { "extends": [ "@commitlint/config-conventional" ] }, "husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }
なお、これらの設定はpackge.json
ではなく、.huskyrc.json
, .commitlinrc.json
を作成して記載してもOKです。semantic-releaseの設定も同様に.releaserc.json
に記載してもOKです。
GitHub Actions
最後に、GitHub Actionsの設定ファイルを作成します。以下の例を参考にします。
今回は使用しないNPM_TOKEN
をコメントアウトした以外は例の通りです。
name: Release on: push: branches: - master jobs: release: name: Release runs-on: ubuntu-18.04 steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v1 with: node-version: 12 - name: Install dependencies run: npm ci - name: Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npx semantic-release
semantic-releaseを試す
コミットメッセージのルール
これまで設定したファイルをfirst commit
というメッセージでコミットすると、以下のようにhuskyによりエラーになります。
$ git commit -m 'first commit' husky > commit-msg (node v12.18.3) ⧗ input: first commit ✖ subject may not be empty [subject-empty] ✖ type may not be empty [type-empty] ✖ found 2 problems, 0 warnings ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint husky > commit-msg hook failed (add --no-verify to bypass)
コミットメッセージはsemantic-releaseによって解析され、メッセージの内容に応じてバージョンが更新される・リリースノートに記載されます。
下記はsemantic-releaseのREADME.mdを参考に作成したメッセージ内容とリリースタイプの対応表です。
コミットメッセージ | リリースタイプ | バージョン更新例 |
---|---|---|
fix(books): 書籍取得関数の取得件数の誤り修正 | パッチリリース | v1.0.0 → v1.0.1 |
feat(books): 書籍削除関数の追加 | マイナーリリース | v1.0.0 → v1.1.0 |
perf(books): 取得件数オプションを削除
BREAKING CHANGE: これは破壊的変更です |
メジャーリリース | v1.0.0 → v2.0.0 |
type(scope): message
という形式でコミットメッセージを書きます。
type
はfix
やfeat
以外にも様々な種類があります。fix
とfeat
以外はセマンティックバージョンの更新に影響しません。いずれのtype
でも、BREAKING CHANGE:
をコミットメッセージの末行に記載するとメジャーアップデートとして処理されます。
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- refactor: A code change that neither fixes a bug nor adds a feature
- perf: A code change that improves performance
- test: Adding missing or correcting existing tests
- chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
scope
は変更対象のモジュール名や機能等を記載します。scope
はGitHubのリリースノートにも記載されるため、適当な文字列ではなくライブラリとして意味のある文字列の方が良いかと思います。
そして、commitlintは上記のようなメッセージ形式になっているかをチェックしてくれます。そのため、コミットメッセージの不足・誤りで重要な機能追加や修正がセマンティックバージョンの更新に反映されない、といった事故を防ぐことができます。
なお、commitlintはConventional Commitsという規約に基づいてチェックしています。commitlintは他にも様々な設定ができて奥が深いです。気になる方はGitHubリポジトリのREADME.mdを参照ください。
では、実際にコミットメッセージをルールにしたがって書いてpushします。今回は初回コミットにつきtype
をchore
にしています。
$ git commit -m 'chore: first commit'
なお、GitHubは新規作成したリポジトリのデフォルトブランチがmainになっているため、新たにmasterブランチを作成してデフォルトブランチをmasterに変更します。mainブランチを利用しても良いのですがmasterの方が馴染みがあるため…。
GitHub Actionsでsemantic-releaseを実行する
GitHub Actionsによるリリースを確認します。まずはsrc/index.ts
を適当に修正します。
- console.log('hello world'); + console.log('hello world!!!!!!!!!');
こちらの修正はfix
扱いとしてコミットし、masterブランチにpushします。
$ git add . $ git commit -m 'fix(hello world): add many exclamation mark' $ git push
GitHub Actionsを確認すると、masterブランチへのpushに対応してワークフローが実行されます。
ワークフロー内では下図のようなジョブが実行されています。
重要なのは下から三番目のRelease
です。下図の通り、semantic-releaseによりコミットメッセージの解析、タグの作成、リリースノートの作成等が自動で実行されています。ちなみに、今回は以前のタグ・リリースが存在しなかったためv1.0.0
として初回リリースが実行されています。
GitHubのリリースを確認すると、タグとリリースノートが作成されていることがわかります。リリースノートにはコミットメッセージ(fix(hello world): add many exclamation mark
)に応じた内容が記載されています。
複数コミットで試してみる
通常の開発シーンではdevelopブランチからfeatureブランチを切ってプルリクエストを作成して…といった手順を踏むと思いますが、今回は簡略化します。
ローカルでmasterブランチからfeatureブランチを切って複数コミットを作成し、masterブランチにマージしてGitHubにpushしてみます。
$ git checkout -b feature/test
以下のようにtype
がfix
とfeat
のコミットを複数作成しました。これら一連のコミットメッセージがどのように分析され、結果的にどのようにバージョン管理されるのか確認します。
$ git log --pretty=format:"%s" -4 feat(hello world): feat2 fix(hello world): fix2 feat(hello world): feat1 fix(hello world): fix1
このfeatureブランチの変更をmasterブランチにマージし、GitHubにpushします。
$ git checkout master $ git merge feature/test $ git push
Github Actionsが実行された結果、マイナーリリースとしてバージョンがv1.0.0からv1.1.0にあがりました。
このように、複数コミットが存在する状態でもsemantic-releaseが適切な次のバージョンを判定してくれます。2回パッチリリース、2回マイナーリリースしてv1.2.2、といった更新にはなりません。
メジャーリリースしてみる
以下のように、コミットメッセージの末行にBREAKING CHANGE
を含むコミットを作成してGitHubにpushします。
feat(hello world): feat3 BREAKING CHANGE: this is a breaking change
GitHub Actionsが実行された結果、メジャーリリースとしてバージョンがv1.1.0からv2.0.0にあがりました。
まとめ
以上のように、semantic-releaseとGitHub Actionsを組み合わせると簡単にnpmパッケージのバージョン管理・リリースノート作成が行えます。本記事のように、npm resistoryへpublishしないプライベートなパッケージでも利用可能です。
今回はnpmパッケージでしたが、semantic-release自体はnpmパッケージ以外(package.json
が存在しないプロジェクト)でも使用できます。また、各種設定やプラグインを利用することで目的に合わせてリリースフローをカスタマイズすることができます(例えばchange logの作成 等)。
もし個人や業務でプライベートなnpmパッケージを開発・運用する場合は、ぜひGitHub Actionsとsemantic-releaseを使ってみてください。