| name | harness-fix-ci |
| description | CI failure auto-fix. Reads failure logs, identifies root cause, applies fix, commits and creates PR. Triggered by workflow_run or called directly via /fix-ci. |
harness-fix-ci:CI 失败自动修复
Killer Feature:GitHub Actions CI 失败后,Claude Code 自动分析日志、修复、创建 PR。全程无需人工介入。
防循环:只监听 workflow_run 事件,不监听 PR/push,永不循环。
触发方式
| 方式 | 说明 |
|---|
/fix-ci | 用户手动触发,需提供日志路径或 job 信息 |
| GitHub workflow_run | CI 失败时自动触发(详见 Step 5) |
输入
/fix-ci [options]
Options:
--job "job-name" 指定失败的 job 名
--run-id N GitHub Actions run ID(自动从环境变量读取)
--branch "name" 分支名(自动从环境变量读取)
--logs "/path/to/file" 本地日志文件路径
--no-pr 只修复,不创建 PR
无参数时:尝试从环境变量和最近 CI 失败日志推断。
Step 1:收集失败信息
A. 从本地日志文件读取
[ -f /tmp/failed_logs.txt ] && cat /tmp/failed_logs.txt
[ -f "$LOG_PATH" ] && cat "$LOG_PATH"
B. 从 GitHub API 获取(workflow_run 触发时)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/runs/$RUN_ID/jobs" \
| jq '.jobs[] | select(.conclusion == "failure")'
LOGS_URL=$(curl -s ... | jq -r '.jobs[] | select(.conclusion=="failure") | .logs_url')
curl -s -H "Authorization: token $GITHUB_TOKEN" "$LOGS_URL" \
| gzip -d > /tmp/failed_logs.txt
C. 提取关键信息
从日志中提取:
- 失败类型:
compile error / test failure / lint error / timeout / permission denied / environment
- 失败文件:哪个文件哪一行出错
- 错误信息:完整的错误描述
- 失败命令:触发错误的 shell 命令
Step 2:分析根因
失败类型分类与修复策略
| 类型 | 特征关键词 | 修复策略 |
|---|
| 编译错误 | error:, cannot find package, undefined: | 检查 import、依赖安装、类型错误 |
| 测试失败 | FAIL, Error:, expected, actual: | 理解测试意图,修复实现或更新测试 |
| Lint 错误 | lint, warning:, Style violation | 应用 lint 修复建议 |
| 格式化错误 | format, gofmt, prettier | 运行 format 命令 |
| 依赖缺失 | package not found, npm ERR, cargo not found | 安装依赖、更新 lock 文件 |
| 权限问题 | permission denied, ENOENT | 检查文件权限、环境变量 |
| 环境问题 | timeout, network, connection | 环境相关,标记为 skip,不做代码修改 |
| 版本冲突 | version mismatch, incompatible | 更新依赖版本 |
根因分析输出
🔍 CI 失败分析
Job: {job-name}
类型: {failure-type}
文件: {file}:{line}
命令: {failed-command}
原因:{简短描述根因}
影响范围:{哪些文件/模块受影响}
修复方案:{简述}
Step 3:实施修复
通用约束
- 只修导致失败的问题,不扩大改动范围
- 不重写整个文件,只改最小必需行
- 保留原有逻辑,除非原逻辑明显错误
- 环境问题:
skip — 不做代码修改,在 PR body 说明
分类型修复
编译错误:
- 读取出错文件
- 定位错误位置
- 修复 import / 类型 / 语法
- 运行
go build / cargo build / npm build 验证
测试失败:
- 读取测试文件和对应实现
- 判断是「实现错误」还是「测试过时」
- 如果是实现错误:修复实现
- 如果是测试过时:在 PR body 说明,更新测试
- 运行
go test / cargo test 验证
Lint 错误:
- 运行
go fmt ./... / cargo fmt / npm run lint:fix
- 如果自动修复不够,手动调整
- 验证 lint 通过
依赖问题:
- 检查
go.mod / package.json / Cargo.toml
- 添加/更新依赖
- 运行 lock 文件更新:
go mod tidy / npm install / cargo update
Step 4:验证修复
go build ./... && echo "✅ Build passed"
go vet ./... && echo "✅ Vet passed"
go test ./... && echo "✅ Tests passed"
✅ 修复验证通过
Build ... ✅
Vet .... ✅
Tests .. ✅
如果验证失败:返回 Step 2,重新分析。
Step 5:提交并创建 PR
提交
BRANCH="claude-auto-fix-ci-$(git rev-parse --abbrev-ref HEAD)-$(date +%Y%m%d%H%M)"
git checkout -b "$BRANCH"
git add -A
git commit -m "fix: CI failure in {job-name} (auto-fix)
Failure type: {failure-type}
Root cause: {root-cause}
Fixed by: Claude Code (harness-fix-ci)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
创建 PR
gh pr create \
--base "$TARGET_BRANCH" \
--head "$BRANCH" \
--title "🤖 CI Auto-Fix: {job-name}" \
--body "$(cat <<'EOF'
## CI Auto-Fix PR
**Job:** {job-name}
**Failure type:** {failure-type}
**Root cause:** {root-cause}
### What was fixed
{Fix description}
### Verification
- ✅ Build
- ✅ Tests
### Logs
{link to failed run}
---
_Generated by [Forge](https://github.com/MOONL0323/forge) / harness-fix-ci_
_If this fix is incorrect, please close this PR and investigate manually._
EOF
)"
标记环境问题
如果失败类型是环境问题(timeout/network):
⚠️ 环境问题,跳过代码修改
原因:{description}
建议:检查 GitHub Actions runner 状态,或增加 timeout 配置
在 PR body 中添加说明,不做代码修改。
Step 6:防循环机制
最大尝试次数
如果这是同一个分支的第 3 次 auto-fix 尝试:
🔴 已达最大自动修复次数(3次)
分支:{branch-name}
尝试次数:3
状态:停止自动修复
请人工介入检查。
防循环触发设计
触发条件:workflow_run (CI workflow completed, conclusion=failure)
↓
不监听:pull_request / push / issue_comment
↓
每次触发都创建新分支:claude-auto-fix-ci-{branch}-{run_id}
↓
PR 只指向目标分支,不触发其他 workflow
↓
即使 PR push 新 commit,也不触发 auto-fix
GitHub Actions workflow_run 配置
在 .github/workflows/harness-ci-auto-fix.yml(由 harness-gc 生成):
name: Harness CI Auto-Fix
on:
workflow_run:
workflows: ["CI", "Test", "Build", "Lint"]
types: [completed]
branches: [main, develop]
permissions:
contents: write
id-token: write
jobs:
auto-fix:
if: github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_branch }}
- name: Get failure details
id: details
run: |
echo "BRANCH=${{ github.event.workflow_run.head_branch }}" >> $GITHUB_OUTPUT
echo "RUN_ID=${{ github.event.workflow_run.id }}" >> $GITHUB_OUTPUT
curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/jobs" \
| jq -r '.jobs[] | select(.conclusion == "failure") | .logs_url' \
| head -1 \
| xargs -I{} curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" {} \
| gzip -d > /tmp/failed_logs.txt
echo "LOGS=/tmp/failed_logs.txt" >> $GITHUB_OUTPUT
- name: Configure git
run: |
git config user.name "Claude Code"
git config user.email "claude+ci@auto-fix"
- name: Run Claude Code fix
uses: anthropics/claude-code-action@v1
env:
CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }}
with:
prompt: |
/fix-ci --logs /tmp/failed_logs.txt --job "${{ steps.details.outputs.JOB_NAME }}"
allowedTools: Read,Write,Edit,Bash,Glob,Grep
- name: Create PR
if: success()
run: |
LATEST_BRANCH=$(git branch --show-current)
if [ -n "$(git log origin/${{ github.event.workflow_run.head_branch }}..HEAD --oneline)" ]; then
gh pr create \
--base ${{ github.event.workflow_run.head_branch }} \
--head "$LATEST_BRANCH" \
--title "🤖 CI Auto-Fix: ${{ env.JOB_NAME }}" \
--body "Auto-generated by Forge harness-fix-ci."
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify failure
if: failure()
run: |
echo "Auto-fix failed after 3 attempts. Manual intervention required."
echo "Run: ${{ github.event.workflow_run.html_url }}"
输出摘要
✅ CI Auto-Fix 完成
Job: {job-name}
类型: {failure-type}
修复文件: {files changed}
提交: {commit-hash}
PR: {pr-url}
如需取消:删除 auto-fix 分支并关闭 PR