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 中文原文镜像 · 以官方页面为准