Skip to content

Notify on Failure

Notify on Failure #205

name: Notify on Failure
# Fires whenever the CI or Release workflow completes with a failure conclusion.
# Creates (or updates) a GitHub Issue so failures are always visible in the
# issue tracker — no need to check the Actions tab manually.
on:
workflow_run:
workflows: ["CI", "Release"]
types: [completed]
permissions:
contents: read
issues: write
jobs:
notify:
name: Create failure issue
# Only run when the triggering workflow actually failed
if: github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Create or update ci-failure issue
uses: actions/github-script@v8
with:
script: |
const { owner, repo } = context.repo;
const run = context.payload.workflow_run;
const shortSha = run.head_sha.substring(0, 8);
// ── Ensure the ci-failure label exists ──────────────────────────
try {
await github.rest.issues.createLabel({
owner, repo,
name: 'ci-failure',
color: 'e11d48',
description: 'Automated: CI/Release workflow failure',
});
} catch (e) {
if (e.status !== 422) throw e; // 422 = already exists — fine
}
const title = `🔴 ${run.name} failed on \`${run.head_branch}\``;
const body = [
`## Workflow Failure Detected`,
``,
`| Field | Value |`,
`|-------|-------|`,
`| **Workflow** | ${run.name} |`,
`| **Branch** | \`${run.head_branch}\` |`,
`| **Commit** | [\`${shortSha}\`](https://github.com/${owner}/${repo}/commit/${run.head_sha}) |`,
`| **Run** | [#${run.run_number} — view logs](${run.html_url}) |`,
`| **Triggered by** | \`${run.event}\` |`,
``,
`### How to debug`,
`1. Open the **[run log](${run.html_url})** and expand the ❌ failed step`,
`2. Trigger the **[Debug Diagnostics](../actions/workflows/debug.yml)** workflow manually for a full environment dump`,
`3. Fix the issue locally, then push to re-trigger CI`,
`4. Close this issue once the next CI run is green ✅`,
``,
`> _Auto-generated by [notify-failure.yml](.github/workflows/notify-failure.yml)_`,
].join('\n');
// ── Avoid duplicates: update existing open issue if one exists ──
const { data: existing } = await github.rest.issues.listForRepo({
owner, repo, state: 'open', labels: 'ci-failure', per_page: 20,
});
const dupe = existing.find(i => i.title.includes(run.name));
if (dupe) {
await github.rest.issues.createComment({
owner, repo,
issue_number: dupe.number,
body: [
`### Failure repeated — ${new Date().toUTCString()}`,
``,
`[Run #${run.run_number}](${run.html_url}) failed again on commit [\`${shortSha}\`](https://github.com/${owner}/${repo}/commit/${run.head_sha}).`,
].join('\n'),
});
core.notice(`Updated existing failure issue #${dupe.number}`);
} else {
const { data: issue } = await github.rest.issues.create({
owner, repo, title, body,
labels: ['ci-failure'],
});
core.notice(`Created failure issue #${issue.number}: ${issue.html_url}`);
}
close-on-success:
name: Close failure issue on success
# Auto-close any open ci-failure issues when the triggering workflow succeeds
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Close open ci-failure issues for this workflow
uses: actions/github-script@v8
with:
script: |
const { owner, repo } = context.repo;
const run = context.payload.workflow_run;
const shortSha = run.head_sha.substring(0, 8);
const { data: existing } = await github.rest.issues.listForRepo({
owner, repo, state: 'open', labels: 'ci-failure', per_page: 20,
});
const toClose = existing.filter(i => i.title.includes(run.name));
for (const issue of toClose) {
await github.rest.issues.createComment({
owner, repo,
issue_number: issue.number,
body: [
`### ✅ ${run.name} is green — auto-closing`,
``,
`[Run #${run.run_number}](${run.html_url}) on [\`${shortSha}\`](https://github.com/${owner}/${repo}/commit/${run.head_sha}) completed successfully.`,
].join('\n'),
});
await github.rest.issues.update({
owner, repo,
issue_number: issue.number,
state: 'closed',
});
core.notice(`Auto-closed failure issue #${issue.number}`);
}