Skip to content

Commit 09c6b3c

Browse files
committed
Add GitHub Action to auto-fix CI failures on Claude PRs
When the Validate workflow fails on a PR from a Claude branch (claude/*), this workflow automatically: 1. Checks if the last commit was an auto-fix (prevents infinite loops) 2. Extracts the failure details and error logs 3. Invokes Claude Code Action to analyze and fix the issues 4. Commits fixes directly to the same branch with [auto-fix] tag 5. Comments on the PR with the fix status The action supports fixing: - ruff formatting errors - ruff linting errors - pytest test failures Requires ANTHROPIC_API_KEY secret to be configured.
1 parent 89d3dac commit 09c6b3c

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

.github/workflows/auto-fix-ci.yml

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
name: Auto Fix CI Failures
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Validate"]
6+
types:
7+
- completed
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
actions: read
13+
issues: write
14+
id-token: write
15+
16+
jobs:
17+
auto-fix:
18+
# Only run when:
19+
# 1. The Validate workflow failed
20+
# 2. There is an associated PR
21+
# 3. The branch was created by Claude (starts with 'claude/')
22+
if: |
23+
github.event.workflow_run.conclusion == 'failure' &&
24+
github.event.workflow_run.pull_requests[0] &&
25+
startsWith(github.event.workflow_run.head_branch, 'claude/')
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
with:
31+
ref: ${{ github.event.workflow_run.head_branch }}
32+
fetch-depth: 10
33+
token: ${{ secrets.GITHUB_TOKEN }}
34+
35+
- name: Check if last commit was auto-fix (prevent loops)
36+
id: check_loop
37+
run: |
38+
LAST_COMMIT_MSG=$(git log -1 --pretty=%s)
39+
if [[ "$LAST_COMMIT_MSG" == *"[auto-fix]"* ]]; then
40+
echo "skip=true" >> $GITHUB_OUTPUT
41+
echo "Last commit was an auto-fix, skipping to prevent loop"
42+
else
43+
echo "skip=false" >> $GITHUB_OUTPUT
44+
fi
45+
46+
- name: Setup git identity
47+
if: steps.check_loop.outputs.skip != 'true'
48+
run: |
49+
git config --global user.email "claude[bot]@users.noreply.github.com"
50+
git config --global user.name "claude[bot]"
51+
52+
- name: Get CI failure details
53+
if: steps.check_loop.outputs.skip != 'true'
54+
id: failure_details
55+
uses: actions/github-script@v7
56+
with:
57+
script: |
58+
const run = await github.rest.actions.getWorkflowRun({
59+
owner: context.repo.owner,
60+
repo: context.repo.repo,
61+
run_id: ${{ github.event.workflow_run.id }}
62+
});
63+
64+
const jobs = await github.rest.actions.listJobsForWorkflowRun({
65+
owner: context.repo.owner,
66+
repo: context.repo.repo,
67+
run_id: ${{ github.event.workflow_run.id }}
68+
});
69+
70+
const failedJobs = jobs.data.jobs.filter(job => job.conclusion === 'failure');
71+
72+
let errorLogs = [];
73+
for (const job of failedJobs) {
74+
try {
75+
const logs = await github.rest.actions.downloadJobLogsForWorkflowRun({
76+
owner: context.repo.owner,
77+
repo: context.repo.repo,
78+
job_id: job.id
79+
});
80+
errorLogs.push({
81+
jobName: job.name,
82+
logs: logs.data.substring(0, 50000) // Limit log size
83+
});
84+
} catch (e) {
85+
errorLogs.push({
86+
jobName: job.name,
87+
logs: `Failed to retrieve logs: ${e.message}`
88+
});
89+
}
90+
}
91+
92+
return {
93+
runUrl: run.data.html_url,
94+
failedJobs: failedJobs.map(j => j.name),
95+
errorLogs: errorLogs
96+
};
97+
98+
- name: Fix CI failures with Claude
99+
if: steps.check_loop.outputs.skip != 'true'
100+
id: claude
101+
uses: anthropics/claude-code-action@v1
102+
with:
103+
prompt: |
104+
The CI workflow "Validate" has failed on a PR that you (Claude) contributed to.
105+
Please analyze the failure and fix the issues.
106+
107+
## Failure Information
108+
- Failed CI Run: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
109+
- Failed Jobs: ${{ join(fromJSON(steps.failure_details.outputs.result).failedJobs, ', ') }}
110+
- PR Number: ${{ github.event.workflow_run.pull_requests[0].number }}
111+
- Branch: ${{ github.event.workflow_run.head_branch }}
112+
- Repository: ${{ github.repository }}
113+
114+
## CI Workflow Steps
115+
The Validate workflow runs these checks:
116+
1. `uv run ruff format --check src/ tests/` - Code formatting
117+
2. `uv run ruff check src/ tests/` - Linting
118+
3. `uv run pytest tests/ -v` - Tests
119+
120+
## Your Task
121+
1. Analyze the error logs below to understand what failed
122+
2. Fix the issues in the code:
123+
- For formatting errors: Run `uv run ruff format src/ tests/`
124+
- For linting errors: Fix the code issues reported by ruff
125+
- For test failures: Fix the failing tests or the code they're testing
126+
3. Commit your fixes with a message that includes "[auto-fix]" tag
127+
4. Push the changes to the branch: ${{ github.event.workflow_run.head_branch }}
128+
129+
IMPORTANT: Your commit message MUST include "[auto-fix]" to prevent infinite loops.
130+
Example: "Fix linting errors in parser module [auto-fix]"
131+
132+
## Error Logs
133+
```
134+
${{ toJSON(fromJSON(steps.failure_details.outputs.result).errorLogs) }}
135+
```
136+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
137+
claude_args: "--allowedTools 'Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(git:*),Bash(uv:*),Bash(python:*),Bash(pytest:*),Bash(ruff:*)'"
138+
139+
- name: Comment on PR with fix status
140+
if: always() && steps.check_loop.outputs.skip != 'true'
141+
uses: actions/github-script@v7
142+
with:
143+
script: |
144+
const prNumber = ${{ github.event.workflow_run.pull_requests[0].number }};
145+
const conclusion = '${{ steps.claude.outcome }}';
146+
const runUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}';
147+
148+
let body;
149+
if (conclusion === 'success') {
150+
body = `## CI Auto-Fix Attempted
151+
152+
Claude has analyzed the CI failure and attempted to fix the issues.
153+
154+
- **Fix workflow run**: ${runUrl}
155+
- **Original failure**: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
156+
157+
Please review the changes pushed to this branch.`;
158+
} else {
159+
body = `## CI Auto-Fix Failed
160+
161+
Claude attempted to fix the CI failure but encountered issues.
162+
163+
- **Fix workflow run**: ${runUrl}
164+
- **Original failure**: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
165+
166+
Manual intervention may be required.`;
167+
}
168+
169+
await github.rest.issues.createComment({
170+
owner: context.repo.owner,
171+
repo: context.repo.repo,
172+
issue_number: prNumber,
173+
body: body
174+
});

0 commit comments

Comments
 (0)