From ca75a43b54ffd0824a4b8e2adb120cdaf08e533c Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 17 Aug 2026 21:26:09 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20automerge=20ignora=20o=20pr=C3=B3prio=20?= =?UTF-8?q?status=20do=20job=20(deadlock=20de=20self-wait)=20e=20exige=20o?= =?UTF-8?q?=20contexto=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/forgejo-pr-automerge.mjs | 4 ++++ scripts/lib/forgejo-api.mjs | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/forgejo-pr-automerge.mjs b/scripts/forgejo-pr-automerge.mjs index b1ac0f1..ea1b9d8 100644 --- a/scripts/forgejo-pr-automerge.mjs +++ b/scripts/forgejo-pr-automerge.mjs @@ -46,6 +46,10 @@ try { console.log(`[forgejo-pr-automerge] PR #${prNumber} já Ready`) } const merged = await api.autoMerge(prNumber, { + // The workflow job that runs this script reports its own status on the + // PR head sha — ignoring it avoids the self-wait deadlock. The required + // `checks` context is enforced by waitForChecks. + ignoreContexts: ['ready-automerge'], log: (line) => console.log(`[forgejo-pr-automerge] ${line}`), }) console.log( diff --git a/scripts/lib/forgejo-api.mjs b/scripts/lib/forgejo-api.mjs index 2349382..caf1de5 100644 --- a/scripts/lib/forgejo-api.mjs +++ b/scripts/lib/forgejo-api.mjs @@ -271,22 +271,32 @@ export const createApi = ({ base, token, repository, fetchImpl } = {}) => { body: { ...payload, branch_name: branch }, }), - /** All required statuses on the PR head are success (or a failure exists). */ - waitForChecks: async (number, { timeoutMs = 30 * 60 * 1000, pollMs = 15000, log } = {}) => { + /** + * All required statuses on the PR head are success (or a failure exists). + * `ignoreContexts` excludes a job's OWN status — a workflow that merges a + * PR sees itself as `pending` on the head sha and would deadlock waiting + * for itself. `requiredContext` (default `checks`) must be success. + */ + waitForChecks: async ( + number, + { timeoutMs = 30 * 60 * 1000, pollMs = 15000, log, ignoreContexts = [], requiredContext = 'checks' } = {}, + ) => { const started = Date.now() for (;;) { const pr = await api.getPullRequest(number) if (!pr || pr.state !== 'OPEN' || pr.merged) return pr const statuses = await api.listCommitStatuses(pr.head.sha) - const failed = statuses.filter( + const relevant = statuses.filter((entry) => !ignoreContexts.some((c) => entry.context.includes(c))) + const failed = relevant.filter( (entry) => entry.state === 'failure' || entry.state === 'error', ) - const pending = statuses.filter((entry) => entry.state === 'pending') + const pending = relevant.filter((entry) => entry.state === 'pending') if (failed.length > 0) { const names = failed.map((entry) => entry.context).join(', ') throw new Error(`Checks falharam no PR #${number}: ${names}`) } - if (statuses.length > 0 && pending.length === 0) return pr + const required = relevant.find((entry) => entry.context === requiredContext) + if (required && required.state === 'success' && pending.length === 0) return pr if (Date.now() - started > timeoutMs) { throw new Error(`Timeout esperando checks do PR #${number}.`) }