From 27b6df119c6cd3b78d4cf46473df52f49561a8b9 Mon Sep 17 00:00:00 2001 From: Molecule AI Infra-SRE Date: Fri, 15 May 2026 06:24:15 +0000 Subject: [PATCH 1/2] fix(merge-queue): add review gates and handle merge failures gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes to the serialized Gitea merge queue: 1. REQUIRED_CONTEXTS now includes qa-review and security-review gates. Previously only CI/all-required and sop-checklist were checked, so PRs with failed reviews were merged (blocked by pre-receive hook) and retried forever — each tick re-attempting the same blocked PR. Adding the explicit review contexts causes the queue to WAIT instead of attempting merge, unblocking the next queued PR. 2. process_once() now catches ApiError on merge attempt and removes the merge-queue label rather than returning 0 and retrying the same PR on every subsequent tick. The comment on the PR informs the author what blocked the merge and tells them to re-add the label once resolved. Fixes: mc# queue infinite retry on review-blocked PRs Co-Authored-By: Claude Opus 4.7 --- .gitea/scripts/gitea-merge-queue.py | 25 ++++++++++++++++++++++++- .gitea/workflows/gitea-merge-queue.yml | 8 +++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.gitea/scripts/gitea-merge-queue.py b/.gitea/scripts/gitea-merge-queue.py index 46b0482ad..63974350b 100644 --- a/.gitea/scripts/gitea-merge-queue.py +++ b/.gitea/scripts/gitea-merge-queue.py @@ -407,7 +407,30 @@ def process_once(*, dry_run: bool = False) -> int: "deferring to next tick" ) return 0 - merge_pull(pr_number, dry_run=dry_run) + try: + merge_pull(pr_number, dry_run=dry_run) + except ApiError as exc: + # Merge failed (pre-receive hook, branch protection, etc.). + # Remove queue label so next tick picks the next PR. + msg = str(exc) + if "405" in msg or "not allowed to merge" in msg.lower(): + hint = "pre-receive hook or branch protection blocked the merge" + elif "422" in msg or "Unprocessable" in msg: + hint = "branch protection required-status check failed" + elif "409" in msg or "conflict" in msg.lower(): + hint = "merge conflict" + else: + hint = msg[:200] + remove_label(pr_number, QUEUE_LABEL, dry_run=dry_run) + post_comment( + pr_number, + ( + f"merge-queue: merge blocked ({hint}). " + f"Label removed — re-add once the block is resolved." + ), + dry_run=dry_run, + ) + return 0 return 0 return 0 diff --git a/.gitea/workflows/gitea-merge-queue.yml b/.gitea/workflows/gitea-merge-queue.yml index 2ad090171..f87f7e4b2 100644 --- a/.gitea/workflows/gitea-merge-queue.yml +++ b/.gitea/workflows/gitea-merge-queue.yml @@ -47,7 +47,13 @@ jobs: UPDATE_STYLE: merge REQUIRED_CONTEXTS: >- CI / all-required (pull_request), - sop-checklist / all-items-acked (pull_request) + sop-checklist / all-items-acked (pull_request), + qa-review / approved (pull_request), + security-review / approved (pull_request) + # qa-review and security-review gates are included so PRs that fail + # review checks are dequeued automatically (label removed) rather than + # causing the queue to retry the same blocked PR on every tick. + # # Push-side required contexts. Checking CI / all-required (push) # explicitly instead of the combined state avoids false-pause when # non-blocking jobs (continue-on-error: true) have failed — those -- 2.52.0 From a1146d2f5f9d644a2a1106119cf738a3319a8cfd Mon Sep 17 00:00:00 2001 From: Molecule AI Infra-SRE Date: Fri, 15 May 2026 07:52:11 +0000 Subject: [PATCH 2/2] fix(merge-queue): remove broken qa/sec gates from REQUIRED_CONTEXTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qa-review and security-review gates permanently fail (mc#1111: SOP_TIER_CHECK_TOKEN missing PAT — token owner not in qa/security teams, HTTP 403 on team membership probe). Adding them to REQUIRED_CONTEXTS would cause the queue to strip the merge-queue label from every PR in the queue, breaking the queue for all contributors. Keep the ApiError error-handling from the previous commit (catches 405/422/409 from merge_pull and removes the label + posts a comment). That logic prevents infinite retries on blocked PRs even without qa/sec gates. Re-add qa-review and security-review to REQUIRED_CONTEXTS once mc#1111 is resolved. Co-Authored-By: Claude Opus 4.7 --- .gitea/workflows/gitea-merge-queue.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/gitea-merge-queue.yml b/.gitea/workflows/gitea-merge-queue.yml index f87f7e4b2..b94d00f68 100644 --- a/.gitea/workflows/gitea-merge-queue.yml +++ b/.gitea/workflows/gitea-merge-queue.yml @@ -47,13 +47,13 @@ jobs: UPDATE_STYLE: merge REQUIRED_CONTEXTS: >- CI / all-required (pull_request), - sop-checklist / all-items-acked (pull_request), - qa-review / approved (pull_request), - security-review / approved (pull_request) - # qa-review and security-review gates are included so PRs that fail - # review checks are dequeued automatically (label removed) rather than - # causing the queue to retry the same blocked PR on every tick. - # + sop-checklist / all-items-acked (pull_request) + # NOTE: qa-review / security-review gates intentionally omitted. + # These gates permanently fail (mc#1111: SOP_TIER_CHECK_TOKEN missing + # PAT — token owner not in qa/security teams). Adding them to + # REQUIRED_CONTEXTS would strip the merge-queue label from every PR + # in the queue, breaking the queue for all contributors. + # Re-add these gates once mc#1111 is resolved. # Push-side required contexts. Checking CI / all-required (push) # explicitly instead of the combined state avoids false-pause when # non-blocking jobs (continue-on-error: true) have failed — those -- 2.52.0