案例 03 · 讓 CI 的失敗自己修好
什麼場景
CI 紅了,多半是些小問題:快照沒更新、型別對不上、一個漏改的呼叫點。 這類東西你自己修也就十分鐘,但打斷的是一段完整的工作時間。
讓它在 CI 失敗後自動試一把:能修好就開個 PR 等你看,修不好就沒事發生。
這個方案的關鍵在結構,不在提示詞
先說結論,再看程式碼。這套流程之所以安全,是因為它把三樣東西徹底分開了:
| 作業 | 有什麼 | 沒有什麼 |
|---|---|---|
generate_fix | OPENAI_API_KEY、contents: read | 沒有寫權限 |
open_pr | contents: write、pull-requests: write | 沒有 API 金鑰 |
兩者之間只通過一個 補丁構件 傳遞。 拿到金鑰的那個作業改不了倉庫,能改倉庫的那個作業看不到金鑰。
這不是過度設計。官方在文件裡寫得很直白:
如果工作流會簽出或執行由程式碼庫控制的程式碼,請勿將
OPENAI_API_KEY或CODEX_API_KEY設定為作業級環境變數。同一作業中的構建指令碼、測試、 依賴項生命週期鉤子或已遭入侵的 GitHub Action 都可以讀取這些環境變數。
npm ci 會跑 postinstall。測試會跑你的程式碼。任何一環被汙染,金鑰就出去了。
完整流程
- 主 CI 工作流失敗 → 觸發後續工作流
- 只用讀權限簽出失敗的那個提交
- 跑設定指令,不向這些步驟暴露 API 金鑰
- 跑 Codex GitHub Action
- 把 Codex 的本地改動存成補丁構件
- 在單獨的作業裡應用補丁、開 PR
name: Codex auto-fix on CI failure
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
generate_fix:
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
has_patch: ${{ steps.diff.outputs.has_patch }}
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: |
if [ -f package-lock.json ]; then npm ci; fi
- name: Run Codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: |
The CI workflow "${{ github.event.workflow_run.name }}" failed for commit
${{ github.event.workflow_run.head_sha }}.
Run `npm test --silent` to reproduce the failure. Identify the minimal
change needed to make the tests pass, implement only that change, and
run `npm test --silent` again.
Do not refactor unrelated files.
- name: Create patch artifact
id: diff
run: |
git add -N .
git diff --binary HEAD > codex.patch
if [ -s codex.patch ]; then
echo "has_patch=true" >> "$GITHUB_OUTPUT"
else
echo "has_patch=false" >> "$GITHUB_OUTPUT"
fi
- name: Upload patch artifact
if: steps.diff.outputs.has_patch == 'true'
uses: actions/upload-artifact@v4
with:
name: codex-fix-patch
path: codex.patch
if-no-files-found: error
open_pr:
runs-on: ubuntu-latest
needs: generate_fix
if: needs.generate_fix.outputs.has_patch == 'true'
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: codex-fix-patch
- name: Apply Codex patch
run: git apply --index codex.patch
- name: Open pull request
env:
GH_TOKEN: ${{ github.token }}
FAILED_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
FAILED_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
RUN_ID: ${{ github.event.workflow_run.run_id }}
run: |
branch="codex/auto-fix-$RUN_ID"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -c "$branch"
git commit -m "Auto-fix failing CI via Codex"
git push origin "$branch"
{
echo "Codex generated this patch after CI failed for \`$FAILED_HEAD_SHA\`."
echo
echo "Review the changes before merging."
} > pr-body.md
gh pr create \
--base "$FAILED_HEAD_BRANCH" \
--head "$branch" \
--title "Auto-fix failing CI via Codex" \
--body-file pr-body.md這是 Node.js 專案的寫法,換技術棧就改 setup 和測試指令。
提示詞裡那三句話
Run `npm test --silent` to reproduce the failure. Identify the minimal
change needed to make the tests pass, implement only that change, and
run `npm test --silent` again.
Do not refactor unrelated files.每一句都在防一種具體的跑偏:
- 先復現——不讓它憑報錯猜,逼它自己看到失敗。
- 最小改動 + 只實現這個改動——防止它順手「最佳化」一圈,給你一個五百行的 PR。
- 改完再跑一遍——自己驗證,不是聲稱。
- 不要重構無關檔案——最重要的一句。缺了它,自動修 CI 會變成自動重構倉庫。
幾個配置上的取捨
safety-strategy 保持預設。 預設是 drop-sudo——跑 Codex 前移除 sudo, 對該作業不可逆,保護記憶體中的機密。別為了省事改成 unsafe, 官方明確說了:絕不要在多租戶執行器上保持 unsafe 模式。
讓 Codex 在作業的最後一步跑。 免得後續步驟繼承它造成的意外狀態改動。 上面的例子裡 Codex 之後只剩生成補丁,符合這條。
限制誰能觸發。 allow-users 和 allow-bots 控制哪些賬戶能觸發工作流; 預設只有有寫權限的使用者能跑。這個工作流由 workflow_run 觸發, 但如果你還掛了 PR 觸發,這一條一定要配。
清洗提示輸入。 從 PR 標題、提交訊息、議題正文裡取內容拼進提示詞, 等於把外部輸入餵給智慧體。官方建議在把 HTML 註釋或隱藏文本給 Codex 之前先審查。
怎麼驗收
別讓它自動合併。 這個流程的產出是一個 PR,不是一次推送——這是故意的。
看這個 PR 的時候問三件事:
- 它改的是問題本身,還是讓測試閉嘴? 把斷言改寬、加個 skip,測試也能過。
- 改動範圍有沒有超出失敗的那個點?
- 如果這次失敗是環境問題(依賴版本、快取),它是不是在程式碼裡打了個補丁繞過去?
第三種最隱蔽,也最常見。
什麼時候不該用這個
- 有安全敏感程式碼的倉庫——讓自動流程改認證、加密、權限相關的程式碼,收益不值風險。
- 測試本身不可靠的倉庫——測試時好時壞的話,它會去修一個根本不存在的 bug。
- 公開 / 開源倉庫——尤其別用 Codex 使用者賬戶那條認證路線,官方明確不推薦。