CIが赤いままマージされるのを止めるゲートと、mainの赤を見張る番犬を入れた
今回やったこと / この記事について
AIが連続してプルリクエストを作る運用では、担当者が毎回CIの完了を待ち、緑を確認してからマージする方法は安定しない。そこで、マージ前に検査の完了と成功を確認するゲートと、マージ後にmainが赤くなった状態を知らせる番犬を分けて実装した。対象はGitHub Actionsを使うリポジトリで、実際に検証・導入した構成を整理する。
失敗を二つの時間帯に分ける
最初に分けたのは「まだマージしてはいけない」と「すでにマージされた後で壊れた」である。前者を扱うのがマージ前ゲートだ。CIが未完了なら待ち、失敗なら止める。検査では、成功・失敗・未完了・取得エラーを別々に扱い、APIのHTTPステータスコードだけでなく、ワークフローの完了状態も判定する。
後者は、マージそのものを取り消す機能ではない。workflow_runでmain上の対象ワークフローの完了を受け取り、failure、timed_out、startup_failureなら、ワークフロー名・コミット・runのURLを異常通知へ渡す。赤くなった事実を止められなくても、「誰も知らない時間」は短くできる。
この分離が必要になった直接の材料は、特定のワークフローがmainで数時間赤いまま通知されなかった記録だった。後続の別プルリクエストで同じテストが落ちたことで初めて発見され、後から見ると原因の読み違えも起きやすい状態だった。ブランチ保護によるrequired status checksは利用できなかったため、停止できる入口と、停止できない出口の通知を別の仕組みで補う判断になった。
「全ワークフロー」を暗黙にしない
番犬側では監視対象をYAMLに明示列挙した。対象の基準は「mainへのpushまたはscheduleで自動的に走るか」である。workflows:の対象指定を曖昧にせず、監視対象の境界をレビュー可能にするためだ。明示列挙に伴う登録漏れを防ぐため、自動実行ワークフローの漏れがないかをテストで点検する。
番犬の並列実行も抑えた。通知の静穏化状態はJSONを読み書きするため、複数の赤を同時に処理すると一方を取りこぼし得る。ワークフロー本体はcancel-in-progress: falseで直列化を試み、通知の重複はワークフロー単位・12時間のスロットルで抑える。緑へ戻った時点では静穏化状態を解除する。
実行場所も同じではない。ゲート用と番犬用の実行環境を分け、片方の障害で両方が黙らないようにした。ジョブは自己ホストランナーを使い、無料枠を消費するubuntu-latestを追加していない。監視を一時停止する安全弁は未設定時に有効(通知ON)とし、設定忘れによる通知漏れを避けている。
機械で守る範囲を残す
この構成だけで赤い変更を完全に防げるわけではない。ゲート外の手動操作、監視対象の列挙漏れ、通知先そのものの障害は別に残る。だから、ゲートの判定テストと番犬の配線テストを別々に持ち、.github/workflows/や番犬本体を変更したプルリクエストで配線検査を必ず実行する形にした。
| 時点 | 仕組み | 目的 |
|---|---|---|
| マージ前 | CIゲート | 未完了・失敗の変更を入口で止める |
| マージ後 | workflow_run番犬 | mainの赤を通知する |
| 変更時 | 配線回帰テスト | 監視対象の漏れを検出する |
やってみてわかったのは、CIの安全性を一つの機能名で考えると、失敗の時間軸を取り落とすということだ。入口で止める機能と、止められなかった後を観測する機能は別の契約にする。その二段をテストと通知の両方で固定して初めて、自動マージの速度を残したまま、赤いmainを放置しにくくできる。
試した環境・参照
- GitHub Actionsのworkflowと自己ホストランナー構成
- CIゲートの実装
workflow_run番犬の実装- CIゲートと番犬のワークフロー、判定・配線テスト
更新履歴
- 2026-09-17: 初稿。実装記録を整理。
訂正履歴
なし。