ビルドが緑でも本番は古い — デプロイのステイルネスを番犬で検知する

  • #Astro
  • #Cloudflare Pages
  • #デプロイ監視
  • #Git
ビルドが緑でも本番は古い — デプロイのステイルネスを番犬で検知する

今回やったこと

静的サイトのデプロイで怖いのは、ビルドが失敗して赤くなるケースだけではない。ビルドが走らず、本番が古い成果物を返し続けるケースがある。しかも古いページでも HTTP 200 を返すため、外形監視だけでは正常に見える。

そこで、本番自身にビルド時のコミットを返す /build-stamp.json を追加し、origin/main と照合する読み取り専用の番犬を実装した。対象は sites/the_ntmsites/zashstudio などの Astro サイトで、サイトのディレクトリを変更したコミットが本番に載っているかを確認する。

200を生存証明にしない

番犬の判定材料は三つに分けた。

  1. /build-stamp.json の HTTP ステータス
  2. content-type が JSON かどうか
  3. 本文を JSON として解析し、commit を持つかどうか

静的ホスティングのフォールバックは、存在しないパスにも 200 text/html を返すことがある。このときステータスだけを見る実装は、未ビルドを生存と誤認する。sites/zashstudio/src/pages/build-stamp.json.ts は、ビルド時に CF_PAGES_COMMIT_SHACF_PAGES_BRANCH を読み込み、静的な JSON として焼き付ける。

const COMMIT = process.env.CF_PAGES_COMMIT_SHA ?? null;
const BRANCH = process.env.CF_PAGES_BRANCH ?? null;

return new Response(
  JSON.stringify({ site: 'zashstudio', commit: COMMIT, branch: BRANCH }),
  { headers: { 'content-type': 'application/json; charset=utf-8' } },
);

200 でも HTML が返った場合、または JSON に commit が無い場合は、最新かどうかを測れない。実装では NO_STAMPUNKNOWN として扱い、古いと断定する判定と区別している。

Gitと本番を同じ基準で比べる

本番から得たコミットが origin/main の祖先であることを確認した後、次の差分を見る。

git rev-list <本番commit>..origin/main -- sites/<site>/

差分が空なら、そのサイトに関係する変更は本番へ載っている。差分がある場合も、マージ直後に即座に異常扱いはしない。ビルド完了までの猶予を置き、未配信コミットのうち最も古いものが既定の90分を超えたときに STALE とする。これで、正常なビルド待ちと長時間の取りこぼしを分けられる。

判定は scripts/deploy_staleness_canary.py に集約し、ネットワークと Git の取得部分から切り離した純粋な判定関数をテストしている。scripts/test_deploy_staleness_canary.py では、最新、猶予内、猶予超過、200でHTML、404、取得失敗、祖先でないコミットを個別に確認する。特に「200 text/html は生存証明にしない」という回帰テストがこの仕組みの中心になる。

運用で残した線引き

回線障害とデプロイ異常も分けた。単発の取得失敗は UNREACHABLE として警告に留め、全対象が取れない場合は実行機の回線障害として判定を保留する。一方、JSON の取得と Git の照合が成立し、猶予を超えた差分が残る場合だけ STALE を異常通知する。

WordPress サイトにはビルドスタンプが無いため、同じ判定を無理に適用しない。トップページが HTML でサイト固有の印を含むか、REST API の最新投稿が許容期間内かを別ルールで見る。監視対象の種類に合わせて、観測できる事実を変える設計である。

やってみてわかったこと

デプロイ監視で必要なのは「サイトが応答するか」だけではない。ビルドが成功したこと、本番がどのコミットで作られたか、対象サイトの変更がその成果物に含まれるかは別々の事実である。build-stamp.json と Git のパス差分を組み合わせると、記事数が増えないレイアウト変更や設定変更も検知できる。

実装はゲートではなく番犬にした。通知が出てもビルドを強制停止せず、猶予と状態名を付けて人間が切り分けられるようにする。自動化を増やすほど、処理本体だけでなく「静かに何も起きなかった」状態を観測できる設計が重要になる。

更新履歴

  • 2026-08-25: 初稿。リポジトリ内の番犬実装と回帰テストをもとに整理。