Skip to content

CI Failure Reporter #765

CI Failure Reporter

CI Failure Reporter #765

name: CI Failure Reporter
on:
workflow_run:
workflows:
- "PR Quality"
- "Coverage"
- "CodeQL Security Scan"
- "Performance Regression"
types: [completed]
permissions:
issues: write
contents: read
jobs:
report:
if: github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-latest
steps:
- name: Create or update failure issue
uses: actions/github-script@v9
with:
script: |
try {
const workflow = context.payload.workflow_run;
const title = `[CI Failure] ${workflow.name}`;
const runUrl = workflow.html_url;
const sha = workflow.head_sha.slice(0, 7);
const branch = workflow.head_branch;
const actor = workflow.actor?.login ?? 'unknown';
// Use search API for reliable deduplication across all open issues
const q = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open label:ci-failure "${title}" in:title`;
const { data: searchResult } = await github.rest.search.issuesAndPullRequests({ q, per_page: 5 });
const existing = searchResult.items.find(i => i.title === title);
if (existing) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body: `🔴 **Failed again** on \`${branch}\` @ \`${sha}\` (actor: @${actor})\n\n[View run](${runUrl})`,
});
console.log(`Updated existing issue #${existing.number}`);
} else {
try {
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: 'ci-failure' });
} catch {
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: 'ci-failure', color: 'd93f0b', description: 'Opened automatically by CI failure reporter' });
}
const { data: issue } = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
labels: ['bug', 'ci-failure'],
body: [
`## ${workflow.name} failed`,
'',
`| Field | Value |`,
`|-------|-------|`,
`| Branch | \`${branch}\` |`,
`| Commit | \`${sha}\` |`,
`| Actor | @${actor} |`,
`| Run | [View logs](${runUrl}) |`,
'',
'_This issue was opened automatically. Close it once the failure is resolved._',
].join('\n'),
});
console.log(`Opened issue #${issue.number}: ${title}`);
}
} catch (error) {
core.error(`CI failure reporter failed: ${error.message}`);
}