From 5bd05cc237517858a6296a37899b8b27911b210b Mon Sep 17 00:00:00 2001 From: Miguel Palau Zarza Date: Fri, 28 Nov 2025 10:41:54 -0600 Subject: [PATCH] feat: support version bump from tags to prevent race conditions (#403) - Add tag detection and branch preparation step - Implement automatic rebase when main has moved ahead - Split commit and push into separate steps for proper rebase flow - Add comprehensive error handling with clear user guidance - Maintain full backward compatibility with branch-based workflow - Update documentation and examples for tag-based usage Allows workflows to run from git tags, locking version bump to specific commits and preventing race conditions when PRs are merged during release. When main has moved ahead, automatically rebases version bump onto latest. --- .../version-bump-changelog/CHANGELOG.md | 10 ++ .../plugins/version-bump-changelog/action.yml | 120 +++++++++++++++++- examples/extra/version-bump-changelog.yml | 27 ++++ 3 files changed, 155 insertions(+), 2 deletions(-) diff --git a/actions/plugins/version-bump-changelog/CHANGELOG.md b/actions/plugins/version-bump-changelog/CHANGELOG.md index cf965d29..9d0888ab 100644 --- a/actions/plugins/version-bump-changelog/CHANGELOG.md +++ b/actions/plugins/version-bump-changelog/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +### 🎉 Features + +* Support running version bump from tags to prevent race conditions during releases ([#403](https://github.com/grafana/plugin-ci-workflows/issues/403)) + - Action now detects when running from a tag (detached HEAD) and handles it appropriately + - Automatically rebases version bump onto latest main if main has moved ahead since tag creation + - Maintains full backward compatibility with branch-based execution + - Provides clear error messages and guidance for conflict scenarios + ## [1.1.0](https://github.com/grafana/plugin-ci-workflows/compare/plugins-version-bump-changelog/v1.0.0...plugins-version-bump-changelog/v1.1.0) (2025-10-08) diff --git a/actions/plugins/version-bump-changelog/action.yml b/actions/plugins/version-bump-changelog/action.yml index effc43ea..877c3d1c 100644 --- a/actions/plugins/version-bump-changelog/action.yml +++ b/actions/plugins/version-bump-changelog/action.yml @@ -1,6 +1,8 @@ name: Generate changelog version bump changelog description: | - Bump npm version, create a git tag and optionally generate a changelog on main using the generate-changelog npm package. + Bump npm version, create a git tag and optionally generate a changelog. + Can be run from main branch or from a tag to lock version at specific commit. + When run from a tag, automatically rebases onto latest main if needed. inputs: version: @@ -57,6 +59,72 @@ runs: git config user.name 'grafana-plugins-platform-bot[bot]' git config user.email '144369747+grafana-plugins-platform-bot[bot]@users.noreply.github.com' + - name: Prepare branch for version bump + shell: bash + run: | + set -e + + # Detect execution context + if ! git symbolic-ref -q HEAD > /dev/null; then + echo "::notice::Detached HEAD detected - running from tag" + IS_TAG_TRIGGER=true + TAG_COMMIT=$(git rev-parse HEAD) + echo "tag-commit=${TAG_COMMIT}" >> $GITHUB_ENV + else + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + echo "::notice::Running from branch: ${CURRENT_BRANCH}" + IS_TAG_TRIGGER=false + fi + echo "is-tag-trigger=${IS_TAG_TRIGGER}" >> $GITHUB_ENV + + # Fetch latest remote state + echo "Fetching latest main from remote..." + git fetch origin main:refs/remotes/origin/main + REMOTE_MAIN_SHA=$(git rev-parse origin/main) + echo "remote-main-sha=${REMOTE_MAIN_SHA}" >> $GITHUB_ENV + + # Handle tag trigger + if [ "$IS_TAG_TRIGGER" = "true" ]; then + echo "::group::Tag-based workflow preparation" + echo "Tag points to: ${TAG_COMMIT}" + echo "Remote main at: ${REMOTE_MAIN_SHA}" + + # Validate tag is in main history + if ! git merge-base --is-ancestor "${TAG_COMMIT}" "${REMOTE_MAIN_SHA}"; then + echo "::error::Tag commit ${TAG_COMMIT} is not in main branch history" + echo "::error::The tag must point to a commit that exists in the main branch" + exit 1 + fi + + # Create local main branch from tag commit + git checkout -B main "${TAG_COMMIT}" + echo "::notice::Created local main branch at tag commit" + + # Check if rebase will be needed + if [ "${TAG_COMMIT}" != "${REMOTE_MAIN_SHA}" ]; then + echo "::warning::Main has moved ahead since tag was created" + echo "::warning::Version bump will be rebased onto latest main before pushing" + echo "needs-rebase=true" >> $GITHUB_ENV + + # Calculate commits that will be rebased over + COMMITS_AHEAD=$(git rev-list --count ${TAG_COMMIT}..${REMOTE_MAIN_SHA}) + echo "::notice::Main is ${COMMITS_AHEAD} commit(s) ahead" + else + echo "::notice::Tag is at main HEAD - fast-forward push" + echo "needs-rebase=false" >> $GITHUB_ENV + fi + echo "::endgroup::" + else + # Running from branch - validate + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + if [ "${CURRENT_BRANCH}" != "main" ]; then + echo "::error::Running from branch '${CURRENT_BRANCH}' but expected 'main'" + echo "::error::This action must be run from main branch or from a tag" + exit 1 + fi + echo "needs-rebase=false" >> $GITHUB_ENV + fi + - name: Get previous tag before bumping id: previous-tag if: ${{ inputs.generate-changelog == 'true' }} @@ -111,11 +179,59 @@ runs: git add package-lock.json || true git add CHANGELOG.md || true # No-op if changelog not generated git commit -m "chore(version): bump version to ${NEW_VERSION}" - git push origin main env: GITHUB_TOKEN: ${{ steps.generate-github-token.outputs.token }} NEW_VERSION: ${{ steps.bump.outputs.new-version }} + - name: Rebase on latest main (if needed) + if: env.needs-rebase == 'true' + shell: bash + run: | + set -e + + echo "::group::Rebasing version bump onto latest main" + echo "Rebasing commit: $(git log -1 --oneline)" + echo "Onto: origin/main (${REMOTE_MAIN_SHA})" + + # Perform rebase + if ! git rebase origin/main; then + echo "::error::Rebase failed with conflicts" + echo "::error::Files modified by version bump conflict with changes in main" + echo "::error::This typically means package.json, package-lock.json, or CHANGELOG.md" + echo "::error::were modified in commits merged to main since tag was created" + echo "::error::" + echo "::error::Resolution options:" + echo "::error:: 1. Create a new tag at current main HEAD and re-run workflow" + echo "::error:: 2. Manually resolve conflicts and push" + + # Abort rebase to leave clean state + git rebase --abort + exit 1 + fi + + echo "::notice::Rebase successful" + echo "::notice::Version bump commit now on top of latest main" + echo "::notice::New commit: $(git log -1 --oneline)" + echo "::endgroup::" + env: + REMOTE_MAIN_SHA: ${{ env.remote-main-sha }} + + - name: Push to main + shell: bash + run: | + echo "::group::Pushing to origin/main" + if git push origin main; then + echo "::notice::Successfully pushed version bump to main" + else + EXIT_CODE=$? + echo "::error::Failed to push to origin/main" + echo "::error::Check permissions and branch protection rules" + exit $EXIT_CODE + fi + echo "::endgroup::" + env: + GITHUB_TOKEN: ${{ steps.generate-github-token.outputs.token }} + - name: Create git tag shell: bash run: | diff --git a/examples/extra/version-bump-changelog.yml b/examples/extra/version-bump-changelog.yml index e41d71bc..bb768262 100644 --- a/examples/extra/version-bump-changelog.yml +++ b/examples/extra/version-bump-changelog.yml @@ -1,3 +1,30 @@ +# Version Bump with Changelog Generation +# +# This workflow bumps the npm version, generates a changelog, and creates a git tag. +# +# USAGE OPTIONS: +# +# Option 1: Run from main branch (traditional) +# - Trigger this workflow while on the main branch +# - Version bump is applied directly to latest main +# - Simple and straightforward for low-traffic repos +# +# Option 2: Run from a tag (prevents race conditions - RECOMMENDED) +# - Create a tag at the desired commit: git tag v1.0.0-prepare && git push origin v1.0.0-prepare +# - Trigger this workflow using that tag (select the tag in GitHub Actions UI) +# - Benefits: +# * Locks the version bump to a specific commit +# * Prevents race conditions if PRs are merged during the release process +# * Action automatically rebases onto latest main if needed +# - If main has moved ahead since tag creation: +# * Version bump is automatically rebased onto latest main before pushing +# * If rebase conflicts occur, clear error messages guide resolution +# +# WHEN TO USE TAG-BASED WORKFLOW: +# Use tags when you need to ensure no new commits are unintentionally included +# in your release, especially in active repositories where PRs may be merged +# during the versioning process. +# name: Version bump, changelog on: