From 73d91725a95c284416d4dc3447bda223da393a89 Mon Sep 17 00:00:00 2001 From: "privilegedescalation-engineer[bot]" <269729446+privilegedescalation-engineer[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 12:13:50 +0000 Subject: [PATCH] fix(plugin-release): correct PR merge logic for BLOCKED state (#133) * fix(plugin-release): correct PR merge logic for BLOCKED state Prior releases failed with 'Resource not accessible by integration' when gh pr merge was called with a branch name on a BLOCKED PR. The root cause is that --auto requires the PR to have a pending status check that can be satistfied by auto-merge. Without --auto, gh attempts an immediate merge but the BLOCKED state (from branch protection requiring status checks) causes GitHub to reject the push. Fix: always use --auto for BLOCKED PRs, and refactor the polling loop so it properly loops until mergeStateStatus is no longer UNKNOWN (up to 3 retries with exponential back-off) before deciding whether to use --auto or merge directly. Also fix the case where gh pr create is called without --json output, so OPEN_PR is always captured correctly regardless of whether we created a new PR or found a pre-existing one. Co-Authored-By: Paperclip * fix: restore MERGED check and use PR number in retry loop - Restore idempotent exit 0 when PR is already MERGED (regression from prior fix) - Use $OPEN_PR instead of hardcoded branch name in gh pr view retry loop - Fallback to --auto when UNKNOWN persists after exhausting retries (safe: auto-merge waits for branch protection) Fixes bugs reported by Regression Regina on PR #133. --------- Co-authored-by: Chris Farhood Co-authored-by: Paperclip --- .github/workflows/plugin-release.yaml | 68 +++++++++++++++------------ 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/.github/workflows/plugin-release.yaml b/.github/workflows/plugin-release.yaml index 5b59ba6..6fe96d9 100644 --- a/.github/workflows/plugin-release.yaml +++ b/.github/workflows/plugin-release.yaml @@ -334,6 +334,7 @@ jobs: - name: Create PR for version bump run: | + set -o pipefail VERSION="${{ inputs.version }}" BODY=$(printf "Automated version bump and checksum update for v%s.\n\ncc @cpfarhood" "${VERSION}") # Create PR only if an OPEN one doesn't already exist. @@ -345,43 +346,50 @@ jobs: --title "release: v${VERSION}" \ --body "$BODY" \ --base main \ - --head "release/v${VERSION}" + --head "release/v${VERSION}" \ + --json number --jq '.number' + # Pull the number again to handle both create and pre-existing cases + OPEN_PR=$(gh pr list --base main --head "release/v${VERSION}" --state open --json number --jq '.[0].number' 2>/dev/null) else echo "::notice::Open PR #${OPEN_PR} for release/v${VERSION} already exists — skipping creation." fi - # Re-fetch open PR number (handles both new and pre-existing open PRs). - PR_STATE=$(gh pr list --base main --head "release/v${VERSION}" --state open --json state --jq '.[0].state' 2>/dev/null || echo "unknown") - if [ "$PR_STATE" != "OPEN" ]; then - echo "No open PR for release/v${VERSION} — it may have already merged. Checking." - PR_STATE=$(gh pr view "release/v${VERSION}" --json state --jq '.state' 2>/dev/null || echo "unknown") - if [ "$PR_STATE" = "MERGED" ]; then - echo "PR release/v${VERSION} is already merged. Skipping merge step." - exit 0 - fi + # Guard: ensure we have a PR number before proceeding + if [ -z "$OPEN_PR" ]; then + echo "::error::Could not determine PR number for release/v${VERSION}." + exit 1 fi - # Use auto-merge only when there are pending status checks to wait for. - # Valid mergeStateStatus values (gh GraphQL): - # BEHIND, BLOCKED, CLEAN, DIRTY, DRAFT, HAS_HOOKS, UNKNOWN, UNSTABLE - # Note: Field was renamed from mergeableState to mergeStateStatus in gh CLI. - MERGE_STATE=$(gh pr view "release/v${VERSION}" --json mergeStateStatus --jq '.mergeStateStatus') - if [ "$MERGE_STATE" = "BLOCKED" ]; then - echo "PR is $MERGE_STATE — enabling auto-merge." - gh pr merge "release/v${VERSION}" --auto --squash --delete-branch - elif [ "$MERGE_STATE" = "UNKNOWN" ]; then - # GitHub is still computing mergeability. Retry once after a brief wait. - echo "PR is $MERGE_STATE — GitHub is computing mergeability. Retrying in 5s." - sleep 5 - MERGE_STATE=$(gh pr view "release/v${VERSION}" --json mergeStateStatus --jq '.mergeStateStatus') - if [ "$MERGE_STATE" = "BLOCKED" ]; then - echo "PR is now $MERGE_STATE — enabling auto-merge." - gh pr merge "release/v${VERSION}" --auto --squash --delete-branch - else - echo "PR is still $MERGE_STATE after retry — merging directly." - gh pr merge "release/v${VERSION}" --squash --delete-branch + echo "::notice::Working with PR #${OPEN_PR}" + + # Check if PR was already merged (idempotency — safe to re-trigger after a stale branch) + MERGED_CHECK=$(gh pr view "$OPEN_PR" --json state --jq '.state' 2>/dev/null) + if [ "$MERGED_CHECK" = "MERGED" ]; then + echo "::notice::PR #${OPEN_PR} was already merged. Nothing to do." + exit 0 + fi + # Determine whether to use --auto or not based on current status. + # Retry the status check up to 3 times with exponential back-off when + # GitHub is still computing the merge state (UNKNOWN state). + MAX_RETRIES=3 + BACKOFF=3 + MERGE_STATE="" + for i in $(seq 1 $MAX_RETRIES); do + MERGE_STATE=$(gh pr view "$OPEN_PR" --json mergeStateStatus --jq '.mergeStateStatus' 2>/dev/null) + if [ "$MERGE_STATE" != "UNKNOWN" ]; then + break fi + if [ $i -lt $MAX_RETRIES ]; then + echo "PR merge state is UNKNOWN (GitHub still computing). Retry ${i}/${MAX_RETRIES} in ${BACKOFF}s..." + sleep $BACKOFF + BACKOFF=$((BACKOFF * 2)) + fi + done + + if [ "$MERGE_STATE" = "BLOCKED" ] || [ "$MERGE_STATE" = "UNKNOWN" ]; then + echo "PR is $MERGE_STATE — enabling auto-merge (safe fallback, waits for branch protection checks)." + gh pr merge "$OPEN_PR" --auto --squash --delete-branch else echo "PR is $MERGE_STATE — merging directly." - gh pr merge "release/v${VERSION}" --squash --delete-branch + gh pr merge "$OPEN_PR" --squash --delete-branch fi env: GH_TOKEN: ${{ steps.app-token.outputs.token }}