Skip to content

Merge Queue Failure Notification #1891

Merge Queue Failure Notification

Merge Queue Failure Notification #1891

# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
# Notifies PR authors when their PR fails in the merge queue.
#
# Two triggers:
# 1. workflow_run: Fires when watched workflows complete — posts a comment if
# the triggering run was a merge_group event that failed.
# 2. merge_group: Pass-through so the merge queue sees a "success" status
# instead of a phantom "failure" (GitHub creates run records for every
# workflow file on push/merge_group; files without a matching trigger
# get 0 jobs → conclusion "failure").
name: Merge Queue Failure Notification
on:
workflow_run:
workflows:
- "Code Quality (Lint)"
- "Unit Tests"
types:
- completed
# Explicit merge_group trigger prevents phantom failure runs in the merge queue
merge_group:
permissions:
contents: read
pull-requests: write
actions: read
jobs:
# Fast pass-through so the merge queue sees a green check for this workflow
merge-queue-pass:
name: Merge Queue Pass-Through
if: github.event_name == 'merge_group'
runs-on: ubuntu-latest
steps:
- run: echo "Pass-through — notification runs via workflow_run trigger"
notify-on-failure:
name: Notify on Merge Queue Failure
runs-on: ubuntu-latest
# Only run when a watched workflow completed AND it was from a merge_group AND it failed
if: >
github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'merge_group' &&
github.event.workflow_run.conclusion == 'failure'
steps:
- name: Post failure comment to PR
uses: actions/github-script@v7
with:
script: |
// Extract PR number from merge queue branch name (e.g., gh-readonly-queue/main/pr-254-...)
const headBranch = context.payload.workflow_run.head_branch || '';
const prMatch = headBranch.match(/pr-(\d+)/);
if (!prMatch) {
console.log('Could not extract PR number from branch:', headBranch);
console.log('This may not be a merge queue run');
return;
}
const prNumber = parseInt(prMatch[1], 10);
const workflowRun = context.payload.workflow_run;
const runUrl = workflowRun.html_url;
const workflowName = workflowRun.name;
// Get the failed jobs for more detail
const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: workflowRun.id,
});
const failedJobs = jobs.jobs.filter(job => job.conclusion === 'failure');
const failedJobsList = failedJobs.length > 0
? failedJobs.map(job => ` - [${job.name}](${job.html_url})`).join('\n')
: ' - Check workflow run for details';
const body = [
'## Merge Queue Failure',
'',
`Your PR was removed from the merge queue due to test failures in **${workflowName}**.`,
'',
'**Failed jobs:**',
failedJobsList,
'',
`**Full workflow run:** [View logs](${runUrl})`,
'',
'<sub>This comment was automatically generated.</sub>',
].join('\n');
// Check if we already posted a comment for this run to avoid duplicates
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const existingComment = comments.find(c =>
c.body.includes('Merge Queue Failure') &&
c.body.includes(runUrl)
);
if (existingComment) {
console.log('Comment already exists for this run, skipping');
return;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: body,
});
console.log(`Posted failure notification to PR #${prNumber}`);