monorepoのリポジトリにRelease Drafterを導入して複数のタグを管理する[NestJSプロジェクトを例に]
はじめに
リリースノートの管理、手動でやると大変ですよね。
- 1日に複数回のリリースを行っている
- 複数のチームで同じリポジトリを触っている
といった状況だと、毎回のリリースの変更管理を手でやるのはなかなか骨が折れます。
こういった場合、Release DrafterというGitHub Actionを導入することで、リリース間にマージされたPR一覧をもとにリリースノートを生成できます。
このRelease Drafterはmonorepoのプロジェクトでも活用できます。
この記事ではNestJSプロジェクトを例に、monorepoのリポジトリにRelease Drafterを導入して、複数のタグを管理する様子を紹介してみます。
Release Drafterの流れ
Release Drafterに関しては以下の記事がとても参考になるので、合わせて読んでいただけると理解が深まるかと思います。
基本的なワークフローは次の通りです。
- Pull Requestを作成する
- PRのtitleやブランチ名に基づいてlabelを自動で付与する(autolabeler)
- 手動でラベルを設定してもよい
- ラベルが設定されたPRをマージする
- 次期リリースのリリースノートが下書き状態(draft)で生成される
- 次期バージョン番号は既存のタグを参照してあらかじめ決めたルールで解決される
- あるいはワークフロー上で自力で次期リリースのタグ番号を決めることも可能(CalVerの場合など)
- 次リリースに含まれるPR一覧がラベルによりカテゴライズされてリリースノートに載る
- リリースする段階になったら、draftをpublishしてリリース完了
よって、導入する際はまず次の内容を決定して導入を進めると良いでしょう。
- どのようなルールでリリースタグを作成するか、タグ名のルール
- Semantic Versioning, Calendar Versioning
- Semantic Versioningの場合はmajor, minor, patchの基準
- どのようなラベルをリポジトリ内で利用するかと、その設定ルール
- feature, bug fix, dependencies, ... etc.
- Release Drafterの組み込みのautolabelerでは以下の正規表現によるマッチに対応している
- 変更したファイルパス (
files
) - ブランチ名 (
branch
) - PRタイトル (
title
) - PR本文 (
body
)
- 変更したファイルパス (
ちなみに最近自社のプロダクトにいくつか追加していったのですが、CalVerで整備していきました。CalVerで利用するにはworkflowにて自前でバージョン番号を作る必要があります。CalVerの設定については次の記事が参考になります。
monorepoに導入する
monorepoの場合は少し工夫が必要です。monorepoに導入する場合は、tag-prefix
と include-paths
の項目を利用します。
tag-prefix
を指定した場合、過去のリリースタグをフィルタして次期バージョンを決定する際に、同じprefixを持ったタグのみにフィルタして次期リリース内容を決定してくれます。これを使うことで、ひとつのリポジトリに複数の命名ルールのタグを作成することができます。
- common-package@0.0.4 # draft。common-package@0.0.3をもとに決定される
- common-package@0.0.3 # リリース済み
- common-package@0.0.2
- common-package@0.0.1
- admin-package@0.0.4 # draft。admin-package@0.0.3をもとに決定される
- admin-package@0.0.3 # リリース済み
- admin-package@0.0.2
- admin-package@0.0.1
include-paths
は指定したパスに含まれるファイルが変更されたPRのみリリースノートに含まれるようになります。
例として、
-
common-package
とadmin-package
というパッケージを同一のリポジトリで開発している -
packages/common-package
とpackages/admin-package
というディレクトリで管理している - タグを
common-package@<major>.<minor>.<path>
,admin-package@<major>.<minor>.<path>
という風に分けて管理する
とした場合、次のように設定することで、そのパッケージ内のファイルを変更したPRのみを含んだリリースノートの生成が可能です。
package | tag-prefix | include-paths |
---|---|---|
common-package | common-package@ | packages/common-package/ |
admin-package | admin-package@ | packages/admin-package/ |
NestJSのmonorepoで試してみる
monorepoで利用する場合どんなイメージになるか、サンプルを作ってみます。NestJSのmonorepo modeでサクッとプロジェクトの構造を作って解説します。
この記事に書くにあたって作成したプロジェクトを以下のrepositoryにpushしています。よければ参考にしてみてください。
NestJSに対する解説はこの記事の本筋ではないので割愛します。以下のバージョンでジェネレーターを動かしています。
$ nest new --version
10.2.1
サンプルプロジェクトの作成
例として、次のような構成を作ってみます。
- 2つのアプリケーション
- products-api
- users-api
- 1つの共有ライブラリ
- shared
ジェネレーターを利用してプロジェクトの枠組みを生成します。
$ nest new products-api
$ cd products-api
$ nest generate app users-api
$ nest generate library shared
コマンドの結果、このようなファイル構成となりました。
./README.md
./apps/
./apps/products-api/
./apps/products-api/src/
./apps/products-api/src/app.controller.spec.ts
./apps/products-api/src/app.controller.ts
./apps/products-api/src/app.module.ts
./apps/products-api/src/app.service.ts
./apps/products-api/src/main.ts
./apps/products-api/test/
./apps/products-api/test/app.e2e-spec.ts
./apps/products-api/test/jest-e2e.json
./apps/products-api/tsconfig.app.json
./apps/users-api/
./apps/users-api/src/
./apps/users-api/src/main.ts
./apps/users-api/src/users-api.controller.spec.ts
./apps/users-api/src/users-api.controller.ts
./apps/users-api/src/users-api.module.ts
./apps/users-api/src/users-api.service.ts
./apps/users-api/test/
./apps/users-api/test/app.e2e-spec.ts
./apps/users-api/test/jest-e2e.json
./apps/users-api/tsconfig.app.json
./libs/
./libs/shared/
./libs/shared/src/
./libs/shared/src/index.ts
./libs/shared/src/shared.module.ts
./libs/shared/src/shared.service.spec.ts
./libs/shared/src/shared.service.ts
./libs/shared/tsconfig.lib.json
./nest-cli.json
./package-lock.json
./package.json
./tsconfig.build.json
./tsconfig.json
workflowの方針決め
次に、どのようなリリース管理を回していくかを設計します。今回のサンプルプロジェクトでは次のような方針とします。
- mainブランチ一本での運用とし、mainブランチに対してPRを作成、マージする
- Semantic Versioningとする
- 2つのアプリケーション、1つの共有ライブラリでそれぞれリリースタグを作成する
products-api@<major>.<minor>.<patch>
users-api@<major>.<minor>.<patch>
shared@<major>.<minor>.<patch>
- major, minor, patchの判定は次のようにする
- major:
major
のラベルを手動でつけた場合にmajorとする - minor:
feature
のラベルがついていたPRが含まれる場合、minorとする - patch: 上記以外。デフォルトでpatchとする
- major:
- auto labelerの設定
- ブランチ名の正規表現によるマッチで行う。
<category>/
のprefixをつけたブランチ名で運用する - 以下のカテゴリーを用意する
- feature
- bug fix
- docs
- ブランチ名の正規表現によるマッチで行う。
初期リリースを作っておく
まずはアプリケーション、ライブラリの最初のリリースを 0.0.1
として手動で作っておきます。
ここからの差分をRelease Drafterで管理します。
Release Drafterの導入
Release Drafterの導入には、大きく分けて2種類のファイル追加を行います。
- Release Drafterの設定ファイル
- GitHub Actionsのworkflow
設定ファイルの追加
.github/
配下に設定ファイルを追加します。autolabelerの設定はアプリケーション、ライブラリですべて共通なので、_extends
を使って重複分は1つのファイルにまとめることが可能です。
ここでは _extends
元のファイルを release-drafter-base-config.yml
としましょう。
categories:
- title: "🚀 Feature"
labels:
- feature
- title: "🐛 Bug Fixes"
labels:
- bug
- title: "📖 Docs"
labels:
- documentation
autolabeler:
- label: "feature"
branch:
- "/^feat/i"
- "/^feature/i"
- label: "bug"
branch:
- "/^fix/"
- label: "documentation"
branch:
- "/^docs/"
version-resolver:
major:
labels:
- "major"
minor:
labels:
- "feature"
default: "patch"
template: |
## Changes
$CHANGES
前述の通り、ブランチ名に対する正規表現のマッチをもとに自動でラベルが設定されるようにしています。feat/
とfeature/
の両方を許容するなど、柔軟に設定可能です。categories
の設定により、ラベルに応じてリリースノート内でPRがカテゴライズされて表示されます。
続いて、このファイルを拡張してアプリケーション、ライブラリの設定ファイルを追加します。それぞれ release-drafter-products-api.yml
, release-drafter-users-api.yml
, release-drafter-shared.yml
とします。ファイルの命名は特に制約はないのでプロジェクトごとに自由に決めましょう。
_extends: nestjs-monorepo-release-drafter-sample:.github/release-drafter-base-config.yml # extend元の設定ファイルをリポジトリ名:ファイルパスで指定
name-template: products-api@$RESOLVED_VERSION # リリースノートのタイトルはタグ名と同じにする
tag-template: products-api@$RESOLVED_VERSION # タイトルとタグ名を同じにする
tag-prefix: products-api@ # tagのprefixを指定
include-paths: # products-apiのリリースノートに含めるファイルパスを指定
- "apps/products-api/"
_extends: nestjs-monorepo-release-drafter-sample:.github/release-drafter-base-config.yml # extend元の設定ファイルをリポジトリ名:ファイルパスで指定
name-template: users-api@$RESOLVED_VERSION # リリースノートのタイトルはタグ名と同じにする
tag-template: users-api@$RESOLVED_VERSION # タイトルとタグ名を同じにする
tag-prefix: users-api@ # tagのprefixを指定
include-paths: # users-apiのリリースノートに含めるファイルパスを指定
- "apps/users-api/"
_extends: nestjs-monorepo-release-drafter-sample:.github/release-drafter-base-config.yml # 継承元の設定ファイルをリポジトリ名:ファイルパスで指定
name-template: shared@$RESOLVED_VERSION # リリースノートのタイトルはタグ名と同じにする
tag-template: shared@$RESOLVED_VERSION # タイトルとタグ名を同じにする
tag-prefix: shared@ # tagのprefixを指定
include-paths: # sharedのリリースノートに含めるファイルパスを指定
- "libs/shared/"
_extends
を使うことで共通部分は省略して設定ファイルを書くことができています。include-paths
の設定により、各アプリケーション、パッケージごとに指定したファイルパスの変更が含まれるPRのみをリリースノートに含めるようにしています。
ワークフローファイルの追加
設定ファイルが用意できたので、この設定ファイルに基づいて動作するワークフローを追加しましょう。
name: Release Drafter
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, synchronize]
jobs:
update_release_draft:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Draft products-api release
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-products-api.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Draft users-api release
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-users-api.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Draft shared library release
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-shared.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PRへのラベル付与と、リリースノートの作成ができるようにpermissionsの設定が必要です。autolabelerを利用しない場合はpull_requestのタイミングでのワークフロー実行は不要です。
これで設定自体は終わりです。あとはPRを作ってマージしていくのみです。
ファイルを変更する
feat/
のprefixをつけたブランチを作成して、apps/products-api
配下のファイルに変更を加えてcommit, PRを作成してみます。このとき、PRに対して自動でラベルが設定されます。
PRをマージすると再度release-drafterのワークフローが動作し、アプリケーション、ライブラリの次期バージョンのリリースのドラフトが作成されます。products-apiの分には今回の変更が一覧に出てきており、minorが上がっています。今回の修正に関係のないproducts-api以外のリリースノートには含まれていないのがポイントです。
今度はshared配下のdocumentの更新をしてみます。docs/
のprefixをつけたブランチとして、PRを作成します。この場合、documentation
というラベルがPRに対して自動で設定されます。
PRをマージしてリリースを確認すると、shared
のリリースのdraftに今回のPRが載り、minorではなくデフォルトのpatchとして次期タグが決定されています。リリース内の見出しもラベルに応じて変わっていることが分かります。
あとはこの繰り返しです。プロジェクトのルールに沿ってdraft状態のリリースを公開し、次にCIが回るとまたdraftのリリースが作成されるというサイクルで開発を進めることができます。
余談:公式にもリリースノートの生成機能あるよね?
公式にもリリースノートの生成機能があり、リリースの画面やAPIから利用することができます。
一方で、この公式の機能だけだと次期バージョン番号の決定までは行えず、リリースノートの生成までに留まります。「リリースの時に何らかまとまったドキュメントが得られれば良い」といった温度感であれば、こちらの公式の機能で十分です。プロジェクトごとに何をどこまで自動化するのかに応じて使い分けると良いと思います。
まとめ
Release Drafterを導入してmonorepoで管理しているアプリケーション・ライブラリのタグを複数作成する流れを解説しました。この記事では触れていませんが、リリースノート内の特定の文字列を置換する機能(Replacers)や、ラベルによるPRの除外機能などもあり、機能も豊富です。
ドキュメンテーションは大事である一方、手動で運用するとコストが見合わずに放置されがちです。コスパよく快適に運用したいものです。ぜひ参考にしてみてください。
Discussion