2
2

More than 1 year has passed since last update.

【Deprecated】pnpm+Changesets+GitHub Actionsでmonorepo内のnpmパッケージのリリースを半自動化する

Last updated at Posted at 2022-08-01

増補改訂版の以下の記事をご利用ください。

概要

ChangesetsGitHub Actions を使用し、monorepoに含まれる複数のnpmパッケージの更新履歴とバージョンの管理およびリリース作業を省力化する。
以下のリリースフローを構築する。

  1. ローカルでは開発の区切り毎に $ pnpm changeset でChangesetsの対話式CLIからバージョンアップ種別の選択とCHANGELOGの入力を行う
    • 一時ファイル(メタデータ入りの CHANGELOG.md の断片)が生成される
  2. 一時ファイルをソースと一緒にGitHubにPushする
  3. 一時ファイルがGitHubのmainブランチに到達すると Changesets Release Action が「その時点でリリース対象となるnpmパッケージを一括リリースするためのPR」を自動生成する
    • PRの内容は「 CHANGESET.md の更新」「 package.jsonversion フィールドのインクリメント」「一時ファイルの削除」
    • PRをマージする前に別の一時ファイルを追加するとリリース用PRの内容は自動で更新される
    • PRには全ての一時ファイルの内容がパッケージ毎に整理されて表示される
  4. 任意のタイミングでリリース用PRをマージするとリリースされる
自動生成されるリリース用PRのサンプル(画像)

image

要件

  • 単独または少人数による開発を想定
  • pnpm Workspaceによるmonorepo環境
  • npmパッケージのバージョンは個別管理とし、CommitやPRには紐付けない
    • npmパッケージ間の相互依存はないものとする
  • CHANGELOG.md の管理と package.json 上のバージョンのインクリメントはChangesetsに一任する
  • GitHub ActionsでChangesets Release Actionを使用してリリース用PRを自動生成する
  • リリース用PRをマージしたら自動でnpmjs.comにデプロイする
  • changeset-bot は扱わない

ベースとなる開発環境は以下の記事の通り。

構築

リポジトリ

Changesets

導入は開発環境構築記事のChangesetsの節を参照のこと。
当該記事における「アップデート対象パッケージの選択とサマリーの入力」を実施した後、生成された一時ファイルのMarkdownをそのままリポジトリにCommitする。
「CHANGELOG.mdの更新とバージョンのインクリメント」相当の手順はChangesets Release ActionがGitHub上で自動実行する。

Tips

概要に載せたスクリーンショットの通り、自動生成されるリリース用PRには「一時ファイルを処理したコミットへのリンク」が設置される。
そのため、個別の開発用ブランチ(いわゆるfeatureブランチ)からリリース用のブランチに直接マージするGitHub Flow的なブランチモデルとの親和性が高い。
開発用ブランチ毎に $ pnpm changeset を実行することでCHANGELOGの項目とマージコミットが紐付く形になるため、後から「GitHub上で パッケージ名@バージョン で検索 → PR → ソース/差分/Issue」と簡単に辿ることができる。

npm-scripts

pnpm publish コマンドで自動実行される prepublishOnly タスクで必要な処理が行われるように各npmパッケージの package.json を設定する。

package.json
// 例
"scripts": {
  "clean": "rimraf ./dist",
  "test": "vitest run",
  "coverage": "vitest run --coverage",
  "tsc": "tsc -p .",
  "build": "pnpm clean && pnpm tsc",
  "prepublishOnly": "pnpm test && pnpm build"
}

GitHub

Secrets追加

npmjs.comにデプロイする際に必要なアクセストークンをGitHubのSecretsに追加する。

  • npmjs.comにログインしユーザーメニューから「Access tokens」に移動
  • 「Generate new token」ボタンから「Automation」トークンを作成する
  • GitHubのリポジトリで Settings -> Secrets -> Actions -> New repository secrets と移動
  • トークンを NPM_TOKEN として追加する

GitHub Actions

リポジトリのルートに .github/workflow ディレクトリを作成して設定用のYAMLを配置する。
ファイル名・Workflow名・Job名は適宜変更のこと。

.github/workflow/changesets.yml
name: Changesets

# mainブランチに対するPush時に動作
on:
  push:
    branches:
      - main

jobs:
  changesets:
    runs-on: ubuntu-latest
    steps:
      # [1] リポジトリをチェックアウト
      - name: Checkout
        uses: actions/checkout@v3

      # [2] Node.js 16系をインストール
      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

      # [3] pnpm 7系をインストール
      - name: Install pnpm
        id: pnpm-install
        uses: pnpm/action-setup@v2
        with:
          version: 7

      # [4] pnpmのストアディレクトリを取得
      # pnpmは依存関係の実体をnode_modulesではなく独自のストアで管理している
      - name: Get pnpm store directory
        id: pnpm-cache
        run: |
          echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"

      # [5] pnpmのストアディレクトリをキャッシュとして定義
      # 一致するものがあれば復元される
      - name: Setup pnpm cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
          # キーは実行環境のOSとpnpm-lock.yamlのハッシュ
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          # ハッシュが一致するものがない場合はOSが一致するものを使用する(step6で更新される)
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      # [6] 依存関係のインストール
      - name: Install dependencies
        run: pnpm install

      # [7] 都度指定しなくていいようにnpmのtokenを .npmrc に追記する
      - name: Setup npmrc
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: |
          cat << EOF > "$HOME/.npmrc"
            //registry.npmjs.org/:_authToken=$NPM_TOKEN
          EOF

      # [8] Changesets Release Actionの設定
      - name: Create release PR or publish to npm
        id: changesets
        uses: changesets/action@v1
        with:
          # リリース用のコマンドは `pnpm publish` の再帰実行とする
          # npmjs.com上のバージョンとpackage.json内のバージョンを比較して更新があったもののみリリースされる
          # デバッグ用にドライランにしたい場合は `pnpm -r publish --dry-run` とする
          publish: pnpm -r publish
          # GitHubのReleaseは自動生成しない(既定では自動生成される)
          # (2022-08-01現在、有効にしても最新のChangesets Release Actionとpnpm7系の組み合わせでは正常にトリガされない)
          createGithubReleases: false
        env:
          # GITHUB_TOKENは自動生成されるためなにもしなくても使用可能
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Linter

基本的な文法チェックには actionlint が便利。

動作テスト

環境変数やoutputsの処理などは nektos/act を使ってローカルでテストできる。

PRの生成などGitHubの機能を使用するものは、テスト用のブランチないしリポジトリを作成し、デプロイ系の動作をdry-runに設定して確認する。

実行

基本的に概要の通り。
実際に運用中のリポジトリにおける一連のフローをサンプルとして掲載する。

  1. changsetコマンドを実行 ※画像
    • この例ではPatchレベルのサマリを2件入力している
  2. 1で生成された一時ファイルをCommit/Push
    • 他のファイルと一緒でも構わない
  3. GitHub Actionsが2を処理してPRを生成する
    • Step名 Create release PR or publish to npm 参照
  4. 3で生成されたPR
  5. 4をマージするとGitHub Actionsが自動でnpmjs.comにデプロイする
    • Step名 Create release PR or publish to npm 参照

※各所のCodecov関連の記述はテスト用のため無視のこと

留意事項

  • 本稿で行った設定では、経緯に関わらずnpmjs.comに公開されているバージョンより新しいバージョンが指定された package.json がChangeset Releace Actionの動作するリモートブランチに到達すると即座にリリースされる
    • 動作としてはリリース用PRがマージされた時と同じ
    • package.json を手作業で編集する場合は予期しないリリースが行われないよう注意が必要
    • 逆にこの仕様を利用して $ pnpm changeset version コマンドをローカルで実行する運用にすることも可能

参考リンク

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2