本番リリースの記録
共通契約
本番へ出したコミットを、タグ、GitHub Release、稼働中の版情報で結び付ける。パッケージのSemVerとは別の運用記録である。プレビューは対象外である。
- タグは
release-YYYY-MM-DD-Nとする。日付は成功した本番デプロイの日本時間(Asia/Tokyo)の日付、Nはその日の1から始まる正の整数である。ゼロ埋めしない。 - 同日の既存タグの最大番号に1を足す。欠番は埋めず、タグの文字列順で大小を決めない。
- タグは実際に配布した完全なコミットSHAを指す。デプロイ開始時に固定し、後から動いたブランチの先端を使わない。
- 配布後の読み取り専用スモークテストに成功してから、タグとGitHub Releaseを作る。
- Releaseのタイトルはタグ名と同じにする。
draft: false、prerelease: falseとし、初回公開のpublished_atを記録の基準日時とする。 - タグの付け替え、既存Releaseの削除・上書き・再公開は行わない。修正やロールバックは新しいリリースとして追記する。
Release の本文
前回の本番リリースのタグから今回のSHAまでのマージ済みPRを、GitHubの自動生成ノートでまとめる。プレビュー用タグやSemVerタグを比較の起点にしない。初回は比較元なしとする。直接コミットやロールバックがある場合は補足する。
次の情報も記録する。
- 配布した完全なコミットSHAとコミットへのリンク
- 本番デプロイ成功日時(ISO 8601、タイムゾーン付き)
- 本番の版確認URL、CI実行URL、デプロイ識別子
- ロールバックの場合は戻し先と理由
公開はデプロイ・検証後に速やかに行う。GitHubの published_at は公開時に付く値であり、過去のデプロイ日時へ書き換えるものではない。記録の復旧が遅れた場合は、本文の本番デプロイ成功日時と published_at が異なることを明示する。
稼働中の版を確認する
Web/APIは /version.json をJSON、Cache-Control: no-store で返す。CDN、Service Workerを含めてキャッシュ対象から外す。外部公開が不適切なサービスでは認証付きの同等エンドポイント、モバイルやデスクトップでは版情報画面などを用意し、リポジトリの手順に確認方法を書く。
必須フィールドは次のとおりである。
| フィールド | 値 |
|---|---|
commit | 配布した完全な Git SHA。現行 GitHub の SHA-1 リポジトリでは 40 桁の小文字 16 進数 |
builtAt | 成果物をビルドした日時。UTC の ISO 8601 文字列 |
ローカル開発では null を許容するが、本番検証では欠落や null を失敗とする。秘密情報、内部パス、環境変数一覧は返さない。
tag と deployedAt は任意である。タグ をビルド前に予約しない実装では tag を無理に埋めない。配布前に生成した時刻を deployedAt と偽らない。実測できない場合は省略し、成功日時をRelease本文に残す。再ビルドしないロールバックでは builtAt は元の値のままである。
CI の役割と順序
導入時は既存の承認方法と本番ブランチを維持する。記録機能の導入を理由に自動デプロイを有効にしない。
- 承認と本番対象を確認し、対象SHAと実行識別子を固定する。
- リポジトリと本番環境単位のconcurrency groupで直列化する。実行中の配布はキャンセルしない。手元の配布も同じ排他に従う。
- 依存関係を固定して検証し、
commitとbuiltAtを成果物へ埋め込む。 - 既存の手順で配布し、デプロイ成功日時と識別子を保存する。
- 本番の版情報と代表的な画面/APIをGETのみで検証する。期待SHAと実測SHAを比較する。
- 成功した場合だけ、同日のリモートタグを確認して番号を割り当て、固定SHAにタグとReleaseを作る。競合時は既存タグを変更せず停止する。
GitHub Actionsの構成例は deploy → smoke → release の3ジョブである。deploy からSHA・デプロイ成功日時・識別子をoutputsまたは永続的な成果物で渡す。smoke はそのSHAを期待値とする。release は両ジョブの成功を必要条件とし、このジョブだけ contents: write を付 与する。SecretをPRやforkの実行へ渡さず、Releaseを作れるトークンをビルドへ渡さない。
失敗と再実行
- 配布や検証が失敗した場合は正式Releaseを作らない。本番が変わっている可能性をデプロイ履歴に残し、復旧を優先する。
- 配布成功後に記録だけ失敗した場合は、元の実行識別子・SHA・日時を使って記録だけを再実行する。現在のブランチ先端で代用しない。
- 同じ実行識別子の記録が既にあり、タグのSHAも一致すれば何も作らず成功とする。不一致は停止する。同じSHAでも別の本番配布なら新規記録となるため、SHAだけで重複判定しない。
- タグだけ作成できた場合は、保存済みの実行記録に基づき同じタグへ未作成のReleaseを追加する。既存タグやReleaseは書き換えない。
- ロールバックも新しい配布として記録する。過去のReleaseが最新の本番状態を示すように編集しない。
ダッシュボードの集計
- GitHub Releases APIの全ページを取得する。通常のlatestエンドポイントやリスト先頭だけに依存しない。
- タグが
^release-[0-9]{4}-[0-9]{2}-[0-9]{2}-[1-9][0-9]*$を満たし、日付が実在するものを選ぶ。draftとprereleaseがともにfalseであり、published_atが有効であること。 published_atが最も新しい記録を「前回のリリース」とする。コミットの作成日時、Releaseのcreated_at、タグの文字列順は使わない。- 現在と
published_atをそれぞれAsia/Tokyoの暦日に変換し、その日付差を「前回のリリースから何日」とする。同日は0日、日付が変われば1日であり、経過時間を24時間で割った値ではない。 - 記録なしは「未記録」、API取得失敗は「取得失敗」とする。未来日時は異常として扱い、0日に丸めない。
これは「正式なリリース記録の公開からの日数」である。実際の本番配布からの日数が必要なダッシュボードでは、別途機械可読のデプロイ成功日時を契約し、その指標名とデータソースを分ける。公開が遅れた記録のタグ日付と published_at の不一致は自動修正しない。
導入範囲
新しいリポジトリはこの契約を採用する。既存リポジトリは将来の配布から導入し、過去のタグやReleaseを一括変更しない。今回の文書化だけでは、どのリポジトリにもデプロイ、タグ作成、Releaseを公開しない。