develop ブランチをデフォルトブランチとし、main ブランチへのマージで本番リリースを行うワークフローを GitHub Actions で自動化する方法を解説します。
実現するワークフロー
- Feature Branch で開発
- develop への PR を作成
- PR がマージされる
- develop → main への PR を自動生成/更新
- 新規作成時: PR タイトルは「[YYYYMMDD]リリース」
- 既存 PR がある場合: PR 本文に新しい PR を追記
- 人間が PR タイトルの YYYYMMDD を実際の日付に変更
- PR をマージ
- main ブランチに YYYYMMDD_XX タグが付与される(同日複数回対応)
実装手順
ステップ 1: develop ブランチをデフォルトに設定
GitHub リポジトリの設定でデフォルトブランチを変更します:
- GitHub リポジトリの Settings → General に移動
- Default branch セクションで
developを選択 - Update をクリック
ステップ 2: develop マージ時の自動 PR 生成ワークフロー
.github/workflows/create-release-pr.yml を作成します:
name: Create Release PR
on:
push:
branches:
- develop
permissions:
contents: write
pull-requests: write
jobs:
create-release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check existing PR
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# 既存のオープンな PR を確認(develop → main)
# gh pr view を使用して直接確認
if gh pr view develop --json number,body,state 2>/dev/null | jq -e '.state == "OPEN"' >/dev/null 2>&1; then
EXISTING_PR=$(gh pr view develop --json number,body)
echo "existing_pr_number=$(echo "$EXISTING_PR" | jq -r '.number')" >> $GITHUB_OUTPUT
# 既存の body を保存(改行を保持)
{
echo "existing_body<<EOF"
echo "$EXISTING_PR" | jq -r '.body'
echo "EOF"
} >> $GITHUB_OUTPUT
echo "Found existing PR: $(echo "$EXISTING_PR" | jq -r '.number')"
else
echo "No existing open PR found from develop to main"
fi
- name: Get latest merged PR
id: latest_pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# 直前にマージされた PR の情報を取得(トリガーとなった PR)
# push イベントから直接取得できないため、最新のマージ済み PR を取得
LATEST_PR=$(gh pr list \
--base develop \
--state merged \
--json number,title,mergedAt \
--jq 'sort_by(.mergedAt) | reverse | .[0] // empty')
if [ -n "$LATEST_PR" ]; then
PR_NUMBER=$(echo "$LATEST_PR" | jq -r '.number')
PR_TITLE=$(echo "$LATEST_PR" | jq -r '.title')
echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "pr_entry=- [ ] #${PR_NUMBER} ${PR_TITLE}" >> $GITHUB_OUTPUT
fi
- name: Create or Update Release PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EXISTING_PR_NUMBER: ${{ steps.check.outputs.existing_pr_number }}
EXISTING_BODY: ${{ steps.check.outputs.existing_body }}
NEW_PR_ENTRY: ${{ steps.latest_pr.outputs.pr_entry }}
MERGED_PR_NUMBER: ${{ steps.latest_pr.outputs.pr_number }}
run: |
if [ -n "$EXISTING_PR_NUMBER" ]; then
# 既存の PR がある場合
# 重複チェック: 同じ PR 番号が既に含まれているか確認
if echo "$EXISTING_BODY" | grep -q "#${MERGED_PR_NUMBER} "; then
echo "PR #${MERGED_PR_NUMBER} is already in the release PR body. Skipping."
exit 0
fi
# 新しい PR エントリを末尾に追記
UPDATED_BODY="${EXISTING_BODY}
${NEW_PR_ENTRY}"
gh pr edit "$EXISTING_PR_NUMBER" --body "$UPDATED_BODY"
echo "Updated existing PR #$EXISTING_PR_NUMBER with new entry: #${MERGED_PR_NUMBER}"
else
# 新規 PR を作成(REST API を使用)
PR_BODY="## リリース内容
以下の変更が含まれています。
${NEW_PR_ENTRY}"
# REST API で PR を作成(GraphQL の権限問題を回避)
gh api repos/${{ github.repository }}/pulls \
--method POST \
-f title="[YYYYMMDD]リリース" \
-f body="$PR_BODY" \
-f head="develop" \
-f base="main"
echo "Created new Release PR"
fiポイント:
- 新規作成時: タイトルは「[YYYYMMDD]リリース」。人間が YYYYMMDD を実際の日付に変更する
- 既存 PR がある場合: PR の説明(body)に新しい PR エントリを追記するのみ。
ステップ 3: main マージ時のタグ付けワークフロー
.github/workflows/tag-release.yml を作成します:
name: Tag Release
on:
pull_request:
branches:
- main
types:
- closed
permissions:
contents: write
jobs:
tag-release:
# マージされた場合のみ実行
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # すべてのタグを取得するため
- name: Extract date from PR title
id: extract
run: |
# PR タイトルから日付を抽出(例: [20241225]リリース → 20241225)
PR_TITLE="${{ github.event.pull_request.title }}"
RELEASE_DATE=$(echo "$PR_TITLE" | grep -oP '\[\K\d{8}(?=\])')
if [ -z "$RELEASE_DATE" ]; then
echo "No date found in PR title, using today's date"
RELEASE_DATE=$(date +%Y%m%d)
fi
echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT
- name: Determine next tag number
id: next_tag
env:
RELEASE_DATE: ${{ steps.extract.outputs.release_date }}
run: |
# リモートのタグを取得
git fetch --tags
# 同じ日付のタグを検索し、最大の連番を取得
# 例: 20241225_01, 20241225_02 → 最大は 02
EXISTING_TAGS=$(git tag --list "${RELEASE_DATE}_*" | sort -V)
if [ -z "$EXISTING_TAGS" ]; then
# 同じ日付のタグがない場合は _01 から開始
NEXT_NUMBER="01"
else
# 最大の連番を取得して +1
LAST_TAG=$(echo "$EXISTING_TAGS" | tail -1)
LAST_NUMBER=$(echo "$LAST_TAG" | grep -oP '_\K\d+$')
NEXT_NUMBER=$(printf "%02d" $((10#$LAST_NUMBER + 1)))
fi
TAG_NAME="${RELEASE_DATE}_${NEXT_NUMBER}"
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
- name: Create and push tag
env:
TAG_NAME: ${{ steps.next_tag.outputs.tag_name }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG_NAME" -m "Release $TAG_NAME"
git push origin "$TAG_NAME"
echo "Created and pushed tag: $TAG_NAME"ポイント:
- 連番形式: タグは
YYYYMMDD_01,YYYYMMDD_02, ... の形式で作成される - 同日複数回対応: 同じ日に複数回リリースしても、連番が自動的にインクリメントされる
- 2桁ゼロ埋め: 連番は
01,02, ... のように 2 桁でゼロ埋めされる
ワークフロー図
トラブルシューティング
PR が自動生成されない
- 権限の確認:
permissionsでpull-requests: writeが設定されているか確認 - ブランチの確認: develop と main の両方が存在するか確認
- 差分の確認: develop と main に差分があるか確認
git log main..develop --onelineタグが作成されない
- 権限の確認:
permissionsでcontents: writeが設定されているか確認 - PR タイトルの確認:
[YYYYMMDD]リリース形式になっているか確認 - fetch-depth の確認:
actions/checkoutでfetch-depth: 0が設定されているか確認
# 同じ日付のタグを確認
git tag --list | grep "$(date +%Y%m%d)"
# 例: 20241225_01, 20241225_02 などが表示されるマージ済み PR が取得できない
GitHub API の制限により、古い PR は取得できない場合があります。--limit オプションで取得数を調整:
gh pr list --base develop --state merged --limit 100PR は作成されるがワークフローが失敗する
Resource not accessible by integration (createPullRequest.pullRequest) エラーが発生する場合、gh pr create コマンドの GraphQL API 呼び出しで問題が発生しています。
これは GitHub CLI の既知の問題で、PR 作成後に追加情報を GraphQL で取得しようとする際に、GITHUB_TOKEN では取得できない情報にアクセスしようとするためです。PR 自体は作成されますが、コマンドはエラーで終了します。
解決策: REST API を使用して PR を作成します。
# gh pr create の代わりに REST API を使用
gh api repos/${{ github.repository }}/pulls \
--method POST \
-f title="[YYYYMMDD]リリース" \
-f body="$PR_BODY" \
-f head="develop" \
-f base="main"REST API は GraphQL API と異なり、追加情報の取得を行わないため、この権限問題を回避できます。
まとめ
このワークフローにより:
- 手動作業の削減: develop へのマージだけで、リリース PR が自動生成/更新される
- リリース内容の可視化: チェックリスト形式で含まれる変更が一目でわかる
- 柔軟なリリース日設定: PR タイトルの日付は人間が自由に設定できる
- 同日複数回リリース対応:
YYYYMMDD_01,YYYYMMDD_02形式で連番管理
自動化される部分と手動の部分
| 項目 | 自動/手動 | 説明 |
|---|---|---|
| develop → main PR の作成 | 自動 | 「[YYYYMMDD]リリース」タイトルで作成 |
| PR 本文への追記 | 自動 | 新しい PR がマージされるたびに追記 |
| PR タイトルの日付設定 | 手動 | YYYYMMDD を実際の日付に変更 |
| タグの連番付与 | 自動 | YYYYMMDD_XX 形式で自動インクリメント |
GitHub CLI を使ったカスタム実装により、プロジェクト固有の要件に完全に対応したワークフローを構築できます。