リリースフロー刷新(preview 廃止 + tagpr CalVer + Vercel タグリリース)
起票日: 2026-07-11
関連: 開発ドキュメント の「初回構築 > リリース」
ステータス: 実装中(Phase A 完了、Phase B〜D 未実施)
起票理由
preview → main の2段ブランチ運用では、main へのマージが即プロダクション反映(Vercel 本番 + prd CDK)になり、リリースタイミングを制御できない。preview を廃止してトランクベース運用にし、リリースを tagpr のリリース PR マージ(= CalVer タグ)に一本化する。Vercel はタグ push をデプロイトリガーにできないため、タグ作成と同一ワークフローの後続ジョブで Vercel CLI から本番デプロイする。
設計方針
論点 |
決定 |
|---|---|
バージョニング |
CalVer |
タグの打ち方 |
tagpr。main へのマージごとにリリース PR を自動作成・追従し、マージでタグ + GitHub Release |
Vercel 本番デプロイ |
|
main 自動デプロイ |
|
CDK トリガ |
main push → dev、CalVer タグ → prd。tagpr.yaml 内で deploy-prd(CDK)→ deploy-vercel の順に実行し API → web の反映順を保証 |
tagpr のトークン |
|
preview ブランチ |
廃止。default branch を main に切り替えて削除。CI / docs / zizmor / deploy 各ワークフローのトリガも main に一本化 |
実装フェーズ
[x] Phase A: 実装(
.tagpr/tagpr.yaml/vercel.json/ 各ワークフローの main 一本化 /infra.yaml/CLAUDE.md/ docs)[ ] Phase B: 事前準備(Vercel secrets 登録 + Actions ワークフロー権限の変更。PR マージ前に完了させる)
[ ] Phase C: カットオーバー(preview → main マージ → default branch 切替 → preview 削除 → ローカル worktree 組み替え)
[ ] Phase D: 動作確認(リリース PR → CalVer タグ → prd CDK + Vercel 本番デプロイ)
手順
Phase B: 事前準備(PR マージ前)
tagpr の初回起動(リリース PR 作成)と deploy ジョブが失敗しないよう、本 PR のマージ前に済ませる。
Vercel CLI デプロイ用シークレットの登録。トークンは https://vercel.com/account/tokens で発行する。
cd apps/web
bunx vercel link
# トークンはシェル履歴に残さないよう対話プロンプトで貼り付ける
gh secret set VERCEL_TOKEN
gh secret set VERCEL_ORG_ID --body "$(jq -r .orgId .vercel/project.json)"
gh secret set VERCEL_PROJECT_ID --body "$(jq -r .projectId .vercel/project.json)"
tagpr が GITHUB_TOKEN でリリース PR を作成できるよう、Actions のワークフロー権限を変更する。gh infra apply は ruleset の required status checks(zizmor のマージブロック)をマニフェストが持たず消してしまうため使わず、対象設定だけを gh api で変更する(infra.yaml は望ましい状態の宣言として更新済み)。
gh api --method PUT repos/shuntaka9576/shuntaka-dev/actions/permissions/workflow \
-f default_workflow_permissions=read \
-F can_approve_pull_request_reviews=true
Phase C: カットオーバー
本 PR を preview ベースで作成し、人間が preview → main にマージする(preview 経由の最後のリリース)。この時点から main push は dev CDK デプロイに変わり、prd 自動デプロイは止まる(タグリリースが通るまでは deploy.yaml の手動 dispatch で prd をカバーできる)。
初回はベースラインの CalVer タグを手動で打つ。バージョンタグが1つも無いと tagpr は現行バージョンを 0.0.0 とみなし、全履歴の PR からリリース PR 本文を生成して GitHub の上限 65536 文字を超え、422 Validation Failed (body is too long) で失敗する(tagpr-from-0.0.0 ブランチが残骸として残る)。現在の main を初回リリースとみなすタグを打てば、以降はそこからの差分だけでリリース PR が作られる。
git tag 2026.0711.0 origin/main
git push origin 2026.0711.0
# 422 失敗時に残った作業ブランチの掃除
git push origin --delete tagpr-from-0.0.0
default branch を main に切り替えて preview を削除する。ruleset protect(enforcement: active)が preview の deletion をブロックしているため、先に対象から preview を外す。protect-preview は ~DEFAULT_BRANCH 参照のためルール自体は切り替えに自動追従する(zizmor の code_scanning ルールごと main に移る)が、名前が実態と合わなくなるため infra.yaml の宣言に合わせて protect-main にリネームする。
gh repo edit shuntaka9576/shuntaka-dev --default-branch main
# ruleset id を名前から取得
PROTECT_ID=$(gh api repos/shuntaka9576/shuntaka-dev/rulesets \
--jq '.[] | select(.name == "protect") | .id')
PROTECT_PREVIEW_ID=$(gh api repos/shuntaka9576/shuntaka-dev/rulesets \
--jq '.[] | select(.name == "protect-preview") | .id')
# protect の対象から preview を外す(deletion ルールの解除)
gh api --method PUT "repos/shuntaka9576/shuntaka-dev/rulesets/${PROTECT_ID}" \
--input - <<'EOF'
{"conditions":{"ref_name":{"include":["refs/heads/main"],"exclude":[]}}}
EOF
# protect-preview を protect-main にリネーム
gh api --method PUT "repos/shuntaka9576/shuntaka-dev/rulesets/${PROTECT_PREVIEW_ID}" \
--input - <<'EOF'
{"name":"protect-main"}
EOF
git push origin --delete preview
ローカル bare clone のメイン worktree を preview/ から main/ に組み替える(.env.local / .envrc はディレクトリごと引き継がれる)。
cd ~/repos/github.com/shuntaka9576/shuntaka-dev
mv preview main
git -C .bare worktree repair
git -C main switch main
cd main && direnv allow .
Phase D: 動作確認
次の main へのマージで tagpr がリリース PR を作成することを確認し、マージする。以下が通れば完了。
CalVer タグ(
2026.XXXX.0)と GitHub Release が作成されるtagpr ワークフローの deploy-prd(CDK)→ deploy-vercel(本番)が順に成功する
https://shuntaka.dev に新しいデプロイが反映される
作業ログ
2026-07-11
Phase A 実装一式。
.tagpr(CalVerYYYY.0M0D.MICRO)、.github/workflows/tagpr.yaml(tagpr → prd CDK → Vercel CLI の3ジョブ構成)、apps/web/vercel.json(main 自動デプロイ無効化)を新規作成ci / docs / zizmor / deploy 各ワークフローのトリガを main に一本化。deploy.yaml は main push → dev 固定に変更(prd はタグ経由へ)
infra.yaml:can_approve_pull_requests: true、ruleset から preview を除去しprotect-preview→protect-mainにリネーム(宣言のみ更新。gh infra applyは zizmor の required status checks を消すため実行せず、実設定変更は Phase B の gh api で行う)。CLAUDE.mdの Git Rules と01_development.mdの preview 前提記述を main に更新.github/release.yaml(tagpr ラベルの changelog 除外)とapps/web/.gitignoreの.vercelは既存のため変更なしVercel CLI(
vercel)を apps/web の devDependency に追加し lockfile 管理に変更(bunx vercelが CI 実行時に latest を解決する非再現性を排除)。renovate の即時更新対象にもvercelを追加Phase B: Actions ワークフロー権限を gh api で変更し、
can_approve_pull_request_reviews: true(default_workflow_permissions: readは維持)を確認live ruleset を調査。
protectは manifest 宣言(disabled)と異なり active、protect-previewには手動追加の code_scanning(zizmor)ルールがあり直接 push をブロックする。preview 削除前にprotectの対象から preview を外す手順を Phase C に追記し、infra.yaml のprotectenforcement を active に同期Songmu/tagpr の SHA ピンを v1 タグの annotated tag オブジェクト SHA で書いていたため zizmor の ref-version-mismatch が発生。v1.20.0 のコミット SHA(e84001b)に修正
カットオーバー実施(default branch 切替 → main へ code_scanning ルールが自動追従、以降 main への直接 push は不可で PR 経由に)
tagpr 初回実行がリリース PR 本文の 65536 文字上限超過(422)で失敗。タグ未作成のため全履歴から本文を生成したのが原因で、ベースラインタグ
2026.0711.0を手動で打つ対処を Phase C に追記ベースラインタグ push 後、tagpr がリリース PR(Release for 2026.0711.1)を正常に作成。ただし自動生成の CHANGELOG.md に cspell 未知語(依存名・Renovate 由来の PR タイトル)が含まれ CI の spell-check が失敗するため、CHANGELOG.md を cspell の ignorePaths に追加