Monorepo におけるリリースフロー構築
はじめに
複数の Next.js アプリケーションを monorepo で管理しており、その際に作成したリリースフローについて解説します。
またリリースフロー・リリースノートの精度を高めるための仕組みについても解説します。
対象読者
- monorepo を用いて開発しているエンジニア
構築したリリースフローについて
構築したリリースフローでの実際の流れを簡単に説明します。
最初にリリース対象とするアプリケーションとバージョンの上げ方(major / minor / patch)を選択します。
選択し、ワークフローを実行するとターゲットとしたアプリケーションの package.json が自動更新され、今回のリリースに関する Pull Request ・ Draft 状態のリリースノートが作成されます。
main から切ったリリース用のブランチが作成されており、この Pull Request に書いてある通りに、デプロイやリリースノートの publish をこなしていくとリリースが完了するというフローになっています。
生成された Draft 状態のリリースノート
(hotfix の場合は main ブランチでワークフローを起動するのではなく、前タグからワークフローを patch 指定で使用します)
リリースフローを構成する技術要素
本題に入り、このリリースフローをどのように実現しているかについて話していこうと思います。
今回のリリースフローは大きく以下の4つを組み合わせて作成されています。
- GitHub CLI
- Release Drafter
- actions/labeler
- branch protection rule
それぞれの役割についてまとめる前に全体像が見えた方がわかりやすいかと思うので、気になる方は以下のトグルよりワークフローを確認してみてください。
ワークフロー全体像
name: 00_App release
run-name: ${{ github.event.inputs.target }} - release
on:
workflow_dispatch:
inputs:
target:
description: "Target application"
required: true
type: choice
options:
- app_a
- app_b
- app_c
semver:
description: "Semantic version"
required: true
type: choice
options:
- major
- minor
- patch
permissions:
contents: write
pull-requests: write
jobs:
create-pr-and-release-note:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# コミットできるユーザー設定
- name: Set up Git user
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# リリースブランチ名を決める
- name: Set release branch name
id: set_release_branch_name
run: |
echo "release_branch=release_${{ github.event.inputs.target }}_$(TZ=Asia/Tokyo date +'%Y%m%d%H%M')" > "${GITHUB_OUTPUT}"
- name: Create release branch
run: |
git checkout -b ${{ steps.set_release_branch_name.outputs.release_branch }}
- name: Push release branch
run: |
git push origin ${{ steps.set_release_branch_name.outputs.release_branch }}
- name: Create version update branch
run: |
git checkout -b chore/version-update
# npm version で package.json のバージョンを更新
# --no-git-tag-version でコミット時にタグを作成しない
- name: version update
id: version_update
run: |
cd apps/${{ github.event.inputs.target }}
new_version=$(npm --no-git-tag-version version ${{ github.event.inputs.semver }})
git add package.json
git commit -m "${new_version}"
echo "new_version=${new_version}" >> "${GITHUB_OUTPUT}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Push version update branch
run: |
git push origin chore/version-update
- name: Create version update PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ラベル付けすることで Release Drafter が正しく解釈したバージョンでリリースノートを作成できる
run: |
gh pr create \
-B ${{ steps.set_release_branch_name.outputs.release_branch }} \
-t "chore: ${{ github.event.inputs.target }} version update ${{ steps.version_update.outputs.new_version }}" \
-a "${{ github.actor }}" \
-b "Update version to ${{ steps.version_update.outputs.new_version }}" \
-l "${{ github.event.inputs.semver }}" \
-l "${{ github.event.inputs.target }}"
# バージョン更新 PR をマージ
- name: Merge version update PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr merge -s -d --admin
# リリース後 main に反映するための PR
- name: Check and Create Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create \
-B main \
-t "[${{ github.event.inputs.target }}] release ${{ steps.version_update.outputs.new_version }}" \
-a "${{ github.actor }}" \
-F ".github/release_pr_template.md"
- name: Create release note
uses: release-drafter/release-drafter@v6
with:
config-name: ${{ github.event.inputs.target }}-release-drafter.yml
commitish: ${{ steps.set_release_branch_name.outputs.release_branch }} # リリースターゲットのブランチ名を指定
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GitHub CLI
みなさんお馴染みかと思いますが GitHub CLI になります。
GitHub CLI はリリースフローの中で、 Pull Request の作成やマージなどを担います。
リリースフローを抜粋したものが以下になりますが、入力した targer (application)
, semver
から package.json の自動更新・Pull Request 作成を以下で実現しています。
# npm version で package.json のバージョンを更新
- name: version update
id: version_update
run: |
cd apps/${{ github.event.inputs.target }}
new_version=$(npm --no-git-tag-version version ${{ github.event.inputs.semver }})
git add package.json
git commit -m "${new_version}"
echo "new_version=${new_version}" >> "${GITHUB_OUTPUT}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Push version update branch
run: |
git push origin chore/version-update
- name: Create version update PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ラベル付けすることで Release Drafter が正しく解釈したバージョンでリリースノートを作成できる
run: |
gh pr create \
-B ${{ steps.set_release_branch_name.outputs.release_branch }} \
-t "chore: ${{ github.event.inputs.target }} version update ${{ steps.version_update.outputs.new_version }}" \
-a "${{ github.actor }}" \
-b "Update version to ${{ steps.version_update.outputs.new_version }}" \
-l "${{ github.event.inputs.semver }}" \
-l "${{ github.event.inputs.target }}"
# バージョン更新 PR をマージ
- name: Merge version update PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr merge -s -d --admin
# リリース後 main に反映するための PR
- name: Check and Create Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create \
-B main \
-t "[${{ github.event.inputs.target }}] release ${{ steps.version_update.outputs.new_version }}" \
-a "${{ github.actor }}" \
-F ".github/release_pr_template.md"
ポイントとしてバージョン更新をするための Pull Request に対して、 semver と同じラベルをつけています。
そうすることで次のリリースノート生成のタイミングで、Release Drafter の version-resolver
で解釈され、正しいバージョンでのリリースノート生成につながります。
Release Drafter
Release Drafter を用いてリリースノートの自動作成を実現しています。
monorepo でのリリースノート生成であるため、特定のラベルがついた Pull Request からリリースノートを構築するための include-labels
オプションは必須になります。
name-template: 'app_a@$RESOLVED_VERSION'
tag-template: 'app_a@$RESOLVED_VERSION'
tag-prefix: 'app_a@'
include-labels:
- 'app_a' # 特定のラベルがついた Pull Request からリリースノートを構築する
template: |
$CHANGES
categories: # 特定のラベルがついた Pull Request をそのタイトルの下に表示する
- title: '新機能の追加'
label: 'feat'
- title: 'バグ修正'
label: 'fix'
- title: 'リファクタ'
label: 'refactor'
- title: 'ドキュメントの追加・修正'
label: 'docs'
- title: 'テストの追加・修正'
label: 'test'
- title: 'その他の変更'
label: 'chore'
version-resolver: # ラベルによってバージョンをどのように上げるかを判断する
major:
labels:
- 'major'
minor:
labels:
- 'minor'
patch:
labels:
- 'patch'
default: minor
ワークフロー内では1番最後に実行しています。
これによって package.json の更新も含まれた内容でリリースノートが生成されます。
- name: Create release note
uses: release-drafter/release-drafter@v6
with:
config-name: ${{ github.event.inputs.target }}-release-drafter.yml
commitish: ${{ steps.set_release_branch_name.outputs.release_branch }} # リリースターゲットのブランチ名を指定
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
(現状アプリケーションの個数分 **-release-drafter.yml
という定義ファイルを用意しているため、1つで済む方法をご存知の方がいらっしゃれば教えていただきたいです 🙏)
actions/labler
Release Drafter でリリースノートを自動生成する際に、ラベルごとにカテゴリ分けをすることができ、それに役立つのが actions/labeler
アクションになります。
ブランチ名の prefix から Pull Request にラベルを付けることで、リリースノートにした際にみやすくカテゴリ分けされます。
リリースフローに直接関わるわけではありませんが、リリースノートの精度を上げるためには必須の機能です。
docs:
- head-branch:
- ^docs/
feat:
- head-branch:
- ^feat/
fix:
- head-branch:
- ^fix/
refactor:
- head-branch:
- ^refactor/
test:
- head-branch:
- ^test/
chore:
- head-branch:
- ^chore/
# ディレクトリごとにラベルを付与する
app_a:
- changed-files:
- any-glob-to-any-file:
- apps/app_a/**
- dockerfiles/app_a/**
- packages/configs/src/app_a/**
app_b:
- changed-files:
- any-glob-to-any-file:
- apps/app_b/**
- dockerfiles/app_b/**
- packages/configs/src/app_b/**
Pull Request についたラベルごとにカテゴリ分けされている
またディレクトリごとにもラベルを付与しています。これは Release Drafter でリリースノートを生成する際に、どのアプリケーションに関する Pull Request を取得するかに起因するため重要です。
branch protection rule
最後は GitHub の branch protection rule です。
この branch protection rule を用いて、プッシュできるブランチに制約をかけています。
こちらもリリースフローに直接的に関わるわけではないですが、ブランチ名に制限を持たせることでラベル付与の精度を高めています。
上の命名規則にあったブランチのみ push できるように
設定はシンプルで上の画像のようにプッシュできるブランチ名を正規表現で絞っておき、 Restrict creations にチェックを入れるだけです。
この設定を加えることで、結果としてリリースノートが自然にカテゴリ分けされるようにしています。
まとめ
この記事では Monorepo におけるリリースフローの構築として、GitHub CLI / Release Drafter / actions/labeler / branch protection rule を用いたリリースフローを紹介しました。
このリリースフローを構築し、以下のような利点を感じています。
- アプリケーションが増えてもリリースフローは1つで済むので管理が楽
- actions/labeler と branch protection rule により、リリースノートのクオリティが担保できている
- PR ベースでやり取りできるため、stg までは担当者 A, prd は担当者 B というケースでも問題なく対応できる
- PR にデプロイしたログなどを残しておいたことで実際に引き継ぎがスムーズにいきました
- PR に必要な手順が書いてあるので、初めてリリースするメンバーでも馴染みやすい
もし Monorepo でのリリースフローに困っている方がいらっしゃればぜひ参考にしてみてください。
参考
Discussion