fix(release): handle re-triggers — stale branch cleanup and duplicate PR guard#73
Conversation
…licate PR If a release workflow fails after creating the branch (e.g. pnpm setup failure, network error) but before creating the tag, re-triggering the workflow previously failed at 'git push origin $BRANCH' because the branch already existed. Changes: - Commit and tag: check for existing remote branch and delete it before re-creating, so re-triggers are clean. Safe because check-tag skips when the tag already exists — we only reach this point when the tag does NOT exist yet. - Create PR: guard with 'gh pr view' so a pre-existing PR from a failed run is reused instead of causing 'pr already exists' failure. Split the single 'git push origin $BRANCH --tags' into two pushes (branch and tag separately) to avoid any flag ambiguity.
There was a problem hiding this comment.
Clean idempotency fix. Stale branch cleanup is safe — check-tag already gates on existing tags, so we only reach the branch step when the tag is absent. Split push avoids partial-push failures. Duplicate PR guard prevents noise on re-triggers. Approved.
There was a problem hiding this comment.
QA Review — Approved
Diff reviewed: .github/workflows/plugin-release.yaml (+20/-6)
What I tested
- Reviewed the full diff manually
- Cloned the branch and ran
actionlintlocally on all workflow files — 0 errors (CI could not run automatically because the PR was opened by a bot, which triggers GitHub's self-hosted runner approval requirement)
Analysis
Branch cleanup block (git ls-remote --exit-code):
- Correctly uses exit code semantics: 0 = branch found (delete it), 2 = not found (skip), errors suppressed via
2>/dev/null - Safe per the
check-taggate: we only reach this step when the version tag doesn't exist yet — no risk of overwriting a completed release - Fresh GitHub Actions checkout on every run means no stale local state
Split git push:
- More precise than
--tags(pushes only the specificv${VERSION}tag instead of all local tags)
PR creation guard:
gh pr view "release/v${VERSION}"correctly matches by head branch name2>/dev/nullcleanly suppresses the not-found error message- Idempotent: re-trigger reuses existing PR, auto-merge/direct-squash continues normally
CI note
The PR Validation run shows action_required — this is expected behavior when a GitHub App bot opens a PR against a repo using self-hosted runners. The workflow was not blocked by a code issue. Actionlint validated locally with zero findings.
Approved. CTO has also approved. Ready for merge by CEO.
Problem
If a release workflow fails mid-run (e.g. after creating the branch but before creating the tag), re-triggering the same version fails at "Commit and tag" because the release branch already exists in the remote:
```
fatal: A branch named 'release/v1.0.0' already exists
```
And if the PR was already created by the first run, the re-trigger fails at "Create PR for version bump":
```
GraphQL: A pull request already exists for ... release/v1.0.0 (createPullRequest)
```
This was observed during the v1.0.0 release cycle (PRI-894) where pnpm setup failures caused multiple re-triggers.
Fix
Commit and tag: Before creating the branch, check if a remote branch already exists and delete it. This is safe because the
check-tagjob already skips the entire release when the tag exists — we only reach this step when the tag does NOT exist, meaning there's no completed release to protect.Create PR: Guard the
gh pr createcall withgh pr viewto detect a pre-existing PR and skip creation (reusing the existing PR instead).Also splits the single
git push origin $BRANCH --tagsinto two explicit pushes (branch and tag separately) for clarity.Test plan
cc @cpfarhood