Notify on Workflow Failure #447
This file contains 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: Notify on Workflow Failure | |
on: | |
workflow_run: | |
workflows: ["Process new code merged to main"] | |
types: | |
- completed | |
permissions: | |
issues: write | |
jobs: | |
notifyFailure: | |
runs-on: ubuntu-latest | |
if: ${{ github.event.workflow_run.conclusion == 'failure' }} | |
steps: | |
- name: Fetch Workflow Run Jobs | |
id: fetch-workflow-jobs | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const runId = "${{ github.event.workflow_run.id }}"; | |
const jobsData = await github.rest.actions.listJobsForWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: runId, | |
}); | |
return jobsData.data; | |
- name: Fetch Previous Workflow Run | |
id: previous-workflow-run | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const runId = ${{ github.event.workflow_run.id }}; | |
const allRuns = await github.rest.actions.listWorkflowRuns({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
workflow_id: 'preDeploy.yml', | |
}); | |
const filteredRuns = allRuns.data.workflow_runs.filter(run => run.actor.login !== 'OSBotify' && run.status !== 'cancelled'); | |
const currentIndex = filteredRuns.findIndex(run => run.id === runId); | |
const previousRun = filteredRuns[currentIndex + 1]; | |
return previousRun; | |
- name: Fetch Previous Workflow Run Jobs | |
id: previous-workflow-jobs | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const previousRun = ${{ steps.previous-workflow-run.outputs.result }}; | |
const runId = previousRun.id; | |
const jobsData = await github.rest.actions.listJobsForWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: runId, | |
}); | |
return jobsData.data; | |
- name: Process Each Failed Job | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const jobs = ${{ steps.fetch-workflow-jobs.outputs.result }}; | |
const previousRun = ${{ steps.previous-workflow-run.outputs.result }}; | |
const previousRunJobs = ${{ steps.previous-workflow-jobs.outputs.result }}; | |
const headCommit = "${{ github.event.workflow_run.head_commit.id }}"; | |
const prData = await github.rest.repos.listPullRequestsAssociatedWithCommit({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
commit_sha: headCommit, | |
}); | |
const pr = prData.data[0]; | |
const prLink = pr.html_url; | |
const prAuthor = pr.user.login; | |
const prMerger = "${{ github.event.workflow_run.actor.login }}"; | |
const failureLabel = 'Workflow Failure'; | |
for (let i = 0; i < jobs.total_count; i++) { | |
if (jobs.jobs[i].conclusion == 'failure') { | |
const jobName = jobs.jobs[i].name; | |
const jobLink = jobs.jobs[i].html_url; | |
const previousJob = previousRunJobs.jobs.find(job => job.name === jobName); | |
if (previousJob?.conclusion === 'success') { | |
const annotations = await github.rest.checks.listAnnotations({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
check_run_id: jobs.jobs[i].id, | |
}); | |
let errorMessage = ""; | |
for(let j = 0; j < annotations.data.length; j++) { | |
errorMessage += annotations.data[j].annotation_level + ": "; | |
errorMessage += annotations.data[j].message + "\n"; | |
} | |
const issueTitle = `Investigate workflow job failing on main: ${ jobName }`; | |
const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + | |
`- **📋 Job Name**: [${ jobName }](${ jobLink })\n` + | |
`- **🔧 Failure in Workflow**: Process new code merged to main\n` + | |
`- **🔗 Triggered by PR**: [PR Link](${ prLink })\n` + | |
`- **👤 PR Author**: @${ prAuthor }\n` + | |
`- **🤝 Merged by**: @${ prMerger }\n` + | |
`- **🐛 Error Message**: \n ${errorMessage}\n\n` + | |
`⚠️ **Action Required** ⚠️:\n\n` + | |
`🛠️ A recent merge appears to have caused a failure in the job named [${ jobName }](${ jobLink }).\n` + | |
`This issue has been automatically created and labeled with \`${ failureLabel }\` for investigation. \n\n` + | |
`👀 **Please look into the following**:\n` + | |
`1. **Why the PR caused the job to fail?**\n` + | |
`2. **Address any underlying issues.**\n\n` + | |
`🐛 We appreciate your help in squashing this bug!`; | |
await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: issueTitle, | |
body: issueBody, | |
labels: [failureLabel, 'Hourly'], | |
assignees: [prMerger] | |
}); | |
} | |
} | |
} |