changesetsを使ってWebサイトのバージョン管理を自動化する
はじめに
Web サイトのバージョン管理にchangesetsを導入してみたところ、めちゃめちゃ便利でした。ただ、ドキュメントがわかりにくかったので、使い方やつまった点など書いていこうと思います。
changesetsにはnpm
への公開機能などもあり、Chakra UI、Astro、Emotionなど名だたるパッケージのバージョン管理に使用されています。ちなみにYamada UIでも使っています。今回はnpm
への公開機能ではなく、Webサイトなどのバージョン管理をすることに焦点を当てたいと思います。
よく比較されるツールでsemantic-releaseというものもあるみたいです。こちらはコミットごとにバージョンが上がっていくようで、自分は適当にコミットすることも多いため、より柔軟にバージョンの操作ができるchangesetsのほうを使っています。
changesetsとは
changesetsはセマンティックバージョニングに基づいたバージョン管理を効率化できるツールです。好きなタイミングで変更のログを書き込むことができ、リリースノートの作成なども GitHub Action 設定すれば自動でやってくれるのでめっちゃ便利です。
ざっくり、以下のような機能があります。
-
変更ログの管理
- どのレベル(Major, Minor, Patch)の変更にするかを都度選択可能
- コミットや PR との紐づけもしてくれる
-
リリースノートの自動作成
- コマンドでの作成も可能
- GitHub Action もあり、リリースの PR を自動でつくってくれる
- PR、コミット、ユーザーの紐づけもやってくれる
リリースノートはこんな感じになります。
PRはこんな感じで作ってくれます。
準備
公式のドキュメントはこちらになります。
intro-to-using-changesets.md
インストールと初期化
cli ツールは@changesets/cli
に入っているので、これをインストールします。
公式ドキュメントだとそのままインストールしていますが、devDependencies
の方に入れればいいと思います。
npm install --save-dev @changesets/cli
cli ツールを入れたら、まず初期化します。
これにより、ルートフォルダに.changeset
フォルダが作成されます。
npx changeset init
package.json
の設定
versioning-apps.md
changesetsでバージョン管理するためにpackage.json
内に最低限、以下の3項目が含まれる必要があります。逆に、この3項目が含まれるpackage.json
さえ作成すれば、どんなプロジェクトでもバージョン管理可能なようです。
{
"name": "package",
"version": "1.0.0",
"private": true
}
- name: パッケージ名を指定します。
- version: changesetsは
version
の値を元にバージョンをあげていきます。o.o.o
から始めたい場合は、ここを書き換えるとそこからバージョンを上げれます。 - private:
private
はfalse
の場合、npm
などに公開するためのスクリプトが走ります。今回は公開しないので、true
にします。
config.json
の設定
npx changeset init
を実行すると、.changeset/config.json
が作成されます。
プログラムをnpm
などに公開しない場合は、以下の設定を追加する必要があります。
{
...
"privatePackages": {
"version": true,
"tag": true
}
}
使い方
changesetsのコマンドで、変更の記録が記載されたファイルを生成することができ、そのファイルを含めてコミットすることで、コミットおよびそれが含まれるPRと合わせて管理できます。
記録された変更を元に、CHANGELOG.md
ファイルにリリースノートを吐き出すことが可能です。
変更を記録する
変更を書き込む場合は、以下のコマンドを実行します。
npx changeset
実行すると、以下のようにどのレベルのバージョン変更をしたのか聞かれます。
🦋 What kind of change is this for changeset-zenn? (current version is 0.1.0) …
❯ patch
minor
major
選択すると、変更内容を聞かれるので、入力します。
🦋 What kind of change is this for changeset-zenn? (current version is 0.1.0) · patch
🦋 Please enter a summary for this change (this will be in the changelogs).
🦋 (submit empty line to open external editor)
🦋 Summary ›
入力してEnter
を押下すると、これでいいですか?と聞かれるので再度Enter
を押下して、変更の記録が記載されたファイルの生成は完了になります。
🦋 === Summary of changesets ===
🦋 patch: changeset-zenn
🦋
🦋 Is this your desired changeset? (Y/n) › true
完了すると、.changeset
フォルダ配下に先ほどの変更が記録されたmdファイルが作成されます。変更の記録を追加するたびに、このファイルは増えていきます。
mdファイルなので、バージョン変更のレベルの変更や、変更内容の文章を更新したい場合は直接変更可能です。
このファイルをコミットに含め、変更の記録は完了になります。
CHANGELOG.md
の作成
リリースのタイミングで、溜まった変更の記録を元にCHANGELOG.md
ファイルを生成します。
以下のコマンドを実行します。
npx changeset version
実行すると、今まで溜まっていた.changeset
配下のmdファイルが削除され、CHANGELOG.md
ファイルが更新されます。Minor
の変更とPatch
の変更二つ実施した場合は以下のようになります。バージョンは0.1.0
→ 0.2.0
に変更になりました。package.json
内のversion
の項目も一緒に変更されます。
# changeset-zenn
## 0.2.0
### Minor Changes
- 8ccbd1d: Test changelog3.
### Patch Changes
- 0903189: Test changelog2.
- 52036f3: Test changelog.
GitHub Action
changesetsが、Github Actionも用意してくれており、これがすごく便利なのでchangesetsを使うのであれば、絶対使うべきです。
npx changeset version
を実行し、その変更を含めコミットした上で、PRの作成まで自動でしてくれます。追加の変更がpushされると、それも取り込んで既存のPRを上書きしてくれます。
設定方法
以下のようなワークフローでmain
ブランチにプッシュされるごとに、変更が記録されたmdファイルがあるかチェックし、あればそれをPRに反映してくれます。
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: npm install
- name: Create Release Pull Request
uses: changesets/action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
こういう感じのPRを作成してくれます。
リリースの時はこのPRをマージするだけで良くなり、手動でnpx changeset version
を実行する必要はなくなります。
PRを紐づける
先ほどのPRで作成されたものは、どのコミットに紐づいているかは記載されていますが、どのPRか、誰がやったかは含まれていません。
リリースノートの文章を変更するには、.changeset/config.json
のchangelog
の設定でカスタマイズできます。(詳細はリリースノートをカスタマイズする)
PRを紐づけれるものはすでに提供されているのでそれを利用します。
@changesets/changelog-github
をインストールします。
npm install --save-dev @changesets/changelog-github
.changeset/config.json
を変更して、リリースノートの生成に@changesets/changelog-github
を使用するように設定します。changelog
の項目を以下のように変更してください。2つ目の項目のrepo
には、自身のプロジェクトのリモートリポジトリを記載してください。
{
...
- "changelog": "@changesets/cli/changelog",
+ "changelog": [
+ "@changesets/changelog-github",
+ { "repo": "108yen/changeset-zenn" }
+ ],
...
}
これで設定は完了であとは同じように利用すれば、自動作成してくれるPRの内容に、変更が含まれるPRや変更を実施した人がリンクされます。
リリース時に他の処理(デブロイなど)を実行する
changesetsが作成したPRをマージする際に、デプロイ処理を走らせたい場合があると思います。
以下のように、別のワークフローを設定すれば可能です。
このワークフローは、mainをターゲットとしたPRがクローズされた時にトリガーされます。加えて、そのPRがマージされており、changeset-release
で始まる名前のブランチからのPRであればデプロイジョブを実行するように条件文を設定しています。
changesetsはchangeset-release/[ターゲットブランチ]
という名称でブランチを作成し、そこからリリースのPRを作成するので、これでリリースのPRがマージされた時のみに処理を実行することができます。
name: Deploy
on:
pull_request_target:
types:
- closed
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
deploy:
name: Deploy
# changeset-release/**からのPRがマージされていれば実行する
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'changeset-release')
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Deploy
run: npm run deploy # デプロイ処理とか
changesets/action@v1
の機能を使って、リリース用のPRの作成もデプロイも一つのワークフローでやる方法もあります。ただ、「デフォルトのブランチにプッシュされるときは必ず変更ログが記録されている」という前提があるので注意が必要です。
releaseワークフローでやるパターン
changesets/action@v1
のpublish
にスクリプトを渡すと、「変更ログが一つもない場合」、実行してくれます。そのため、「変更ログが一つもない状態」==デプロイ処理を走らせる必要がある
のであればこの方法でよいと思います。デフォルトのブランチにプッシュするときは必ず変更ログを残すという運用が立てついている必要があります。
逆にそうでないのであれば、ワークフローを分けましょう。例えば、変更ログをCHANGELOG.md
に吐き出したあとは「変更ログが一つもない状態」になります。このまま変更ログを作成せずデフォルトのブランチにプッシュするとpublish
に渡したスクリプトは実行されてしまいます。
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: npm install
- name: Create Release Pull Request
uses: changesets/action@v1
with:
publish: npm run deploy # デプロイ処理とか
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
リリース用のブランチが別にある場合
Vercelのように設定したブランチにプッシュするとデプロイされる場合があります。changesetsが発行するPRのターゲットブランチは変更することが可能です。release
ブランチを作成して、それをデプロイ用のブランチにするのがおすすめです。
changesets/action@v1
のbranch
パラメーターにターゲットブランチを設定します。
これでデフォルトブランチ(main
)からチェックアウトし、release
ブランチに向けたPRが作成されます。
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: npm install
- name: Create Release Pull Request
uses: changesets/action@v1
with:
branch: release # これを追加する
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
こんな感じになります。
補足
リリースノートをカスタマイズする
modifying-changelog-format.md
PRを紐づけるでも変更した.changeset/config.json
のchangelog
の項目でカスタマイズが可能です。.changeset
フォルダ配下にchangelog-function.js
ファイルを作成し、それをchangelog
で読み込みます。
...
- "changelog": "@changesets/cli/changelog",
+ "changelog": "./changelog-function.js",
...
getReleaseLine
の出力がリリースノートの1行になります。getDependencyReleaseLine
はモノレポ構成になっているような場合に、依存関係が記載されるものです。(Webで使うときにはあまり気にする必要はないと思います。)
イメージ
getReleaseLine
の戻り値として返した文字列が、赤枠で囲った1行分になります。
getDependencyReleaseLine
の戻り値は、以下の赤枠で囲った部分になります。
getReleaseLineの引数
引数は以下脳ようになっています。
type getReleaseLine(
changeset: {
// This is the string of the summary from the changeset markdown file
summary: string
// This is an array of information about what is going to be released. each is an object with name: the name of the package, and type, which is "major", "minor", or "patch"
releases
// the hash for the commit that introduced the changeset
commit
},
// the type of the change this changeset refers to, as "major", "minor", or "patch"
type
// This needs to be explained - see @changesets/changelog-github's code for how this works
changelogOpts
) => string
async function getReleaseLine(changesets, type, options) {
return "changelog"
}
async function getDependencyReleaseLine(changesets, dependenciesUpdated,options) {
return
}
const changelogFunctions = {
getReleaseLine,
getDependencyReleaseLine
}
module.exports = changelogFunctions
@changesets/changelog-github
も同じ構成になっています。
値を渡す
options
で受け取るパラメータを設定するには以下のようにします。これで、options.repo
から108yen/changeset-zenn
が取れます。
...
- "changelog": "@changesets/cli/changelog",
+ "changelog": [
+ "@changesets/changelog-github",
+ { "repo": "108yen/changeset-zenn" }
+ ],
...
GitHub の release を作成する
GitHubの右側に表示されるやつです。
特に公開するわけではないので必須ではないのですが、自分は作成したかったので設定しています。changesetsではGitHubのリリースの作成機能が、npm
の公開の機能と紐づいており、それ単体で動かせなかったため、専用のGitHub Actionを作ってみました。よければ使ってください。
name: Deploy
on:
pull_request_target:
types:
- closed
branches:
- release
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
deploy:
name: Deploy
permissions:
contents: write
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'changeset-release')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Create GitHub Release
uses: 108yen/changeset-release@v1
with:
target: release # デフォルトブランチ以外にリリースを作成する場合は、ターゲットブランチを設定してください。
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy
run: [デプロイ処理とか]
自分で管理したい場合は、以下のコードコピペして使ってください。
やっていることはそんなにむつかしくなく、package.json
のversion
とCHANGELOG.md
該当のバージョンの内容を読みだして、それらをもとにGitHubのリリースを作成しています。
web ページにバージョンを表示する
changesetsはpackage.json
のversion
も変更してくれるので、ここの値を取り出せば良いです。find-packagesというのがあるので、使うと便利です。
npm install find-packages
NextjsのApp Routerであれば以下のようにして表示できます。
import { findPackages } from "find-packages";
export default function Home() {
const packages = await findPackages("./");
const {version} = packages[0].manifest;
return <p>{version}</p>
}
tag を発行する
以下のコマンドを実行すると、git tagを作成してくれます。
pushはしてくれないので、GitHub Action上でタグを発行する場合はgit push --follow-tags
でtagをpushしてください。
npx changeset tag
まとめ
長くなりましたが、使いこなせるとバージョン管理をほとんど自動化できるので、ぜひ使ってみてください。monorepoの管理や、GitHub Actionの機能など紹介しきれなかった機能もあるので、興味があれば調べてみてください!
Discussion