CI Failure Reporter #765
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}`); | |
| } |