🏷️

semantic-release を使って Node パッケージのリリースワークフローを自動化する

2024/09/17に公開

これはなに

semantic-release の基礎知識と、これを用いて Node パッケージのリリースワークフローを自動化する方法をまとめたものです。

現代のソフトウェアパッケージにおけるリリースフローには、バージョン番号の管理、変更履歴の記録、リリースノートの作成、各種共有レジストリーへの公開など、多くの手順が要求されます。これらの手順を自動化することでソフトウェア開発者はリリースに費やす時間を短縮し、ヒューマンエラーの削減が期待できます。

semantic-release とは

https://semantic-release.gitbook.io/semantic-release

ソフトウェアパッケージのリリースフローを自動化するための OSS ツールです。Semantic Versioning(SemVer2)に準拠した次期バージョン番号の決定、リリースノートの作成、npmjs といった共有レジストリーへの公開[1]といったリリースワークフローを包括的に自動化します。

次期バージョン番号の自動決定

semantic-release の最大の特徴は、それまで蓄積されたコミットメッセージの内容から次期バージョン番号を自動的に選定する点にあります。semantic-release は、Conventional Commits などに代表されるコミットメッセージ規約に基づいてコードベースの変更内容を解析し、それに応じた次期バージョン番号を決定してリリースワークフローを進めます(コミットメッセージの内容次第でリリースを見送る判断もします)。

この仕様は一貫したバージョン番号の管理を実現するのと同時に、コミットの粒度やそれを表すコミットメッセージに対して一定の規約を開発者に課すことで、コードベースの変更履歴を明確なものに保つ効果も期待できます。

用途ごとに提供されるプラグイン

semantic-release はプラグインシステムを採用しており、リリースワークフローの各ステップに特化したプラグインを提供しています。以下は主要なプラグインの一覧ですが、そのうちいくつかは semantic-release 本体に標準でバンドルされています。

プラグイン名 説明 標準バンドル
commit-analyzer 蓄積されたコミットメッセージを解析し、SemVer2 に準拠した次期バージョン番号を決定する。
release-notes-generator Release Notes を生成する。
npm npmパッケージのバージョン更新およびリリース処理を実行する。package.jsonversion フィールドの更新作業も担う。
github Github Release や Comment を生成する。
changelog CHANGELOG.md を生成する。 -
git リリースフローで発生した差分を Git リポジトリにコミットする。
例: package.jsonversion フィールドの更新。 CHANGELOG.md の更新。
-
exec 任意のコマンドを実行する。 -

この他にも多くのプラグインが提供されており、ユースケースに応じて適切なプラグインを選択することでリリースワークフローを柔軟にカスタマイズできます。

https://semantic-release.gitbook.io/semantic-release/extending/plugins-list

次期バージョン番号の決定ロジック

先述した @semantic-release/commit-analyzer というプラグインがコミット履歴を解析し、Conventional Commits に準拠したコミットメッセージに含まれる type フィールドに基づいて次期バージョン番号を決定します。

Conventional Commits の基本的な書式
<type>[optional scope]: <description>
  │     │                │
  │     │                └─ コミットの内容を簡潔に記述
  │     │
  │     └─ コミットの影響範囲を指定するスコープ(省略可)
  │
  └─ コミットの種類を指定するタイプ
type 解析内容
feat 新機能が追加されたとみなし、マイナーアップデートと判断される。
fix バグの修正パッチが適用されたとみなし、パッチアップデートと判断される。
perf パフォーマンスチューニングがされたとみなし、パッチアップデートと判断される。

この他にも refactortest など様々な type がありますが、原則として上記以3つ以外の type はアップデートの対象となりませんrefactor はバグの修正や機能の追加を含まないコードのリファクタリングを表し、test はそのソフトウェアの開発生産性向上を目的としたテストコードの追加や変更を表すものです。つまり上記3つ以外はソフトウェアのユーザーに影響を与えるものではないため、アップデートして公開するに値しないというわけです。

また、メジャーアップデートは以下の特定のキーワードによって判断されます。

キーワード 解析内容
BREAKING CHANGE コミットメッセージのフッター部分に BREAKING CHANGE を含めると破壊的変更(= 重大な変更)が入ったとみなされ、メジャーアップデートと判断される。
! type/scope の直後に ! が入ると BREAKING CHANGE のエイリアスとして機能する(メジャーアップデートと判断される)。
initial commit v1.0.0 として処理される。
メジャーアップデートを示すコミットメッセージの例
feat: add new feature

BREAKING CHANGE: this is a breaking change
エイリアスを使った例
feat!: add new feature
feat(api)!: add new feature

セットアップ

ここでは以下の要件を満たすセットアップをご紹介します。

  • Node.js パッケージのリリースフローを自動化する
    • package.jsonversion フィールドを次期バージョン番号で更新する
    • npmjs に Node パッケージを公開する
    • npm dist-tag コマンドを実行して npmjs で公開するパッケージにタグを追加する
  • Changelog ファイルおよび GitHub の Release Notes を生成する

インストール

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

semantic-release 本体(および標準バンドルされたプラグイン)に加えて Changelog ファイルを生成する @semantic-release/changelog と、リリースフローで更新される package.jsonCHANGELOG.md の差分を Git リポジトリにコミットする @semantic-release/git をインストールします。

設定ファイルの作成

リポジトリーのルート直下に release.config.js という名前で設定ファイルを作成します。

release.config.js
export default {
  plugins: [
    // 1. コミット履歴から次期バージョン番号を算出する。
    "@semantic-release/commit-analyzer",

    // 2. リリースノートのためのコンテンツ(テキスト)を生成する。
    "@semantic-release/release-notes-generator",

    // 3. "2" で生成されたリリースノート用コンテンツを Changelog ファイルに記述する。
    '@semantic-release/changelog',

    // 4-a. package.json の version フィールドを次期バージョン番号で更新する。
    // 4-b. npmjs にパッケージを公開する。
    // 4-c. npm dist-tag コマンドを実行して npmjs で公開するパッケージにタグを追加する。
    '@semantic-release/npm',

    // 5. リリースフローで発生したアセットの更新差分をリポジトリにコミットする。
    [
      '@semantic-release/git',
      {
        assets: ['CHANGELOG.md', 'package.json', 'package-lock.json'],
      },
    ],

    // 6. "2" で生成されたリリースノート用コンテンツを用いて GitHub Release を追加する。
    '@semantic-release/github',
  ],
};

plugins フィールドにリリースワークフローの各ステップに対応するプラグインを指定することで、semantic-release はそれらを指定された順序で実行します。この他にもさまざまな設定フィールドが提供されていますが、基本的に plugins 以外はデフォルト値のままで問題ありません。

https://semantic-release.gitbook.io/semantic-release/usage/configuration

! エイリアスの有効化

メジャーアップデートを示すために ! エイリアスを使用したいところですが、残念ながらデフォルト設定の commit-analyzer! エイリアスを認識しません。これは commit-analyzer ならびに release-notes-generator のプリセット初期値が angular という Angular Commit Guidelines に準拠したものに設定されているためです。! エイリアスは Conventional Commits の仕様なので[2]、これを認識させるには双方プラグインのプリセットを conventionalcommits に変更する必要があります。

このプリセットはオプション扱いなので、別途インストールします。

npm install --save-dev conventional-changelog-conventionalcommits

次に release.config.js を以下のように修正します。

release.config.js
export default {
  plugins: [
-   "@semantic-release/commit-analyzer",
+   [
+     "@semantic-release/commit-analyzer",
+     { preset: "conventionalcommits" },
+   ],

-   "@semantic-release/release-notes-generator",
+   [
+     "@semantic-release/release-notes-generator",
+     { preset: "conventionalcommits" },
+   ],
    // ...
  ],
};

これで semantic-release に ! エイリアスを含むコミットメッセージをメジャーアップデートとして認識させられます。

参考文献

環境変数の設定

先述した設定で semantic-release を実行するためには、以下の環境変数を設定する必要があります。

環境変数名 説明
NPM_TOKEN npmjs に Node パッケージを公開するための npm アクセストークン。
詳細: About access tokens | npm Docs
GITHUB_TOKEN GitHub リポジトリーに更新差分をコミットしたり Release Notes を追加するための GitHub アクセストークン。

npm scripts の追加

package.json に以下の npm scripts を追加します。

package.json
{
  "scripts": {
    "release": "semantic-release"
  }
}

シンプルに semantic-release コマンドを実行するだけで同階層ディレクトリーにある release.config.js を読み込み、リリースワークフローが実行されます。

GitHub Actions の設定

semantic-release は CI/CD ツールとして使用することを想定されているため、ここでは GitHub Actions を使用してリリースワークフローを自動化する方法を紹介します。

.github/workflows/release.yml
name: Release

on:
  push:
    branches:
      - main

permissions:
  contents: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup node.js
        uses: actions/setup-node@v4

      - name: Install dependencies
        run: npm install

      - name: Release
        run: npm run release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # あらかじめリポジトリーの secrets に NPM_TOKEN という名前で npm アクセストークンを登録しておく
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

ここでは例として main ブランチに push されるとリリースワークフローが実行されるように設定しています。npm scripts に追加した release コマンドを実行するステップを定義し、その際に環境変数として GitHub アクセストークンと npm アクセストークンを渡しています。これでリリースワークフローが自動化されます。

まとめ

ソフトウェアパッケージのリリースワークフローを自動化するツールは他にもいくつか存在しますが、semantic-release はその中でも特に「自動化」に重点を置いたツールです。Conventional Commits に準拠したコミットメッセージを使って次期バージョン番号を自動決定するという特徴は、ソフトウェアのリリースフローを一貫性のあるものに保つという点で非常に有用です。エコシステムもなかなか充実しており、本稿で紹介したプラグイン以外にも monorepo での利用をサポートする semantic-release-monorepo などが提供されています。

https://github.com/pmowrer/semantic-release-monorepo

参考文献

https://zenn.dev/wakamsha/articles/about-publishing-node-packages

https://zenn.dev/wakamsha/articles/about-conventional-commits

脚注
  1. プラグインを組み合わせることで Docker HubGitHub Packages などさまざまなレジストリーにも対応できます。 ↩︎

  2. Conventional Commits は Angular Commit Guidelines を拡張したものです。 ↩︎

Discussion