Skip to main content

本番リリースの記録

共通契約​

本番へ出したコミットを、タグ、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 の役割と順序​

導入時は既存の承認方法と本番ブランチを維持する。記録機能の導入を理由に自動デプロイを有効にしない。

  1. 承認と本番対象を確認し、対象SHAと実行識別子を固定する。
  2. リポジトリと本番環境単位のconcurrency groupで直列化する。実行中の配布はキャンセルしない。手元の配布も同じ排他に従う。
  3. 依存関係を固定して検証し、commit と builtAt を成果物へ埋め込む。
  4. 既存の手順で配布し、デプロイ成功日時と識別子を保存する。
  5. 本番の版情報と代表的な画面/APIをGETのみで検証する。期待SHAと実測SHAを比較する。
  6. 成功した場合だけ、同日のリモートタグを確認して番号を割り当て、固定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が最新の本番状態を示すように編集しない。

ダッシュボードの集計​

  1. GitHub Releases APIの全ページを取得する。通常のlatestエンドポイントやリスト先頭だけに依存しない。
  2. タグが ^release-[0-9]{4}-[0-9]{2}-[0-9]{2}-[1-9][0-9]*$ を満たし、日付が実在するものを選ぶ。draft と prerelease がともにfalseであり、published_at が有効であること。
  3. published_at が最も新しい記録を「前回のリリース」とする。コミットの作成日時、Releaseの created_at、タグの文字列順は使わない。
  4. 現在と published_at をそれぞれAsia/Tokyoの暦日に変換し、その日付差を「前回のリリースから何日」とする。同日は0日、日付が変われば1日であり、経過時間を24時間で割った値ではない。
  5. 記録なしは「未記録」、API取得失敗は「取得失敗」とする。未来日時は異常として扱い、0日に丸めない。

これは「正式なリリース記録の公開からの日数」である。実際の本番配布からの日数が必要なダッシュボードでは、別途機械可読のデプロイ成功日時を契約し、その指標名とデータソースを分ける。公開が遅れた記録のタグ日付と published_at の不一致は自動修正しない。

導入範囲​

新しいリポジトリはこの契約を採用する。既存リポジトリは将来の配布から導入し、過去のタグやReleaseを一括変更しない。今回の文書化だけでは、どのリポジトリにもデプロイ、タグ作成、Releaseを公開しない。