Skip to content

fix(release): reuse only an OPEN promote PR, not one in any state - #3424

Merged
myasnikovdaniil merged 1 commit into
mainfrom
fix/promote-pr-state-guard
Jul 22, 2026
Merged

fix(release): reuse only an OPEN promote PR, not one in any state#3424
myasnikovdaniil merged 1 commit into
mainfrom
fix/promote-pr-state-guard

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What this PR does

The promote step guarded PR creation with gh pr view <branch>, which resolves a pull request by head branch in any state. A closed one satisfied the guard, so creation was skipped while the step still exited 0 — a promotion that reports success and leaves nothing to merge. Same silent-skip shape as the enumeration bug fixed in #3404: the run is green, the artifact is missing, and nothing says so.

STABLE_BRANCH is derived from the stable version (release-${version}), not from the rc number, so every rc promoted to that version shares it. Once any promote PR for a version has been closed, both re-dispatch paths this workflow documents as supported are wedged: re-dispatching the same rc, and promoting a newer rc to the same version. That second one is named in the workflow's own header as the reason a leftover draft is tolerated — the tolerance was implemented for the release draft and missed for the PR.

Neither path recovers by reopening the old PR, because the step's own git checkout -B plus force-push makes that PR's head unreachable, and GitHub then refuses:

422 Validation Failed
  state cannot be changed. The release-1.6.0 branch was force-pushed or recreated.

The fix asks the question the guard meant to ask — is there an open PR for this head — and creates one otherwise.

Hit live. Promoting v1.6.0-rc.4 after #3397 had been closed: run 29917852639 went green, logged PR already open for release-1.6.0, and opened no PR. This currently blocks the v1.6.0 release.

Verified in both directions against live data, so the change is not vacuous:

head branch PR state old guard new guard
release-1.6.0 #3397 closed TRUE → skip (bug) FALSE → create ✅
chore/gitignore #3412 open TRUE → skip TRUE → skip ✅

actionlint exits 0 and zizmor reports no findings. The guard reads ${STABLE_BRANCH} as a shell variable from the step's env: block rather than as a ${{ }} expansion inside run:, so no expression-injection surface is added.

Screenshots

N/A — no UI change.

Downstream repositories

Walked the trigger map in docs/agents/contributing.md against the diff, which is one file: .github/workflows/promote-rc.yaml. The only workflow-related trigger in the map is cozystack/ccp on "change release-prep behaviour in .github/workflows/tags.yaml", which this does not touch. This change also restores the documented behaviour of promote-rc.yaml rather than altering its contract, so nothing downstream sees a result different from what the docs already promise.

Release note

fix(release): promoting a release candidate now opens the promotion pull request even when an earlier promotion attempt for the same version was abandoned. Previously the workflow mistook a closed pull request for an open one, skipped creating a new one, and reported success with nothing left to merge.

Summary by CodeRabbit

  • Bug Fixes
    • Improved release PR creation checks so closed or previously merged pull requests no longer prevent new release PRs from being opened.

The promote step guarded PR creation with `gh pr view <branch>`, which
resolves a pull request by head branch in any state. A closed one
satisfied the guard, so creation was skipped while the step still exited
0 — a promotion that reports success and leaves nothing to merge, the
same silent-skip shape as the enumeration bug fixed in #3404.

STABLE_BRANCH is derived from the stable version, so every rc promoted to
that version shares it. Both re-dispatch paths this workflow documents as
supported are therefore wedged once any promote PR for that version has
been closed: re-dispatching the same rc, and promoting a newer rc to the
same version. Neither recovers by reopening the old PR, because the
step's own force-push makes its head unreachable and GitHub refuses with
"state cannot be changed. The <branch> branch was force-pushed or
recreated."

Hit live while promoting v1.6.0-rc.4 after #3397 had been closed: the run
went green and opened no PR.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added area/release Issues or PRs related to release tooling (changelog, backport, release pipeline) kind/bug Categorizes issue or PR as related to a bug size/S This PR changes 10-29 lines, ignoring generated files labels Jul 22, 2026
@dosubot dosubot Bot added the area/ci Issues or PRs related to CI workflows, GitHub Actions, automation label Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The release workflow now detects existing open PRs for the stable branch using gh pr list, preventing closed or other-state PRs from suppressing new PR creation.

Changes

Release PR detection

Layer / File(s) Summary
Open PR guard
.github/workflows/promote-rc.yaml
The workflow queries open PRs with ${STABLE_BRANCH} as the head and uses the first PR number to choose between reusing an existing PR and creating a new one.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: androndo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the fix: reuse only an open promote PR instead of any PR state.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/promote-pr-state-guard

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
.github/workflows/promote-rc.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/promote-rc.yaml (1)

273-279: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail closed on PR lookup errors.
gh pr list is only checked for non-empty output here, so an API/auth failure that returns no stdout still falls through to gh pr create and can hide the real error. Capture the command status first and exit before treating the result as “no open PR”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/promote-rc.yaml around lines 273 - 279, Update the PR
lookup logic around gh pr list to capture its exit status separately from its
output. If the lookup fails, report the error and stop the workflow; only
proceed to gh pr create when the lookup succeeds and returns no open PR,
preserving the existing message and creation arguments.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/promote-rc.yaml:
- Line 273: Update the PR lookup condition in the promotion workflow to filter
by both STABLE_BRANCH as the head and BASE as the base, and capture the gh pr
list result/status separately so command failures are handled explicitly rather
than treated as an empty result. Only continue the existing “PR exists” logic on
a successful lookup with a matching open PR; propagate or fail on lookup errors.

---

Outside diff comments:
In @.github/workflows/promote-rc.yaml:
- Around line 273-279: Update the PR lookup logic around gh pr list to capture
its exit status separately from its output. If the lookup fails, report the
error and stop the workflow; only proceed to gh pr create when the lookup
succeeds and returns no open PR, preserving the existing message and creation
arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f2cfac93-3469-4425-9117-7f03d693ce00

📥 Commits

Reviewing files that changed from the base of the PR and between 55195de and baf0158.

📒 Files selected for processing (1)
  • .github/workflows/promote-rc.yaml

# version. Neither can recover by reopening, since the force-push
# below makes the old pr's head unreachable and GitHub then refuses
# the reopen outright ("branch was force-pushed or recreated").
if [ -n "$(gh pr list --head "${STABLE_BRANCH}" --state open --json number --jq '.[0].number')" ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/promote-rc.yaml"

# Show the relevant workflow section with line numbers.
sed -n '240,290p' "$FILE" | cat -n

# Search for the PR-listing and PR-creation logic nearby.
rg -n 'gh pr list|gh pr create|BASE|STABLE_BRANCH' "$FILE"

Repository: cozystack/cozystack

Length of output: 4456


🏁 Script executed:

python3 - <<'PY'
import subprocess, textwrap, tempfile, os, sys

script = r'''#!/bin/bash
set -euo pipefail
echo before
if [ -n "$(false)" ]; then
  echo nonempty
else
  echo empty
fi
echo after
'''
p = subprocess.run(["bash", "-lc", script], capture_output=True, text=True)
print("exit:", p.returncode)
print(p.stdout)
print(p.stderr)
PY

Repository: cozystack/cozystack

Length of output: 186


Scope the PR lookup to BASE and handle gh errors explicitly.

gh pr list currently matches only on STABLE_BRANCH, so an open PR on the same head but a different base can suppress the PR this run needs. And if gh pr list fails, [ -n "$(…)" ] treats it like “no PR”, which can hide the failure and create duplicates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/promote-rc.yaml at line 273, Update the PR lookup
condition in the promotion workflow to filter by both STABLE_BRANCH as the head
and BASE as the base, and capture the gh pr list result/status separately so
command failures are handled explicitly rather than treated as an empty result.
Only continue the existing “PR exists” logic on a successful lookup with a
matching open PR; propagate or fail on lookup errors.

@myasnikovdaniil
myasnikovdaniil merged commit 706c3f0 into main Jul 22, 2026
19 of 20 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the fix/promote-pr-state-guard branch July 22, 2026 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ci Issues or PRs related to CI workflows, GitHub Actions, automation area/release Issues or PRs related to release tooling (changelog, backport, release pipeline) kind/bug Categorizes issue or PR as related to a bug size/S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants