Skip to content

release: validate tag version before publish#286

Merged
brianmhunt merged 1 commit intopc/release_taggingfrom
bmh/sc-release-tag-version-check
Apr 6, 2026
Merged

release: validate tag version before publish#286
brianmhunt merged 1 commit intopc/release_taggingfrom
bmh/sc-release-tag-version-check

Conversation

@brianmhunt
Copy link
Copy Markdown
Member

@brianmhunt brianmhunt commented Apr 6, 2026

Summary

  • validate that the pushed tag version matches the repo release version before publish
  • fail fast before build, test, and changeset publish if the tag is mistargeted or mistyped

Testing

  • npx prettier .github/workflows/release.yml --check

Summary by CodeRabbit

  • Chores
    • Enhanced release process validation to ensure version consistency during deployment.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

A validation step was added to the publish job in the release workflow that verifies the pushed Git tag version matches the computed release version from tools/release-version.cjs, failing the job if they diverge.

Changes

Cohort / File(s) Summary
Release Workflow Validation
.github/workflows/release.yml
Added validation step comparing pushed Git tag version (with v prefix stripped) against computed release_version. Emits error and exits with non-zero code on mismatch; continues to build, test, and publish steps on match.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

  • release: automate GitHub releases #278: Also modifies release workflow to utilize tools/release-version.cjs for deriving the release version; this PR adds tag-to-version validation on top of that foundation.

Poem

🐰 A tag's a promise, a version's a truth,
So check they align (we test them, forsooth!)
If mismatched they be, the workflow shall cry,
But match? Then we publish and reach for the sky!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'release: validate tag version before publish' directly and clearly summarizes the main change: adding validation to ensure the Git tag version matches the release version before publishing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bmh/sc-release-tag-version-check

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 and usage tips.

@brianmhunt
Copy link
Copy Markdown
Member Author

@coderabbitai please review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the release workflow file
fd "release.yml" --type f
cat .github/workflows/release.yml 2>/dev/null || echo "File not found"

Length of output: 5505


🏁 Script executed:

#!/bin/bash
# Check the release-version tool
fd "release-version.cjs" --type f
cat tools/release-version.cjs 2>/dev/null || echo "File not found"

# Check if there are any package.json files with version info
cat package.json 2>/dev/null | head -20

Length of output: 1134


@brianmhunt: The workflow is well-structured and the tag-validation logic is sound. A few observations:


✅ What looks good

  1. Validation logic (publish job, "Validate tag matches release version" step):

    • ${TAG_VERSION#v} correctly strips the v prefix before comparing.
    • Error is written to stderr (>&2) and exits with code 1 — correct behavior for CI.
  2. tools/release-version.cjs:

    • Using process.stdout.write (no newline) is the right call; shell $() would strip a trailing newline anyway, but this is cleaner.
    • Asserting all non-private packages share exactly one version is a strong guard against version drift across the monorepo.
  3. github-release job:

    • Idempotency check (handles a pre-existing release on the same SHA gracefully) is a nice touch.
    • Correctly resolves both annotated tags (object.type == "tag") and lightweight tags to their target commit SHA before comparing.
    • Prerelease detection (alpha/beta/rc) passes --prerelease automatically.
  4. Security hygiene: all action references are pinned to SHA digests, persist-credentials: false is set, and environment variables are passed via env: blocks rather than inline shell expansion.


🔍 Suggestions

1. Unquoted $prerelease_flag in gh release create (minor)

gh release create "$tag" \
  --repo "$GITHUB_REPOSITORY" \
  --target "$TARGET_SHA" \
  --title "TKO ${VERSION}" \
  --generate-notes \
  $prerelease_flag   # <-- unquoted

This works correctly in bash (an empty variable expands to nothing, adding no extra argument), but ShellCheck will flag it. To silence it cleanly:

gh release create "$tag" \
  --repo "$GITHUB_REPOSITORY" \
  --target "$TARGET_SHA" \
  --title "TKO ${VERSION}" \
  --generate-notes \
  ${prerelease_flag:+$prerelease_flag}

Or simply use an array:

release_args=(
  "$tag"
  --repo "$GITHUB_REPOSITORY"
  --target "$TARGET_SHA"
  --title "TKO ${VERSION}"
  --generate-notes
)
[ -n "$prerelease_flag" ] && release_args+=("$prerelease_flag")
gh release create "${release_args[@]}"

2. Consider an early-exit validation job (optional enhancement)

Currently, prepare-release always runs before publish — meaning on a mis-tagged push, GitHub Actions will still spend time on checkout, Node setup, npm ci, and the changesets action before the tag mismatch is caught. For a faster signal, you could add a lightweight validate-tag job that runs in parallel or as a prerequisite:

validate-tag:
  name: Validate Tag
  runs-on: ubuntu-latest
  steps:
    - name: Checkout
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      with:
        persist-credentials: false
    - name: Setup Node.js
      uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
      with:
        node-version: 24.x
    - name: Validate tag matches release version
      env:
        TAG_VERSION: ${{ github.ref_name }}
      run: |
        release_version="$(node tools/release-version.cjs)"
        tag_version="${TAG_VERSION#v}"
        if [ "$tag_version" != "$release_version" ]; then
          echo "Tag $tag_version does not match release version $release_version." >&2
          exit 1
        fi

Then prepare-release and publish would both needs: validate-tag. That said, the current approach already meets the PR's stated goal of failing before the expensive build/test/publish steps, so this is strictly optional.


Overall the PR is clean and accomplishes its goal well. The only thing I'd flag as worth fixing is the unquoted $prerelease_flag for ShellCheck cleanliness.


🧠 Learnings used
Learnt from: CR
Repo: knockout/tko PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-05T17:45:34.212Z
Learning: Avoid manual workstation publishes; use the release CI workflow (release.yml) for publishing to npm
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

165-177: Consider quoting or using parameter expansion for $prerelease_flag.

The unquoted $prerelease_flag on line 177 works correctly in bash (empty variables expand to nothing), but ShellCheck flags this pattern (SC2086). For explicitness and to satisfy linters:

♻️ Option 1: Use conditional parameter expansion
          gh release create "$tag" \
            --repo "$GITHUB_REPOSITORY" \
            --target "$TARGET_SHA" \
            --title "TKO ${VERSION}" \
            --generate-notes \
-           $prerelease_flag
+           ${prerelease_flag:+"$prerelease_flag"}
♻️ Option 2: Build an argument array
-         prerelease_flag=""
+         release_args=("$tag" --repo "$GITHUB_REPOSITORY" --target "$TARGET_SHA" --title "TKO ${VERSION}" --generate-notes)
          case "$VERSION" in
            *-alpha*|*-beta*|*-rc*)
-             prerelease_flag="--prerelease"
+             release_args+=(--prerelease)
              ;;
          esac

-         gh release create "$tag" \
-           --repo "$GITHUB_REPOSITORY" \
-           --target "$TARGET_SHA" \
-           --title "TKO ${VERSION}" \
-           --generate-notes \
-           $prerelease_flag
+         gh release create "${release_args[@]}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 165 - 177, The unquoted variable
$prerelease_flag passed into the gh release create command (after computing
prerelease_flag from VERSION) should be expanded safely to avoid SC2086; change
the invocation to either use conditional parameter expansion so the flag is only
inserted when non-empty (e.g., ${prerelease_flag:+$prerelease_flag}) or build an
argument array and append prerelease_flag only when set, then invoke gh release
create with that array (refer to the prerelease_flag variable, the gh release
create invocation, and the VERSION/tag/TARGET_SHA variables to locate the code
to change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 165-177: The unquoted variable $prerelease_flag passed into the gh
release create command (after computing prerelease_flag from VERSION) should be
expanded safely to avoid SC2086; change the invocation to either use conditional
parameter expansion so the flag is only inserted when non-empty (e.g.,
${prerelease_flag:+$prerelease_flag}) or build an argument array and append
prerelease_flag only when set, then invoke gh release create with that array
(refer to the prerelease_flag variable, the gh release create invocation, and
the VERSION/tag/TARGET_SHA variables to locate the code to change).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d805c7b-8eb4-4460-8b7e-05aa06140a37

📥 Commits

Reviewing files that changed from the base of the PR and between efdeb74 and 1561279.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

@brianmhunt brianmhunt merged commit 4d6dd51 into pc/release_tagging Apr 6, 2026
6 checks passed
@brianmhunt brianmhunt deleted the bmh/sc-release-tag-version-check branch April 6, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant