ci(release): add automatic cleanup of stale release branches#24
Conversation
Adds a cleanup step to the release workflow that automatically deletes old release-* branches after creating a new release. This prevents accumulation of stale branches that was causing semantic-release to fail with "too many release branches" errors. Assisted-by: GLM 4.7 via Claude Code Signed-off-by: Xe Iaso <xe@tigrisdata.com>
There was a problem hiding this comment.
Pull request overview
This PR adds an automatic cleanup step to the release workflow that deletes old release-* branches to prevent accumulation of stale branches that can cause semantic-release to fail with "ERELEASEBRANCHES" errors. The cleanup runs at the end of the workflow using if: always() to ensure it executes even if earlier steps fail.
Changes:
- Added a cleanup step at the end of the release workflow that fetches all
release-*branches and deletes them except the current one - The step uses
if: always()to run even on workflow failure
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| # Get all release branches except the current one | ||
| for branch in $(git ls-remote --heads origin "release-*" | awk '{print $2}' | sed 's|refs/heads/||' | grep -v "^${BRANCH_NAME}$"); do |
There was a problem hiding this comment.
The BRANCH_NAME variable is not available in this step. BRANCH_NAME is defined in the "Create release branch and run semantic-release" step's script (line 41), but environment variables set within a step's run script are not automatically available to subsequent steps. This means the grep filter will not work correctly, and the script may attempt to delete the newly created branch.
To fix this, you need to export BRANCH_NAME as a step output in the previous step using GITHUB_OUTPUT, and then reference it in this step. Alternatively, you could recalculate the branch name in this step or use a different approach to identify which branch to keep.
| --label "release" | ||
|
|
||
| - name: Clean up old release branches | ||
| if: always() |
There was a problem hiding this comment.
The cleanup step runs with if: always(), which means it executes even when the previous step fails. If the "Create release branch and run semantic-release" step fails before creating and pushing a release branch (e.g., during npm ci or semantic-release execution), this cleanup will still run and delete all existing release-* branches, potentially including branches with open PRs that haven't been merged yet.
Consider adding a condition to only run cleanup if a branch was successfully created and pushed, or add logic to preserve branches that have open PRs. You could check if the branch was pushed by verifying the exit status of line 56, or check for open PRs using the GitHub API before deleting branches.
| run: | | ||
| # Get all release branches except the current one | ||
| for branch in $(git ls-remote --heads origin "release-*" | awk '{print $2}' | sed 's|refs/heads/||' | grep -v "^${BRANCH_NAME}$"); do | ||
| echo "Deleting old release branch: ${branch}" | ||
| git push origin --delete "${branch}" || true | ||
| done |
There was a problem hiding this comment.
The cleanup script could potentially cause race conditions if multiple release workflows run concurrently. If two workflows are running simultaneously, they could both try to clean up the same branches, or one workflow's cleanup could delete the branch that the other workflow just created (especially given that BRANCH_NAME filtering won't work as currently implemented).
While workflow_dispatch typically prevents concurrent runs by default, consider adding a concurrency group to the workflow to ensure only one release workflow runs at a time, or add more robust synchronization logic to the cleanup step.
|
🎉 This PR is included in version 0.6.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
release-*branchesDetails
The semantic-release configuration allows only 1-3 release branches, but stale
release-*branches from previous release PRs were accumulating. This caused semantic-release to fail with "ERELEASEBRANCHES" errors.The fix adds a cleanup step that runs at the end of the release workflow (even if earlier steps fail) and deletes all old
release-*branches except the one just created.Test plan
go build ./...npm run format