時期を決めて定期的に更新するnpmパッケージをChangesetsで管理する
毎月や半年に一回といったように、リリースする時期(間隔)を決めて更新するタイプのパッケージがあります。 具体的には、次のtextlintのプリセットルールは1月と7月という形で半年に一回リリースしています。
- textlint-ja/textlint-rule-preset-japanese: textlint rule preset for Japanese.
- textlint-ja/textlint-rule-preset-ja-technical-writing: 技術文書向けのtextlintルールプリセット
なぜ、このようにリリースする時期を決めているかというと、これらのパッケージは他のパッケージに依存していて、他のパッケージの更新がそのままメジャーアップデートになりやすい性質があるためです。 そのため、依存を更新してリリースすると、頻繁にメジャーアップデートしないといけなくなります。
具体的には、Semantic Versioningに則っているので次のルールでバージョンを更新しています。
- Patch リリース
- 各ルールのバグ修正 (警告を減らす方向への修正)
- ドキュメントの改善
- 内部的な変更 (リファクタリングやテストの改善など)
- リリース失敗時の再リリース
- Minor リリース
- 各ルールのバグ修正 (警告を増やす方向への修正)
- 新オプションの追加
- 既存ルールの非推奨化
- Major リリース
- プリセットへのルールの追加
- プリセットからルールの削除
- 既存のオプション値の変更
ルールの追加やルールのメジャーアップデートがあると、プリセットもメジャーアップデートが必要になります。 頻繁にメジャーアップデートすると、ユーザーがメジャーアップデートをするのが面倒になるので、ある程度の間隔にまとめてリリースするようにしています。
まとめてリリースすると、それまでの間に最新のルールを使えない問題があります。
その対応として@next
のdist-tag付きでインストールすると最新のルールも含んだパッケージをインストールできるようにしています。
npm install textlint-rule-preset-ja-technical-writing@next
このような、定期的にリリース + 最新を常に@next
のdist-tagでリリースといったパッケージを管理するために、Changesetsを利用しています。
Changesetsの特徴
Changesetsはmonorepoのパッケージのリリースツールとして知られていますが、個別のパッケージでも利用できます。 特徴として、PRごとの変更内容とその変更内容のsemverを記録して、それをまとまったものとしてリリースできるというものです。
マージのたびにリリースする場合は、変更内容は覚えているのでその場で書けます。 しかし、時間が空くと何を変更したのか忘れてしまうことがあります。 そのため、変更(PR)ごとの変更内容を記録しておくことで、リリース時はただリリースするだけで良くなります。
Changesetsでは、変更内容のリリースノートだけではなく、その変更がpatch
/minor
/major
のどれになるかを記録しています。
これによって、リリースするときは記録から自動的に次のバージョンを決めてくれます。
Changesetsの導入
ドキュメントや次の記事と基本的に同じになるので、そちらを参照してください。
- changesets/docs/intro-to-using-changesets.md at main · changesets/changesets
- Changesetsで頑張らないリリース(モノレポ対応)
基本的な導入の流れを書くと次のようになります。
- changesetsをインストールする
- changeset-botをリポジトリにインストールする
- Changesets Release Actionを設定する
changesetsは最初に設定だけして、その後はあまり意識しないです。
changeset-botをインストール後に、PRを出すとBotがChangesets(変更内容)を記録するかを尋ねてきます。 バージョンに影響があるものなら、Botの指示に従ってChangesetsを記録していくだけです。
このChangesetsは次のようなMarkdownになっていて、Frontmatterにsemverを記録して、あとは変更内容を書くだけです。 Botの指示に従ってやれば、ブラウザ上で編集できるのでわざわざエディタを開く必要もありません。
---
"textlint-rule-preset-ja-technical-writing": major
---
:sparkles: [ルール名](https://example.com)をアップデート
変更点の解説
パッケージのリリースは、Changesets Release Actionを使ってCIから行うように設定できます。 CIからnpmにリリースするにはNPMのtokenが必要ですが、次のような感じで設定ファイルを一つ書くだけです。
name: Release
on:
push:
branches:
- master
permissions:
contents: write
pull-requests: write
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Dependencies
run: yarn install
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
publish: yarn run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.SHARED_BOT_NPM_TOKEN }}
{}
ここまで設定できると、PRをマージするたびにそこまでの変更履歴をまとめたVersion PackagesというPRを作ってくれます。 このPRには、次のバージョンに入る変更がまとまっているので、そのままマージするとリリースされます。
リリースもブラウザ上でできるので、PRを出した時にちゃんと変更内容を書いていれば、リリースは簡単です。
Canary Release
先ほどまでの設定で、まとめてリリースはできるようになります。 半年に一度のリリースなので、リリースするまで最新のバージョンが利用できないのは不便です。
changesetsにはSnapshot Releasesという仕組みがあり、いわゆるCanary Releaseが可能です。
この設定も簡単で、次のような感じで設定ファイルを一つ書くだけです。
{% raw %}
name: "Snapshot Release@next"
on:
push:
branches:
- master
permissions:
contents: read
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org' # to create .npmrc file with NODE_AUTH_TOKEN
node-version: 18
- name: Install Dependencies
run: yarn install
- name: "Release @next"
run: |
yarn changeset version --snapshot next
yarn changeset publish --no-git-tag --snapshot --tag next
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.SHARED_BOT_NPM_TOKEN }}
これで、PRをマージするたびに0.0.0-next-20230716010722
のようなバージョンでnpmへリリースできます。
npmのdist-tagという仕組みを使い、通常のnpm install
した時に参照される@latest
ではなく、適当な@next
のようなタグをつけてパッケージを公開できます。
# @latestのdist-tagがインストールされる
npm install textlint-rule-preset-ja-technical-writing
# 次と意味は同じ
npm install textlint-rule-preset-ja-technical-writing@latest
# @nextのdist-tagがインストールされる
npm install textlint-rule-preset-ja-technical-writing@next
これによって、通常のユーザーは@latest
をインストールしますが、試したい人だけ@next
で最新のバージョンをインストールできます。
定期的なリリースのタイミングをGitHub Actionsで通知する
半年に一回リリースしますが、リリース自体は手動でVersion PackagesのPRをマージする必要があります。 このタイミングを忘れてしまう問題もあるので、次のようなGitHub Actionsを半年に一回リリース用のIssueを作成して通知しています。
name: Create ReleaseIssue
on:
schedule:
# 1月/7月にリリース
- cron: "0 0 1 */6 *"
jobs:
create_issue:
name: Create ReleaseIssue
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Create team sync issue
uses: imjohnbo/[email protected]
with:
assignees: "azu"
labels: "Type: Release"
title: "Next Release"
body: |
### 次のリリースの準備ができました
- [ ] マージ忘れのPRがないかを確認
- [ ] Version PackagesのPRをマージ
- [ ] 新しいバージョンがリリースされたことを確認
pinned: false
close-previous: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
おわりに
Changesetsはリリースまでの間隔をあけるようなプロジェクトにはとても便利です。
これらの仕組みを使って、それぞれのパッケージのメジャーアップデートをリリースしています。
- Release v8.0.0 · textlint-ja/textlint-rule-preset-japanese
- Release v8.0.0 · textlint-ja/textlint-rule-preset-ja-technical-writing
自分が管理する大部分のパッケージは、マージのたびにリリースしています。 この場合のパッケージのリリースには、GitHub Releasesを使った方法を利用しています。 詳細は次の記事を参照してください。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。