From 09403bd734b0d03d29dd2d3f809e14660f403f4c Mon Sep 17 00:00:00 2001 From: Luke Mino-Altherr Date: Wed, 27 May 2026 16:26:08 -0700 Subject: [PATCH] Revert to latest-per-reviewer approval check for OSS repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dismissed approvals should NOT be counted in OSS repos — PRs require current approval at merge time. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/detect-unreviewed-merge.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/detect-unreviewed-merge.yml b/.github/workflows/detect-unreviewed-merge.yml index c23a35b2c..aaea4b313 100644 --- a/.github/workflows/detect-unreviewed-merge.yml +++ b/.github/workflows/detect-unreviewed-merge.yml @@ -41,17 +41,26 @@ jobs: core.info(`Found PR #${pr.number}: ${pr.title}`); - // Check for approvals. DISMISSED counts because "dismiss stale reviews - // on new commits" changes APPROVED → DISMISSED when commits are pushed - // after approval — the review still happened. + // Check for approving reviews using latest review per reviewer. + // DISMISSED approvals (from "dismiss stale reviews on new commits") + // are intentionally NOT counted — OSS PRs require current approval. const reviews = await github.paginate(github.rest.pulls.listReviews, { owner, repo, pull_number: pr.number, }); - const hasApproval = reviews.some( - r => r.state === 'APPROVED' || r.state === 'DISMISSED' + const latestByReviewer = new Map(); + for (const r of reviews) { + if (!r.user || r.state === 'COMMENTED') continue; + const prev = latestByReviewer.get(r.user.login); + if (!prev || new Date(r.submitted_at) > new Date(prev.submitted_at)) { + latestByReviewer.set(r.user.login, r); + } + } + + const hasApproval = Array.from(latestByReviewer.values()).some( + r => r.state === 'APPROVED' ); if (hasApproval) { core.info('PR has an approving review — no action needed.');