From 3290c4f95336beeb379c6de61c9b405d32fb3936 Mon Sep 17 00:00:00 2001 From: znerol2 <6800371+znerol2@users.noreply.github.com> Date: Wed, 21 Oct 2026 15:36:24 +0200 Subject: [PATCH] ci(automerge): decide from the PR's live labels and draft state The workflow read the hold label and the draft flag from the event payload. The payload can be stale: a label added or a draft toggled after the event fired was not seen, so a held or draft PR could still get auto-merge armed. A failed or malformed read also did not stop the run. The workflow now reads the PR's labels and draft state live when it runs, and fails closed if the read errors or returns a reply it cannot parse. This is the same file as the umbrella workflow, copied byte-identical. The regression test lives in the umbrella (tests/automerge-live-state.sh) and runs against this exact file. Refs dmfdeploy/dmfdeploy#439 Signed-off-by: znerol2 <6800371+znerol2@users.noreply.github.com> --- .github/workflows/automerge.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 7b8135d..5328041 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -22,16 +22,25 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_URL: ${{ github.event.pull_request.html_url }} - PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ' ') }} - PR_DRAFT: ${{ github.event.pull_request.draft }} run: | set -eu - case " ${PR_LABELS} " in *" hold "*) + # Decide from the PR's labels and draft state as they are now, not + # from the event payload, which is a snapshot: `gh pr create --label` + # fires `opened` without the label, and a late `opened` run would + # arm a held PR (#439). If the read fails, exit non-zero without + # touching auto-merge, so a failed read never arms. The same holds + # for a reply that is not a boolean isDraft and named labels. + pr=$(gh pr view "${PR_URL}" --json labels,isDraft) + jq -e '(.isDraft | type == "boolean") and (.labels | type == "array") + and all(.labels[]; (.name | type) == "string")' <<<"${pr}" >/dev/null + labels=$(jq -r '[.labels[].name] | join(" ")' <<<"${pr}") + draft=$(jq -r '.isDraft' <<<"${pr}") + case " ${labels} " in *" hold "*) gh pr merge --disable-auto "${PR_URL}" || true echo "hold label present — auto-merge disarmed" exit 0 ;; esac - if [ "${PR_DRAFT}" = "true" ]; then + if [ "${draft}" = "true" ]; then echo "draft PR — skipping" exit 0 fi