Skip to content

案例 03 · 讓 CI 的失敗自己修好 ​

什麼場景 ​

CI 紅了,多半是些小問題:快照沒更新、型別對不上、一個漏改的呼叫點。 這類東西你自己修也就十分鐘,但打斷的是一段完整的工作時間。

讓它在 CI 失敗後自動試一把:能修好就開個 PR 等你看,修不好就沒事發生。

這個方案的關鍵在結構,不在提示詞 ​

先說結論,再看程式碼。這套流程之所以安全,是因為它把三樣東西徹底分開了:

作業有什麼沒有什麼
generate_fixOPENAI_API_KEY、contents: read沒有寫權限
open_prcontents: write、pull-requests: write沒有 API 金鑰

兩者之間只通過一個 補丁構件 傳遞。 拿到金鑰的那個作業改不了倉庫,能改倉庫的那個作業看不到金鑰。

這不是過度設計。官方在文件裡寫得很直白:

如果工作流會簽出或執行由程式碼庫控制的程式碼,請勿將 OPENAI_API_KEY 或 CODEX_API_KEY 設定為作業級環境變數。同一作業中的構建指令碼、測試、 依賴項生命週期鉤子或已遭入侵的 GitHub Action 都可以讀取這些環境變數。

npm ci 會跑 postinstall。測試會跑你的程式碼。任何一環被汙染,金鑰就出去了。

完整流程 ​

  1. 主 CI 工作流失敗 → 觸發後續工作流
  2. 只用讀權限簽出失敗的那個提交
  3. 跑設定指令,不向這些步驟暴露 API 金鑰
  4. 跑 Codex GitHub Action
  5. 把 Codex 的本地改動存成補丁構件
  6. 在單獨的作業裡應用補丁、開 PR
yaml
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 的時候問三件事:

  1. 它改的是問題本身,還是讓測試閉嘴? 把斷言改寬、加個 skip,測試也能過。
  2. 改動範圍有沒有超出失敗的那個點?
  3. 如果這次失敗是環境問題(依賴版本、快取),它是不是在程式碼裡打了個補丁繞過去?

第三種最隱蔽,也最常見。

什麼時候不該用這個 ​

  • 有安全敏感程式碼的倉庫——讓自動流程改認證、加密、權限相關的程式碼,收益不值風險。
  • 測試本身不可靠的倉庫——測試時好時壞的話,它會去修一個根本不存在的 bug。
  • 公開 / 開源倉庫——尤其別用 Codex 使用者賬戶那條認證路線,官方明確不推薦。

相關官方文件 ​

非官方社群教程 · 「官方文件」區為 learn.chatgpt.com 中文原文映象 · 以官方頁面為準