Skip to content

Conversation

@AbanoubGhadban
Copy link
Collaborator

@AbanoubGhadban AbanoubGhadban commented Oct 8, 2025

Summary

Modernizes the release process to properly handle the monorepo structure with both NPM packages (react-on-rails and react-on-rails-pro) and the Ruby gem.

Changes

🔧 Updated Release Workflow

  • Replaced release-it with native Yarn workspace publish commands
  • Updated rakelib/release.rake to handle both NPM packages plus the gem
  • Synchronized versions across all 5 locations:
    • lib/react_on_rails/version.rb (source of truth)
    • package.json (root)
    • packages/react-on-rails/package.json
    • packages/react-on-rails-pro/package.json (version + dependency)
    • spec/dummy/Gemfile.lock

📦 Package Dependencies

  • Set exact version dependency: react-on-rails-pro now depends on the exact version of react-on-rails (e.g., "16.1.1" not "*")
  • This ensures users can't install incompatible version combinations

🗑️ Cleanup

  • Removed obsolete packages/react-on-rails/scripts/release script
  • Removed release:patch, release:minor, release:major scripts from package.json files

📚 Documentation

  • Completely rewrote docs/contributor-info/releasing.md with:
    • Clear step-by-step release process
    • Dry run instructions
    • Troubleshooting guide
    • Version synchronization explanation

Release Task Options

The rake release task now supports comprehensive options for different release scenarios:

Parameters

  1. gem_version (optional): Version in rubygem format (e.g., 16.2.0). Omit for auto patch bump.
  2. dry_run (optional): Pass true to preview changes without publishing
  3. registry (optional):
    • verdaccio - Publish to local Verdaccio registry (http://localhost:4873/)
    • npm - Publish to npmjs.org (default)
  4. skip_push (optional): Pass skip_push to skip git push operations (commits/tags created locally only)

Usage Examples

# Production releases
rake release[16.2.0]                          # Release version 16.2.0 to production
rake release                                  # Auto-bump patch version

# Testing & validation
rake release[16.2.0,true]                     # Dry run to preview changes
rake release[16.2.0,false,verdaccio]          # Test release to local Verdaccio registry
rake release[16.2.0,false,npm,skip_push]      # Release without pushing to remote

# Development workflow
rake release[16.2.0-beta.1,false,verdaccio]   # Test pre-release versions locally

Verdaccio Support

The release task now includes full support for local testing with Verdaccio:

  • Local registry testing: Test the complete release process without publishing to production
  • Pre-release validation: Verify package installations work correctly before production release
  • Setup requirements:
    1. Install Verdaccio: npm install -g verdaccio
    2. Start server: verdaccio
    3. Authenticate: npm adduser --registry http://localhost:4873/
    4. Run release: rake release[16.2.0,false,verdaccio]

When using Verdaccio mode:

  • NPM packages publish to http://localhost:4873/
  • Ruby gem publication is skipped (Verdaccio is NPM-only)
  • Git commits and tags are still created

Release Process Flow

The task executes these steps:

  1. Validates parameters (registry and skip_push values)
  2. Checks for uncommitted changes
  3. Pulls latest changes from remote
  4. Bumps gem version in lib/react_on_rails/version.rb
  5. Updates all package.json files with converted npm version
  6. Updates react-on-rails-pro dependency to exact version
  7. Commits all version changes
  8. Creates git tag v{version}
  9. Pushes commits and tags (unless skip_push is set)
  10. Publishes react-on-rails to NPM/Verdaccio
  11. Publishes react-on-rails-pro to NPM/Verdaccio
  12. Publishes react_on_rails to RubyGems (unless Verdaccio mode)

Benefits

✅ No external release-it dependency
✅ Both NPM packages properly released
✅ Version sync enforced across all packages
✅ Simpler, pure Ruby + Yarn solution
✅ Single command releases everything
✅ Local testing support with Verdaccio
✅ Flexible workflow options (dry-run, skip-push)
✅ Parameter validation prevents typos

Test Plan

  • RuboCop passes with no offenses
  • Parameter validation works correctly
  • Verdaccio support implemented
  • Test dry run: rake release[16.1.2,true]
  • Verify all files would be updated correctly
  • Review commit message format

🤖 Generated with Claude Code


This change is Reviewable

- Replace release-it with native Yarn workspace publish commands
- Update release rake task to handle both NPM packages (react-on-rails and react-on-rails-pro)
- Synchronize versions across gem and both NPM packages
- Set react-on-rails-pro to use exact version dependency on react-on-rails
- Remove obsolete release scripts from package.json files
- Delete packages/react-on-rails/scripts/release
- Update documentation with comprehensive release instructions

The new release process:
1. Bumps version in lib/react_on_rails/version.rb (source of truth)
2. Updates all 3 package.json files with new version
3. Updates react-on-rails-pro dependency to exact version
4. Commits, tags, and pushes changes
5. Publishes both NPM packages using yarn workspace publish
6. Publishes Ruby gem

Benefits:
- No external release-it dependency
- Both NPM packages properly released
- Version sync enforced across all 5 files
- Single command releases everything: rake release[X.Y.Z]

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Warning

Rate limit exceeded

@AbanoubGhadban has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 324fb8c and 1ddaf2d.

📒 Files selected for processing (1)
  • packages/react-on-rails-pro/package.json (1 hunks)

Walkthrough

Replaces local npm release scripts with a unified Rake-driven release flow that reads/bumps the Ruby gem version, derives and propagates an npm semver to root and package package.json files, updates inter-package dependency, and orchestrates git/tag/push and publishing to npm (or Verdaccio) and optionally RubyGems.

Changes

Cohort / File(s) Summary of Changes
Docs: Release notes
docs/contributor-info/releasing.md
Updated to reflect unified versioning across the Ruby gem and npm packages.
Root npm scripts cleanup
package.json
Removed release scripts: release:patch, release:minor, release:major.
Package npm scripts cleanup
packages/react-on-rails/package.json
Removed release scripts: release:patch, release:minor, release:major.
Removed package release script
packages/react-on-rails/scripts/release
Deleted legacy shell release script used for publishing the package.
Release flow overhaul (Rake)
rakelib/release.rake
Rewrote release task: added require "json", changed task signature to accept gem_version, dry_run, registry, skip_push; reads bumped gem version, derives npm semver, updates root and package package.json files (including pinning react-on-rails in pro package), adds Verdaccio vs npm registry handling, OTP guidance, conditional gem publish, and optional git push control; updates commit/tag/push/publish sequence and dry-run output.
Package dependency pinning
packages/react-on-rails-pro/package.json
Replaced permissive dependency ("*") with a fixed react-on-rails version ("16.1.2").
Version file
lib/react_on_rails/version.rb
Bumped public VERSION constant to the new unified release version.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Rake as rakelib/release.rake
  participant Git as Git
  participant NPM as npm Registry (npmjs / Verdaccio)
  participant Gem as RubyGems
  participant Root as root/package.json
  participant ROR as packages/react-on-rails/package.json
  participant RORPro as packages/react-on-rails-pro/package.json

  Dev->>Rake: rake release[gem_version, dry_run, registry, skip_push]
  Rake->>Git: git pull --rebase
  Rake->>Rake: bump or read gem version (lib/react_on_rails/version.rb)
  Rake->>Rake: derive npm semver from gem version
  Rake->>Root: update root/package.json version
  Rake->>ROR: update packages/react-on-rails/package.json version
  Rake->>RORPro: update packages/react-on-rails-pro/package.json version & pin dependency
  alt dry-run
    Rake-->>Dev: show diffs and commands (no pushes/publishes)
  else execute
    Rake->>Git: git add/commit changes
    Rake->>Git: git tag
    alt skip_push == false
      Rake->>Git: git push && git push --tags
    end
    Rake->>NPM: publish npm packages (OTP prompt if required)
    alt registry == Verdaccio
      Note right of NPM: Verdaccio mode — skip gem publish
    else
      Rake->>Gem: push gem to RubyGems
    end
    Rake-->>Dev: summary and post-release instructions
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • justin808
  • alexeyr-ci2
  • Romex91

Poem

I hop on the rake and nudge each tag,
One version now guides both gem and pack.
Old scripts tucked away, the Rake leads the trail,
Two npm carrots and one ruby grail.
Dry-run checks my burrow — then we hop and prevail! 🥕🐇

Pre-merge checks and finishing touches

✅ 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 succinctly captures the core objective of the changeset by stating that the release process for the monorepo is being modernized, which aligns directly with the PR’s focus on replacing tooling, synchronizing versions, and updating the release workflow.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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.

@claude
Copy link

claude bot commented Oct 8, 2025

Code Review: Modernize Release Process for Monorepo

✅ Strengths

  1. Excellent Simplification: Removing the external release-it dependency and moving to a pure Ruby + Yarn solution is a great decision. This reduces the tool chain complexity and keeps the release process more maintainable.

  2. Comprehensive Documentation: The rewritten docs/contributor-info/releasing.md is outstanding - it's clear, detailed, and includes troubleshooting guidance that will be invaluable for future releases.

  3. Version Synchronization: The implementation properly synchronizes versions across all 5 locations, including the critical fix of setting an exact version dependency in react-on-rails-pro instead of *.

  4. Dry Run Support: The dry run feature is well-implemented and provides clear output showing what would change.


🐛 Potential Issues

1. Critical: Error Handling in Version Extraction (rakelib/release.rake:57-61)

The regex extraction could fail if the version format changes or the file is malformed. If match returns nil, accessing [1] will raise NoMethodError and the release will fail in a confusing way.

Recommendation: Add error handling to raise a clear error message if version extraction fails.

2. Potential Race Condition: Git Push Before Publish (rakelib/release.rake:100-101)

The code pushes commits and tags to the remote repository BEFORE actually publishing to NPM and RubyGems. If NPM or RubyGems publish fails (network issues, 2FA timeout, permissions), the git tag and version bump are already public, but packages aren't published. This creates an inconsistent state.

Recommendation: Consider pushing after successful publish, or at minimum add a rollback mechanism.

3. JSON Formatting Inconsistency (rakelib/release.rake:84)

The code uses JSON.pretty_generate which may not match the existing formatting style in package.json files (indentation, spacing, key ordering). This could create large diffs with formatting changes unrelated to the version bump. Since CLAUDE.md emphasizes Prettier as the sole authority for formatting, this could cause conflicts.

Recommendation: Run Prettier after modifying package.json files to ensure consistent formatting.

4. Missing Validation: Yarn Publish Command (rakelib/release.rake:110, 115)

The yarn workspace publish commands don't verify success before proceeding. If the first publish fails or is cancelled, the script continues and may partially succeed, leaving an inconsistent state.


⚠️ CRITICAL: CLAUDE.md Compliance Issue

According to CLAUDE.md, files MUST end with a newline character, and this is enforced by git hooks and CI. Please verify rakelib/release.rake has a trailing newline.

Also, RuboCop MUST pass before any commit. The PR checklist shows RuboCop passed, but recommend running bundle exec rubocop rakelib/release.rake one more time to confirm.


🧪 Test Coverage Concerns

The release rake task has no RSpec tests. Given the critical nature of the release process, consider adding tests that verify version extraction, package.json updates, and dry run mode behavior.


📝 Minor Suggestions

  1. Line 142: The emoji violates CLAUDE.md rule (Only use emojis if the user explicitly requests it). Consider removing the emoji from RELEASE COMPLETE.

  2. Documentation Consistency: Standardize on Ruby gem (not ruby gem) throughout docs.

  3. Consider Adding a Confirmation Prompt: For non-dry-run releases, consider adding a final confirmation prompt before pushing and publishing.


✅ Summary

This is a solid improvement to the release process. Main concerns:

  1. Add error handling for version extraction
  2. Consider the git push timing vs. publish operations
  3. Ensure JSON formatting doesn't conflict with Prettier
  4. Remove emoji from output per CLAUDE.md
  5. Verify trailing newline in all modified files

The documentation is excellent, the approach is sound, and the version synchronization fix is critical. With the above issues addressed, this will be a great enhancement.

Recommendation: Request changes for the error handling and formatting issues, then approve.

Copy link
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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rakelib/release.rake (1)

41-49: Dry-run currently mutates the repo (gem bump, JSON writes, bundle, git pull)

This breaks the promise of a “preview only” mode and risks a dirty working tree. Gate all mutating steps behind unless is_dry_run, and compute the target versions without writing files in dry runs.

Apply these diffs:

  • Gate the pre-steps and git pull:
-  # Having the examples prevents publishing
-  Rake::Task["shakapacker_examples:clobber"].invoke
-  # Delete any react_on_rails.gemspec except the root one
-  sh_in_dir(gem_root, "find . -mindepth 2 -name 'react_on_rails.gemspec' -delete")
-
-  # Pull latest changes
-  sh_in_dir(gem_root, "git pull --rebase")
+  unless is_dry_run
+    # Having the examples prevents publishing
+    Rake::Task["shakapacker_examples:clobber"].invoke
+    # Delete any react_on_rails.gemspec except the root one
+    sh_in_dir(gem_root, "find . -mindepth 2 -name 'react_on_rails.gemspec' -delete")
+    # Pull latest changes
+    sh_in_dir(gem_root, "git pull --rebase")
+  end
  • Compute actual_gem_version without mutating in dry-run; only bump when not dry-run:
-  # Bump gem version using gem-release
-  sh_in_dir(gem_root, "gem bump --no-commit #{%(--version #{gem_version}) unless gem_version.strip.empty?}")
-
-  # Read the actual version that was set
-  actual_gem_version = begin
-    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
-    version_content = File.read(version_file)
-    version_content.match(/VERSION = "(.+)"/)[1]
-  end
+  version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
+  if is_dry_run
+    current = File.read(version_file)
+    m = current.match(/VERSION\s*=\s*"(.+?)"/)
+    raise "Could not parse version.rb for VERSION" unless m
+    current_gem_version = m[1]
+    actual_gem_version =
+      if gem_version.to_s.strip.empty?
+        parts = current_gem_version.split(".")
+        parts[2] = (parts[2].to_i + 1).to_s
+        parts.join(".")
+      else
+        gem_version.strip
+      end
+  else
+    # Bump gem version using gem-release
+    sh_in_dir(gem_root, "gem bump --no-commit #{%(--version #{gem_version}) unless gem_version.to_s.strip.empty?}")
+    # Read the actual version that was set
+    version_content = File.read(version_file)
+    m = version_content.match(/VERSION\s*=\s*"(.+?)"/)
+    raise "Could not parse version.rb for VERSION" unless m
+    actual_gem_version = m[1]
+  end
  • Gate package.json writes and Bundler lockfile updates:
-  puts "Updating package.json files to version #{actual_npm_version}..."
-
-  # Update all package.json files
-  package_json_files = [
-    File.join(gem_root, "package.json"),
-    File.join(gem_root, "packages", "react-on-rails", "package.json"),
-    File.join(gem_root, "packages", "react-on-rails-pro", "package.json")
-  ]
-
-  package_json_files.each do |file|
-    content = JSON.parse(File.read(file))
-    content["version"] = actual_npm_version
-
-    # For react-on-rails-pro, also update the react-on-rails dependency
-    if file.include?("react-on-rails-pro")
-      content["dependencies"] ||= {}
-      content["dependencies"]["react-on-rails"] = actual_npm_version
-    end
-
-    File.write(file, "#{JSON.pretty_generate(content)}\n")
-    puts "  Updated #{file}"
-  end
-
-  # Update dummy app's Gemfile.lock
-  bundle_install_in(dummy_app_dir)
+  unless is_dry_run
+    puts "Updating package.json files to version #{actual_npm_version}..."
+    package_json_files = [
+      File.join(gem_root, "package.json"),
+      File.join(gem_root, "packages", "react-on-rails", "package.json"),
+      File.join(gem_root, "packages", "react-on-rails-pro", "package.json")
+    ]
+    package_json_files.each do |file|
+      content = JSON.parse(File.read(file))
+      content["version"] = actual_npm_version
+      if file.include?("react-on-rails-pro")
+        content["dependencies"] ||= {}
+        content["dependencies"]["react-on-rails"] = actual_npm_version
+      end
+      File.write(file, "#{JSON.pretty_generate(content)}\n")
+      puts "  Updated #{file}"
+    end
+    # Update dummy app's Gemfile.lock
+    bundle_install_in(dummy_app_dir)
+  end

Also applies to: 50-55, 56-61, 65-90

🧹 Nitpick comments (3)
docs/contributor-info/releasing.md (2)

147-151: Avoid yarn publish --new-version in manual recovery steps

Using --new-version can re-tag/re-commit and conflict with the earlier tag/commit. Recommend publishing the already-versioned workspaces with npm publish instead:

  • cd packages/react-on-rails && npm publish --access public
  • cd ../react-on-rails-pro && npm publish --access public

157-185: Add language to fenced code block (markdownlint MD040)

Annotate the code fence as diff.

-```
+```diff
 commit abc123...
 ...
 }
-```
+```
rakelib/release.rake (1)

78-82: Confirm dependency field for Pro → OSS coupling

You’re forcing an exact version under dependencies. If the intention is to require the app to install the same OSS version alongside Pro, this is OK. If Pro should be compatible with a range in caller apps, consider peerDependencies (with peerDependenciesMeta.optional as needed) to avoid duplicate installs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bce94a and 34f9363.

📒 Files selected for processing (5)
  • docs/contributor-info/releasing.md (1 hunks)
  • package.json (0 hunks)
  • packages/react-on-rails/package.json (1 hunks)
  • packages/react-on-rails/scripts/release (0 hunks)
  • rakelib/release.rake (3 hunks)
💤 Files with no reviewable changes (2)
  • packages/react-on-rails/scripts/release
  • package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails/package.json
  • docs/contributor-info/releasing.md
{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}: All Ruby code must pass RuboCop with zero offenses before commit/push
RuboCop is the sole authority for Ruby file formatting; never manually format Ruby files

Files:

  • rakelib/release.rake
🪛 markdownlint-cli2 (0.18.1)
docs/contributor-info/releasing.md

157-157: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: markdown-link-check
  • GitHub Check: claude-review
  • GitHub Check: build
  • GitHub Check: build-and-test
🔇 Additional comments (1)
packages/react-on-rails/package.json (1)

17-18: Removal of release scripts looks good

Change aligns with the unified rake-based release flow.

AbanoubGhadban and others added 2 commits October 8, 2025 18:30
- Remove extra end statement that was causing syntax error
- Fix string concatenation to use interpolation
- Fix operator precedence ambiguity
- Convert multi-line unless blocks to modifier form
- Refactor unless/else to if/else for clarity
- Fix line length violation
- Eliminate duplicate separator line code

All RuboCop violations resolved. File now passes bundle exec rubocop with zero offenses.
- Add 4th argument 'skip_push' to skip git push operations
- Useful for testing the release process locally
- Commits and tags are still created, but not pushed to remote
- Displays warning message when push is skipped
- Update documentation with new parameter and example usage

Example: rake release[16.2.0,false,npm,skip_push]

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
Copy link

claude bot commented Oct 8, 2025

Code Review: Modernize Release Process for Monorepo

Overall, this is a well-architected improvement that modernizes the release process. The changes are logical, well-documented, and reduce external dependencies.


Strengths

1. Excellent Simplification

  • Removing release-it dependency and using native Yarn commands is a smart move
  • Single unified rake task handles all three packages consistently

2. Strong Documentation

  • The rewritten docs/contributor-info/releasing.md is comprehensive and user-friendly
  • Good troubleshooting section with recovery procedures

3. Version Synchronization

  • Setting exact version dependency in react-on-rails-pro prevents version mismatches
  • Updating all 5 version locations ensures consistency

4. Verdaccio Support

  • Adding local registry testing is excellent for safe validation before production release

Issues & Recommendations

Critical Issues

1. Regex Vulnerability (rakelib/release.rake:64)
The regex (.+) is too greedy and does not validate version format. Recommend using a more specific pattern.

2. Lack of Error Handling (rakelib/release.rake:111-149)
If publishing fails partway through (e.g., NPM publish succeeds but RubyGems fails), users are left with inconsistent state. Add begin/rescue blocks with rollback capability.

3. Missing JSON Parse Validation (rakelib/release.rake:79)
JSON.parse will raise an exception if JSON is malformed. Wrap in begin/rescue with helpful error message.

Medium Priority

4. Missing step 2 in documentation (line 201)
Step numbering goes 1, 3, 4. Either add step 2 or renumber.

5. Race condition with git pull (line 55)
Git pull happens after checking for uncommitted changes. If a teammate pushes between the check and pull, you could end up with merge conflicts.

6. No verification of successful publish (lines 130-136)
The task does not verify that packages were actually published to npm/RubyGems.

Minor Issues

  1. Unused variable npm_registry_url (line 96)
  2. Fragile string matching with include? (line 83)
  3. Inconsistent shell command quoting
  4. Missing rollback instructions in documentation

Security

No major security issues detected:

  • No secrets exposed
  • No unsafe shell injection
  • Proper 2FA prompts
  • Verdaccio URLs are localhost

Test Coverage

Concern: This PR does not include tests for the new release task.

Recommendations:

  • Add rake task tests (at minimum, test dry run mode)
  • Test version synchronization logic
  • Test Verdaccio mode
  • Consider adding smoke tests that verify version extraction logic

Performance

Performance looks good:

  • File operations are minimal
  • Git operations are standard
  • Network operations are unavoidable
  • Verdaccio support allows for fast local testing

Additional Suggestions

  1. Add validation that gem-release is installed before proceeding
  2. Consider adding a confirmation prompt for production releases (not dry run)
  3. Log timestamps for each major step to help with debugging
  4. Consider adding CI checks to ensure this task can run in dry-run mode

CLAUDE.md Compliance

  • Formatting follows RuboCop standards
  • Documentation is comprehensive
  • Uses proper git workflow
  • Follows project conventions

Note: Make sure to run bundle exec rubocop before committing (as per CLAUDE.md requirements).


Summary

This is a high-quality PR that meaningfully improves the release process. The main concerns are:

  1. Regex validation (security/correctness)
  2. Error handling (reliability)
  3. Test coverage (maintainability)

With these issues addressed, this will be an excellent improvement to the project. Great work!

Copy link
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.

Actionable comments posted: 2

♻️ Duplicate comments (2)
rakelib/release.rake (2)

60-65: Harden version parsing from version.rb

If the regex doesn't match, match(...)[1] will raise. Add a nil-check and clearer error.

Apply this diff:

-  actual_gem_version = begin
-    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
-    version_content = File.read(version_file)
-    version_content.match(/VERSION = "(.+)"/)[1]
-  end
+  actual_gem_version = begin
+    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
+    version_content = File.read(version_file)
+    m = version_content.match(/VERSION\s*=\s*"([^"]+)"/)
+    raise "Could not parse VERSION from #{version_file}" unless m
+    m[1]
+  end

130-136: Prevent duplicate tagging when publishing.

yarn workspace ... publish --new-version will bump package.json, commit, and tag again after you've already done so. Publish the already-versioned packages directly.

Apply this diff:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails"), "npm publish --access public #{npm_publish_args}")
     
-    sh_in_dir(gem_root,
-              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails-pro"), "npm publish --access public #{npm_publish_args}")

Alternatively, if you must use Yarn workspace commands, add --no-git-tag-version:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} --no-git-tag-version #{npm_publish_args}")
     
-    sh_in_dir(gem_root,
-              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(gem_root,
+              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} --no-git-tag-version #{npm_publish_args}")
🧹 Nitpick comments (1)
rakelib/release.rake (1)

148-148: Verify gem-release availability before use.

The task relies on the gem-release gem but doesn't verify it's available. Consider adding an early check to provide a clearer error message.

Add this check at the beginning of the task (after line 46):

begin
  require "gem/release"
rescue LoadError
  abort "Error: gem-release is required. Install it with: gem install gem-release"
end
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34f9363 and ffc8950.

📒 Files selected for processing (1)
  • rakelib/release.rake (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}: All Ruby code must pass RuboCop with zero offenses before commit/push
RuboCop is the sole authority for Ruby file formatting; never manually format Ruby files

Files:

  • rakelib/release.rake
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: claude-review
  • GitHub Check: build
  • GitHub Check: markdown-link-check
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: build-and-test
🔇 Additional comments (4)
rakelib/release.rake (4)

18-36: Well-documented task interface.

The updated documentation clearly explains the release process for both NPM packages and the gem, including version format conversion, dry run support, and Verdaccio testing.


78-90: Excellent version synchronization logic.

The implementation correctly updates all package.json files and properly synchronizes the react-on-rails dependency version in react-on-rails-pro to maintain exact version matching.


95-109: Verdaccio support is well-implemented.

The conditional registry configuration with clear instructions and user confirmation provides a good developer experience for testing releases locally.


158-208: Excellent dry run and success messaging.

The comprehensive output clearly shows what will be modified and provides helpful next steps, with appropriate differentiation between Verdaccio test mode and production releases.

Comment on lines 113 to 121
sh_in_dir(gem_root, "git add -A")
sh_in_dir(gem_root, "git commit -m 'Bump version to #{actual_gem_version}'")

# Create git tag
sh_in_dir(gem_root, "git tag v#{actual_gem_version}")

# Push commits and tags
sh_in_dir(gem_root, "git push")
sh_in_dir(gem_root, "git push --tags")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for git operations.

If git push or git push --tags fails after local commits/tags are created, the repository is left in an inconsistent state with no rollback mechanism. Consider wrapping these operations in error handling that provides guidance on manual recovery.

Example implementation:

begin
  sh_in_dir(gem_root, "git push")
  sh_in_dir(gem_root, "git push --tags")
rescue StandardError => e
  puts "\n❌ Git push failed: #{e.message}"
  puts "Local commits and tags created but not pushed."
  puts "To recover:"
  puts "  1. Fix the issue (e.g., network, permissions)"
  puts "  2. Run: git push && git push --tags"
  puts "  3. Then manually publish packages"
  abort
end
🤖 Prompt for AI Agents
In rakelib/release.rake around lines 113 to 121, the git push and git push
--tags commands are executed without error handling which can leave local
commits and tags unpushed; wrap those sh_in_dir calls in a begin/rescue that
catches StandardError, prints a clear error message including e.message and
recovery steps (explain the local state, advise fixing network/permission
issues, and provide the exact git push && git push --tags command to run), and
then abort the rake task to prevent further release steps from proceeding.

Comment on lines +138 to 149
if use_verdaccio
puts "Skipping Ruby gem publication (Verdaccio mode)"
puts "=" * 80
else
sh_in_dir(gem_root, "git commit -m 'Update Gemfile.lock for version #{gem_version}'")
puts "\n#{'=' * 80}"
puts "Publishing Ruby gem..."
puts "=" * 80

# Publish Ruby gem
puts "\nCarefully add your OTP for Rubygems when prompted."
sh_in_dir(gem_root, "gem release")
end
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for gem publication.

If gem release fails after NPM packages are already published, the release is left in an inconsistent state. Consider adding error handling to guide manual recovery.

Example implementation:

begin
  puts "\nCarefully add your OTP for Rubygems when prompted."
  sh_in_dir(gem_root, "gem release")
rescue StandardError => e
  puts "\n❌ Gem release failed: #{e.message}"
  puts "NPM packages already published:"
  puts "  - react-on-rails@#{actual_npm_version}"
  puts "  - react-on-rails-pro@#{actual_npm_version}"
  puts "To complete the release:"
  puts "  1. Fix the issue"
  puts "  2. Run: gem release"
  abort
end
🤖 Prompt for AI Agents
In rakelib/release.rake around lines 138 to 149, the gem publication step runs
`gem release` without error handling which can leave the release in an
inconsistent state if it fails after NPM packages were published; wrap the
`sh_in_dir(gem_root, "gem release")` call in a begin/rescue that catches
StandardError, prints a clear failure message including the exception message,
lists the already-published NPM packages and their versions (use the existing
variable holding the actual_npm_version), instructs the user how to recover (fix
issue then run `gem release`), and aborts the rake task to stop further
processing.

- Validate registry parameter accepts only: 'verdaccio', 'npm', or empty string
- Validate skip_push parameter accepts only: 'skip_push' or empty string
- Raise ArgumentError with clear message for invalid values
- Prevents silent failures from typos or incorrect usage

Examples of errors:
  rake release[16.2.0,false,foo] → ArgumentError: Invalid registry value 'foo'
  rake release[16.2.0,false,npm,skip] → ArgumentError: Invalid skip_push value 'skip'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
Copy link

claude bot commented Oct 8, 2025

This is a well-structured modernization of the release process. Key strengths: excellent documentation, proper version synchronization across all packages, developer-friendly features (dry run, Verdaccio, skip push), and simplified architecture by removing release-it dependency. Main concerns: (1) CRITICAL - Missing rollback mechanism if NPM publishing fails after git push, creating version inconsistency. Recommend pushing git changes AFTER successful package publishing or adding recovery task. (2) git add -A is too broad and may stage unintended WIP files. (3) Missing automated tests for critical release logic. (4) Regex pattern for version extraction could be more robust. Overall a solid improvement that would be ready to merge after addressing the rollback issue.

Copy link
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.

Actionable comments posted: 1

♻️ Duplicate comments (4)
rakelib/release.rake (4)

64-69: Harden version parsing from version.rb

If the regex doesn't match, match(...)[1] will raise NoMethodError. Add a nil-check and a clearer error message.

Apply this diff to add error handling:

-  actual_gem_version = begin
-    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
-    version_content = File.read(version_file)
-    version_content.match(/VERSION = "(.+)"/)[1]
-  end
+  actual_gem_version = begin
+    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
+    version_content = File.read(version_file)
+    m = version_content.match(/VERSION\s*=\s*"(.+?)"/)
+    raise "Could not parse VERSION from #{version_file}. Check version.rb formatting." unless m
+    m[1]
+  end

136-136: Prevent duplicate tagging when publishing NPM packages

yarn workspace ... publish --new-version will bump package.json, create a commit, and tag again. Since you've already updated versions (lines 82-94), committed (line 118), and tagged (line 121), this will cause duplicate tags or failures.

Apply this diff to publish the already-versioned packages:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(gem_root, "yarn workspace react-on-rails publish #{npm_publish_args}")
-    sh_in_dir(gem_root,
-              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(gem_root, "yarn workspace react-on-rails-pro publish #{npm_publish_args}")

If you must keep --new-version, add --no-git-tag-version to prevent re-tagging.

Also applies to: 141-142


125-127: Add error handling for git operations

If git push or git push --tags fails after local commits/tags are created, the repository is left in an inconsistent state with no rollback mechanism.

Apply this diff to add recovery guidance:

-    unless skip_push
-      sh_in_dir(gem_root, "git push")
-      sh_in_dir(gem_root, "git push --tags")
-    end
+    unless skip_push
+      begin
+        sh_in_dir(gem_root, "git push")
+        sh_in_dir(gem_root, "git push --tags")
+      rescue StandardError => e
+        puts "\n❌ Git push failed: #{e.message}"
+        puts "Local commits and tags created but not pushed."
+        puts "To recover:"
+        puts "  1. Fix the issue (e.g., network, permissions)"
+        puts "  2. Run: git push && git push --tags"
+        puts "  3. Then manually complete release: rake release[#{actual_gem_version}]"
+        abort
+      end
+    end

153-154: Add error handling for gem publication

If gem release fails after NPM packages are already published, the release is left in an inconsistent state.

Apply this diff to add recovery guidance:

-      puts "\nCarefully add your OTP for Rubygems when prompted."
-      sh_in_dir(gem_root, "gem release")
+      begin
+        puts "\nCarefully add your OTP for Rubygems when prompted."
+        sh_in_dir(gem_root, "gem release")
+      rescue StandardError => e
+        puts "\n❌ Gem release failed: #{e.message}"
+        puts "NPM packages already published:"
+        puts "  - react-on-rails@#{actual_npm_version}"
+        puts "  - react-on-rails-pro@#{actual_npm_version}"
+        puts "To complete the release:"
+        puts "  1. Fix the issue"
+        puts "  2. Run: gem release"
+        abort
+      end
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffc8950 and 604b823.

📒 Files selected for processing (1)
  • rakelib/release.rake (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}: All Ruby code must pass RuboCop with zero offenses before commit/push
RuboCop is the sole authority for Ruby file formatting; never manually format Ruby files

Files:

  • rakelib/release.rake
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: build
  • GitHub Check: markdown-link-check
  • GitHub Check: claude-review
  • GitHub Check: build-and-test

Next steps:
1. Update CHANGELOG.md: bundle exec rake update_changelog
3. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix step numbering

The step numbering skips from 1 to 3 (missing step 2).

Apply this diff:

         Next steps:
           1. Update CHANGELOG.md: bundle exec rake update_changelog
-          3. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
-          4. Push changes: git push
+          2. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
+          3. Push changes: git push
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
3. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
Next steps:
1. Update CHANGELOG.md: bundle exec rake update_changelog
2. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
3. Push changes: git push
🤖 Prompt for AI Agents
In rakelib/release.rake around line 217 the numbered step jumps from "1." to
"3." so update the sequence to use the correct numbering (change "3. Commit
CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and
spec/dummy Gemfile.lock'" to use "2." instead) so the steps read 1, 2, 3 in
order; ensure any adjacent step numbers are consistent if present.

Copy link
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.

Actionable comments posted: 0

♻️ Duplicate comments (5)
rakelib/release.rake (5)

79-84: Harden version parsing from version.rb.

If the regex doesn't match, match(...)[1] will raise. Add a nil-check and clearer error.

Apply this diff:

-  actual_gem_version = begin
-    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
-    version_content = File.read(version_file)
-    version_content.match(/VERSION = "(.+)"/)[1]
-  end
+  actual_gem_version = begin
+    version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
+    version_content = File.read(version_file)
+    m = version_content.match(/VERSION\s*=\s*"(.+?)"/)
+    raise "Could not parse version.rb for VERSION" unless m
+    m[1]
+  end

138-142: Add error handling for git operations.

If git push or git push --tags fails after local commits/tags are created, the repository is left in an inconsistent state with no rollback mechanism.

Example implementation:

begin
  unless skip_push
    sh_in_dir(gem_root, "git push")
    sh_in_dir(gem_root, "git push --tags")
  end
rescue StandardError => e
  puts "\n❌ Git push failed: #{e.message}"
  puts "Local commits and tags created but not pushed."
  puts "To recover:"
  puts "  1. Fix the issue (e.g., network, permissions)"
  puts "  2. Run: git push && git push --tags"
  puts "  3. Then manually publish packages"
  abort
end

148-157: Prevent duplicate tagging when publishing.

Yarn's publish --new-version will bump package.json, commit, and tag again. The package.json files are already updated (lines 97-109) and committed (line 133), so this causes duplicate commits/tags.

Publish the already-versioned packages directly:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails"), "npm publish --access public #{npm_publish_args}")
-    sh_in_dir(gem_root,
-              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails-pro"), "npm publish --access public #{npm_publish_args}")

If you must use Yarn, add --no-git-tag-version to prevent re-bumping/tagging.


167-169: Add error handling for gem publication.

If gem release fails after NPM packages are already published, the release is left in an inconsistent state.

Example implementation:

begin
  puts "\nCarefully add your OTP for Rubygems when prompted."
  sh_in_dir(gem_root, "gem release")
rescue StandardError => e
  puts "\n❌ Gem release failed: #{e.message}"
  puts "NPM packages already published:"
  puts "  - react-on-rails@#{actual_npm_version}"
  puts "  - react-on-rails-pro@#{actual_npm_version}"
  puts "To complete the release:"
  puts "  1. Fix the issue"
  puts "  2. Run: gem release"
  abort
end

232-232: Fix step numbering.

The step numbering skips from 1 to 3 (missing step 2).

Apply this diff:

         Next steps:
           1. Update CHANGELOG.md: bundle exec rake update_changelog
-          3. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
-          4. Push changes: git push
+          2. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
+          3. Push changes: git push
🧹 Nitpick comments (1)
rakelib/release.rake (1)

118-128: Consider non-interactive environment handling.

The $stdin.gets at line 127 will block in non-interactive environments (e.g., CI/CD pipelines). Consider adding a check for TTY availability or an environment variable to bypass the prompt:

if use_verdaccio
  puts "\n#{'=' * 80}"
  puts "VERDACCIO LOCAL REGISTRY MODE"
  puts "=" * 80
  puts "\nBefore proceeding, ensure:"
  puts "  1. Verdaccio server is running on http://localhost:4873/"
  puts "  2. You are authenticated with Verdaccio:"
  puts "     npm adduser --registry http://localhost:4873/"
  puts "\nPress ENTER to continue or Ctrl+C to cancel..."
  $stdin.gets if $stdin.tty? && !is_dry_run
end
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 604b823 and 2dc17f0.

📒 Files selected for processing (1)
  • rakelib/release.rake (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}: All Ruby code must pass RuboCop with zero offenses before commit/push
RuboCop is the sole authority for Ruby file formatting; never manually format Ruby files

Files:

  • rakelib/release.rake
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-test
🔇 Additional comments (5)
rakelib/release.rake (5)

1-9: LGTM!

The addition of require "json" is necessary for the package.json manipulation logic introduced later in the file.


18-40: LGTM!

The expanded task signature with comprehensive documentation and examples helps users understand the new Verdaccio and skip_push features.


49-64: LGTM!

Parameter validation for registry and skip_push is implemented correctly with clear error messages for invalid values.


73-74: Verify git pull timing relative to uncommitted changes check.

The uncommitted changes check occurs at line 44, but git pull --rebase is executed here. If the pull introduces conflicts or new changes, those won't be validated by the earlier check. Consider moving the git pull before the uncommitted changes check, or re-checking after the pull.


86-109: LGTM!

The package.json update logic correctly:

  • Updates version in all three package.json files
  • Updates the react-on-rails dependency version in react-on-rails-pro
  • Preserves formatting with pretty_generate and trailing newline

@claude
Copy link

claude bot commented Oct 8, 2025

Code Review: Modernize Release Process for Monorepo

Overall Assessment ✅

This is a well-executed modernization of the release process. The PR successfully consolidates the release workflow, eliminates external dependencies, and provides excellent documentation.


🎯 Strengths

  1. Excellent Documentation - Comprehensive rewrite with dry-run instructions and troubleshooting
  2. Unified Release Process - Single rake release command handles gem + both NPM packages
  3. Safety Features - Dry run mode, Verdaccio support, skip_push option
  4. Improved Version Management - Exact version dependency prevents incompatible combinations

🐛 Critical Issues

1. Version Mismatch in Pro Package ⚠️

Location: packages/react-on-rails-pro/package.json:57

The pro package depends on version 16.1.2, but current version is 16.1.1. This creates an immediate dependency conflict.

Fix: Update to "16.1.1" to match current version

2. Dry Run Modifies Files

Location: rakelib/release.rake:92-110

Even in dry-run mode, package.json files are modified before the dry-run check at line 126. Running rake release[X.Y.Z,true] will modify local files.

Fix: Wrap file modifications in dry-run check

3. Missing Error Handling

Location: rakelib/release.rake:79-83

The regex version_content.match(/VERSION = "(.+)"/)[1] could crash if match fails.

Fix: Add validation that match succeeded before accessing [1]

4. No NPM Authentication Check

The script publishes without verifying user is logged in to npm.

Fix: Add npm whoami check before publishing


🧪 Test Coverage

Missing automated tests for:

  • Release rake task functionality
  • Dry-run mode behavior
  • Error conditions

Recommend adding RSpec tests with mocked external calls.


✅ Checklist Before Merge

  • Fix version mismatch (16.1.2 → 16.1.1)
  • Fix dry-run to not modify files
  • Add error handling for version regex
  • Add npm authentication check
  • Test with dry-run

🎉 Conclusion

High-quality PR with excellent consolidation. Address the version mismatch and dry-run issues before merge.

Recommendation: Approve after fixes

Great work! 🚀

Copy link
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.

Actionable comments posted: 1

♻️ Duplicate comments (5)
rakelib/release.rake (5)

79-84: Harden version parsing from version.rb.

The regex match can raise if the VERSION constant format is unexpected. This issue was previously flagged but remains unaddressed.

Add nil-check as previously suggested:

-    version_content = File.read(version_file)
-    version_content.match(/VERSION = "(.+)"/)[1]
+    version_content = File.read(version_file)
+    m = version_content.match(/VERSION\s*=\s*"(.+?)"/)
+    raise "Could not parse VERSION from #{version_file}" unless m
+    m[1]

152-158: Prevent duplicate git operations when publishing.

Using yarn workspace ... publish --new-version will bump package.json, commit, and tag again after you've already done so at lines 133-137. This issue was previously flagged but remains unaddressed.

Apply the previously suggested fix to publish without re-versioning:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails"), "npm publish --access public #{npm_publish_args}")

-    sh_in_dir(gem_root,
-              "yarn workspace react-on-rails-pro publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(File.join(gem_root, "packages", "react-on-rails-pro"), "npm publish --access public #{npm_publish_args}")

Alternatively, use --no-git-tag-version if you prefer staying with yarn:

-    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} #{npm_publish_args}")
+    sh_in_dir(gem_root, "yarn workspace react-on-rails publish --new-version #{actual_npm_version} --no-git-tag-version #{npm_publish_args}")

141-142: Add error handling for git push operations.

Git push failures leave local commits/tags unpushed with no recovery guidance. This issue was previously flagged but remains unaddressed.

Wrap in error handling as previously suggested:

begin
  sh_in_dir(gem_root, "git push")
  sh_in_dir(gem_root, "git push --tags")
rescue StandardError => e
  puts "\n❌ Git push failed: #{e.message}"
  puts "Local commits and tags created but not pushed."
  puts "To recover:"
  puts "  1. Fix the issue (e.g., network, permissions)"
  puts "  2. Run: git push && git push --tags"
  puts "  3. Then manually publish packages"
  abort
end

170-170: Add error handling for gem publication.

Gem release failures leave npm packages published with no gem. This issue was previously flagged but remains unaddressed.

Wrap in error handling as previously suggested:

begin
  puts "\nCarefully add your OTP for Rubygems when prompted."
  sh_in_dir(gem_root, "gem release")
rescue StandardError => e
  puts "\n❌ Gem release failed: #{e.message}"
  puts "NPM packages already published:"
  puts "  - react-on-rails@#{actual_npm_version}"
  puts "  - react-on-rails-pro@#{actual_npm_version}"
  puts "To complete the release:"
  puts "  1. Fix the issue"
  puts "  2. Run: gem release"
  abort
end

233-233: Fix step numbering.

Steps jump from 1 to 3. This issue was previously flagged but remains unaddressed.

-          3. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
-          4. Push changes: git push
+          2. Commit CHANGELOG: cd #{gem_root} && git commit -a -m 'Update CHANGELOG.md and spec/dummy Gemfile.lock'
+          3. Push changes: git push
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc17f0 and 324fb8c.

📒 Files selected for processing (2)
  • packages/react-on-rails-pro/package.json (1 hunks)
  • rakelib/release.rake (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}: All Ruby code must pass RuboCop with zero offenses before commit/push
RuboCop is the sole authority for Ruby file formatting; never manually format Ruby files

Files:

  • rakelib/release.rake
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails-pro/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: claude-review
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: markdown-link-check
  • GitHub Check: build-and-test
🔇 Additional comments (1)
rakelib/release.rake (1)

1-242: Overall modernization approach looks solid.

The refactored release process successfully:

  • Centralizes version management in Ruby gem version as source of truth
  • Synchronizes versions across gem and both npm packages
  • Adds Verdaccio support for local testing
  • Provides skip_push for testing without remote operations
  • Includes comprehensive dry-run and success messaging

However, several issues from previous reviews remain unaddressed and should be resolved before merging.

@claude
Copy link

claude bot commented Oct 8, 2025

Code Review Summary

This PR successfully modernizes the release process to properly handle the monorepo structure. The changes are well-structured and significantly improve the release workflow. Here's my detailed feedback:


Strengths

1. Excellent Code Quality

  • Clean, readable Ruby code following project conventions
  • Proper parameter validation with clear error messages
  • Good separation of concerns and logical flow
  • Comprehensive dry-run support for safe testing

2. Strong Documentation

  • docs/contributor-info/releasing.md is exceptionally well-written
  • Clear examples for all use cases
  • Excellent troubleshooting section
  • Step-by-step release process documentation

3. Great Features

  • Verdaccio support - Brilliant addition for local testing before production releases
  • Version synchronization - Ensures all 5 version locations stay in sync
  • Exact dependency pinning - react-on-rails-pro now depends on exact version of react-on-rails
  • Flexible workflow options - dry_run, skip_push, and registry parameters provide great flexibility

4. Proper Monorepo Handling

  • Uses Yarn workspaces correctly (yarn workspace <name> publish)
  • Handles both NPM packages plus the Ruby gem
  • Removes external release-it dependency

🔍 Potential Issues & Suggestions

Critical: Potential Regex Failure ⚠️

Location: rakelib/release.rake:83

version_content.match(/VERSION = \"(.+)\"/)[1]

Issue: This will raise NoMethodError if the regex doesn't match (returns nil).

Recommendation:

actual_gem_version = begin
  version_file = File.join(gem_root, "lib", "react_on_rails", "version.rb")
  version_content = File.read(version_file)
  match = version_content.match(/VERSION = \"(.+)\"/)
  raise "Could not parse version from #{version_file}" unless match
  match[1]
end

Medium: Dry Run Side Effects ⚠️

Location: rakelib/release.rake:77-113

Issue: Dry run mode still modifies files on disk:

  • Updates lib/react_on_rails/version.rb (via gem bump --no-commit)
  • Updates all package.json files (lines 97-109)
  • Runs bundle install (lines 111-113)

Current State: Files are modified but not committed

Expected Behavior for Dry Run:

  1. Preview what would be changed without touching disk
  2. Or clearly document that dry run modifies files but doesn't commit/publish

Recommendation: Either:

  • Add early return after reading version in dry-run mode
  • Or add clear warning: "Note: Dry run will modify version files locally but won't commit or publish"

Medium: JSON Pretty Print Formatting 📝

Location: rakelib/release.rake:107

File.write(file, "#{JSON.pretty_generate(content)}\n")

Issue: JSON.pretty_generate uses 2-space indentation by default, but package.json files might use different formatting.

Recommendation: Use Prettier to format after modification:

File.write(file, "#{JSON.pretty_generate(content)}\n")
# Let Prettier handle formatting per project standards
sh_in_dir(gem_root, "yarn prettier --write #{file}")

This aligns with CLAUDE.md which states "Prettier is the SOLE authority for formatting non-Ruby files."


Low: String Detection Could Be More Robust

Location: rakelib/release.rake:102

if file.include?("react-on-rails-pro")

Issue: Simple string matching could potentially match unexpected paths.

Recommendation:

if File.basename(File.dirname(file)) == "react-on-rails-pro"

Low: Missing Rollback Instructions 📚

Issue: Documentation has good "If Release Fails" section but lacks instructions for partial failures between NPM packages.

Example Scenario:

  • react-on-rails publishes successfully
  • react-on-rails-pro fails
  • Both packages now have mismatched versions in registry

Recommendation: Add to docs:

### If Packages Published with Mismatched Versions

If `react-on-rails` published but `react-on-rails-pro` failed:

1. Fix the issue that caused the failure
2. Manually publish the failed package:
   cd packages/react-on-rails-pro && yarn publish --new-version X.Y.Z

Low: Documentation Numbering Issue 📝

Location: docs/contributor-info/releasing.md:233

Jumps from step 1 to step 3 (missing step 2)

Fix: Should be numbered 1, 2, 3 consecutively


🔒 Security Considerations

No security issues found

  • No credential exposure
  • No unsafe shell interpolations
  • Proper OTP token handling
  • No vulnerable dependencies introduced

🧪 Test Coverage

The test plan shows:

  • RuboCop passes
  • Parameter validation works
  • Verdaccio support implemented
  • Actual dry run testing
  • File update verification
  • Commit message validation

Recommendation: Complete the remaining test plan items before merging, particularly:

  1. Run actual dry run: rake release[16.1.2,true]
  2. Verify version synchronization works correctly
  3. Test Verdaccio flow end-to-end

📊 Performance

✅ No performance concerns - this is a release script that runs rarely


🎯 Recommendations Priority

Must Fix Before Merge:

  1. ✅ Fix potential NoMethodError in version parsing (rakelib/release.rake:83)
  2. ✅ Document or fix dry-run side effects behavior

Should Fix Before Merge:

  1. Fix documentation numbering (step 2 missing)
  2. Use Prettier for JSON formatting (aligns with CLAUDE.md standards)

Nice to Have:

  1. More robust file path detection
  2. Enhanced rollback documentation

🎉 Conclusion

This is a high-quality PR that significantly improves the release process. The Verdaccio support and comprehensive documentation are particularly valuable additions. With the critical issue fixed (regex error handling) and dry-run behavior clarified, this will be production-ready.

Recommendation: Approve with requested changes addressing the critical regex handling issue.

Great work! 🚀


Review generated with Claude Code following CLAUDE.md guidelines

@AbanoubGhadban AbanoubGhadban merged commit 80eb21e into master Oct 8, 2025
15 of 17 checks passed
@AbanoubGhadban AbanoubGhadban deleted the modernize-release-process branch October 8, 2025 16:30
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.

2 participants