案例 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 用户账户那条认证路线,官方明确不推荐。