Skip to content

Auto Fix CI Failures #12

Auto Fix CI Failures

Auto Fix CI Failures #12

name: Auto Fix CI Failures
on:
workflow_run:
workflows:
- "CI"
types:
- completed
permissions:
contents: write
pull-requests: write
actions: read
issues: write
id-token: write
jobs:
auto-fix:
# Security: only run in MonumentalSystems org + only fix Claude's branches
if: |
github.repository_owner == 'MonumentalSystems' &&
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.pull_requests[0] &&
startsWith(github.event.workflow_run.head_branch, 'claude/')
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ github.event.workflow_run.head_branch }}
fetch-depth: 0
- name: Get CI failure details
id: failure_details
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const run = await github.rest.actions.getWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }}
});
const jobs = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }}
});
const failedJobs = jobs.data.jobs.filter(job => job.conclusion === 'failure');
let errorLogs = [];
for (const job of failedJobs) {
try {
const logs = await github.rest.actions.downloadJobLogsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
job_id: job.id
});
const logText = typeof logs.data === 'string' ? logs.data : String(logs.data);
errorLogs.push({
jobName: job.name,
logs: logText.slice(-3000)
});
} catch (e) {
errorLogs.push({
jobName: job.name,
logs: `Failed to retrieve logs: ${e.message}`
});
}
}
return {
runUrl: run.data.html_url,
prNumber: run.data.pull_requests[0]?.number,
failedJobs: failedJobs.map(j => j.name),
errorLogs: errorLogs
};
- name: Fix CI failures with Claude
id: claude
uses: anthropics/claude-code-action@f669191d7d1e67f08a54b0c11cf5683a9a391951 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.ORG_PAT || github.token }}
use_commit_signing: true
track_progress: true
plugin_marketplaces: |
https://github.com/richlander/dotnet-skills.git
plugins: |
dotnet-skills@richlander-dotnet-skills
prompt: |
The CI build/test failed on this branch. Analyze the error logs and fix the code.
Failed CI Run: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
PR: #${{ fromJSON(steps.failure_details.outputs.result).prNumber }}
Failed Jobs: ${{ join(fromJSON(steps.failure_details.outputs.result).failedJobs, ', ') }}
Branch: ${{ github.event.workflow_run.head_branch }}
Repository: ${{ github.repository }}
Error logs:
${{ toJSON(fromJSON(steps.failure_details.outputs.result).errorLogs) }}
Instructions:
- This is a MercuryBank API helper library — .NET NuGet package
- Read CLAUDE.md for project conventions if it exists
- Run `dotnet build` to verify your fix compiles
- Run `dotnet test --filter "Category!=Integration&Category!=Skipped"` to verify tests pass
- Commit the fix and push to the same branch
- Comment on the PR explaining what was wrong and how you fixed it
claude_args: |
--model claude-opus-4-6
--max-turns 30
--allowedTools "Bash(git:*)" "Bash(dotnet:*)" "Bash(gh:*)" "Read" "Edit" "Write" "Glob" "Grep" "WebFetch" "WebSearch"