CIが緑でも、確かめたコミットと最新のmainを見てからマージする

  • #CI
  • #GitHub
  • #自動化
CIが緑でも、確かめたコミットと最新のmainを見てからマージする

今回やったこと

CIの表示が緑であることと、これから取り込む変更が現在のmainと組み合わさっても安全であることは、同じ確認ではない。個別の変更を別々の時点のmainに対して検査した場合、両方が単独では通っていても、連続して取り込んだ結果として組み合わせが壊れることがある。この問題に対し、マージ前に「検査が見た基点からmainが進んでいないか」を判定するゲートを追加した記録である。

対象は、複数の変更が同じ検査範囲に触れうるリポジトリで、CI成功を自動マージ条件にしている開発チームだ。ここで扱うのは、特定サービスの設定手順ではなく、CIの成功判定に何を含めるべきかという設計である。

単独の成功が組み合わせの成功を保証しない

ある変更Aと変更Bが短い間隔で作られたとする。AのCIはmainの状態Mを基準に通過した。Bも同じくMを基準に通過した。しかしAが先にmainへ入ると、Bを取り込む直前のmainはMではなく、Aを含んだM’になる。Bが見たテスト結果は、実際にマージされる組み合わせを検査した結果ではない。

今回の記録では、別々に緑だった二つの変更を続けて取り込んだ後、main上で初めて失敗が現れた。失敗を防ぐには「CIが成功したか」だけでなく、「そのCIがどのheadとbaseを見たか」を判断材料に加える必要があった。

マージ直前の状態CIが検査した状態判断
baseが変わっていない現在のmainを含む他の条件も満たせば続行可能
mainが進み、差分が検査範囲と重なる古いmain最新mainへ更新して再検査
mainは進んだが検査範囲と交差しない古いmain影響範囲の判定結果を確認

この表は、単に「mainが進んだらすべて止める」という意味ではない。止めるべきかどうかは、進んだ差分と対象変更が依存する検査範囲の交差で決める。無関係な変更まで毎回やり直すと、待ち時間が増え、ゲートが形だけのものとして迂回される動機を作る。反対に、交差を調べず常に通すなら安全弁にならない。

baseのずれをマージ前に判定する

マージゲートは、変更のheadとbaseに加えて、baseから現在のmainまでに入った差分を取得する。ワークフローごとに定義された検査対象パスとその差分が交わる場合は、変更をbehindと判定し、マージを保留する。更新ブランチを作って最新main上でCIを再実行すれば、実際に取り込む組み合わせに近い状態を再確認できる。

重要なのは、判定関数を作るだけでは不十分な点だ。人が使うマージ操作、自動化スクリプト、待機後の再判定など、マージを決めるすべての経路が同じ評価を通る必要がある。入口が一つでも旧判定を使えば、ゲートは迂回される。実装に合わせて、代表的な経路が新しい評価関数を通ることも回帰テストで確認する。

テストでは「止まる」ケースだけを作らない。古いbaseで検査範囲が重なるケースは止め、最新baseなら通す。mainが進んでいても対象外の差分なら通す。この対照があれば、常に拒否するだけの実装も、常に許可するだけの実装も検出できる。

実装ステータスと運用上の注意

実装ステータスは実装済み。対象の変更範囲と検査範囲の照合、更新後の再検査、各マージ経路への配線を一緒に扱う構成になっている。技術的な根拠は、リポジトリ内のマージゲート実装と、その実データを使った回帰テストにある。

この仕組みだけで競合や意味上の不整合をすべて見つけられるわけではない。パスの重なりはリスクの手がかりであり、プログラムの意味的な依存を完全に表すものではない。また、CIが誤った範囲を検査している場合、base判定以前にワークフロー定義の見直しが必要だ。ゲートはテストを置き換えるのではなく、成功したテスト結果が古くなっていないかを確認する層として設計する。

更新履歴

  • 2026-10-05: 初稿を作成。