はじめに
こんにちは、Acsim 開発チームの笹沢です。
AI 要件定義サービス Acsim のリリースフローを整備しました。目指したのは、開発者が普段どおり PR を main にマージしているだけで、バージョニングされた Draft Release が自動で出来上がっている状態です。リリース時にやることは Draft Release の内容を確認して Publish ボタンを押すだけです。本記事では、このフローを実現するために取り組んだ4つの取り組みを紹介します。
リリースフローの全体像
このフローで作成されるリリースノートの例です。AI による補足が付いているので、非開発者にも内容が伝わりやすくなっています。

それでは、各取り組みを詳しく見ていきます。
取り組み1: GitHub Release の Publish をデプロイのトリガーにする
本番環境へのデプロイのトリガーを GitHub Release の Publish にしました。
デプロイの起点としてよく使われるのは production ブランチへのマージです。シンプルで分かりやすい反面、リリース内容の記録はブランチには残らないため、いつ何をリリースしたかを管理したり追跡したりするには不向きです1。
GitHub Release の Publish を本番デプロイの起点にすると、いつ・何をリリースしたかが Tag・リリースノートとともに GitHub 上に一元的に記録されます。
緊急ロールバック用に workflow_dispatch も用意しています。障害時に前の安定版の SHA を指定して手動デプロイできるようにしています。
# deploy-production.yml
on:
release:
types: [published]
workflow_dispatch:
inputs:
sha:
description: "デプロイ対象のコミット SHA"
取り組み2: Release Drafter で Draft Release を自動生成する
GitHub Release の Publish がデプロイの起点になったことで、次は「Draft Release の中身をどう管理するか」が重要になります。手動管理では漏れが発生するため、Release Drafter を導入しました。PR を main にマージするたびに Draft Release が自動で更新されます。
Release Drafter の役割は2つあり、それぞれトリガーが異なるため Workflow を分けています。
① PR タイトルからの自動ラベル付与
PR が作成・更新されたタイミングで、Conventional Commits に基づいてラベルを自動付与します。PR タイトルが feat: で始まれば feature ラベルが、fix: なら fix ラベルが付きます。
ラベル付与だけを行うため disable-releaser: true としています。
# .github/workflows/labeler.yml
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
label:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
with:
# ラベル付与のみ。Draft Release の更新は行わない
disable-releaser: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ラベルと PR タイトルの対応は .github/release-drafter.yml の autolabeler で定義します。
# .github/release-drafter.yml(抜粋)
autolabeler:
- label: "feature"
title:
- "/^feat:/i"
- label: "fix"
title:
- "/^fix:/i"
- label: "documentation"
title:
- '/^docs(\(.+\))?:/'
② Draft Release の自動更新とバージョン計算
PR が main にマージされたタイミングで、付与済みのラベルに基づいて Draft Release を自動で作成・更新します。Draft Release を開けば「次のリリースに何が含まれるか」が一目でわかります。
自動生成された Draft Release を見ると、PR のタイトル種別ごとに分類されていることが分かります。

バージョン番号もラベルから自動計算されます。
| PR ラベル | カテゴリ | バージョン変更 |
|---|---|---|
feature | 機能追加 | Minor |
fix | バグ修正 | Patch |
chore, refactor, documentation | その他 | Patch |
major, breaking | 大規模改修 | Major |
こちらのワークフローでは disable-autolabeler: true としています2。
# .github/workflows/release-drafter.yml
on:
push:
branches:
- main
jobs:
update_release_draft:
permissions:
contents: write
pull-requests: read
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
with:
# ラベル付与は labeler.yml で実行済みのため無効化
disable-autolabeler: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
これにより、Draft Release を覗けばこれから何がリリースされるのか確認できるようになりました。
取り組み3: リリースブロック機構の導入
リリース時は Staging 環境で動作確認を行います。このとき新規の PR が main にマージされると、動作確認済みの内容とリリースされる内容に食い違いが生じます。
従来は Slack で「リリース作業中なのでマージしないでください」と呼びかけていましたが、Slack を見ていないメンバー(私です)が気付かずにマージしてしまうことがありました。そのため仕組みでブロックする必要がありました。
最初に検討したのは GitHub の Lock branch 機能です。しかし、この機能を有効にするには Admin 権限が必要であり、最小権限の原則から逸脱するため採用できませんでした。
release-lock ブランチで状態管理
Admin 権限なしで実現するアプローチとして、release-lock という特別なブランチの存在有無でロック状態を表現する方法を採用しました3。
内容はシンプルで workflow_dispatch で lock を選択すれば release-lock ブランチが作成され、unlock を選択すれば release-lock ブランチが削除されるだけです。
# .github/workflows/release-lock.yml
on:
workflow_dispatch:
inputs:
action:
type: choice
options: [lock, unlock]
jobs:
toggle:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Toggle lock
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
REPO="${{ github.repository }}"
BRANCH="release-lock"
if [ "${{ inputs.action }}" = "lock" ]; then
SHA=$(gh api repos/$REPO/git/refs/heads/main --jq '.object.sha')
gh api repos/$REPO/git/refs \
-f ref="refs/heads/$BRANCH" -f sha="$SHA" 2>/dev/null || echo "Already locked"
else
gh api repos/$REPO/git/refs/heads/$BRANCH -X DELETE 2>/dev/null || echo "Already unlocked"
fi
もう1つのワークフローが、release-lock ブランチの存在をチェックするものです。ブランチが存在すればジョブが失敗するようになっています。
# .github/workflows/check-release-lock.yml
on:
pull_request:
branches:
- main
merge_group:
jobs:
check-release-lock:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- name: Check if locked
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if gh api repos/${{ github.repository }}/git/refs/heads/release-lock &>/dev/null; then
echo "::error::Release lock is active. Please wait until the release is complete."
exit 1
fi
echo "Not locked - OK to merge"
最後にステータスチェックにこのワークフローを追加します。

これにより、PR のステータスチェックと Merge Queue の両方でロック状態(release-lock ブランチの有無)が検証されるため、リリース作業中に PR がマージされることはありません4。
取り組み4: リリースノート作成を AI で支援する
Release Drafter が生成する Draft Release には PR タイトルの一覧が並びますが、そのままではリリースノートとしては情報が不足しています。各 PR の内容を読み解き、関連する変更をグループ化し、わかりやすい説明を書く作業は手間がかかります。
この作業を AI コーディングエージェントで自動化しています。以下のような手順書を用意し、AI に実行させるだけでリリースノートが完成します。
gh release listで Draft Release のタグ名を取得し、gh release viewで本文を取得する- Draft Release に含まれる各 PR の内容を確認する
- 関連する PR をグループ化し、技術的な詳細とビジネス的な価値の両方を伝えるリリースノートを作成する
手順書にはリリースノートの構成例も定義しています。機能追加・バグ修正・その他のカテゴリに分け、各項目に対応する PR へのリンクを付ける形式です。AI はこの構成例に従ってリリースノートを生成するため、毎回一貫したフォーマットのリリースノートが得られます。
まとめ
本記事では、GitHub Release の Publish を起点としたリリースフローの整備について、4つの取り組みを紹介しました。
- GitHub Release の Publish をデプロイのトリガーに ── リリース履歴が Tag・リリースノートとして一元管理される
- Release Drafter で Draft Release を自動生成 ── PR のラベルに基づくカテゴリ分けとバージョン計算を自動化
- Release Lock の導入 ── リリース作業中の PR マージを仕組みでブロック
- AI でリリースノート作成を支援 ── PR の内容を読み解き、整理されたリリースノートを自動生成
もし GitHub Release 起点のリリースフローを整備したい方がいれば参考にしてみてください。
Footnotes
-
Trunk Based Development では、Tag からのリリースは多くのチームにとって良い最適化であり、リリースブランチを完全に省略できると述べられています。また CircleCI のブログ では、Tag は一度作成するとコミットを追加できない固定の参照点であるのに対し、ブランチは開発とともに進み続ける独立した開発ラインであると解説されています。 ↩
-
disable-autolabelerとdisable-releaserのドキュメントが不足していたため、ささやかですが PR #1472 でコントリビュートしました。 ↩ -
このアプローチは suzuki-shunsuke/lock-action を参考にしています。ブランチの存在有無でロック状態を管理するというアイデアを借用しました。 ↩
-
当初は
merge_groupトリガーのみで実装しましたが、Branch Protection の Required status checks に登録するにはpull_requestトリガーが必要でした。merge_groupのみでは候補に表示されないため、pull_requestトリガーを追加して解決しています。 ↩
