Skip to content

Instantly share code, notes, and snippets.

@x7ddf74479jn5
Last active April 4, 2023 15:29
Show Gist options
  • Save x7ddf74479jn5/a899d12737e9f1bf3072c64cbf5deddc to your computer and use it in GitHub Desktop.
Save x7ddf74479jn5/a899d12737e9f1bf3072c64cbf5deddc to your computer and use it in GitHub Desktop.
monorepo(pnpm)+Chagesetsでリリース自動化

monorepo(pnpm)+Changesets でリリース自動化

TOC

概要

モチベーション

  • 自分が使っている config 系(eslint, textlint, renovate...)をモノレポ構成で管理したかった。
  • 自分しか使わない想定で npm Registry ではなく GitHub Packages を使いたい。
  • リリース作業をやんわり自動化したい。(自動生成ツール使ってみたい)

リリース自動化ツール選定

リリース自動化ツール比較記事: CHANGELOG 生成系について調べてみた - くらげになりたい。

前提

コミット方法に Emoji Prefix を使っている。Emoji Prefix は✨ feat: なんか追加のように先頭に絵文字をつける文化圏の記法。認知負荷が低いため個人的には推している。ただ、Conventional Commitsの形式にならないため、Conventional Commits を前提としたツールチェインを利用できない。(e.g. Release Please, conventional-changelogなどのツールはコミットベースで CHANGELOG を自動生成する)

結果

pnpm が推している1のでchangesets/changesetsを選定。

Changesets の特徴

  • 手動/PR でリリースを自動化できるツール。
  • 対話型 CLI で  CHANGELOG.md  の追記と  package.json  のバージョンのインクリメントを行う。
  • 標準で pnpm の Workspace に対応しておりインストールするだけで設定不要で使用できる。
  • 他の自動リリースツールより手動の手順が多いけれども、その分柔軟性も高い。

要件

  • pnpm Workspace を使ったモノレポ構成
  • レジストリは GitHub Packages
  • パッケージ間の相互依存はないものとする
  • CHANGELOG.md  の管理と  package.json  上のバージョンのインクリメントは Changesets に一任する
  • リリースフローを GitHub Actions で自動化

インストールと初期設定

pnpm init
pnpm add -D @changesets/cli @changesets/changelog-github
pnpm changeset init

プロジェクトの初期化。.changeset/config.jsonREADME.mdが出力される。

package.jsonの設定例

{
  "name": "configs",
  "version": "1.0.0",
  "description": "x7ddf74479jn5 configs",
  "scripts": {
    "ci:version": "changeset version",
    "ci:publish": "changeset publish",
    "lint": "eslint --fix **/*.{ts,d.ts,js,cjs,mjs}",
    "prettier": "prettier --write --ignore-unknown **/*",
    "prepare": "simple-git-hooks"
  },
  "author": "x7ddf74479jn5 <[email protected]> (https://github.com/x7ddf74479jn5)",
  "license": "MIT",
  "private": "true",
  "homepage": "https://github.com/x7ddf74479jn5/configs",
  "repository": {
    "type": "git",
    "url": "https://github.com/x7ddf74479jn5/configs.git"
  },
  "bugs": {
    "url": "https://github.com/x7ddf74479jn5/configs/issues"
  },
  "devDependencies": {
    "@changesets/changelog-github": "0.4.8",
    "@changesets/cli": "2.26.1",
    "lint-staged": "13.2.0",
    "prettier": "2.8.7",
    "simple-git-hooks": "2.8.1",
    "typescript": "5.0.3"
  },
  "simple-git-hooks": {
    "pre-commit": "pnpm exec lint-staged --concurrent false -c lint-staged.mjs"
  }
}

config.jsonを以下のように編集。

{
  "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
  "changelog": ["@changesets/changelog-github", { "repo": "x7ddf74479jn5/configs" }], // [1]
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "public", // [2]
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": []
}
  1. "changelog": "@changesets/changelog-github"にオプションを設定することで author name や commit hash がリリースノートに含まれるようになる。repoはプロジェクトルートを設定する。
  2. private にする場合はrestrictedを設定。

ワークスペースの設定

最終的なディレクトリツリーの例(関係ないものは省略)

.
├── .changeset
│   ├── README.md
│   └── config.json
├── .github
│   └── workflows
│       └── release.yaml
├── .gitignore
├── package.json
├── packages
│   ├── module-a
│   │   ├── CHANGELOG.md
│   │   └── package.json
│   └── module-b
│       ├── CHANGELOG.md
│       └── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

pnpm-workspace.yaml

packages:
  - "packages/*"

パッケージ側のpackage.jsonの例

packages/module-a

{
  "name": "@x7ddf74479jn5/module-a", // [1]
  "version": "1.0.0",
  "description": "module-a",
  "main": "index.js",
  "keywords": ["module-a"],
  "author": "x7ddf74479jn5 <[email protected]> (https://github.com/x7ddf74479jn5)",
  "license": "MIT",
  "homepage": "https://github.com/x7ddf74479jn5/configs/tree/main/packages/module-a#readme",
  "repository": {
    "type": "git",
    "url": "https://github.com/x7ddf74479jn5/config.git",
    "directory": "packages/module-a"
  },
  "bugs": {
    "url": "https://github.com/x7ddf74479jn5/config/issues"
  }
}
  1. Github Packages の命名規則で**@<user-name/package-name>**の形式にする必要がある。

Changesets の使い方

1. 変更履歴の積み上げ

pnpm changeset
  1. アップデートするパッケージを選択
  2. メジャーアップデートするパッケージを選択
  3. マイナーアップデートするパッケージを選択
  4. サマリーを記述
  5. 確認

正常に終了すると.changeset/中にランダムなファイル名(random-name.md)で CHANGELOG の断片が吐き出される。この Markdown には対象サブプロジェクトとバージョンアップの種別を示すメタデータ、および  CHANGELOG.md  に追記される内容(=入力したサマリー)が記録されている。サマリーを複数入力する場合やパッケージ毎に異なるサマリーを入力する場合は  pnpm changeset  を複数回実行する。

---
"@x7ddf74479jn5/module-a": minor
---

✨ feat: なんか追加

2. CHANGELOG.md の生成とリリース

2.1 手元で生成する場合

2.1.1 生成

次のコマンドで CHANGELOG に統合される。

pnpm changeset version
# @x7ddf74479jn5/module-a

## 1.1.0

### Minor Changes

- [`14e046d`](https://github.com/x7ddf74479jn5/configs/commit/14e046d26ec2974acd4530c877f56c2df3adc724) Thanks [@x7ddf74479jn5](https://github.com/x7ddf74479jn5)! - ✨ feat: なんか追加
2.1.2 変更をコミット

コードと.changeset/*に生成されるマークダウンをまとめてコミット。

2.1.3 公開

GitHub の PAT を取得の上、.npmrcにレジストリ(npm.pkg.github.com)の設定とともに記述する。.gitignore.npmrcを追記する。

//npm.pkg.github.com/:_authToken=gh......................OU
@x7ddf74479jn5:registry=https://npm.pkg.github.com/ // [1]
  1. @:registry=https://npm.pkg.github.comの形式。

注意する点として、private なパッケージにする場合、利用側の.npmrcもこの記述をしないと権限で弾かれる。

続いて、次のコマンドでパッケージの公開を行う。

pnpm changeset publish

これにより、現在 GitHub Packages に掲載されているバージョンよりも遅いバージョンの各パッケージでnpm publishが実行される。

2.2 CI で生成する場合

2.2.1 GitHub Actions でChangesets Release Actionを使用してリリース用 PR を自動生成する
name: Release

on:
  push:
    branches:
      - main

concurrency: ${{ github.workflow }}-${{ github.ref }}

defaults:
  run:
    shell: bash
jobs:
  release:
    runs-on: ubuntu-latest
    // [1]: write権限が必要
    permissions:
      packages: write
      contents: write
      pull-requests: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Get pnpm store directory
        id: pnpm-cache
        run: |
          echo "PNPM_CACHE_DIR=$(pnpm store path)" >> $GITHUB_OUTPUT

      - name: Setup pnpm cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.pnpm-cache.outputs.PNPM_CACHE_DIR }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install
      // [2]
      - name: Setup npmrc
        env:
          NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          cat << EOF > "$HOME/.npmrc"
            //npm.pkg.github.com/:_authToken=$NPM_TOKEN
            @x7ddf74479jn5:registry=https://npm.pkg.github.com/
          EOF

      - name: Create release PR or publish to GitHub Packages
        id: changesets
        uses: changesets/action@v1
        with:
          version: pnpm ci:version // [3]: CHANGELOG.md生成
          publish: pnpm ci:publish // [4]: リリース用PR作成
          title: '[ci] release'
          commit: '[ci] release'
          createGithubReleases: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }} // [5]: 必須
  1. GitHub Packages に公開、リリースノートの自動作成、リリース PR に write 権限が必要。
  2. @<github-username>:registry=https://npm.pkg.github.com/の形式でレジストリの向き先を GitHub Packages に変える。changesets/action.npmrcを自動生成するが、すでにある.npmrcを検知するとそれを活用する。
  3. 対応する npm script はchangeset version
  4. 対応する npm script はchangeset release
  5. npm Registry ではなく Github Packages を使用する場合でも publish するならNPM_TOKENを環境変数に設定する。

.npmrcに各パッケージ共通のレジストリ設定を書く方法でやっているが、各パッケージのpackage.jsonで以下のように書いても OK。

// ...
"publishConfig": {
  "registry": "https://npm.pkg.github.com"
},
// ...

上記のアクションでリリース用の PR が作成される。

3. リリース用 PR をマージし、自動で GitHub Packages にデプロイする

CI 実行した場合**[ci] release**のタイトルで PR が作成されているので、確認して問題なければマージする。成果物として次のものが自動実行される。

  • リリースノートの生成
  • タグ付け
  • パッケージのデプロイ

(optional) GitHub Action のインストール

GitHub Apps - changeset-bot

Changesets に変更がないか PR でメンションしてくれる。

References

Footnotes

  1. pnpm と Changesets を組み合わせて使用する PNPM https://pnpm.io › using-changesets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment