← 所有文章

工程 · 2026.10.02 · 5 min · 更新於 2026.10.26

多個 AI agent 同時開 PR:GitHub required check 與部署預檢怎麼守住 main 和 PROD

好幾個 AI agent 同時開 PR,每次 merge 又多一個 release commit,只靠 GitHub branch protection 守不住。整理 required check 跟路徑過濾打架的修法,以及部署預檢和 merge script 怎麼補洞。

閱讀設定
主題
字級
寬度

有一天,repo 裡所有 PR 都卡在 BLOCKED,CI 全綠也 merge 不了。

這個 repo 只有我一個人維護,但同時開 PR 的常常是好幾個 AI agent session。

只靠 GitHub 的 branch protection 守不住:required check 會跟路徑過濾打架,strict 開了又會讓每個 PR 反覆重跑 CI。

最後我沒開長期 branch,也沒開 strict,改補兩樣東西:PROD 部署前的預檢,和一支負責排隊、必要時跟上 main 的 merge script。

每次 merge 多一個 release commit

以前每個 PR 自己改版本檔。好幾個 agent 同時開 PR 時,大家搶著 bump 同一個版號,一直衝突。

現在 PR 只在 changelog.d/ 放一個 changeset fragment(描述這次改動的小檔案),merge 之後由 release workflow 計版、彙整 changelog、打 tag。

副作用是每次 merge 之後,main 上都會多一個帶 [skip ci] 的 release commit,這個標記會讓 GitHub Actions 不觸發這次 push 的 workflow。

後面好幾個設計都跟這件事有關。

required check 跟路徑過濾打架

那天是 main 的 branch protection 自己重新生效,帶回來的還是舊的 required check(PR 要通過才能 merge 的檢查)名稱。

CI workflow 又用 workflow 層的 paths: 過濾,純後端的 PR 不會觸發前端 workflow,前端的 check 永遠不會回報,PR 就這樣全部卡住。

Associated checks stay in a "Pending" state and block merging

這句出自 GitHub 的 required status checks 排查文件,講的是整個 workflow 被路徑過濾跳過的情況。同一頁也寫到,skipped 算是成功的狀態。

用 --admin 繞過等於紅著 merge,所以我先修 branch protection。required check 改成現在的名稱。

pull_request 拿掉 workflow 層的 paths:,每個 PR 都觸發,先判斷有沒有改到相關檔案。無關時主 job 顯示 skipped,branch protection 把 skipped 當成通過。

偵測本身失敗時,主 job 照跑整套。

對照圖:以前 CI 用 workflow 層的 paths: 過濾,純後端的 PR 不觸發前端 workflow,前端 check 永遠不回報,所有 PR 卡在 BLOCKED;現在 pull_request 拿掉 paths:,每個 PR 都觸發並先判斷有沒有改到相關檔案,無關時主 job 顯示 skipped、branch protection 當成通過,偵測失敗就照跑整套。

這次學到的是:改任何一個 CI job 的名稱,都要同步改 required check,不然所有 PR 又會卡住。

不開長期 branch,也不開 strict

strict 是 branch protection 裡「要求 branch 先跟上 base 才能 merge」的設定。這幾個方案我都沒採用:

選項 為何不採用
STG/PROD 長期 branch 不會讓 CI 變快;多兩道 merge,hotfix 要 cherry-pick,branch 會漂移
PR 只跑改到的測試 單一 crate,最常改的又是共用層,能省的只有 30 多秒測試的一部分;漏測會讓 main 紅、擋住 STG 部署
branch protection 開 strict 每次 merge 都有 release commit,開了之後每個 PR 都要反覆跟上 main 重跑 CI

但這些方案原本想擋的洞是真的。

PROD 是手動部署,可以選 main 上任何一個 commit。merge 完馬上按部署,E2E 可能還沒跑完,甚至已經失敗。

strict 沒開,並行 merge 時前一次 release 還沒推上去就 merge 下一個,release bot 的 push 會被拒。

migration PR 落後 main 就 merge,撞號或依賴問題要等 merge 之後 main 才紅。

PROD 部署預檢

兩支 PROD 部署 workflow 在 build 之前先跑預檢。目標 commit 要有一次 main 上 push 觸發、成功的 E2E,CI 如果有跑,最新一次也要成功。

查詢出錯一律當失敗。只改 infra 或緊急時可以 skip,但要填理由。

麻煩在 main 的 HEAD 通常是 release commit,它帶 [skip ci],沒有 E2E 紀錄。

預檢改看它唯一的父 commit,並檢查兩者只差版本檔和 changelog:兩份 package JSON 刪掉 version 欄位後要完全相同。

偽裝成 release commit 的改動會在這裡被擋下來。擋下時,預檢會往回找最近一個能通過的 release tag,建議改部署那個。

它也有擋不到的地方:重跑舊的 PROD run、用筆電上的部署 script,都不經過預檢。它也只看 E2E,不確認 STG 部署有沒有成功。

流程圖:PROD 部署前的預檢。一般 commit 檢查它自己,release commit 帶 skip ci 標記、沒有 E2E,改看唯一的父 commit,且兩者只能差版本檔和 changelog;接著要有 main 上 push 觸發、成功的 E2E,CI 有跑的話最新一次也要成功,查詢出錯當失敗。通過才 build、部署,擋下時建議最近一個能通過的 release tag。只改 infra 或緊急可以填理由 skip,重跑舊的 PROD run 和筆電上的部署 script 擋不到。

用 merge script 取代 strict

scripts/merge-pr.sh <PR#> 先在 git common dir(同一個 repo 各個 worktree 共用的 git 目錄)底下拿鎖,讓同一台機器上的多個 session 排隊。

接著確認 PR 狀態、等 required check(清單從 branch protection 讀)、等上一次 release 落地,最後用 gh pr merge --squash --match-head-commit。確認 CI 綠之後才 push 的 commit,不會被一起 merge。

migration PR 如果落後 main,script 會先 gh pr update-branch,等 CI 再跑一輪。

這是拿來取代 strict 的:撞號和依賴問題要在 merge 前跟上 main 才看得出來,但只有 migration PR 需要付這個代價。

流程圖:merge script 依序拿鎖讓同一台機器的 session 排隊、確認 PR 狀態、等 required check、等上一次 release 落地,最後 squash merge,CI 綠之後才 push 的 commit 不會被一起 merge;migration PR 落後 main 時先 gh pr update-branch、等 CI 再跑一輪,用來取代 strict。

我開了一個帶 migration、刻意落後 main 的測試 PR 跑過整段,第二個 process 也會排隊等鎖。後來 CRAP 指標的檢查也掛在這裡,細節在用 CRAP 指標找出最該補測試的函式。

限制是鎖只在同一台機器有效,從 GitHub 網頁或 cloud session 直接 merge,不受保護。

#CI/CD#部署#GitHub Actions#branch protection

留言

載入留言中…

下一篇

工具

Bitfinex 放貸利率月報自動化:Claude Code routine 每月開 PR,等人審才合併

利率月報只用 Bitfinex 公開 K 線算,任何人都能重算;Claude Code 的雲端 routine 每月自動開 PR,但一定等人審過才合併。