Releasing Shakapacker
This guide is for Shakapacker maintainers who need to publish a new release.
Prerequisites
-
Install required tools:
bundle install
gem install gem-release # Provides `gem bump` and `gem release`
npm --version # Required because release task uses `npx release-it`
gh --version # Required for automatic GitHub release creation -
Ensure you have publishing access:
- npm: You must be a collaborator on the shakapacker npm package
- RubyGems: You must be an owner of the shakapacker gem
-
Enable 2FA on both platforms:
- npm: 2FA is required for publishing
- RubyGems: 2FA is required for publishing
-
Authenticate GitHub CLI:
- Run
gh auth loginand ensure your account/token has write access to this repository - Required for automatic GitHub release creation after publishing
- Run
-
No global
release-itinstall required:- The release task runs
npx --yes release-it ...automatically
- The release task runs
Release Process
1. Update the Changelog
Always update CHANGELOG.md before running the release task. The release task reads the version from CHANGELOG.md and automatically creates a GitHub release from the changelog section.
- Ensure all desired changes are merged to
mainbranch - Run
/update-changelog release(orrcorbetafor prereleases) to:- Find merged PRs missing from the changelog
- Add changelog entries under the appropriate category headings
- Auto-compute the next version based on changes (breaking → major, features → minor, fixes → patch)
- Stamp the version header (e.g.,
## [v10.0.0] - April 8, 2026)
- Review the changelog entries and verify the computed version
- Commit and push CHANGELOG.md
If you forget this step, the release task will print a warning and the GitHub release will need to be created manually afterward using sync_github_release.
2. Run the Release Task
The simplest way to release is with no arguments — the task reads the version from CHANGELOG.md:
# Recommended: reads version from CHANGELOG.md (requires step 1)
bundle exec rake release
# For a specific version (overrides CHANGELOG.md detection)
bundle exec rake "release[10.1.0]"
# For a beta release (note: use period, not dash)
bundle exec rake "release[10.2.0.beta.1]" # Creates npm package 10.2.0-beta.1
# For a release candidate
bundle exec rake "release[10.6.0.rc.0]"
# Dry run to test without publishing
bundle exec rake "release[10.1.0,true]"
# Skip interactive confirmations (for scripted maintainer runs)
AUTO_CONFIRM=true bundle exec rake release
# Override version policy checks (monotonic + changelog/bump consistency)
RELEASE_VERSION_POLICY_OVERRIDE=true bundle exec rake "release[10.1.0]"
bundle exec rake "release[10.1.0,false,true]"
# Override the CI status gate (use only for known-unrelated failures)
RELEASE_CI_STATUS_OVERRIDE=true bundle exec rake "release[10.1.0]"
bundle exec rake "release[10.1.0,false,false,true]"
When called with no arguments, release:
- Reads the first versioned header from CHANGELOG.md (e.g.,
## [v10.6.0]) - Compares it to the current gem version
- If the changelog version is newer, prompts for confirmation and uses it
- If no new version is found, falls back to a patch bump
Dry runs use a temporary git worktree so version bumps and installs do not modify your current checkout.
The temporary worktree fetches and rebases onto origin/main, matching the commit a live
release would evaluate instead of checking a potentially stale local HEAD.
When no version argument is provided, changelog-version selection also happens after that
refresh, so the preview uses the refreshed changelog and current gem version.
Dry runs now also print explicit "skipping confirmation" messages and the would-run GitHub release command.
release validates release-version policy before publishing:
- Target version must be greater than the latest tagged release.
- If the versioned target changelog section exists (
## [vX.Y.Z...]; notUNRELEASED), it maps to expected bump type:- Breaking changes => major bump
- Added/New Features/Features/Enhancements => minor bump
- Fixed/Fixes/Bug Fixes/Security/Improved/Deprecated => patch bump
- Other headings => no inferred bump level (consistency check is skipped)
Use override only when needed:
RELEASE_VERSION_POLICY_OVERRIDE=true- Or task arg override (
release[..., ..., true])
release also refuses to publish unless GitHub CI is green for the commit being
released (the commit at HEAD after git pull --rebase, which is the parent of the
version-bump commit the task creates):
- The exact SHA must have a
push/mainrun for every release-gating workflow: Dummy specs, Generator specs, Node based checks, Ruby based checks, and Test Both Bundlers. Successful pull-request checks cannot satisfy this requirement because the main workflows run broader test matrices. - Every required workflow must be completed with a
successconclusion. Missing, queued, in-progress, failed, cancelled, or timed-out workflows all block the release. - The path-conditional Babel 8 smoke workflow is not required when absent, but when GitHub
created a
push/mainrun for the exact SHA, its latest run must also complete successfully. - Unrelated conditional push workflows, such as documentation rebuilds, are not part of the release-gating suite.
- Legacy commit statuses are evaluated as supplemental fail-closed signals. Some integrations (CodeRabbit, for one) report only as commit statuses; GitHub's combined-status endpoint supplies only the most recent status per context.
- The gate fails closed: if CI status cannot be read (no
gh, API error), the release stops. - Dry runs report what the gate would do instead of aborting.
Override only for failures you have confirmed are unrelated to the release — for example an upstream registry outage — never to paper over a real regression:
RELEASE_CI_STATUS_OVERRIDE=true- Or task arg override (
release[..., ..., ..., true])
3. What the Release Task Does
The release task automatically:
- Validates release prerequisites:
- Verifies npm authentication
- Warns if CHANGELOG.md section is missing for the target version
- Pulls latest changes from the repository, then aborts unless CI is green for the resulting commit
- Bumps version numbers in:
lib/shakapacker/version.rb(Ruby gem version)package.json(npm package version - converted from Ruby format)
- Publishes to npm:
- Prompts for npm OTP (2FA code)
- Creates git tag
- Pushes to GitHub
- Publishes to RubyGems:
- Prompts for RubyGems OTP (2FA code)
- Updates spec/dummy lockfiles:
- Runs
bundle installto updateGemfile.lock - Runs
yarn installto refresh the Yarn-managed dummy app lockfile - Runs
npm installto keeppackage-lock.jsonin sync for npm compatibility/testing
- Runs
- Commits and pushes lockfile changes automatically
- Creates GitHub release from CHANGELOG.md (if the matching section exists)
4. Version Format
Important: Use Ruby gem version format (no dashes):
- ✅ Correct:
10.1.0,10.2.0.beta.1,10.0.0.rc.2 - ❌ Wrong:
10.1.0-beta.1,10.0.0-rc.2
The task automatically converts Ruby gem format to npm semver format:
- Ruby:
10.2.0.beta.1→ npm:10.2.0-beta.1 - Ruby:
10.0.0.rc.2→ npm:10.0.0-rc.2
CHANGELOG.md headers use npm semver format (with dashes):
## [v10.6.0-rc.1]— correct (matches git tag format)## [v10.6.0.rc.1]— wrong (RubyGems format, will not be found by release tasks)
Examples:
# Regular release
bundle exec rake "release[10.1.0]" # Gem: 10.1.0, npm: 10.1.0
# Beta release
bundle exec rake "release[10.2.0.beta.1]" # Gem: 10.2.0.beta.1, npm: 10.2.0-beta.1
# Release candidate
bundle exec rake "release[10.0.0.rc.1]" # Gem: 10.0.0.rc.1, npm: 10.0.0-rc.1
# Prerelease: use /update-changelog rc first, then release reads it
bundle exec rake release # reads v10.0.0-rc.0 from CHANGELOG.md
5. During the Release
If you are running non-interactively, set AUTO_CONFIRM=true to skip confirmation prompts.
- When prompted for npm OTP, enter your 2FA code from your authenticator app
- Accept defaults for release-it options
- When prompted for RubyGems OTP, enter your 2FA code
- If using
releasewith no version, confirm the version detected from CHANGELOG.md (or the computed patch version) - The script will automatically commit and push lockfile updates
- The script will automatically create a GitHub release (if CHANGELOG.md section exists)
6. After Release
-
Verify the release on:
-
Check that the lockfile commit was pushed:
git log --oneline -5
# Should see "Update spec/dummy lockfiles after release" -
Announce the release (if appropriate):
- Post in relevant Slack/Discord channels
- Tweet about major releases
- Update documentation if needed
Syncing GitHub Releases Manually
If the automatic GitHub release creation was skipped (e.g., CHANGELOG.md section was missing during release), you can create it manually after updating the changelog:
- Update
CHANGELOG.mdwith the published version section- For prerelease entries, use npm semver header format with dashes, for example
## [v10.6.0-rc.1]
- For prerelease entries, use npm semver header format with dashes, for example
- Commit and push
CHANGELOG.md - Run:
# Stable
bundle exec rake "sync_github_release[10.6.0]"
# Prerelease
bundle exec rake "sync_github_release[10.6.0.rc.1]"
sync_github_release reads release notes from the matching CHANGELOG.md section and creates/updates the GitHub release for the corresponding tag.
Troubleshooting
Release Blocked by the CI Gate
If the release aborts with CI is not green for <sha>:
- Read the listed workflows and statuses.
Still runningmeans CI has not finished — wait and retry. - For real failures, fix them on
mainand rerun the release once CI is green. - If the failure is confirmed unrelated to the release (for example an upstream npm
registry outage), rerun with
RELEASE_CI_STATUS_OVERRIDE=true.
If it aborts with Missing main-push workflows, the commit is not on main yet or the
complete main suite has not started. Push or merge the commit to main and let CI run.
If it aborts with Unable to verify CI status, the gate could not read CI results — check
gh auth status and network access. The gate fails closed on purpose.
Uncommitted Changes After Release
If you see uncommitted changes to lockfiles after a release, this means:
- The release was successful but the lockfile commit step may have failed
- Solution: Manually commit these files:
git add spec/dummy/Gemfile.lock spec/dummy/package-lock.json spec/dummy/yarn.lock
git commit -m 'Update spec/dummy lockfiles after release'
git push
Failed npm or RubyGems Publish
If publishing fails partway through:
- Check which step failed (npm or RubyGems)
- If npm failed: Fix the issue and manually run
npm publish - If RubyGems failed: Fix the issue and manually run
gem release - Then manually update and commit spec/dummy lockfiles
GitHub Release Sync Fails
If package publishing succeeds but GitHub release creation fails:
-
Fix GitHub auth (
gh auth login) or permissions -
Ensure
CHANGELOG.mdhas matching header## [vX.Y.Z...](npm format for prereleases) -
Rerun only:
bundle exec rake "sync_github_release[<gem_version>]"
Wrong Version Format
If you accidentally use npm format (with dashes):
- The gem will be created with an invalid version
- Solution: Don't push the changes, reset your branch:
git reset --hard HEAD - Re-run with correct Ruby gem format
Manual Release Steps
If you need to release manually (not recommended):
-
Bump version:
gem bump --version 10.1.0
bundle install -
Publish to npm:
npx --yes release-it 10.1.0 --npm.publish -
Publish to RubyGems:
gem release -
Update lockfiles:
cd spec/dummy
bundle install
npm install
yarn install
cd ../..
git add spec/dummy/Gemfile.lock spec/dummy/package-lock.json spec/dummy/yarn.lock
git commit -m 'Update spec/dummy lockfiles after release'
git push
Questions?
If you encounter issues not covered here, please:
- Check the CONTRIBUTING.md guide
- Ask in the maintainers channel
- Update this documentation for future releases