Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ bundle exec rake "create_release[9.6.0.rc.0]"
# Dry run to test without publishing
bundle exec rake "create_release[9.1.0,true]"

# Skip interactive confirmations (for scripted maintainer runs)
AUTO_CONFIRM=true bundle exec rake create_release

# Override version policy checks (monotonic + changelog/bump consistency)
RELEASE_VERSION_POLICY_OVERRIDE=true bundle exec rake "create_release[9.1.0]"
bundle exec rake "create_release[9.1.0,false,true]"
Expand All @@ -74,6 +77,7 @@ When called with no arguments, `create_release`:
4. 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.
Dry runs now also print explicit "skipping confirmation" messages and the would-run GitHub release command.

`create_release` validates release-version policy before publishing:

Expand Down Expand Up @@ -148,6 +152,8 @@ bundle exec rake create_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.

1. When prompted for **npm OTP**, enter your 2FA code from your authenticator app
2. Accept defaults for release-it options
3. When prompted for **RubyGems OTP**, enter your 2FA code
Expand Down
53 changes: 37 additions & 16 deletions rakelib/release.rake
Original file line number Diff line number Diff line change
Expand Up @@ -367,33 +367,46 @@ def ensure_git_tag_exists!(gem_root:, tag:)
abort "❌ Git tag #{tag.inspect} was not found locally or remotely. Verify the tag exists before syncing GitHub release."
end

def github_release_command(gem_root:, release_context:, notes_file_path:)
release_exists = system("gh", "release", "view", release_context[:tag], chdir: gem_root, out: File::NULL, err: File::NULL)
abort "❌ Unable to run `gh`. Ensure GitHub CLI is installed and on PATH." if release_exists.nil?

if release_exists
# `gh release edit` accepts `--prerelease=true|false`; there is no `--no-prerelease` flag.
["gh", "release", "edit", release_context[:tag], "--title", release_context[:title], "--notes-file", notes_file_path,
"--prerelease=#{release_context[:prerelease]}"]
else
command = ["gh", "release", "create", release_context[:tag], "--verify-tag", "--title", release_context[:title],
"--notes-file", notes_file_path]
command << "--prerelease" if release_context[:prerelease]
command
end
end
Comment thread
justin808 marked this conversation as resolved.
Outdated

def publish_or_update_github_release(gem_root:, release_context:, dry_run:)
# Keep this check before the dry-run return so preflight runs catch missing tags.
ensure_git_tag_exists!(gem_root: gem_root, tag: release_context[:tag])

if dry_run
preview_command = github_release_command(
gem_root: gem_root,
release_context: release_context,
notes_file_path: "<release-notes-file>"
)
puts "DRY RUN: Would create or update GitHub release #{release_context[:tag]}#{release_context[:prerelease] ? ' (prerelease)' : ''}"
puts "DRY RUN: Would run: #{Shellwords.join(preview_command)}"
Comment on lines +399 to +401
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The three-line dry-run block is functional, but the first and third lines express the same caveat in slightly different ways. The third line is the more precise one.

Consider collapsing to avoid the redundancy and potential confusion about what the command preview actually represents:

Suggested change
)
puts "DRY RUN: Would create or update GitHub release #{release_context[:tag]}#{release_context[:prerelease] ? ' (prerelease)' : ''}"
puts "DRY RUN: Would run: #{Shellwords.join(preview_command)}"
puts "DRY RUN: Would run (assuming new release): #{Shellwords.join(preview_command)}"
puts "DRY RUN: If the release already exists, the live run will use `gh release edit` instead."

Dropping the first generic "Would create or update" line keeps the output focused on what's actionable — the actual command.

return
end

Tempfile.create(["shakapacker-release-notes-", ".md"]) do |tmp|
tmp.write(release_context[:notes])
tmp.flush

# The view probe only needs a boolean result, so use array-form system to avoid an extra shell layer.
release_exists = system("gh", "release", "view", release_context[:tag], chdir: gem_root, out: File::NULL, err: File::NULL)
abort "❌ Unable to run `gh`. Ensure GitHub CLI is installed and on PATH." if release_exists.nil?

release_command = if release_exists
# `gh release edit` accepts `--prerelease=true|false`; there is no `--no-prerelease` flag.
["gh", "release", "edit", release_context[:tag], "--title", release_context[:title], "--notes-file", tmp.path,
"--prerelease=#{release_context[:prerelease]}"]
else
command = ["gh", "release", "create", release_context[:tag], "--verify-tag", "--title", release_context[:title],
"--notes-file", tmp.path]
command << "--prerelease" if release_context[:prerelease]
command
end
release_command = github_release_command(
gem_root: gem_root,
release_context: release_context,
notes_file_path: tmp.path
)

puts "Publishing GitHub release #{release_context[:tag]}#{release_context[:prerelease] ? ' (prerelease)' : ''}"
success = system(*release_command, chdir: gem_root)
Expand Down Expand Up @@ -468,7 +481,11 @@ def perform_release(
)
if requested_gem_version.empty?
puts "Computed next patch version: #{resolved_target_gem_version}"
confirm_or_abort!("Proceed with patch release #{resolved_target_gem_version}?") unless dry_run
if dry_run
puts "DRY RUN: Skipping confirmation prompt for patch release #{resolved_target_gem_version}."
else
confirm_or_abort!("Proceed with patch release #{resolved_target_gem_version}?")
end
end

bump_command = if requested_gem_version.empty?
Expand Down Expand Up @@ -564,7 +581,11 @@ task :create_release, %i[gem_version dry_run override_version_policy] do |_t, ar

if changelog_version && Gem::Version.new(changelog_version) > Gem::Version.new(current_version)
puts "Found CHANGELOG.md version: #{changelog_version} (current: #{current_version})"
confirm_or_abort!("Release #{changelog_version} from CHANGELOG.md?") unless is_dry_run
if is_dry_run
puts "DRY RUN: Skipping confirmation prompt for CHANGELOG.md version #{changelog_version}."
else
confirm_or_abort!("Release #{changelog_version} from CHANGELOG.md?")
end
requested_version = changelog_version
else
puts "No new version found in CHANGELOG.md (latest: #{changelog_version || 'none'}, current: #{current_version})."
Expand Down
Loading