Skip to content

Conversation

@AbanoubGhadban
Copy link
Collaborator

@AbanoubGhadban AbanoubGhadban commented Oct 26, 2025

Summary

This PR implements three major improvements to React on Rails version management:

  1. Unified release script - Consolidates separate release workflows into a single, atomic release process
  2. Strict version validation - Adds fail-fast validation to prevent runtime configuration errors
  3. Node renderer gem version validation - Ensures gem and node renderer versions match at runtime

These changes work together to ensure consistent versioning across all packages (both during releases and at runtime).


Part 1: Unified Release Script

Problem

Previously, React on Rails had two separate release scripts with different workflows:

  • Root script: Released react-on-rails gem + react-on-rails NPM + react-on-rails-pro NPM
  • Pro script: Released react_on_rails_pro gem + node-renderer NPM

This created maintenance overhead and potential for version mismatches.

Solution: Synchronized Versioning

All packages now share the same version number, managed by a single release script at the root.

Packages unified:

  • react-on-rails (gem + NPM)
  • react-on-rails-pro (gem + NPM)
  • node-renderer (NPM)

Version files updated atomically (6 locations):

  1. /lib/react_on_rails/version.rb
  2. /packages/react-on-rails/package.json
  3. /packages/react-on-rails-pro/package.json
  4. /react_on_rails_pro/lib/react_on_rails_pro/version.rb
  5. /react_on_rails_pro/package.json (node-renderer)
  6. /package.json (root workspace)

Key Features

  • Automatic Ruby version detection and switching - Detects required Ruby version from gemspecs and switches automatically
  • Semver bump support - Use patch, minor, major or specify exact version
  • Exact version dependencies - Updates all dependency declarations with exact versions (no ^ or ~)
  • Dry run mode - Test all changes without actually publishing
  • Verdaccio support - Test NPM packages locally before publishing
  • Skip push option - Test git operations without pushing
  • Uncommitted changes validation - Prevents accidental releases with uncommitted work
  • Atomic updates - All version files updated together

Usage Examples

# Semver bump
rake release[patch]                           # Bump patch version
rake release[minor]                           # Bump minor version
rake release[major]                           # Bump major version

# Explicit version
rake release[17.0.0]                          # Set to specific version

# With options
rake release[patch,true]                      # Dry run
rake release[17.0.0,false,verdaccio]          # Test with Verdaccio
rake release[17.0.0,false,npm,skip_push]      # Skip git push

Files Removed (Deprecated)

  • react_on_rails_pro/rakelib/release.rake - Replaced by unified script
  • react_on_rails_pro/script/release - No longer needed
  • react_on_rails_pro/package.json - Not needed at Pro root

Documentation Updated

  • docs/contributor-info/releasing.md - Complete rewrite with new process
  • react_on_rails_pro/CONTRIBUTING.md - Updated to reference root script
  • react_on_rails_pro/docs/contributors-info/releasing.md - Updated release docs

Part 2: Strict Version Validation

Problem

Users could misconfigure package.json (wrong versions, conflicting packages, semver wildcards) and only discover issues at runtime. Warnings were easily missed in logs.

Solution: Fail-Fast Validation

Application now validates configuration at boot and raises clear errors for misconfigurations.

Implementation

  • New validation method: validate_version_and_package_compatibility!
  • Integrated into Rails engine: Runs on config.after_initialize
  • Four validation checks:
    1. package.json exists - Ensures the file is present
    2. Package compatibility - Detects conflicting installations (both base and Pro)
    3. Exact versions - Enforces exact version matching (no ^, ~, >, <, *)
    4. Version match - Ensures gem and npm package versions match exactly

Validation Scenarios

1. Missing package.json

**ERROR** ReactOnRails: package.json file not found.
Expected location: /path/to/package.json

2. Both packages installed

**ERROR** ReactOnRails: Both 'react-on-rails' and 'react-on-rails-pro' packages are installed.
Fix: Remove 'react-on-rails' - the Pro package includes all base functionality.

3. Pro gem with base package

**ERROR** ReactOnRails: You have the Pro gem installed but are using the base 'react-on-rails' package.
Fix: Install react-on-rails-pro package instead.

4. Pro package without Pro gem

**ERROR** ReactOnRails: You have the 'react-on-rails-pro' package installed but the Pro gem is not installed.
Fix: Install the Pro gem or use the base package.

5. Non-exact version (semver wildcard)

**ERROR** ReactOnRails: The 'react-on-rails' package version is not an exact version.
Detected: ^16.1.1
Do not use ^, ~, >, <, *, or other semver ranges.

6. Version mismatch

**ERROR** ReactOnRails: The 'react-on-rails' package version does not match the gem version.
Package: 16.1.2
Gem: 16.1.1

Code Quality Improvements

  • Applied DRY principle: Created shared package_installed?(package_name) helper
  • Added package detection methods: react_on_rails_package?, react_on_rails_pro_package?
  • Removed deprecated log_if_gem_and_node_package_versions_differ method
  • Clear, actionable error messages with package.json location

Test Coverage

  • Added 9 new test cases for validation scenarios
  • Created 3 new fixture files for Pro package testing
  • Total: 53 tests, all passing
  • Zero RuboCop offenses

Part 3: Node Renderer Gem Version Validation

Problem

When using React on Rails Pro with a remote node renderer, version mismatches between the Ruby gem (react_on_rails_pro) and the node renderer package (@shakacode-tools/react-on-rails-pro-node-renderer) could cause subtle runtime issues. These mismatches were difficult to diagnose.

Solution: Runtime Version Validation

The node renderer now validates that the gem version matches the node renderer package version on every request, with environment-aware behavior.

Implementation

Ruby Side Changes (lib/react_on_rails_pro/utils.rb):

  • Added railsEnv to common_form_data method
  • Rails environment is now sent with every request to the node renderer

Node Renderer Changes (packages/node-renderer/src/worker/checkProtocolVersionHandler.ts):

  • Added gem version validation alongside existing protocol version checks
  • Created normalizeVersion() function to handle version format differences
  • Environment-aware error handling

Version Normalization

The normalizeVersion() function handles these edge cases:

Ruby Gem Format NPM Format Result
4.0.0.rc.1 4.0.0-rc.1 ✅ Match
4.0.0-RC.1 4.0.0-rc.1 ✅ Match (case-insensitive)
4.0.0 4.0.0 ✅ Match (whitespace trimmed)
4.0.0.alpha.1 4.0.0-alpha.1 ✅ Match
4.0.0.beta.2 4.0.0-beta.2 ✅ Match

Behavior

Development Environment:

Status: 412 Precondition Failed

Version mismatch error: React on Rails Pro gem version (4.0.0) does not
match node renderer version (4.0.1). Using exact matching versions is
recommended for best compatibility.

Gem version: 4.0.0
Node renderer version: 4.0.1

Update either the gem or the node renderer package to match versions.

Production Environment:

Status: 200 OK (request allowed)

[WARN] React on Rails Pro gem version (4.0.0) does not match node renderer
version (4.0.1). Using exact matching versions is recommended for best
compatibility.

Environment Detection

The node renderer determines if it's in production using either:

  • railsEnv === 'production' (sent from Ruby)
  • NODE_ENV === 'production' (node environment variable)

If either is production, the permissive behavior applies.

Test Coverage

Updated Tests (packages/node-renderer/tests/worker.test.ts):

  • Updated 11 existing tests to include railsEnv field in requests

New Tests Added (6 comprehensive tests):

  1. ✅ Allows request when gem version matches package version
  2. ✅ Rejects request in development when versions don't match (412 error)
  3. ✅ Allows request in production when versions don't match (with warning)
  4. ✅ Normalizes gem version with dot before prerelease (4.0.0.rc.1 == 4.0.0-rc.1)
  5. ✅ Normalizes gem version case-insensitively (4.0.0-RC.1 == 4.0.0-rc.1)
  6. ✅ Handles whitespace in gem version

Test Results:

  • Test Suites: 1 passed, 1 total
  • Tests: 17 passed, 17 total (11 updated + 6 new)
  • Time: 2.364 s

Files Changed

Release Script Changes

  • rakelib/release.rake - Unified release logic for all packages
  • rakelib/task_helpers.rb - Enhanced with Ruby version detection
  • docs/contributor-info/releasing.md - Complete documentation rewrite
  • react_on_rails_pro/CONTRIBUTING.md - Updated contributor guide
  • react_on_rails_pro/docs/contributors-info/releasing.md - Updated release docs
  • react_on_rails_pro/react_on_rails_pro.gemspec - Dependency updates

Version Validation Changes

  • lib/react_on_rails/engine.rb - Added validation initializer
  • lib/react_on_rails/version_checker.rb - Core validation logic (205 lines changed)
  • spec/react_on_rails/version_checker_spec.rb - Comprehensive test coverage (327 lines changed)
  • spec/react_on_rails/fixtures/ - New test fixtures (3 files)

Node Renderer Validation Changes

  • lib/react_on_rails_pro/utils.rb - Added railsEnv to request data
  • packages/node-renderer/src/worker/checkProtocolVersionHandler.ts - Added gem version validation with normalization
  • packages/node-renderer/tests/worker.test.ts - Updated existing tests + 6 new tests

Removed Files

  • react_on_rails_pro/rakelib/release.rake - Deprecated
  • react_on_rails_pro/script/release - No longer needed
  • react_on_rails_pro/package.json - Not needed at Pro root

Benefits

Unified Release Script

  • One command to release everything - No more switching between scripts
  • Prevents version mismatches - All packages updated atomically
  • Automatic Ruby version switching - No manual version management
  • Clearer mental model - One version number to track
  • Consistent dependency pinning - Exact versions everywhere
  • Reduced maintenance overhead - Single script to maintain

Strict Version Validation

  • Fail-fast - Errors caught at boot time, not runtime
  • Clear error messages - Users know exactly what's wrong and how to fix it
  • Prevents misconfigurations - No more version mismatches or conflicting packages
  • Better developer experience - Immediate feedback on configuration issues
  • Complements release script - Ensures versions installed match what was released

Node Renderer Validation

  • Runtime version enforcement - Catches version mismatches at request time
  • Environment-aware - Strict in development, permissive in production
  • Handles edge cases - Normalizes Ruby gem vs NPM version format differences
  • Clear error messages - Users know exactly which versions are mismatched
  • Complements boot-time validation - Provides additional layer of safety for remote renderers
  • Better debugging - Version mismatches identified immediately with clear warnings

Testing

Release Script Testing

# Dry run test
rake release[17.0.0,true]

# Verdaccio test
rake release[17.0.0,false,verdaccio]

# Test without pushing
rake release[17.0.0,false,npm,skip_push]

Validation Testing

# Run validation tests
bundle exec rspec spec/react_on_rails/version_checker_spec.rb

# Check linting
bundle exec rubocop lib/react_on_rails/version_checker.rb spec/react_on_rails/version_checker_spec.rb

Node Renderer Testing

# Run node renderer tests
cd react_on_rails_pro
yarn jest packages/node-renderer/tests/worker.test.ts

# Run all tests
yarn nps test

Migration Notes

For Contributors

  • Use the root rake release command instead of Pro-specific scripts
  • The script will automatically detect and switch Ruby versions as needed
  • All version files are now updated atomically

For Users

  • Applications will now fail to boot with clear error messages if package.json is misconfigured
  • Ensure exact versions in package.json (no ^, ~, etc.)
  • If using Pro, ensure both Pro gem and Pro npm package are installed
  • For remote node renderer users: Version mismatches will be caught at request time
    • Development: Requests will fail with 412 error
    • Production: Requests will succeed with warning logs

Fixes #1876

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com


This change is Reviewable

Summary by CodeRabbit

  • New Features

    • Stronger startup/version compatibility checks with clearer, actionable errors and environment-aware leniency for renderer/gem mismatches.
    • Package-manager detection with safe, manager-specific install/remove commands.
    • Remote node-renderer validates gem version per-request (strict in dev, permissive in prod) with normalization and caching.
  • Chores

    • Unified single-version release across packages with multi-registry publishing and local Verdaccio testing support; removed old per-package release scripts.
  • Documentation

    • Consolidated and rewritten release and contributor guidance.
  • Tests

    • New fixtures and expanded tests covering version checks, package-manager detection, and env-aware validation.

AbanoubGhadban and others added 16 commits October 26, 2025 14:02
- Unified release task for all packages (core + pro) with synchronized versioning
- Supports semver bump types (patch/minor/major) and explicit versions
- Publishes all packages: react-on-rails, react-on-rails-pro NPM packages,
  react_on_rails gem, react_on_rails_pro gem, and node-renderer package
- Verdaccio mode now publishes all NPM packages for complete local testing
- Removed release-it dependency in favor of direct yarn publish
- react_on_rails_pro.gemspec now dynamically references ReactOnRails::VERSION
- Uses gem bump for both core and pro version updates (no manual file writing)
- Deprecated separate pro release script, redirects to unified task
- Preserves dry-run, verdaccio testing, and skip-push options

Examples:
  rake release[patch]                    # Bump patch version
  rake release[16.2.0]                   # Set explicit version
  rake release[patch,true]               # Dry run
  rake release[16.2.0,false,verdaccio]   # Test with Verdaccio

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

Co-Authored-By: Claude <noreply@anthropic.com>
- Detects required Ruby version using 'bundle platform --ruby'
- Automatically switches to required version before bundle install
- Uses Open3.capture3 for safe command execution with proper error handling
- Supports pre-release versions (rc, preview, dev, alpha, beta)
- Requires exact version match (as enforced by Bundler)
- Supports rvm, rbenv, and asdf version managers
- Configurable via RUBY_VERSION_MANAGER environment variable
- Falls back gracefully if version detection fails

This prevents bundle install failures due to Ruby version mismatches
during the release process, especially when releasing pro packages
that require different Ruby versions than the system default.

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

Co-Authored-By: Claude <noreply@anthropic.com>
- Changed approach: run version switch and bundle install in single command
- RVM: Use 'rvm VERSION do bundle install' to run in version context
- rbenv: Use RBENV_VERSION environment variable
- asdf: Chain commands with && to maintain shell context
- Removed separate switch_ruby_version method (doesn't persist across subshells)
- Simplified bundle_install_in to detect version and call appropriate method

This ensures Ruby version switching actually takes effect for bundle install.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This reverts commit f55c485.
- Implement bundle_install_in() to detect and auto-switch Ruby versions
- Use 'bundle platform --ruby' to detect required Ruby version from Gemfile
- Support rvm, rbenv, and asdf version managers (via RUBY_VERSION_MANAGER env var)
- Run detection in unbundled environment to avoid conflicts
- Automatically switch Ruby version when needed during bundle install
- Skip version switching when no Ruby version is specified in Gemfile
- Handle pre-release versions (rc, alpha, beta)

This enables the release script to work across directories with different
Ruby version requirements (e.g., pro dummy app requires Ruby 3.3.7).
Use word boundary (\b) in regex to ensure we only match VERSION constant,
not PROTOCOL_VERSION. This prevents accidentally updating the protocol
version when bumping the package version.
…se files

Documentation updates:
- Update root releasing.md with all 5 packages and 4 parameters
- Add comprehensive requirements section (NPM, RubyGems, GitHub Packages auth)
- Add Ruby version management documentation
- Add Testing with Verdaccio section
- Update version synchronization to include all files
- Clarify public vs private package publishing

- Update pro CONTRIBUTING.md to reference root release task
- Replace pro releasing.md with redirect to root documentation
- Delete deprecated pro rakelib/release.rake
- Delete deprecated pro script/release

All release documentation now correctly reflects unified versioning
and the single release script at repository root.
When using skip_push parameter, we should also skip git pull since
the branch might not have upstream tracking (e.g., during testing).
This reverts commit 18ff99c.
This commit implements fail-fast validation for package.json configuration
to prevent runtime errors and ensure proper package/gem compatibility.

Changes:
- Add new validation method that raises errors instead of logging warnings
- Validate package.json file exists before checking versions
- Detect conflicting package installations (both base and Pro)
- Enforce exact version matching (no semver wildcards)
- Validate Pro gem/package compatibility
- Remove deprecated log_if_gem_and_node_package_versions_differ method

Implementation details:
- Created validate_version_and_package_compatibility! method with 4 validations:
  1. validate_package_json_exists! - Ensures package.json is present
  2. validate_package_gem_compatibility! - Checks Pro/base package conflicts
  3. validate_exact_version! - Enforces exact versions (no ^, ~, >, <, *)
  4. validate_version_match! - Ensures gem and package versions match
- Added package detection methods (react_on_rails_package?, react_on_rails_pro_package?)
- Applied DRY principle with shared package_installed? helper
- Integrated validation into Rails engine initializer (config.after_initialize)
- Added comprehensive test coverage (53 tests, all passing)
- Included helpful error messages with package.json location

Fixes #1876

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

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

coderabbitai bot commented Oct 26, 2025

Walkthrough

Unifies release into a single, unified-version rake task for five packages; enforces strict boot-time gem↔npm compatibility via a new validator that raises on misconfiguration; adds package-manager-aware install/remove helpers and Ruby-version-aware bundle install; removes pro-specific release automation; updates docs, fixtures, and tests.

Changes

Cohort / File(s) Summary
Release Docs
docs/contributor-info/releasing.md, react_on_rails_pro/CONTRIBUTING.md, react_on_rails_pro/docs/contributors-info/releasing.md
Rewrote release documentation to describe a unified 5-package release, new rake signature rake release[version,dry_run,registry,skip_push], multi-registry publishing (npmjs.org / GitHub Packages / Verdaccio), auth/token steps, and expanded testing/troubleshooting guidance.
Unified Release Task & Removal
Root: rakelib/release.rake
Removed pro release: react_on_rails_pro/rakelib/release.rake (deleted), react_on_rails_pro/script/release (deleted), react_on_rails_pro/package.json
Replaced per-repo release with a root unified release task (arg renamed to version), unified version propagation across gems and npm packages, semver-bump or explicit-version handling, dry-run/skip_push/Verdaccio modes; removed pro-specific release automation and release-it config; updated messaging and artifact lists.
Version Validation & Engine Init
lib/react_on_rails/version_checker.rb, lib/react_on_rails/engine.rb, spec/react_on_rails/version_checker_spec.rb, spec/react_on_rails/fixtures/*
Replaced soft warnings with validate_version_and_package_compatibility! (raises on errors); added checks for package.json presence, package/gem compatibility, semver wildcard rejection, and exact-version equality; moved invocation to config.after_initialize; added fixtures and expanded tests.
Task Helpers & Ruby Switching
rakelib/task_helpers.rb
Added detect_bundler_ruby_version and bundle_install_with_ruby_version to detect required Ruby from Bundler and run bundle install via rvm/rbenv/asdf when needed; added require "English" and adjusted method visibility.
Pro Gem & Versioning
react_on_rails_pro/react_on_rails_pro.gemspec, react_on_rails_pro/lib/react_on_rails_pro/version.rb
Bumped Pro VERSION to 16.1.1; pro gem runtime dependency on react_on_rails now references ReactOnRails::VERSION dynamically (removed hard-coded ">= 16.0.0").
Node Renderer Runtime Check
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts, react_on_rails_pro/packages/node-renderer/tests/worker.test.ts
Added gemVersion and railsEnv payload handling, prerelease normalization, comparison caching, and environment-aware behavior (development: reject mismatch; production: warn once); tests include railsEnv.
Package Manager Detection & Commands
lib/react_on_rails/utils.rb, spec/react_on_rails/utils_spec.rb
Added package-manager detection (:yarn/:pnpm/:bun/:npm) via package.json or lockfiles and helper methods to generate install/remove commands with validation and Shellwords escaping; added tests covering detection and command generation.
Test Fixtures
spec/react_on_rails/fixtures/both_packages.json, spec/react_on_rails/fixtures/pro_package.json, spec/react_on_rails/fixtures/pro_semver_caret_package.json
New fixtures modeling react-on-rails / react-on-rails-pro combinations (exact and caret semver) used by VersionChecker tests.
Misc — Pro utils
react_on_rails_pro/lib/react_on_rails_pro/utils.rb
Added railsEnv to common_form_data payload.
Changelogs
CHANGELOG.md, react_on_rails_pro/CHANGELOG.md
Documented breaking changes (strict boot-time validation, node renderer validation) and security note for command-injection protections.

Sequence Diagram(s)

sequenceDiagram
    participant Rails as Rails Init
    participant Engine as ReactOnRails::Engine
    participant VC as VersionChecker
    participant App as Application Boot

    Rails->>Engine: config.after_initialize
    Engine->>VC: VersionChecker.build.validate_version_and_package_compatibility!
    rect #F3F7FF
      VC->>VC: validate_package_json_exists!
      alt package.json missing
        VC-->>App: raise ReactOnRails::Error (install guidance)
      else
        VC->>VC: validate_package_gem_compatibility!
        alt conflict detected
          VC-->>App: raise ReactOnRails::Error (conflict remediation)
        else
          VC->>VC: validate_exact_version!
          alt semver wildcard found
            VC-->>App: raise ReactOnRails::Error (use exact version)
          else
            VC->>VC: validate_version_match!
            alt gem/npm mismatch
              VC-->>App: raise ReactOnRails::Error (sync versions)
            else
              VC-->>Engine: validation passed
            end
          end
        end
      end
    end
Loading
sequenceDiagram
    participant Dev as Developer
    participant Rake as Rake Release Task
    participant Helper as TaskHelpers
    participant RubyMgr as Ruby Manager
    participant Registry as NPM/GH/Gem Registries

    Dev->>Rake: rake release[version,dry_run,registry,skip_push]
    Rake->>Helper: bundle_install_in(dir)
    Helper->>Helper: detect_bundler_ruby_version(dir)
    alt ruby mismatch
      Helper->>RubyMgr: bundle_install_with_ruby_version(...)
    else
      Helper->>Rake: run bundle install normally
    end
    Rake->>Rake: update unified version across gems & npm packages
    Rake->>Registry: publish npm packages (npmjs / GH / Verdaccio)
    alt publishing public
      Rake->>Registry: publish RubyGems
    end
    Rake-->>Dev: release summary / dry-run output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas needing extra attention:
    • lib/react_on_rails/version_checker.rb — error messaging, semver detection, boot-time raising behavior and test coverage.
    • rakelib/release.rake — unified version propagation, registry branching, dry-run and skip_push semantics, and side-effect ordering.
    • rakelib/task_helpers.rb — external command construction, Ruby manager support, and failure handling.
    • lib/react_on_rails/utils.rb — package manager detection heuristics and shell-escaped command generation.
    • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts — normalization/caching and environment gating.

Possibly related PRs

Suggested reviewers

  • justin808
  • Judahmeek
  • ihabadham

Poem

🐇 I hopped through diffs with a tiny drum,

One version to rule them — all packages come.
Validators guard the boot-time gate,
Ruby swaps smooth, installs cooperate.
A carrot for releases — unified and fun.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Unify release scripts and add strict version validation" directly and accurately summarizes the two primary changes in this PR. The changeset implements a unified release script that consolidates separate root and Pro release workflows into a single rake task (rakelib/release.rake rewritten, Pro-specific tasks removed), and adds strict boot-time version validation through the new validate_version_and_package_compatibility! method. The title is clear, specific, and captures the main developer intent without vagueness or noise.
Linked Issues Check ✅ Passed The PR satisfies all primary coding requirements from issue #1876. The unified versioning strategy is implemented with all packages synchronized to version 16.1.1 across six version files (engine, core/pro gems, core/pro NPM packages). The single root rake task (rakelib/release.rake) atomically updates all version locations, supports exact dependency pinning, semver bump types (patch/minor/major), and preserves dry-run, Verdaccio, skip-push, and uncommitted-changes validation. Strict version validation is enforced via boot-time checks including package.json existence, package conflict detection, prohibition of semver ranges, and exact gem-npm version matching. Node renderer gem-version validation is implemented with environment-aware behavior (412 in development, warning in production) and railsEnv propagation.
Out of Scope Changes Check ✅ Passed All changes directly support the stated PR objectives. The unified release implementation includes rakelib/release.rake rewrite, rakelib/task_helpers.rb enhancements for Ruby version detection, removal of Pro-specific release files, and lib/react_on_rails/utils.rb utilities for package manager command generation. Strict validation is implemented via lib/react_on_rails/engine.rb initializer and lib/react_on_rails/version_checker.rb with comprehensive test coverage and fixtures. Node renderer validation is added to checkProtocolVersionHandler.ts with supporting utils.rb changes and tests. Documentation, version bumps, gemspec updates, and changelog entries are all consistent with the unified versioning and validation objectives. No extraneous changes detected.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch strict-version-validation

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d16d32e and 85cbb19.

📒 Files selected for processing (2)
  • CHANGELOG.md (2 hunks)
  • react_on_rails_pro/CHANGELOG.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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:

  • CHANGELOG.md
  • react_on_rails_pro/CHANGELOG.md
🧠 Learnings (2)
📚 Learning: 2025-10-23T17:22:01.064Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • react_on_rails_pro/CHANGELOG.md
⏰ 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). (12)
  • 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-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: markdown-link-check
  • GitHub Check: build
  • GitHub Check: lint-js-and-ruby
🔇 Additional comments (4)
react_on_rails_pro/CHANGELOG.md (1)

33-33: Well-documented feature addition.

Line 33 clearly describes the node renderer gem version validation feature with environment-aware behavior, version normalization details, and proper attribution. The entry follows keepachangelog format and provides context for both development and production deployments.

CHANGELOG.md (3)

30-30: Clear and concise improvement entry.

The "Improved Error Messages" entry succinctly describes the enhancement with package-manager-specific commands, includes the PR reference, and maintains consistency with existing Added entries.


111-114: Comprehensive breaking changes documentation with migration guidance.

Both breaking change entries are thorough and actionable:

  • Line 111 clearly states the boot-time failure behavior, explains the exact-version requirement, and provides a concrete migration example.
  • Line 113 documents Pro-specific node renderer validation with environment-aware behavior (412 in development, warning in production) and clear migration step.

Both entries include appropriate PR references and align with the PR objectives for version validation enforcement.


115-117: Security section properly introduced and documented.

The new "Security" section is well-placed in the Breaking Changes area and the "Command Injection Protection" entry clearly describes the hardening without unnecessary detail. The entry includes the PR reference and is consistent with the security improvements described in the PR objectives.


Comment @coderabbitai help to get the list of available commands and usage tips.

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: 4

🧹 Nitpick comments (2)
rakelib/task_helpers.rb (1)

97-101: Optional: tolerate Ruby patchlevel suffixes

Bundler may output ruby 3.2.1p20. Current regex captures 3.2.1, which works for equality against RUBY_VERSION; if you want to preserve prerelease and ignore optional p\d+, consider:

-      match = output.strip.match(/ruby\s+([\d.]+(?:-[a-zA-Z0-9.]+)?)/)
+      match = output.strip.match(/ruby\s+([\d.]+)(?:p\d+)?(?:-([a-zA-Z0-9.]+))?/)
+      version = match && [match[1], match[2]].compact.join("-")
+      version
docs/contributor-info/releasing.md (1)

3-211: Comprehensive documentation of the unified release process.

The documentation clearly explains the 5-package unified release workflow, including:

  • Command usage with practical examples
  • Public vs private package distinctions
  • Authentication requirements for multiple registries
  • Verdaccio testing workflow

This aligns well with the PR objectives of simplifying the release process.

As per coding guidelines, consider adding language specifiers to the fenced code blocks at lines 153 and 169 to address the markdownlint warnings:

For line 153 (.npmrc content):

-  ```
+  ```ini
   //npm.pkg.github.com/:_authToken=<TOKEN>

For line 169 (.gem/credentials content):

-  ```
+  ```yaml
   :github: Bearer <GITHUB_TOKEN>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d527ddf and bf5681a.

⛔ Files ignored due to path filters (2)
  • react_on_rails_pro/spec/dummy/Gemfile.lock is excluded by !**/*.lock
  • react_on_rails_pro/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • docs/contributor-info/releasing.md (3 hunks)
  • lib/react_on_rails/engine.rb (1 hunks)
  • lib/react_on_rails/version_checker.rb (4 hunks)
  • rakelib/release.rake (9 hunks)
  • rakelib/task_helpers.rb (2 hunks)
  • react_on_rails_pro/CONTRIBUTING.md (1 hunks)
  • react_on_rails_pro/docs/contributors-info/releasing.md (1 hunks)
  • react_on_rails_pro/package.json (0 hunks)
  • react_on_rails_pro/rakelib/release.rake (0 hunks)
  • react_on_rails_pro/react_on_rails_pro.gemspec (2 hunks)
  • react_on_rails_pro/script/release (0 hunks)
  • spec/react_on_rails/fixtures/both_packages.json (1 hunks)
  • spec/react_on_rails/fixtures/pro_package.json (1 hunks)
  • spec/react_on_rails/fixtures/pro_semver_caret_package.json (1 hunks)
  • spec/react_on_rails/version_checker_spec.rb (4 hunks)
💤 Files with no reviewable changes (3)
  • react_on_rails_pro/package.json
  • react_on_rails_pro/script/release
  • react_on_rails_pro/rakelib/release.rake
🧰 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:

  • lib/react_on_rails/engine.rb
  • rakelib/release.rake
  • rakelib/task_helpers.rb
  • react_on_rails_pro/react_on_rails_pro.gemspec
  • spec/react_on_rails/version_checker_spec.rb
  • lib/react_on_rails/version_checker.rb
**/*.{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:

  • spec/react_on_rails/fixtures/both_packages.json
  • spec/react_on_rails/fixtures/pro_package.json
  • docs/contributor-info/releasing.md
  • react_on_rails_pro/CONTRIBUTING.md
  • react_on_rails_pro/docs/contributors-info/releasing.md
  • spec/react_on_rails/fixtures/pro_semver_caret_package.json
🧠 Learnings (3)
📚 Learning: 2025-10-23T17:22:01.064Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/engine.rb
  • react_on_rails_pro/react_on_rails_pro.gemspec
  • spec/react_on_rails/version_checker_spec.rb
  • lib/react_on_rails/version_checker.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • spec/react_on_rails/fixtures/pro_package.json
  • react_on_rails_pro/react_on_rails_pro.gemspec
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • react_on_rails_pro/react_on_rails_pro.gemspec
🧬 Code graph analysis (3)
lib/react_on_rails/engine.rb (1)
lib/react_on_rails/version_checker.rb (3)
  • build (12-14)
  • build (173-175)
  • validate_version_and_package_compatibility! (28-33)
spec/react_on_rails/version_checker_spec.rb (2)
spec/react_on_rails/support/version_test_helpers.rb (1)
  • stub_gem_version (3-5)
lib/react_on_rails/version_checker.rb (8)
  • validate_version_and_package_compatibility! (28-33)
  • build (12-14)
  • build (173-175)
  • raw (185-207)
  • react_on_rails_package? (209-211)
  • react_on_rails_pro_package? (213-215)
  • package_name (217-221)
  • semver_wildcard? (223-228)
lib/react_on_rails/version_checker.rb (1)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (215-223)
🪛 GitHub Actions: Lint JS and Ruby
rakelib/task_helpers.rb

[error] 91-91: RuboCop: Style/NumericPredicate - Use exit_status.zero? instead of exit_status == 0. (autocorrectable). Command failed: bundle exec rubocop

🪛 markdownlint-cli2 (0.18.1)
docs/contributor-info/releasing.md

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

(MD040, fenced-code-language)


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

(MD040, fenced-code-language)

react_on_rails_pro/CONTRIBUTING.md

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

(MD040, fenced-code-language)


309-309: 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: build-dummy-app-webpack-test-bundles
  • GitHub Check: claude-review
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: markdown-link-check
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22)
  • GitHub Check: lint-js-and-ruby
🔇 Additional comments (18)
react_on_rails_pro/react_on_rails_pro.gemspec (1)

41-41: Implement exact version pinning for unified versioning.

Replacing the hardcoded version constraint with ReactOnRails::VERSION enforces an exact version match between the core package and Pro gem, eliminating version mismatch risks. This is a key requirement for the unified release workflow described in the PR objectives.

spec/react_on_rails/fixtures/pro_semver_caret_package.json (1)

1-10: Fixture correctly exercises caret-range violation

Looks good for asserting the “no semver ranges” rule against react-on-rails-pro.

Please confirm VersionChecker only enforces exact versions for our packages (react-on-rails, react-on-rails-pro) and not all deps/devDeps. If broader, add/adjust tests accordingly.

spec/react_on_rails/fixtures/pro_package.json (1)

1-10: Happy-path fixture LGTM

Exact pin for react-on-rails-pro with permissive versions elsewhere makes sense for pass-case.

react_on_rails_pro/CONTRIBUTING.md (1)

316-328: Release commands align with rake signature

Commands match task args: version, dry_run, registry, skip_push. No action needed.

rakelib/release.rake (4)

131-153: Version propagation to all package.json files looks correct

Root, workspace packages, and pro node-renderer updated; pro depends on core with exact version. Good.


198-208: Workspace publish flow LGTM

Publishing core and pro via Yarn workspaces with --new-version aligns with unified versioning.


82-90: Review comment is based on incorrect assumptions about the repository structure.

The verification output shows only 2 gemspec files exist (root and react_on_rails_pro directory). The find commands with -mindepth 2 and -mindepth 3 are correctly scoped—they don't match either existing file and won't delete anything. The code is working as intended; there is no safety risk.

Likely an incorrect or invalid review comment.


253-277: Review suggestion assumes unified versioning, but codebase uses split pro versioning

The suggested verification script would fail in the current state. The pro package maintains independent versions (4.0.0) while main uses 16.1.1, with partial misalignment between pro Ruby gem and NPM packages.

Before implementing the sync check suggested, clarify whether:

  • Pro should track main versions (unify to 16.1.1), or
  • Pro maintains intentional independent versioning (current state needs alignment fixes within pro track itself)

The current dry-run message lists pro version.rb as updateable but uses single version variables, suggesting potential release logic gaps for dual-versioning scenarios.

lib/react_on_rails/version_checker.rb (5)

20-33: LGTM! Well-structured validation orchestration.

The new validation flow clearly separates concerns into four distinct checks, each with targeted error messages. The fail-fast approach aligns with the project's goal of early error detection.


37-55: Clear and actionable error message for missing package.json.

The error message provides excellent guidance with expected location, explanation, and fix commands. Using gem_version in the fix ensures version consistency.


57-113: Comprehensive package/gem compatibility validation with clear remediation steps.

The three misconfig scenarios (both packages, pro gem + base package, pro package without gem) are handled with detailed, actionable error messages. The use of package_json_location helper maintains DRY principles.


185-207: LGTM! Pro package correctly takes precedence.

The precedence logic (checking react-on-rails-pro before react-on-rails) aligns with the unified versioning strategy and prevents ambiguity when both packages are present.


209-221: Clean public API for package detection.

The new helper methods (react_on_rails_package?, react_on_rails_pro_package?, package_name) provide a clear interface for determining which package is installed, improving readability throughout the validation logic.

lib/react_on_rails/engine.rb (1)

7-15: LGTM! Well-placed fail-fast validation.

The after_initialize timing ensures Rails is fully loaded before validation runs, while still failing fast enough to prevent runtime issues. The info-level logging provides good visibility without being verbose.

spec/react_on_rails/fixtures/both_packages.json (1)

1-11: LGTM! Fixture correctly simulates conflicting package installation.

This fixture appropriately tests the scenario where both react-on-rails and react-on-rails-pro are installed, which should trigger the validation error in validate_package_gem_compatibility!.

react_on_rails_pro/docs/contributors-info/releasing.md (1)

1-40: LGTM! Clear redirection to unified release documentation.

The documentation appropriately redirects users to the centralized release process while providing quick-reference commands. The prominent outdated notice prevents confusion.

spec/react_on_rails/version_checker_spec.rb (2)

16-207: LGTM! Comprehensive test coverage for validation scenarios.

The test suite thoroughly covers all validation paths:

  • Package/gem compatibility checks (both packages, mismatches)
  • Version validation (exact, wildcard, local paths)
  • Missing package.json handling
  • Pro gem and package combinations

All tests use proper mocking and clear expectations.


394-496: Excellent coverage of package detection logic.

The Pro package detection tests verify:

  • Base-only, Pro-only, and both-packages scenarios
  • Precedence rules (Pro takes priority)
  • Semver wildcard detection
  • Default behavior when no packages found

AbanoubGhadban and others added 2 commits October 26, 2025 19:55
Adds validation to ensure the React on Rails Pro gem version matches
the node renderer package version for best compatibility.

**Changes:**

- **Ruby side** (`lib/react_on_rails_pro/utils.rb`):
  - Add `railsEnv` to `common_form_data` method to send Rails environment
    to the node renderer

- **Node renderer** (`packages/node-renderer/src/worker/checkProtocolVersionHandler.ts`):
  - Add gem version validation logic with environment-aware behavior
  - Create `normalizeVersion()` function to handle version format differences:
    - Ruby gem format: `4.0.0.rc.1` → NPM format: `4.0.0-rc.1`
    - Case-insensitive comparison (e.g., `4.0.0-RC.1` == `4.0.0-rc.1`)
    - Whitespace trimming
  - **Development**: Throw 412 error when versions don't match
  - **Production**: Log warning but allow request to proceed

- **Tests** (`packages/node-renderer/tests/worker.test.ts`):
  - Update 11 existing tests to include `railsEnv` field
  - Add 6 new comprehensive tests for gem version validation:
    - Matching versions test
    - Development environment mismatch rejection
    - Production environment mismatch allowance
    - Version normalization tests (dot format, case-insensitive, whitespace)

**Edge cases handled:**
- ✅ Prerelease format differences (4.0.0.rc.1 vs 4.0.0-rc.1)
- ✅ Case sensitivity (4.0.0-RC.1 vs 4.0.0-rc.1)
- ✅ Whitespace trimming

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

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

claude bot commented Oct 26, 2025

Pull Request Review: Unify release scripts and add strict version validation

Summary

This is an excellent PR that significantly improves the React on Rails release process and runtime validation. The changes are well-structured, thoroughly tested, and address real pain points in version management.

🎯 Strengths

1. Unified Release Process ✅

  • Clear improvement: Consolidating two separate release scripts into one atomic process eliminates version drift
  • Smart implementation: Handles 5 packages with unified versioning
  • Developer-friendly: Semver bump support (patch/minor/major)
  • Well-documented: Extensive inline help and examples

2. Strict Version Validation ✅

  • Fail-fast approach: Catches misconfigurations at boot time
  • Excellent error messages: Clear, actionable messages with exact fix instructions
  • Comprehensive scenarios: 6 distinct validation cases with specific guidance
  • Good separation of concerns: Each validation in its own method

3. Test Coverage ✅

  • Thorough testing: 53 tests covering all validation scenarios
  • Good use of fixtures: New fixture files for Pro package testing
  • Proper mocking: Well-structured test doubles
  • Edge cases covered: Both packages, Pro-only, base-only, version mismatches

4. Code Quality ✅

  • DRY principle: Shared package_installed? helper
  • Clear method naming: react_on_rails_package?, react_on_rails_pro_package?
  • Good memoization: Prevents repeated file reads
  • Zero RuboCop offenses: Follows project style

🔍 Code Quality Observations

Version Checker (lib/react_on_rails/version_checker.rb)

Excellent:

  • Well-separated validation methods
  • Error messages include context
  • Pro package detection with precedence logic

Minor suggestions:

  1. Early return pattern (line 95): Double-negative logic could be clearer
  2. Edge case: Consider error handling for malformed JSON (line 262)

Release Script (rakelib/release.rake)

Excellent:

  • Clear progression: validate → bump → update → publish
  • Dry run and Verdaccio support
  • Atomic version updates across 6 locations

Observations:

  1. Consider documenting recovery steps for partial publish failures
  2. Pro version regex with word boundary is exactly right

🔒 Security ✅

  • Credentials handled correctly
  • Uncommitted changes check prevents accidents
  • Registry validation prevents typos

⚡ Performance ✅

  • File I/O properly memoized
  • Boot-time impact minimal (one-time validation)

🐛 Potential Issues

Critical: None ✅

Minor:

  1. JSON parsing error handling could be added
  2. Package precedence logic flow could be documented better

📚 Documentation Quality ✅

Outstanding:

  • Comprehensive PR description
  • Release docs completely rewritten
  • Clear inline comments
  • Error messages with troubleshooting

🧪 Testing Recommendations

Consider adding:

  1. Integration test in Rails boot context
  2. Malformed JSON test
  3. File permission test

🎨 Best Practices ✅

Follows CLAUDE.md requirements and Rails/Ruby conventions perfectly.

🚀 Migration Impact

Potentially breaking for misconfigured setups, but:

  • Error messages are clear and actionable ✅
  • Better to fail at boot than at runtime ✅

Recommendation: Consider temporary ENV flag (REACT_ON_RAILS_SKIP_VERSION_CHECK) for smoother migration.

📊 Overall Assessment

  • Code Quality: 9.5/10
  • Test Coverage: 10/10
  • Documentation: 10/10
  • Security: 10/10
  • Best Practices: 10/10

✅ Recommendation: APPROVE with minor suggestions

This PR is a significant quality improvement. The implementation is clean, well-tested, and thoroughly documented.

Optional improvements:

  1. Add JSON parsing error handling
  2. Consider ENV-based escape hatch for validation
  3. Add integration test for Rails boot validation
  4. Clarify Pro version gsub comment

Before merge:

  • ✅ All tests passing (53 specs)
  • ✅ Zero RuboCop offenses
  • ✅ Documentation complete
  • ✅ No security concerns
  • ⚠️ Consider migration impact

Great work! This will make releases much more reliable and user configurations much safer.


Review generated by Claude Code

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

🧹 Nitpick comments (5)
react_on_rails_pro/lib/react_on_rails_pro/utils.rb (1)

175-182: Avoid sending nulls; omit nil-valued fields

When RSC support is disabled, dependencyBundleTimestamps is nil. Prefer omitting nil entries to keep the payload clean and future‑proof.

Apply this minimal change:

       {
         "gemVersion" => ReactOnRailsPro::VERSION,
         "protocolVersion" => ReactOnRailsPro::PROTOCOL_VERSION,
         "password" => ReactOnRailsPro.configuration.renderer_password,
-        "dependencyBundleTimestamps" => dependencies,
-        "railsEnv" => Rails.env.to_s
-      }
+        "dependencyBundleTimestamps" => dependencies,
+        "railsEnv" => Rails.env.to_s
+      }.compact
react_on_rails_pro/packages/node-renderer/tests/worker.test.ts (1)

332-470: Great coverage; consider two small additions

  • Add a test for missing railsEnv to confirm behavior defaults (dev should 412, prod should allow with warning).
  • Add a test for prerelease without dot (e.g., 4.0.0.rc1) to lock down normalization.

Would you like me to draft these two Jest cases?

react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (3)

7-10: Don’t default NODE_ENV to 'production'

Defaulting to 'production' can silently allow version mismatches when railsEnv is absent. Let it be undefined and rely on explicit railsEnv or the actual environment.

-const NODE_ENV = process.env.NODE_ENV || 'production';
+const NODE_ENV = process.env.NODE_ENV;

11-29: Broaden prerelease normalization (optional)

Your normalization misses cases like 4.0.0.rc1. A small regex tweak keeps scope tight while covering this common variant.

-  // Replace the first dot after major.minor.patch with a hyphen to handle Ruby gem format
-  // Examples: "4.0.0.rc.1" -> "4.0.0-rc.1", "4.0.0.alpha.1" -> "4.0.0-alpha.1"
-  normalized = normalized.replace(/^(\d+\.\d+\.\d+)\.([a-z]+)/, '$1-$2');
+  // Handle Ruby gem prerelease forms:
+  // "4.0.0.rc.1" -> "4.0.0-rc.1", "4.0.0.alpha.1" -> "4.0.0-alpha.1", "4.0.0.rc1" -> "4.0.0-rc.1"
+  normalized = normalized
+    .replace(/^(\d+\.\d+\.\d+)\.([a-z]+)\.(\d+)$/, '$1-$2.$3')
+    .replace(/^(\d+\.\d+\.\d+)\.([a-z]+)(\d+)$/, '$1-$2.$3');

Consider adding a unit test for "rc1".


54-81: Env-aware mismatch handling looks correct; minor DX polish

Consider appending the evaluated environment (railsEnv/NODE_ENV) to the message to aid debugging when behavior differs between dev and prod.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf5681a and e04c313.

⛔ Files ignored due to path filters (1)
  • react_on_rails_pro/spec/dummy/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • react_on_rails_pro/lib/react_on_rails_pro/utils.rb (1 hunks)
  • react_on_rails_pro/lib/react_on_rails_pro/version.rb (1 hunks)
  • react_on_rails_pro/package.json (1 hunks)
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (2 hunks)
  • react_on_rails_pro/packages/node-renderer/tests/worker.test.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • react_on_rails_pro/package.json
🧰 Additional context used
📓 Path-based instructions (3)
{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:

  • react_on_rails_pro/lib/react_on_rails_pro/version.rb
  • react_on_rails_pro/lib/react_on_rails_pro/utils.rb
**/*.{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:

  • react_on_rails_pro/packages/node-renderer/tests/worker.test.ts
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • react_on_rails_pro/packages/node-renderer/tests/worker.test.ts
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
🧬 Code graph analysis (1)
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (1)
react_on_rails_pro/packages/node-renderer/src/worker/types.ts (1)
  • FastifyRequest (11-11)
⏰ 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). (9)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: claude-review
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: markdown-link-check
  • GitHub Check: lint-js-and-ruby
🔇 Additional comments (3)
react_on_rails_pro/lib/react_on_rails_pro/version.rb (1)

4-4: Version update verified across all packages.

All six version locations are correctly synchronized to 16.1.1, confirming successful implementation of the unified versioning strategy. PROTOCOL_VERSION remains appropriately unchanged at 2.0.0.

react_on_rails_pro/lib/react_on_rails_pro/utils.rb (1)

179-181: railsEnv addition looks good

Including Rails.env in the payload matches the new protocol and tests. No issues.

react_on_rails_pro/packages/node-renderer/tests/worker.test.ts (1)

29-31: railsEnv plumbed through tests coherently

The tests now include railsEnv where protocol/gem checks apply. This aligns with handler logic.

Also applies to: 47-55, 75-85, 118-122, 145-149, 172-176, 200-203, 291-299, 315-323

Optimize version validation by caching comparison results to avoid
repeated normalization on every rendering request.

**Changes:**

- Add `versionCache` Map to store version comparison results
- Cache is checked before performing normalization (key: gemVersion)
- Normalization only runs on cache miss
- Production logging is now conditional on cache miss (logs only once)

**Performance benefits:**
- Eliminates repeated string operations (trim, toLowerCase, regex) on every request
- Prevents log spam in production (warning logged only once per unique version)
- Minimal memory overhead (few cached version strings)

**Cache behavior:**
- Cache key: gemVersion string
- Cache value: boolean (true if versions match, false if mismatch)
- If key exists, comparison result is used directly
- Production warning only logs when version is first encountered (justCached flag)

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

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

claude bot commented Oct 26, 2025

Pull Request Review: Unified Release Scripts and Strict Version Validation

Overall Assessment

This is an excellent and comprehensive PR that significantly improves the React on Rails release process and runtime validation. The changes are well-architected, thoroughly tested, and will prevent many common configuration issues.

Recommendation: ✅ Approve with minor suggestions


Part 1: Unified Release Script - EXCELLENT ⭐

Strengths

  1. Atomic Version Updates - All 6 version files updated together prevents inconsistencies
  2. Smart Ruby Version Handling - Auto-detection and switching is a game-changer
  3. Comprehensive Options - Dry-run, Verdaccio testing, skip-push provide flexibility
  4. Clear Documentation - Thorough desc with helpful examples
  5. Semver Support - Keywords and explicit versions

Code Quality Highlights

  • rakelib/release.rake:126 - Smart use of word boundary to avoid matching PROTOCOL_VERSION
  • rakelib/release.rake:146 - Package name comparison works well

Suggestions

  1. Consider error handling wrapper around gem bump call (line 103)
  2. Consider pre-flight check to ensure expected files exist

Part 2: Strict Version Validation - EXCELLENT ⭐

Strengths

  1. Fail-Fast Philosophy - Boot-time vs runtime error detection
  2. Clear Error Messages - Include problem, location, and fix commands
  3. Comprehensive Checks - All validation scenarios covered
  4. DRY Code - package_installed helper eliminates duplication

Suggestions

  1. Add tests for malformed JSON edge cases
  2. Consider config option to disable in production (though overhead is negligible)

Part 3: Node Renderer Version Validation - VERY GOOD ⭐

Strengths

  1. Environment-Aware - Strict in dev, permissive in prod
  2. Smart Normalization - Handles Ruby gem vs NPM format differences
  3. Caching - Prevents repeated logging
  4. Comprehensive Testing - 17 tests including edge cases

Suggestions

  1. Document that either railsEnv or NODE_ENV being production triggers permissive mode
  2. Test single-segment prereleases like 4.0.0.rc1
  3. Consider REACT_ON_RAILS_STRICT_VERSION_CHECK env var for staging

Part 4: Ruby Version Auto-Switching - VERY GOOD ⭐

Strengths

  1. Version Manager Support - rvm, rbenv, asdf
  2. Error Handling - Clear failure messages
  3. Smart Detection - bundle platform --ruby approach
  4. Same-Shell Context - Version switch persists

Suggestions

  1. Consider caching detected versions
  2. Document manual testing procedure

Summary Scores

Security: ✅ No Issues
Performance: ✅ Good
Testing: ✅ Excellent (53 version checker tests, 17 node renderer tests, zero RuboCop offenses)
Documentation: ⭐ Outstanding
Code Style: ✅ Perfect


Final Recommendations

Must Address

  • None - production-ready as-is

Should Consider

  1. Error handling around gem bump
  2. Document environment detection behavior
  3. Test single-segment prereleases

Nice to Have

  1. Cache Ruby version detection
  2. Integration tests for release workflow
  3. Update CLAUDE.md with new process

Summary

This PR represents a significant improvement to React on Rails:

✅ Eliminates version mismatches at multiple levels
✅ Reduces maintenance burden
✅ Improves developer experience
✅ Well-tested with comprehensive coverage
✅ Excellent documentation

Code quality is high, architecture is sound, implementation is thorough.

Great work! 🎉


Review by Claude Code per CLAUDE.md guidelines

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e04c313 and 730798c.

📒 Files selected for processing (1)
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (2 hunks)
🧰 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:

  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
🧬 Code graph analysis (1)
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (2)
react_on_rails_pro/spec/dummy/client/node-renderer.js (1)
  • process (5-5)
react_on_rails_pro/packages/node-renderer/src/worker/types.ts (1)
  • FastifyRequest (11-11)
⏰ 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). (11)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22)
  • GitHub Check: claude-review
  • GitHub Check: markdown-link-check
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.2, 20)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
🔇 Additional comments (5)
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (5)

7-8: LGTM!

The log import is correctly added to support the production warning behavior introduced later in the file.


9-14: LGTM!

The version cache is a good optimization that prevents repeated normalization and avoids log spam for duplicate version checks. In typical deployments, the cache will only contain 1-2 entries, so unbounded growth is not a practical concern.


16-34: LGTM!

The normalization function correctly handles the Ruby gem prerelease format conversion (e.g., 4.0.0.rc.14.0.0-rc.1). The regex is well-targeted for the documented use case, and the lowercase/trim operations ensure robust comparison.


36-40: LGTM!

The RequestBody interface provides clear type safety for the destructured request body fields.


59-75: LGTM with the production detection caveat!

The gem version validation logic is well-implemented:

  • The caching strategy correctly avoids repeated normalization and prevents log spam by using the justCached flag to log warnings only once per unique gem version.
  • Error messages are clear and actionable, guiding users to update either the gem or the node renderer package.
  • The environment-aware behavior (warning in production vs. 412 error in development) provides a good balance between strict validation during development and operational flexibility in production.

Assuming the production detection logic at line 76 is corrected, this implementation achieves the PR objective of strict version validation with appropriate environment-sensitive handling.

Also applies to: 77-100

@claude
Copy link

claude bot commented Oct 26, 2025

📋 Best Practices and Final Verdict

Best Practices Assessment

Follows repository conventions (per CLAUDE.md):

  • RuboCop compliant (as stated in PR description)
  • Test coverage included
  • Documentation updated
  • No manual formatting (deferred to automated tools)

Ruby best practices:

  • Proper use of strip_heredoc for multiline strings
  • DRY principle applied
  • Clear method names
  • Proper error handling

TypeScript best practices:

  • Type safety maintained
  • Clear function documentation
  • Proper null/undefined handling
  • LRU cache pattern

Recommendations

Must Fix: None - code is production-ready as-is.

Should Consider:

  1. Add rollback guidance for failed releases
  2. Add GitHub token validation before publishing
  3. Add JSON parse error handling for invalid package.json

Nice to Have:

  1. Extract isProduction logic in node renderer
  2. Add version to Rails boot log
  3. Expand documentation with common error scenarios

Code Metrics

  • Lines changed: 1153 additions, 1479 deletions (net: -326 lines)
  • Files changed: 22 files
  • Test files: 3 files with comprehensive coverage
  • Documentation files: 3 files updated

Net negative lines is excellent - code is getting simpler despite adding features!

✨ Final Verdict

This PR is exemplary work. It demonstrates:

  • Clear problem identification
  • Well-designed solutions
  • Thorough implementation
  • Comprehensive testing
  • Excellent documentation

The unified versioning approach significantly improves maintainability, the strict validation prevents user errors, and the node renderer validation adds runtime safety.

Approved pending linting/CI checks. 🚀

cc: @AbanoubGhadban


Review performed by Claude Code following CLAUDE.md conventions

# Install and Release

We're releasing this as a combined Ruby gem plus two NPM packages. We keep the version numbers in sync across all packages.
We're releasing this as a unified release with 5 packages total. We keep the version numbers in sync across all packages using unified versioning.
Copy link
Collaborator

@alexeyr-ci2 alexeyr-ci2 Oct 26, 2025

Choose a reason for hiding this comment

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

It looks like "using unified versioning" means something in addition to "We keep the version numbers in sync across all packages" but if so it isn't clear what.

Detect package manager (yarn/pnpm/bun/npm) and show correct commands
in error messages instead of hardcoding yarn commands.

Detection strategy:
1. Check packageManager field in package.json (Node.js Corepack standard)
2. Fall back to lock file detection (yarn.lock, pnpm-lock.yaml, etc.)
3. Default to yarn for backward compatibility

Changes:
- Add Utils.detect_package_manager method
- Add Utils.package_manager_install_exact_command method
- Add Utils.package_manager_remove_command method
- Update all 6 error messages in VersionChecker to use dynamic commands
- Add 22 comprehensive specs with 100% coverage

Supported package managers:
- yarn: yarn add package@1.0.0 --exact
- pnpm: pnpm add package@1.0.0 --save-exact
- bun: bun add package@1.0.0 --exact
- npm: npm install package@1.0.0 --save-exact

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

Co-Authored-By: Claude <noreply@anthropic.com>
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 (1)
lib/react_on_rails/version_checker.rb (1)

60-124: Missing validation: base gem without npm package installation.

The method validates Pro gem/package compatibility but doesn't ensure the base gem has a corresponding npm package installed. If has_base_package and has_pro_package are both false with is_pro_gem = false (base gem scenario), all three conditional blocks skip and validation passes.

Impact: Users with the base gem but missing the react-on-rails npm package will pass validation, causing runtime failures later.

Add validation after line 63:

 is_pro_gem = ReactOnRails::Utils.react_on_rails_pro?
+
+# Error: No package installed
+if !has_base_package && !has_pro_package
+  base_install_cmd = ReactOnRails::Utils.package_manager_install_exact_command("react-on-rails", gem_version)
+  pro_install_cmd = ReactOnRails::Utils.package_manager_install_exact_command("react-on-rails-pro", gem_version)
+
+  raise ReactOnRails::Error, <<~MSG.strip
+    **ERROR** ReactOnRails: No React on Rails package found in package.json dependencies.
+
+    React on Rails requires either 'react-on-rails' or 'react-on-rails-pro' package installed.
+
+    Fix:
+      Run: #{base_install_cmd}
+
+      Or if using React on Rails Pro:
+      Run: #{pro_install_cmd}
+
+    #{package_json_location}
+  MSG
+end

Note: A past review comment suggested using an inline guard clause style for line 103. The current return unless pattern is acceptable, but you may consider the alternative for consistency with error-first patterns.

🧹 Nitpick comments (2)
lib/react_on_rails/utils.rb (1)

286-319: Consider consolidating duplicate package manager detection logic.

The file lib/generators/react_on_rails/generator_messages.rb (lines 182-192) has a similar detect_package_manager method that only checks lock files without checking the packageManager field. This creates maintenance overhead and potential inconsistency.

Consider refactoring generator_messages.rb to use ReactOnRails::Utils.detect_package_manager instead:

# In lib/generators/react_on_rails/generator_messages.rb
def detect_package_manager
  ReactOnRails::Utils.detect_package_manager.to_s
end

This ensures consistent detection logic across the codebase.

lib/react_on_rails/version_checker.rb (1)

181-183: Optional: Simplify namespace reference.

Since this method is already inside the VersionChecker module, you can simplify line 182.

 def package_json_location
-  "Package.json location: #{VersionChecker::NodePackageVersion.package_json_path}"
+  "Package.json location: #{NodePackageVersion.package_json_path}"
 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 4f18aa5 and 230d190.

📒 Files selected for processing (3)
  • lib/react_on_rails/utils.rb (1 hunks)
  • lib/react_on_rails/version_checker.rb (4 hunks)
  • spec/react_on_rails/utils_spec.rb (1 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:

  • lib/react_on_rails/utils.rb
  • spec/react_on_rails/utils_spec.rb
  • lib/react_on_rails/version_checker.rb
🧠 Learnings (2)
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • spec/react_on_rails/utils_spec.rb
📚 Learning: 2025-10-23T17:22:01.064Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/version_checker.rb
🧬 Code graph analysis (3)
lib/react_on_rails/utils.rb (3)
lib/generators/react_on_rails/generator_messages.rb (1)
  • detect_package_manager (183-193)
lib/react_on_rails/version_checker.rb (1)
  • package_json_path (192-194)
lib/react_on_rails/configuration.rb (1)
  • configuration (14-57)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/version_checker.rb (1)
  • package_json_path (192-194)
lib/react_on_rails/utils.rb (3)
  • detect_package_manager (291-294)
  • package_manager_install_exact_command (327-340)
  • package_manager_remove_command (346-359)
lib/react_on_rails/version_checker.rb (1)
lib/react_on_rails/utils.rb (3)
  • package_manager_install_exact_command (327-340)
  • react_on_rails_pro? (215-223)
  • package_manager_remove_command (346-359)
🪛 ast-grep (0.39.6)
spec/react_on_rails/utils_spec.rb

[warning] 704-704: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 704-704: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 720-720: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 720-720: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 728-728: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 728-728: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 729-729: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 729-729: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 737-737: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 737-737: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 738-738: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 738-738: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 739-739: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 739-739: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 747-747: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 747-747: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 748-748: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 748-748: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 749-749: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 749-749: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 750-750: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "package-lock.json")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 750-750: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "package-lock.json"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 758-758: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 758-758: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 759-759: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 759-759: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 760-760: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 760-760: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 761-761: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "package-lock.json")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 761-761: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "package-lock.json"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 775-775: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 775-775: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 788-788: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 788-788: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 789-789: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)


[warning] 789-789: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

🪛 GitHub Actions: Lint JS and Ruby
spec/react_on_rails/utils_spec.rb

[error] 671-671: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 670. (autocorrectable)


[error] 679-679: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 678. (autocorrectable)


[error] 687-687: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 686. (autocorrectable)


[error] 695-695: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 694. (autocorrectable)


[error] 704-704: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 703. (autocorrectable)


[error] 715-715: RuboCop: Layout/MultilineMethodCallIndentation: Align .and_return with .with on line 714. (autocorrectable)

⏰ 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). (9)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: claude-review
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: markdown-link-check
  • GitHub Check: build-dummy-app-webpack-test-bundles
🔇 Additional comments (11)
spec/react_on_rails/utils_spec.rb (2)

658-795: Excellent test coverage for package manager detection.

The test suite comprehensively covers all detection scenarios:

  • packageManager field parsing for all supported managers (yarn, pnpm, bun, npm)
  • Fallback to lock file detection
  • Edge cases (parsing failures, missing package.json, unknown managers)
  • Proper mocking strategy with and_call_original to avoid interference

797-897: Command generation tests look solid.

Tests verify correct command syntax for install and remove operations across all package managers, including proper handling of unknown managers with sensible yarn defaults.

lib/react_on_rails/utils.rb (2)

286-319: Well-designed package manager detection with proper fallbacks.

The implementation follows best practices:

  • Prioritizes Corepack standard (packageManager field in package.json)
  • Falls back to lock file detection
  • Sensible yarn default when all detection fails
  • Proper error handling for JSON parsing failures

327-359: Command syntax verified correct for all package managers.

The install and remove command generation properly handles manager-specific syntax:

  • Exact version flags: --exact (yarn, bun), --save-exact (pnpm, npm)
  • Remove commands: remove (yarn, pnpm, bun), uninstall (npm)
lib/react_on_rails/version_checker.rb (7)

20-33: LGTM! Clear validation orchestration.

The fail-fast validation sequence is well-structured and follows a logical order: existence → compatibility → exactness → version match.


37-58: LGTM! Clear error messaging with actionable remediation.

The validation properly checks file existence and provides user-friendly install commands for both base and Pro packages.


126-148: LGTM! Exact version enforcement with clear remediation.

The early return (line 127) relies on prior validations to ensure raw is not nil when a package should be present. This reinforces the importance of comprehensive checks in validate_package_gem_compatibility!.


150-171: LGTM! Version matching validation with actionable fix.

The exact version comparison ensures gem and npm package versions are synchronized, with clear commands to resolve mismatches.


224-236: LGTM! Clean package detection helpers.

The helper methods provide clear abstractions with appropriate Pro-first precedence in package_name. These support the validation logic effectively.


265-270: LGTM! Safe package detection implementation.

The method correctly handles missing files and uses safe navigation (dig) to check dependencies, preventing errors if the structure is unexpected.


276-278: LGTM! Efficient JSON parsing with memoization.

The two-level memoization pattern (file contents → parsed JSON) ensures efficient access without redundant I/O or parsing.

AbanoubGhadban and others added 3 commits October 27, 2025 12:58
- Fix RuboCop indentation violations in utils_spec.rb
- Fix RSpec mock issues in version_checker_spec.rb by properly mocking Rails.root as Pathname
- Add validation to require at least one package (react-on-rails or react-on-rails-pro) to be installed
- Add test coverage for missing package validation
- Add pre-release version examples to documentation (releasing.md and release.rake)

All tests now pass with 0 failures and 0 RuboCop offenses.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Extract validation logic into separate private methods to satisfy:
- Metrics/AbcSize
- Metrics/CyclomaticComplexity
- Metrics/MethodLength

Changes:
- Split validate_package_gem_compatibility! into 4 focused validation methods:
  - validate_packages_installed! - Ensures at least one package is installed
  - validate_no_duplicate_packages! - Prevents both packages being installed
  - validate_pro_gem_uses_pro_package! - Enforces Pro gem uses Pro package
  - validate_pro_package_has_pro_gem! - Enforces Pro package requires Pro gem
- Remove rubocop:disable and rubocop:enable comments
- Maintain same functionality and test coverage (54 specs pass)

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

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

claude bot commented Oct 27, 2025

Pull Request Review: Unified Release Scripts and Strict Version Validation

Overview

This is an excellent PR that addresses critical pain points in the React on Rails release and version management process. The changes are well-structured, comprehensive, and demonstrate strong software engineering practices.

✅ Strengths

1. Code Quality & Best Practices

  • Excellent error messaging: The validation errors in version_checker.rb are exceptionally clear and actionable, with specific fix commands tailored to the user's package manager
  • DRY principle applied: Good use of helper methods like package_installed?(), detect_package_manager(), and command generation utilities
  • Well-documented: Comprehensive inline comments and clear method documentation with @param and @return annotations
  • Defensive programming: Proper error handling with rescue StandardError in detect_package_manager_from_package_json (lib/react_on_rails/utils.rb:307)
  • Separation of concerns: Clear separation between validation logic, package detection, and command generation

2. Architecture & Design

  • Unified versioning: Smart decision to synchronize all package versions - eliminates a major source of configuration errors
  • Package manager agnostic: Excellent support for npm, yarn, pnpm, and bun with proper detection logic (lib/react_on_rails/utils.rb:286-359)
  • Fail-fast validation: Moving from warnings to hard errors at boot time is the right approach - prevents runtime issues
  • Environment-aware: The node renderer validation correctly distinguishes between development (strict) and production (permissive)

3. Testing & Reliability

  • Comprehensive test coverage: The PR description mentions 53 tests passing with 9 new test cases for validation scenarios
  • Dry-run support: The release script includes dry-run mode for safe testing
  • Verdaccio support: Local registry testing before production release is excellent practice

4. Documentation

  • Outstanding documentation updates: The docs/contributor-info/releasing.md rewrite is comprehensive and clear
  • Helpful examples: Multiple usage examples for different scenarios
  • Clear migration notes: Users know exactly what to expect and how to fix issues

🔍 Issues & Suggestions

High Priority

1. Security: Potential command injection in package manager detection

Location: lib/react_on_rails/utils.rb:327-340 and :346-358

The package_manager_install_exact_command and package_manager_remove_command methods directly interpolate package_name and version into shell commands. If these values come from untrusted sources (e.g., parsed from a malicious package.json), this could lead to command injection.

Recommendation: Add input validation or use proper shell escaping with Shellwords.escape

2. Error handling: JSON parsing without rescue

Location: lib/react_on_rails/version_checker.rb:300

The parsed_package_contents method parses JSON but doesn't handle malformed JSON gracefully. Add rescue JSON::ParserError => e block with clear error message.

Medium Priority

3. Performance: Boot-time validation impact

Location: lib/react_on_rails/engine.rb:9-11

The validation runs on config.after_initialize, which means it runs on every Rails boot. For large applications, repeatedly parsing package.json and checking files could add milliseconds to boot time. Consider caching or adding a configuration option to disable in production.

4. Regex edge cases in version normalization

Location: lib/react_on_rails/version_checker.rb:10

The VERSION_PARTS_REGEX uses a non-anchored pattern that could match invalid versions like "foo 1.2.3 bar". Consider adding anchors: /\A(\d+).(\d+).(\d+)(?:-.)?\z/

5. Redundant validation checks

Consider combining the file existence check with the "no packages installed" check to avoid redundancy.

Low Priority

6. RuboCop disable comments

Location: lib/react_on_rails/version_checker.rb:60,147

Consider refactoring validate_package_gem_compatibility! into smaller methods to avoid disabling RuboCop cops.

7. Missing test for edge cases

Consider adding tests for:

  • Malformed package.json (invalid JSON)
  • package.json without dependencies field
  • Local file:// dependencies
  • git+ssh:// dependencies
  • Scoped packages like @scope/package-name

🔒 Security Considerations

Overall: Low Risk

The PR primarily deals with build-time tooling rather than runtime user data, which limits security exposure. However:

  1. Command injection risk (see High Priority TODO for first version #1 above)
  2. File system access: The code reads package.json from configurable locations - ensure ReactOnRails.configuration.node_modules_location is validated
  3. Dependency on external binaries: The release script calls gem, yarn, bundle, etc. - ensure PATH is controlled in CI/CD environments

⚡ Performance Considerations

  1. Boot time impact: Validation on every Rails boot could slow down development
  2. File I/O: Multiple reads of package.json - currently mitigated by memoization (@parsed_package_contents)
  3. Regex performance: The version regex is simple and should be fast
  4. Release script performance: No concerns - this is a one-time operation

Recommendation: Add telemetry to track validation duration and consider optimization if it exceeds 50ms.

📊 Test Coverage

Excellent - The PR mentions:

  • 53 total tests passing
  • 9 new validation test cases
  • 17 node renderer tests (11 updated + 6 new)
  • Multiple fixture files for testing different scenarios
  • Zero RuboCop offenses

📚 Documentation Quality

Outstanding - The documentation is comprehensive and well-structured with clear examples, migration notes, troubleshooting guidance, and visual formatting.

🎯 Breaking Changes

The switch from warnings to errors is technically a breaking change. Applications with version mismatches will now fail to boot. However, given that version mismatches cause runtime issues anyway, failing fast is arguably the right choice. Error messages provide clear fix instructions.

🏁 Final Recommendation

APPROVE with minor changes

This is a high-quality PR that significantly improves the developer experience and reduces the likelihood of version-related bugs. The implementation is solid, well-tested, and thoroughly documented.

Before Merging:

  1. MUST FIX: Address the command injection security issue (High Priority TODO for first version #1)
  2. SHOULD FIX: Add JSON parsing error handling (High Priority Make work with turbolinks and better helper #2)
  3. NICE TO HAVE: Consider the other medium/low priority suggestions

Post-Merge Follow-ups:

  1. Monitor boot time impact in production applications
  2. Gather user feedback on error message clarity
  3. Consider adding telemetry for validation failures to identify common issues

📝 Adherence to CLAUDE.md

✅ The PR follows the project's conventions:

  • RuboCop passed with zero offenses
  • Tests are comprehensive
  • Documentation is updated
  • No manual formatting issues (would be caught by git hooks)

Excellent work! This PR demonstrates thoughtful engineering and will make React on Rails more reliable and easier to maintain.

@claude
Copy link

claude bot commented Oct 27, 2025

Pull Request Review: Unified Release Scripts and Strict Version Validation

Overall Assessment

Verdict: Excellent work! 🎉 This is a comprehensive, well-architected PR that significantly improves the React on Rails release process and version management. The implementation is thorough, well-tested, and follows best practices throughout.

Recommendation: APPROVE with minor suggestions


🌟 Strengths

1. Exceptional Code Quality

  • Clean, well-structured code following Ruby and TypeScript best practices
  • Excellent use of DRY principles (e.g., package_installed? helper, package manager detection)
  • Clear separation of concerns in validation methods
  • Comprehensive inline documentation and comments

2. Outstanding User Experience

  • Clear, actionable error messages - Every error tells users exactly what is wrong and how to fix it
  • Package manager detection - Automatically detects npm/yarn/pnpm/bun and provides correct commands
  • Environment-aware behavior - Strict in development, permissive in production (smart!)
  • Fail-fast validation - Catches issues at boot time, not runtime

3. Robust Testing

  • Ruby tests: 9 new test cases + 53 total tests passing
  • TypeScript tests: 6 new tests + 11 updated = 17 total passing
  • Test fixtures: Well-organized fixture files for different scenarios
  • Edge cases covered: Version normalization, semver wildcards, missing files, etc.

4. Excellent Documentation

  • Complete rewrite of releasing.md with clear examples
  • Comprehensive PR description (one of the best I have seen!)
  • Inline code comments explaining non-obvious logic
  • Clear migration notes for contributors and users

📋 Detailed Analysis

Part 1: Unified Release Script

rakelib/release.rake:128

pro_version_content.gsub\!(/\bVERSION = ".+"/, "VERSION = \"#{actual_gem_version}\"")

Excellent use of word boundary (\b) to avoid matching PROTOCOL_VERSION

rakelib/release.rake:92

sh_in_dir(gem_root, "git pull --rebase") unless is_dry_run || skip_push

Smart conditional - Skips pull in dry-run and skip_push modes

Suggestions:

  1. Consider adding a confirmation prompt before publishing in non-dry-run mode
  2. Could benefit from a rollback mechanism if any step fails mid-release

Part 2: Version Validation

lib/react_on_rails/version_checker.rb:28-33

def validate_version_and_package_compatibility\!
  validate_package_json_exists\!
  validate_package_gem_compatibility\!
  validate_exact_version\!
  validate_version_match\!
end

Perfect method composition - Clear, readable, well-ordered validations

lib/react_on_rails/version_checker.rb:240-244

# Check for react-on-rails-pro first (Pro takes precedence)
return @raw = deps["react-on-rails-pro"] if deps.key?("react-on-rails-pro")

# Fall back to react-on-rails
return @raw = deps["react-on-rails"] if deps.key?("react-on-rails")

Smart precedence handling - Pro package correctly takes priority

lib/react_on_rails/utils.rb:296-310

def self.detect_package_manager
  manager = detect_package_manager_from_package_json || detect_package_manager_from_lock_files
  manager || :yarn # Default to yarn if no detection succeeds
end

Excellent implementation - Follows Node.js Corepack standard first, then fallback to lock files

Potential Issue:

lib/react_on_rails/version_checker.rb:272

raw.blank? || raw.start_with?(/[~^><*]/) || raw.include?(" - ") || raw.include?(" || ")

⚠️ Edge case: This might not catch all semver ranges (e.g., "1.x", "*"). Consider testing with more semver patterns.

Part 3: Node Renderer Validation

packages/node-renderer/src/worker/checkProtocolVersionHandler.ts:24-34

function normalizeVersion(version: string): string {
  if (\!version) return "";
  
  let normalized = version.trim().toLowerCase();
  
  // Replace the first dot after major.minor.patch with a hyphen
  normalized = normalized.replace(/^(\d+\.\d+\.\d+)\.([a-z]+)/, "$1-$2");
  
  return normalized;
}

Clean normalization logic - Handles Ruby gem vs NPM format differences elegantly

checkProtocolVersionHandler.ts:11-14

const versionCache = new Map<string, boolean>();

Smart caching - Prevents repeated logging for same version mismatches

checkProtocolVersionHandler.ts:76

const isProduction = railsEnv === "production" || NODE_ENV === "production";

Dual environment detection - Considers both Rails and Node environments

Suggestion:
Consider adding a test for the caching behavior to ensure it does not log warnings repeatedly.


🔒 Security Considerations

No security concerns identified

  • No user input directly executed in shell commands
  • Version strings are validated before use
  • File paths use Rails.root.join for safe path construction
  • No credentials or secrets exposed

⚡ Performance Considerations

Good performance characteristics:

  • Version validation runs once at boot (not on every request)
  • Caching in node renderer prevents repeated comparisons
  • File reads are cached appropriately
  • No N+1 queries or unnecessary iterations

Minor optimization opportunity:

  • parsed_package_contents is memoized, but JSON parsing could fail silently. Consider adding error handling.

🧪 Test Coverage

Excellent test coverage:

  • ✅ All validation scenarios covered
  • ✅ Edge cases tested (semver wildcards, missing files, both packages installed)
  • ✅ Pro package compatibility tested
  • ✅ Version normalization tested (case-insensitive, dot vs hyphen, whitespace)
  • ✅ Environment-aware behavior tested (dev vs production)

Missing test (minor):

  • Consider testing the caching behavior in checkProtocolVersionHandler to ensure warnings are not logged repeatedly

📝 Documentation Quality

Outstanding documentation:

  • Comprehensive PR description with clear sections
  • Rewritten release docs with multiple examples
  • Clear migration notes for contributors and users
  • Inline comments explain complex logic

Minor suggestion:

  • Add troubleshooting section to release docs for common issues (e.g., "What if Verdaccio test fails?")

🎯 Best Practices Followed

Repository conventions (from CLAUDE.md):

  • RuboCop compliant (0 offenses mentioned)
  • Files end with newlines
  • No manual formatting (Prettier/RuboCop handles it)
  • Tests passing before commit

Code organization:

  • Clear separation of concerns
  • Single Responsibility Principle followed
  • DRY principle applied consistently
  • Meaningful method and variable names

🐛 Potential Issues & Suggestions

Minor Issues

  1. Edge case in semver detection (lib/react_on_rails/version_checker.rb:272)

    raw.blank? || raw.start_with?(/[~^><*]/) || raw.include?(" - ") || raw.include?(" || ")

    This might not catch patterns like "1.x", "latest", or standalone "*". Consider more comprehensive regex.

  2. JSON parsing error handling (lib/react_on_rails/version_checker.rb:307)

    @parsed_package_contents ||= JSON.parse(package_json_contents)

    If package.json has invalid JSON, this will raise an error without context. Consider wrapping in begin/rescue.

  3. Node renderer test does not verify cache behavior
    The versionCache is tested indirectly, but there is no explicit test verifying that warnings are not logged multiple times for the same version.

Suggestions for Future Enhancements

  1. Release script rollback: Add ability to rollback if release fails mid-way
  2. Version check in CI: Add a CI job to verify all version files are in sync
  3. Automated release notes: Consider generating release notes from commit messages
  4. Package manager lock file validation: Verify lock files are in sync after version updates

🎓 What I Learned from This PR

This is a masterclass in:

  • Writing clear, actionable error messages
  • Building user-friendly developer tools
  • Comprehensive testing with fixtures
  • Handling cross-platform version format differences
  • Environment-aware validation strategies

✅ Checklist

  • ✅ Code quality and best practices
  • ✅ Test coverage comprehensive
  • ✅ Documentation excellent
  • ✅ No security concerns
  • ✅ Performance acceptable
  • ✅ Error handling robust
  • ✅ User experience excellent
  • ✅ Follows repository conventions

Final Recommendation

APPROVE - This PR is production-ready with only minor suggestions for future improvements. The unified versioning approach is excellent and will significantly reduce maintenance overhead while improving reliability.

Great work, @AbanoubGhadban! This is exactly the kind of comprehensive improvement that makes maintaining a project easier. 🚀


Review generated by Claude Code

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 (2)
rakelib/release.rake (1)

200-210: Avoid tag/commit churn: add --no-git-tag-version to yarn publish

You already pre-write versions and create the git tag. yarn publish with --new-version can re-tag/commit. Align with node‑renderer publish and add --no-git-tag-version.

Apply:

- 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}")
lib/react_on_rails/version_checker.rb (1)

268-273: Bug: start_with? does not accept Regexp

String#start_with? only accepts strings; passing a Regexp will raise TypeError. This will crash for normal versions. Use match? or string prefixes.

Apply:

- raw.blank? || raw.start_with?(/[~^><*]/) || raw.include?(" - ") || raw.include?(" || ")
+ return true if raw.blank?
+ return true if raw.match?(/\A[~^><*]/)
+ raw.include?(" - ") || raw.include?(" || ")

Add a spec asserting semver_wildcard? for values starting with each of ^ ~ > < *.

♻️ Duplicate comments (1)
docs/contributor-info/releasing.md (1)

178-182: Add language to fenced code block (MD040)

Credentials file is YAML; tag it accordingly.

-  ```
+  ```yaml
   :github: Bearer <GITHUB_TOKEN>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>spec/react_on_rails/version_checker_spec.rb (1)</summary><blockquote>

`274-282`: **Inconsistent comment about fixture content**

The two comments contradict (“not react-on-rails packages” vs “has react-on-rails”). Clarify to reduce reader confusion.

</blockquote></details>
<details>
<summary>rakelib/release.rake (1)</summary><blockquote>

`91-93`: **Pull behavior on release**

git pull --rebase before bump is fine; skipping pull when skip_push=true is reasonable. Consider printing a reminder when skip_push is set that local is not synced before tagging.

</blockquote></details>
<details>
<summary>docs/contributor-info/releasing.md (2)</summary><blockquote>

`3-3`: **Clarify “unified versioning” in one sentence**

Suggest adding: “Unified versioning means a single source-of-truth gem version is applied to all gems and npm packages, with npm prerelease syntax derived from the gem version.”

---

`148-170`: **Scoped registry snippet for GitHub Packages**

Add the scope mapping so yarn/npm route the private package to GitHub Packages without extra flags.

```diff
 **For private packages (GitHub Packages):**
 
 - Get a GitHub personal access token with `write:packages` scope
 - Add to `~/.npmrc`:
-  ```ini
+  ```ini
+  @shakacode-tools:registry=https://npm.pkg.github.com
   //npm.pkg.github.com/:_authToken=<TOKEN>
   always-auth=true

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 230d1901669dec9ea70f168cdf02affe73d4cd23 and 2395751e4d2642cd3404cf11aea1583fbfe05ee2.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `docs/contributor-info/releasing.md` (3 hunks)
* `lib/react_on_rails/version_checker.rb` (4 hunks)
* `rakelib/release.rake` (9 hunks)
* `spec/react_on_rails/utils_spec.rb` (1 hunks)
* `spec/react_on_rails/version_checker_spec.rb` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (2)</summary>

<details>
<summary>**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

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

Files:
- `docs/contributor-info/releasing.md`

</details>
<details>
<summary>{Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}}</summary>


**📄 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:
- `spec/react_on_rails/version_checker_spec.rb`
- `rakelib/release.rake`
- `lib/react_on_rails/version_checker.rb`
- `spec/react_on_rails/utils_spec.rb`

</details>

</details><details>
<summary>🧠 Learnings (3)</summary>

<details>
<summary>📚 Learning: 2025-10-23T17:22:01.064Z</summary>

Learnt from: AbanoubGhadban
PR: #1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The react_on_rails_pro? method validates licenses and should raise errors early (including during path resolution in methods like server_bundle?) to enforce licensing requirements rather than failing later with obscure errors.


**Applied to files:**
- `spec/react_on_rails/version_checker_spec.rb`
- `lib/react_on_rails/version_checker.rb`

</details>
<details>
<summary>📚 Learning: 2025-09-29T12:23:13.615Z</summary>

Learnt from: CR
PR: shakacode/react_on_rails#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T12:23:13.615Z
Learning: Applies to {Gemfile,Rakefile,config.ru,**/*.{rb,rake,gemspec,ru}} : All Ruby code must pass RuboCop with zero offenses before commit/push


**Applied to files:**
- `spec/react_on_rails/utils_spec.rb`

</details>
<details>
<summary>📚 Learning: 2025-02-18T13:08:01.477Z</summary>

Learnt from: AbanoubGhadban
PR: #1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:

  1. Pro version check in run_stream_inside_fiber
  2. RSC support check during pack generation via ReactOnRailsPro.configuration.enable_rsc_support
  3. RSC support validation during component registration
    This makes additional validation in the helper methods unnecessary.

**Applied to files:**
- `spec/react_on_rails/utils_spec.rb`

</details>

</details><details>
<summary>🧬 Code graph analysis (3)</summary>

<details>
<summary>spec/react_on_rails/version_checker_spec.rb (2)</summary><blockquote>

<details>
<summary>spec/react_on_rails/support/version_test_helpers.rb (1)</summary>

* `stub_gem_version` (3-5)

</details>
<details>
<summary>lib/react_on_rails/version_checker.rb (8)</summary>

* `validate_version_and_package_compatibility!` (28-33)
* `build` (12-14)
* `build` (218-220)
* `raw` (230-252)
* `react_on_rails_package?` (254-256)
* `react_on_rails_pro_package?` (258-260)
* `package_name` (262-266)
* `semver_wildcard?` (268-273)

</details>

</blockquote></details>
<details>
<summary>lib/react_on_rails/version_checker.rb (1)</summary><blockquote>

<details>
<summary>lib/react_on_rails/utils.rb (3)</summary>

* `package_manager_install_exact_command` (327-340)
* `react_on_rails_pro?` (215-223)
* `package_manager_remove_command` (346-359)

</details>

</blockquote></details>
<details>
<summary>spec/react_on_rails/utils_spec.rb (3)</summary><blockquote>

<details>
<summary>lib/react_on_rails/version_checker.rb (1)</summary>

* `package_json_path` (222-224)

</details>
<details>
<summary>lib/react_on_rails/utils.rb (3)</summary>

* `detect_package_manager` (291-294)
* `package_manager_install_exact_command` (327-340)
* `package_manager_remove_command` (346-359)

</details>
<details>
<summary>lib/generators/react_on_rails/generator_messages.rb (1)</summary>

* `detect_package_manager` (183-193)

</details>

</blockquote></details>

</details><details>
<summary>🪛 ast-grep (0.39.6)</summary>

<details>
<summary>spec/react_on_rails/utils_spec.rb</summary>

[warning] 704-704: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 704-704: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 720-720: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 720-720: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 728-728: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 728-728: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 729-729: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 729-729: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 737-737: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 737-737: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 738-738: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 738-738: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 739-739: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 739-739: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 747-747: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 747-747: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 748-748: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 748-748: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 749-749: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 749-749: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 750-750: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "package-lock.json")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 750-750: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "package-lock.json"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 758-758: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 758-758: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 759-759: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 759-759: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 760-760: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "bun.lockb")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 760-760: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "bun.lockb"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 761-761: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "package-lock.json")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 761-761: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "package-lock.json"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 775-775: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 775-775: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 788-788: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "yarn.lock")).and_return(false)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 788-788: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "yarn.lock"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 789-789: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(File).to receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml")).and_return(true)
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

---

[warning] 789-789: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:exist?).with(File.join(Rails.root, "pnpm-lock.yaml"))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
     https://cwe.mitre.org/data/definitions/522.html

(hardcoded-secret-rsa-passphrase-ruby)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>docs/contributor-info/releasing.md</summary>

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

(MD040, fenced-code-language)

---

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

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ 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)</summary>

* GitHub Check: dummy-app-integration-tests (3.2, 20)
* GitHub Check: dummy-app-integration-tests (3.4, 22)
* GitHub Check: lint-js-and-ruby
* GitHub Check: markdown-link-check
* GitHub Check: build-dummy-app-webpack-test-bundles
* GitHub Check: rspec-package-tests (3.4, minimum)
* GitHub Check: rspec-package-tests (3.2, minimum)
* GitHub Check: rspec-package-tests (3.4, latest)
* GitHub Check: rspec-package-tests (3.2, latest)
* GitHub Check: build-dummy-app-webpack-test-bundles

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>spec/react_on_rails/utils_spec.rb (4)</summary><blockquote>

`658-795`: **Excellent test coverage for package manager detection.**

The tests comprehensively cover all detection scenarios:
- Detection from `package.json` `packageManager` field for all supported managers
- Fallback to lock file detection when the field is missing or invalid
- Proper handling of unparsable/missing `package.json`
- Correct default to yarn when no indicators exist

The test structure is clear, and the mocking strategy correctly uses `and_call_original` with selective stubbing.

---

`797-846`: **Well-structured tests for install command generation.**

The tests verify the correct command format and flags for each package manager:
- Proper exact-version flags (`--exact`, `--save-exact`)
- Correct package@version format
- Appropriate default to yarn for unknown managers

---

`848-897`: **Complete test coverage for remove command generation.**

The tests verify the correct remove/uninstall command for each package manager with appropriate defaults for unknown managers.

---

`658-897`: **Note: Static analysis false positives.**

The ast-grep warnings about "hardcoded RSA passphrase" for lines containing lock file paths (e.g., `"yarn.lock"`, `"pnpm-lock.yaml"`) are false positives. These are legitimate file paths used in test stubs, not security credentials.

</blockquote></details>
<details>
<summary>spec/react_on_rails/version_checker_spec.rb (2)</summary><blockquote>

`19-22`: **Good defaulting of Pro detection for tests**

Stubbing ReactOnRails::Utils.react_on_rails_pro? to false by default keeps contexts isolated. LGTM.

---

`24-39`: **Test matrix coverage is strong**

Scenarios cover dual-install, missing packages, Pro/base mismatches, semver wildcards, version mismatch, local paths, matching Pro, and missing package.json. Assertions read clearly and align with initializer behavior. Nice work.



Also applies to: 41-60, 62-85, 86-109, 111-129, 131-150, 152-169, 171-187, 189-209, 211-234

</blockquote></details>
<details>
<summary>lib/react_on_rails/version_checker.rb (2)</summary><blockquote>

`180-201`: **Exact match check: good guardrails**

Short-circuiting for nil and local paths avoids false positives; comparing normalized parts matches gem prerelease dotted vs npm hyphenated. LGTM.

---

`37-58`: **Error UX is clear and actionable**

Messages include install/remove commands tailored to the detected package manager and show the expected package.json location. This aligns with fail‑fast boot behavior.

Optionally add one integration spec to assert the message includes the exact command for each package manager (npm, yarn, pnpm, bun).


Also applies to: 71-91, 93-111, 113-131, 132-154

</blockquote></details>
<details>
<summary>rakelib/release.rake (2)</summary><blockquote>

`220-231`: **publishConfig is correctly set to GitHub Packages**

Verification confirms the concern is unfounded. The `react_on_rails_pro/package.json` file properly sets `publishConfig.registry` to `https://npm.pkg.github.com`, which ensures yarn publishes the scoped package to GitHub Packages when the `registry` parameter is not `verdaccio`. The code correctly enforces this routing without requiring additional arguments.

---

`87-90`: **The review comment's portability concern is incorrect.**

Based on my verification:

1. **-mindepth is supported on macOS/BSD**: Modern macOS find (BSD-based) explicitly supports `-mindepth` in its man pages.

2. **-delete already implies -depth**: On both GNU and BSD systems, the `-delete` action implicitly enables depth-first traversal, making explicit `-depth` unnecessary and the suggested workaround redundant.

3. **Actual portability**: `-mindepth` / `-maxdepth` are actually *more* portable than older BSD-specific options. The code is correct as written.

The current implementation using `find . -mindepth 2 -name 'react_on_rails.gemspec' -delete` works correctly on macOS/BSD and requires no changes.



> Likely an incorrect or invalid review comment.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

…gs and improving JSON parsing error handling
@claude
Copy link

claude bot commented Oct 27, 2025

Pull Request Review: Unified Release Scripts and Strict Version Validation

This is an ambitious and well-executed PR that significantly improves version management across React on Rails. The changes are well-structured and thoroughly tested. Here's my detailed feedback:


Strengths

1. Excellent Code Quality

  • Clear separation of concerns: Version validation logic is properly separated into discrete methods
  • Comprehensive error messages: All error messages provide actionable fixes with package-manager-specific commands
  • DRY principle applied: Good use of helper methods like package_installed?, package_manager_install_exact_command
  • Proper caching: Node renderer uses a cache to avoid repeated logging of the same version mismatch

2. Robust Validation Logic

The version_checker.rb improvements are excellent:

  • Fail-fast approach: Catches misconfigurations at boot time (line 9-14 in engine.rb)
  • Comprehensive semver wildcard detection: Lines 268-291 handle all edge cases including latest, next, x-ranges, etc.
  • Package manager detection: Smart fallback from package.json packageManager field to lock files (utils.rb:286-319)
  • Pro/base package compatibility checks: Prevents common misconfiguration scenarios

3. Well-Designed Release Script

The unified release script (release.rake) shows good engineering:

  • Supports both semver bumps and explicit versions: Flexible for different use cases
  • Atomic version updates: All 6+ version files updated together
  • Dry run and Verdaccio testing: Excellent for testing before production release
  • Ruby version auto-detection: Reduces manual setup overhead

4. Excellent Test Coverage

  • 53 tests in version_checker_spec.rb: Covers all validation scenarios
  • 17 tests in node renderer: 11 updated + 6 new tests for version validation
  • Good use of test fixtures: Pro package fixtures added for comprehensive testing

5. Great Documentation

  • The PR description is exceptionally detailed with clear examples
  • Updated docs/contributor-info/releasing.md is comprehensive and user-friendly
  • Error messages include package.json location and package-manager-specific commands

🔍 Potential Issues & Suggestions

1. Performance Consideration (Minor)

Location: lib/react_on_rails/engine.rb:9-14

The validation runs on config.after_initialize, which means it runs on every Rails boot, including in production. While this is generally fast, consider:

  • Suggestion: Add a configuration option to skip validation in production (while keeping it enabled by default in development)
  • Rationale: In production, if the app boots successfully once, the versions are correct. Subsequent boots do not need revalidation.
# Potential enhancement
initializer "react_on_rails.validate_version_and_package_compatibility" do
  config.after_initialize do
    unless ReactOnRails.configuration.skip_version_validation
      Rails.logger.info "[React on Rails] Validating package version and compatibility..."
      VersionChecker.build.validate_version_and_package_compatibility\!
      Rails.logger.info "[React on Rails] Package validation successful"
    end
  end
end

2. Regex Edge Case (Minor)

Location: lib/react_on_rails/version_checker.rb:282

The wildcard detection regex /\dx\b/i might not catch all x-range patterns:

  • ✅ Catches: 1.x, 1.2.x
  • ❌ Might miss: x.1.0, *.1.0

Suggestion: Consider more comprehensive regex:

def wildcard_or_x_range?
  raw == "*" || 
  raw =~ /^[xX*]$/ || 
  raw =~ /^[xX*]\./ || 
  raw =~ /\.[xX*]\b/ || 
  raw =~ /\.[xX*]$/
end

3. Node Renderer Version Normalization (Minor)

Location: react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts:24-34

The normalizeVersion function is well-implemented, but consider edge cases:

Current regex: /^(\d+\.\d+\.\d+)\.([a-z]+)/

  • ✅ Handles: 4.0.0.rc.1, 4.0.0.alpha.1
  • ❌ Won't handle multiple prerelease segments: 4.0.0.beta.1.rc.2

Suggestion: Handle multiple prerelease segments:

function normalizeVersion(version: string): string {
  if (\!version) return '';

  let normalized = version.trim().toLowerCase();

  // Replace dots with hyphens in prerelease section
  normalized = normalized.replace(/^(\d+\.\d+\.\d+)\.(.*)/,  (_, base, prerelease) => {
    return base + '-' + prerelease.replace(/\./g, '-');
  });

  return normalized;
}

4. Security: JSON Parsing Error Handling (Good, but could be enhanced)

Location: lib/react_on_rails/version_checker.rb:327-344

The JSON parsing error handling is good, but consider:

Suggestion: Add file permission check before parsing:

def parsed_package_contents
  return @parsed_package_contents if defined?(@parsed_package_contents)

  unless File.readable?(package_json)
    raise ReactOnRails::Error, "package.json exists but is not readable. Check file permissions."
  end

  begin
    @parsed_package_contents = JSON.parse(package_json_contents)
  rescue JSON::ParserError => e
    # ... existing error handling
  end
end

5. Breaking Change Communication

This PR introduces breaking changes for users:

  • Apps will now fail to boot if versions mismatch (previously just warnings)
  • Remote node renderer will reject requests in development if versions mismatch

Suggestion:

  • Add a CHANGELOG.md entry marking this as a BREAKING CHANGE
  • Consider a deprecation warning period in a minor version before enforcing strict validation in a major version
  • Or clearly document this in the release notes for the next major version

🛡️ Security Considerations

No security concerns identified

  • Proper JSON parsing with error handling
  • No SQL injection or XSS vectors
  • No sensitive data exposed in error messages
  • Good input validation on version strings

🧪 Testing Recommendations

Additional Test Cases to Consider:

  1. Test Rails boot failure scenarios: Ensure apps fail gracefully with clear errors
  2. Test version cache in node renderer: Verify cache works correctly across multiple requests
  3. Test package manager detection edge cases: What happens if multiple lock files exist?
  4. Test release script rollback: What happens if NPM publish fails midway?

📝 Documentation Review

Excellent documentation

  • Clear examples for all release scenarios
  • Verdaccio testing instructions are helpful
  • Error messages guide users to solutions

Minor suggestions:

  • Add a troubleshooting section for common version mismatch scenarios
  • Include a migration guide for existing users upgrading to this version

🎯 Performance Considerations

  1. Boot time impact: Validation runs on every Rails boot (~10-50ms impact estimated)
  2. Node renderer caching: Good use of Map cache prevents repeated logging ✅
  3. File I/O: Multiple package.json reads - consider caching if performance becomes an issue

Best Practices Followed

  • SOLID principles: Single Responsibility, well-separated concerns
  • DRY code: No unnecessary repetition
  • Clear error messages: Users know exactly what's wrong and how to fix it
  • Comprehensive testing: Edge cases covered
  • Good git hygiene: Atomic commits, clear commit messages
  • Documentation: Well-documented changes

🏁 Conclusion

This is a high-quality PR that significantly improves the developer experience and prevents common configuration errors. The code is well-tested, clearly documented, and follows Ruby/TypeScript best practices.

Recommendation: Approve with minor suggestions

The issues identified are minor and mostly edge cases. The PR is ready to merge after addressing the breaking change communication (consider adding to CHANGELOG and ensuring users are aware of the behavioral changes).

Priority Fixes Before Merge:

  1. High: Add CHANGELOG entry about breaking changes
  2. 🟡 Medium: Consider adding production skip option for validation
  3. 🟡 Medium: Enhance normalizeVersion to handle multiple prerelease dots
  4. 🟢 Low: Add additional test cases for edge cases

Great work on this PR! The unified versioning approach will make releases much more reliable and prevent user configuration errors. The fail-fast validation is a significant UX improvement.


🤖 Review generated by Claude Code

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

🧹 Nitpick comments (1)
lib/react_on_rails/version_checker.rb (1)

180-201: LGTM! Version matching validation is correct.

The validation properly compares version parts and provides clear error messaging.

The guard clause node_package_version.raw.nil? || node_package_version.local_path_or_url? is repeated at lines 157 and 181. Consider extracting to a helper method like skip_version_validation? for DRY:

def skip_version_validation?
  node_package_version.raw.nil? || node_package_version.local_path_or_url?
end

Then use:

return if skip_version_validation?
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2395751 and d7523d5.

📒 Files selected for processing (1)
  • lib/react_on_rails/version_checker.rb (4 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:

  • lib/react_on_rails/version_checker.rb
🧠 Learnings (1)
📚 Learning: 2025-10-23T17:22:01.064Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/version_checker.rb
🧬 Code graph analysis (1)
lib/react_on_rails/version_checker.rb (1)
lib/react_on_rails/utils.rb (3)
  • package_manager_install_exact_command (327-340)
  • react_on_rails_pro? (215-223)
  • package_manager_remove_command (346-359)
⏰ 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). (12)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: build
  • GitHub Check: markdown-link-check
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: claude-review
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
🔇 Additional comments (10)
lib/react_on_rails/version_checker.rb (10)

20-33: LGTM! Well-orchestrated validation entry point.

The method provides a clear, fail-fast validation flow with comprehensive documentation. The four validation steps are logically ordered and will catch all the misconfiguration scenarios mentioned in the PR objectives.


37-58: LGTM! Clear validation with actionable error messages.

The validation correctly checks for package.json existence and provides package manager-specific installation commands for both base and Pro packages.


60-154: LGTM! Comprehensive package/gem compatibility enforcement.

The validation suite correctly enforces mutual consistency between gem and package installations with clear, actionable error messages. The use of ReactOnRails::Utils.react_on_rails_pro? at line 63 properly validates licenses and raises early as intended.


156-178: LGTM! Enforces exact version requirement clearly.

The validation correctly prohibits semver ranges and wildcards with a clear error message that shows both the detected version and the expected gem version.


211-213: LGTM! Simple helper promotes consistent error messaging.


230-252: LGTM! Improved package detection with clear precedence.

The updated logic correctly prioritizes the Pro package and handles all edge cases. The warning at lines 247-250 is now redundant with validate_packages_installed! but serves as a useful debugging signal.


254-266: LGTM! Clean public interface for package detection.

The new methods provide a clear API for checking package installation and retrieving the canonical package name, with appropriate Pro package precedence.


268-291: LGTM! Excellent decomposition of semver wildcard detection.

The refactoring into focused helper methods significantly improves readability and maintainability. Each helper method has a clear, single responsibility and the patterns correctly identify all major semver range/wildcard syntaxes.


313-318: LGTM! Safe and defensive package presence check.

The method correctly handles missing files and safely navigates the package.json structure. Limiting the check to dependencies (not devDependencies) is appropriate for runtime validation.


324-345: LGTM! Robust JSON parsing with excellent error guidance.

The method properly handles JSON parsing errors with a comprehensive error message that lists common JSON syntax mistakes, making debugging much easier for users.

…r commands

This commit adds defense-in-depth protection against potential command injection
in package manager command generation methods.

Changes:
- Add Shellwords escaping for all package names and versions in generated commands
- Add input validation for package names following npm naming standards
- Add input validation for version strings to allow only safe semver patterns
- Validate inputs before command generation in both install and remove methods

Security benefits:
- Multiple layers of protection (validation + escaping)
- Clear error messages for invalid inputs
- Future-proof against usage pattern changes

All existing tests pass with these security enhancements.

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

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

claude bot commented Oct 27, 2025

Code Review: PR #1881 - Unified Release & Strict Version Validation

This is an excellent PR that improves React on Rails version management significantly.

Overall Assessment: APPROVE with minor suggestions

Strengths:

  • Comprehensive solution with excellent error messages
  • Strong security practices (input validation, prevents command injection)
  • Thorough test coverage (53 tests for validation, 17 for node renderer)
  • Well-documented with clear examples

Part 1: Unified Release Script - Excellent

Strengths:

  1. Atomic version updates across all 6 version files
  2. Semver support with safety features (dry run, verdaccio)
  3. Ruby version auto-switching for better DevX

Suggestions:

  • Add git tag existence check before creating (release.rake:188)
  • Add verdaccio connectivity validation before publish
  • Consider rollback logic if npm publish fails mid-way

Part 2: Strict Version Validation - Excellent

Security - EXCELLENT:

  • Prevents command injection via validate_package_name! and validate_package_command_inputs!
  • Uses Shellwords.escape for shell safety
  • Validates against npm package name specification

Strengths:

  • Comprehensive wildcard detection (version_checker.rb:268-291)
  • Package manager detection (npm, yarn, pnpm, bun)
  • Developer-friendly error messages with exact fix commands

Potential Issues:

  1. Performance: Validation runs on every Rails boot - consider making optional in production
  2. Edge case: Only checks dependencies, not devDependencies
  3. Missing newline: Pro version file write may not add trailing newline (release.rake:129)

Part 3: Node Renderer Validation - Excellent

Strengths:

  • Version normalization handles Ruby/NPM format differences elegantly
  • Environment-aware behavior (strict in dev, permissive in prod)
  • Good test coverage with 6 new comprehensive tests

Security Review: EXCELLENT

No security concerns identified. The input validation is exemplary:

  • Command injection prevention
  • Proper shell escaping
  • Validates package names against npm spec
  • Safe JSON parsing with error handling

Test Coverage: EXCELLENT

Ruby Tests: 53 tests covering all validation scenarios
TypeScript Tests: 17 tests with 6 new validation tests

Suggestion: Add integration test that runs release script in dry-run mode

Potential Bugs

  1. Missing newline - Pro version file write (release.rake:129) may not add trailing newline per CLAUDE.md requirements
  2. Git tag collision - No check if tag already exists before creating (release.rake:188)
  3. devDependencies ignored - Only checks dependencies in package.json validation

Performance Considerations

  1. Boot time impact: Validation on every Rails boot - consider caching or config flag for production
  2. Node renderer: Version check on every request - consider caching per process

Recommendations Before Merge

High Priority:

  1. ✅ Verify newline at EOF for pro_version_file write (release.rake:129)
  2. ⚠️ Add git tag existence check before creating tag
  3. ⚠️ Consider checking devDependencies in package.json

Medium Priority:

  1. 💡 Add config option to disable validation in production
  2. 💡 Add verdaccio connectivity check
  3. 💡 Document rollback procedure if release fails

Low Priority (Future):

  1. 💡 Cache validation results for performance
  2. 💡 Add integration test for release rake task

Final Verdict

Recommendation: APPROVE with minor fixes

This is high-quality work that significantly improves React on Rails maintainability and user experience.

The implementation demonstrates:

  • Strong understanding of Ruby and TypeScript ecosystems
  • Excellent security practices
  • Thoughtful error handling and UX
  • Comprehensive testing

The core functionality is solid and ready for merge after addressing the newline validation issue.

Great Work! 🎉

This PR exemplifies good software engineering:

  • Solves real problems with elegant solutions
  • Well-tested and thoroughly documented
  • Security-conscious throughout
  • User-friendly error messages
  • Maintains backward compatibility

Thank you for this valuable contribution to React on Rails!


Reviewed by: Claude Code
Review Date: 2025-10-27

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

🧹 Nitpick comments (3)
lib/react_on_rails/utils.rb (3)

301-312: Consider more precise regex for npm package names.

The current regex permits patterns that are invalid for npm packages:

  • Allows @ anywhere in the name (e.g., abc@def)
  • Scoped packages must follow @scope/package format exactly

While this permissive approach may work for basic safety checks, consider tightening the regex to match the npm spec more closely if stricter validation is desired.

Example of a more precise pattern:

-      return if package_name.match?(%r{\A[@a-z0-9][a-z0-9._/-]*\z}i)
+      # Match unscoped (alphanumeric + hyphens/dots/underscores) or scoped (@scope/package)
+      return if package_name.match?(%r{\A(@[a-z0-9-]+/)?[a-z0-9][a-z0-9._-]*\z}i)

319-331: Version regex is overly permissive for semver validation.

The regex allows versions starting with letters (e.g., abc.def.ghi), but the PR context indicates this is for exact version pinning. Semver versions should start with digits.

While npm tags like latest or next are valid version specifiers, they shouldn't be used with exact version pinning.

Apply this diff to enforce digit-first semver pattern:

-      return if version.match?(/\A[a-z0-9][a-z0-9._-]*\z/i)
+      # Enforce semver pattern: must start with digit, allows prerelease/build metadata
+      return if version.match?(/\A\d+(\.\d+)*(-[a-z0-9._-]+)?(\+[a-z0-9._-]+)?\z/i)
 
       raise ReactOnRails::Error, "Invalid version: #{version.inspect}. " \
-                                 "Versions must contain only alphanumeric characters, dots, hyphens, and underscores."
+                                 "Versions must follow semver format (e.g., 1.2.3, 1.2.3-beta.1)."

333-346: Overly broad exception handling masks potential issues.

The rescue StandardError (line 344) catches all errors silently, including JSON parsing failures, file permission errors, and encoding issues. This makes debugging difficult when package.json exists but has problems.

Consider catching specific exceptions to allow real errors to surface during development:

-    rescue StandardError
+    rescue JSON::ParserError, Errno::ENOENT, Errno::EACCES
       nil
     end

This still gracefully handles missing or malformed packageManager fields while allowing other errors to propagate in development mode.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7523d5 and a6b3636.

📒 Files selected for processing (1)
  • lib/react_on_rails/utils.rb (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:

  • lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/generators/react_on_rails/generator_messages.rb (1)
  • detect_package_manager (183-193)
lib/react_on_rails/version_checker.rb (2)
  • package_name (262-266)
  • package_json_path (222-224)
⏰ 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). (12)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: build
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: markdown-link-check
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: claude-review
  • GitHub Check: lint-js-and-ruby
🔇 Additional comments (5)
lib/react_on_rails/utils.rb (5)

8-8: LGTM! Good security practice.

Adding Shellwords for safe command construction is the right approach for preventing command injection vulnerabilities.


348-356: LGTM! Lock file detection logic is sound.

The detection order (yarn → pnpm → bun → npm) is reasonable, and checking at Rails.root is correct since lock files are always at the project root. The separation from package.json detection (which checks node_modules_location) is appropriate.


387-404: LGTM! Remove command generation is correct.

The method properly validates inputs, escapes package names, and uses the correct removal command for each package manager (including npm's uninstall).


364-381: Code changes are correct; Shellwords.escape preserves package@version format.

Verification confirms that Shellwords.escape does not modify the @ character since it's not a shell metacharacter. Both standard packages (react-on-rails@16.0.0) and scoped packages (@scope/package@1.0.0) are preserved correctly, so the commands generated for each package manager will work as intended.


287-295: Disregard this review comment – the flagged methods serve different purposes and intentionally have separate defaults.

These are two independent implementations with different signatures and contexts:

  • lib/react_on_rails/utils.rb::detect_package_manager is a public class method that returns symbols and defaults to :yarn for runtime package detection, with comprehensive logic (checks package.json first per Corepack standard, then lock files).
  • lib/generators/react_on_rails/generator_messages.rb::detect_package_manager is an instance method that returns strings and defaults to "npm" for generator/setup context (Shakapacker 8.x default), with simpler logic (checks lock files only).

They have different return types (symbols vs strings), different scopes, and are used in entirely different contexts. The different defaults are intentional, not an inconsistency requiring fixes.

Likely an incorrect or invalid review comment.

AbanoubGhadban and others added 2 commits October 27, 2025 14:19
Enhance the wildcard_or_x_range? method to catch more edge cases:
- Detect standalone wildcards: *, x, X
- Detect wildcard prefixes: *.1.2, x.1.2, X.1.2
- Detect wildcard in middle: 1.*.2, 1.x.2, 1.X.2
- Detect wildcard suffixes: 1.2.*, 1.2.x, 1.2.X

Previous pattern missed some valid npm wildcard patterns.
New pattern is more explicit and comprehensive.

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

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

claude bot commented Oct 27, 2025

Pull Request Review - PR #1881

Completed comprehensive review. This is impressive, well-architected work that significantly improves React on Rails release and version management.

Overall Assessment

Status: APPROVED with minor suggestions

Demonstrates excellent practices: unified versioning, comprehensive testing (53 Ruby + 17 TS tests), detailed docs, fail-fast validation, thoughtful error handling.

Key Strengths

1. Unified Release Process

  • Eliminates version drift between packages
  • Atomic updates across 6 version files
  • Excellent dry-run and Verdaccio testing
  • Automatic Ruby version detection/switching

2. Fail-Fast Version Validation (lib/react_on_rails/version_checker.rb)

  • Clear, actionable error messages with fix commands
  • Early detection at boot time
  • Package manager detection
  • Handles edge cases (local paths, git URLs)

3. Node Renderer Version Validation

  • Environment-aware: strict dev (412 error), permissive prod (warning)
  • Smart version normalization (4.0.0.rc.1 → 4.0.0-rc.1)
  • Caching to avoid log spam

4. Test Coverage

  • 9 new Ruby validation tests
  • 6 new TypeScript version tests
  • Comprehensive edge case coverage

5. Security Practices

  • Input validation before shell commands
  • Shellwords.escape used consistently
  • Regex validation for packages/versions

Issues Found

Critical: None

High Priority Suggestions:

  1. Release script (rakelib/release.rake:106-120): Pro gem version uses string replacement while core uses gem bump. Consider verification step to ensure both match.

  2. Version normalization (checkProtocolVersionHandler.ts:24-34): normalizeVersion() silently returns empty string for null/undefined. Consider explicit validation/logging.

Improvement Suggestions

Code Quality:

  • DRY opportunity in Utils (lib/react_on_rails/utils.rb:364-404) for package manager commands
  • Version cache unbounded (checkProtocolVersionHandler.ts:14) - document or add max size
  • Add detected package manager to validation errors

Documentation:

  • Document release failure recovery/rollback procedures
  • Add inline examples for version normalization cases

Performance:

  • Consider caching validation results in production
  • Monitor regex performance if it becomes hot path

Security:

  • Validate auth files exist before attempting publish
  • Document Verdaccio should not be public-facing

Testing:

  • Add E2E test for release script dry-run
  • Unit tests for package manager detection
  • Snapshot tests for error messages

Final Verdict

LGTM ✅

Merge confidence: High

Production-ready code. Suggestions are minor improvements that don't block merging.

Special recognition for:

  • Environment-aware error handling
  • Actionable error messages with fix commands
  • Comprehensive test coverage
  • Security attention to detail

Excellent work! 🚀


Review by Claude Code per CLAUDE.md

@claude
Copy link

claude bot commented Oct 27, 2025

Code Review: Unify release scripts and add strict version validation

I've completed a comprehensive review of PR #1881. This is an excellent PR that significantly improves the React on Rails release process and version management. Here's my detailed analysis:


🎯 Overall Assessment

Verdict: ✅ APPROVE with minor suggestions

This PR demonstrates exceptional engineering quality with:

  • Well-architected unified release system
  • Robust error handling and validation
  • Comprehensive test coverage
  • Clear documentation
  • Security-conscious implementation

🌟 Strengths

1. Unified Release Script (rakelib/release.rake)

Excellent Design:

  • Atomic updates across all 5 packages prevent version drift
  • Automatic Ruby version detection/switching is a great developer experience improvement
  • Support for semver bumps AND explicit versions provides flexibility
  • Dry-run and Verdaccio testing modes reduce deployment risk

Security:

  • Good validation of registry and skip_push parameters
  • Uncommitted changes check prevents accidental releases
  • Clear separation between public (npmjs/rubygems) and private (GitHub Packages) registries

2. Version Validation (lib/react_on_rails/version_checker.rb)

Robust Implementation:

  • Fail-fast approach is the correct strategy for configuration errors
  • Clear separation of validation concerns (package existence, compatibility, exact version, version match)
  • Excellent error messages with actionable fix instructions
  • Good use of DRY principle with helper methods

Highlights:

  • semver_wildcard? method is comprehensive, catching all edge cases (*, x, X, special strings like 'latest', range operators)
  • JSON parsing error handling is excellent with helpful debugging tips
  • Package manager detection and command generation is well-abstracted

3. Node Renderer Validation (checkProtocolVersionHandler.ts)

Smart Design:

  • Environment-aware behavior (strict in dev, permissive in prod) is the right balance
  • Version caching prevents log spam and improves performance
  • normalizeVersion() handles Ruby gem vs NPM format differences elegantly
  • Clear separation between protocol version and gem version validation

4. Package Manager Utilities (lib/react_on_rails/utils.rb)

Security-Conscious:

  • Input validation with validate_package_name\! and validate_package_command_inputs\! prevents command injection
  • Use of Shellwords.escape is correct practice
  • Regex validation follows npm package naming standards

Good Coverage:

  • Support for all major package managers (npm, yarn, pnpm, bun)
  • Follows Node.js Corepack standard (packageManager field)
  • Sensible fallback to yarn as default

📋 Code Quality Assessment

Best Practices ✅

  • DRY principle: Shared helpers like package_installed?, validate_package_name\!
  • Single Responsibility: Each validation method has a clear, focused purpose
  • Clear naming: Method names are self-documenting
  • Error messages: Actionable with specific fix commands
  • Security: Input validation and shell escaping throughout
  • Documentation: Inline comments and YARD docs where helpful

Test Coverage ✅

Based on the PR description:

  • Ruby: 9 new test cases for validation scenarios + 3 new fixtures = 53 total tests passing
  • TypeScript: 6 new tests + 11 updated tests = 17 total tests passing
  • Good coverage of edge cases (version normalization, env detection, caching)

🔍 Potential Issues & Suggestions

1. Minor: Double Negation in Code

# lib/react_on_rails/version_checker.rb:132
def validate_pro_package_has_pro_gem\!(is_pro_gem, has_pro_package)
  return unless \!is_pro_gem && has_pro_package  # Consider: return if is_pro_gem || \!has_pro_package

Suggestion: De Morgan's law would make this more readable:

return if is_pro_gem || \!has_pro_package

2. Minor: Regex Optimization

# lib/react_on_rails/version_checker.rb:290
def range_operator?
  raw.start_with?(/[~^><*]/)  # Regex not needed for character class
end

Suggestion: Use string character class for better performance:

def range_operator?
  raw.start_with?('^', '~', '>', '<', '*')
end

3. Enhancement: Package Manager Detection Could Handle Edge Cases

The package manager detection silently catches all exceptions:

# lib/react_on_rails/utils.rb:344
rescue StandardError
  nil
end

Suggestion: Consider logging when JSON parsing fails to aid debugging:

rescue StandardError => e
  Rails.logger.debug("Failed to detect package manager from package.json: #{e.message}")
  nil
end

4. Enhancement: Version Normalization Could Document Edge Cases

// checkProtocolVersionHandler.ts:31
normalized = normalized.replace(/^(\d+\.\d+\.\d+)\.([a-z]+)/, '$1-$2');

This handles 4.0.0.rc.1 but what about 4.0.0.rc (no number)? Or multiple dots like 4.0.0.rc.1.2?

Suggestion: Add test cases for these edge cases or document assumptions.

5. Performance: Version Regex Could Be Compiled Once

# lib/react_on_rails/version_checker.rb:10
VERSION_PARTS_REGEX = /(\d+)\.(\d+)\.(\d+)(?:[-.]([0-9A-Za-z.-]+))?/

This is already a constant - good! But in the NodePackageVersion class methods, regexes are inline:

raw =~ /^[xX*]$/
raw =~ /^[xX*]\./

Suggestion: Consider extracting these to constants if performance becomes a concern.


🔒 Security Analysis

✅ Strengths:

  1. Command Injection Prevention:

    • Input validation regex for package names and versions
    • Shellwords.escape used consistently
    • No direct string interpolation into shell commands
  2. Input Validation:

    • Package names validated against npm naming standards
    • Version strings validated against semver patterns
    • Registry parameter whitelist (only 'verdaccio', 'npm', or empty)
  3. Error Handling:

    • JSON parse errors caught and handled gracefully
    • File existence checks before reads
    • Clear error messages don't leak sensitive information

⚠️ Minor Concerns:

  1. Environment Variable Trust:

    • Node renderer trusts NODE_ENV and railsEnv for security decisions
    • This is generally fine but document that production detection should be reliable
  2. Version Cache in Node Renderer:

    • Cache is in-memory and unbounded
    • Unlikely to be an issue in practice, but could theoretically grow with many version changes
    • Suggestion: Consider a max cache size or TTL if this becomes a concern

🧪 Test Coverage Analysis

What's Tested Well:

  • ✅ Version mismatch scenarios
  • ✅ Package compatibility (base vs pro)
  • ✅ Version normalization (Ruby gem ↔ NPM format)
  • ✅ Environment-aware behavior (dev vs prod)
  • ✅ Caching behavior
  • ✅ Package manager detection
  • ✅ JSON parsing errors
  • ✅ Semver wildcard detection

Potential Gaps:

  1. Release Script:

    • No automated tests for the rake task itself (likely tested manually)
    • Consider adding integration tests or at least dry-run tests in CI
  2. Edge Cases:

    • Multiple pre-release identifiers (4.0.0.rc.1.2)
    • Unicode in package names (probably won't happen, but worth considering)
    • Very long version strings
  3. Error Recovery:

    • What happens if Verdaccio is down?
    • What happens if GitHub Packages auth fails?
    • These might be out of scope but worth documenting

📚 Documentation Quality

✅ Excellent:

  • Comprehensive PR description with examples
  • Updated releasing.md with clear instructions
  • Inline code comments where needed
  • YARD documentation for public methods
  • Migration notes for contributors and users

💡 Suggestions:

  1. Add a troubleshooting section for common version mismatch errors
  2. Document the version cache behavior in the node renderer
  3. Add examples of invalid package names/versions to validation method docs

🚀 Performance Considerations

✅ Good:

  • Version cache in node renderer prevents repeated normalization
  • Package manager detection is lazy and cached in instance variables
  • Regex compilation happens once (constants)

💡 Minor Optimizations:

  1. package_json_contents is memoized but parsed_package_contents parses on every validator call
    • This is fine since validation happens once at boot
  2. File existence checks happen multiple times - could batch if performance critical

Overall: Performance is not a concern for this code. It runs at boot and on request validation, not in hot paths.


🎨 Code Style

Adherence to CLAUDE.md:

  • ✅ RuboCop compliance (confirmed by PR description: "Zero RuboCop offenses")
  • ✅ Files end with newlines
  • ✅ Proper use of Prettier/RuboCop for formatting
  • ✅ Following existing code conventions

Ruby Style:

  • ✅ Proper use of private_class_method
  • ✅ Guard clauses for early returns
  • frozen_string_literal: true
  • ✅ Clear method names

TypeScript Style:

  • ✅ Proper typing with interfaces
  • ✅ Clear function documentation
  • ✅ Sensible variable names

🎓 Learning Points

This PR demonstrates several excellent engineering practices:

  1. Unified Versioning: Single source of truth prevents drift
  2. Fail-Fast Validation: Catch errors early with clear messages
  3. Environment-Aware Behavior: Strict in dev, lenient in prod
  4. Security by Default: Input validation and escaping throughout
  5. Developer Experience: Auto-detection, clear errors, actionable fixes
  6. Comprehensive Testing: Edge cases and integration scenarios covered

✅ Recommendations

Must Do:

  • ✅ This PR is ready to merge as-is

Should Consider (for this PR or follow-ups):

  1. Apply De Morgan's law to validate_pro_package_has_pro_gem\! condition
  2. Optimize range_operator? to use string character class
  3. Add debug logging to package manager detection error handling
  4. Document version normalization edge cases or add test coverage
  5. Consider max cache size for node renderer version cache

Nice to Have (follow-up PRs):

  1. Integration tests for release rake task
  2. Troubleshooting guide for common version errors
  3. Metrics/telemetry for version mismatch frequency in production

🏁 Conclusion

This is outstanding work. The PR:

  • ✅ Solves real pain points (version drift, unclear errors)
  • ✅ Improves developer experience significantly
  • ✅ Follows security best practices
  • ✅ Has excellent test coverage
  • ✅ Is well-documented
  • ✅ Maintains backward compatibility where appropriate
  • ✅ Follows project conventions

The unified versioning approach is the right architectural decision, and the implementation is solid. The fail-fast validation will save users hours of debugging time.

Recommendation: Merge after addressing any minor suggestions the team feels are important. The suggestions above are truly optional - this PR is merge-ready as-is.

Great work! 🎉


Reviewed with ❤️ by Claude Code

@claude
Copy link

claude bot commented Oct 27, 2025

Comprehensive Code Review: PR #1881

Summary

This is an excellent, well-architected PR that significantly improves React on Rails' version management and release process. The three-part approach (unified release script, strict validation, and node renderer validation) is well thought out and thoroughly tested.

✅ Strengths

1. Exceptional Code Quality

  • Clean architecture: Version validation is well-structured with single-responsibility methods
  • Excellent error messages: Clear, actionable guidance for users (includes detected values, expected values, and exact fix commands)
  • DRY principle: Good use of helper methods like package_installed?() and shared validation logic
  • Comprehensive documentation: Outstanding PR description with examples and migration notes

2. Outstanding Test Coverage

  • 53 Ruby tests covering all validation scenarios (both packages installed, version mismatches, Pro/base compatibility, etc.)
  • 17 TypeScript tests for node renderer (11 updated + 6 new)
  • Test quality: Uses proper mocking, covers edge cases, and verifies both positive and negative cases
  • Fixtures: Well-organized test fixtures for different package.json scenarios

3. Security - Well Handled

  • Command injection prevention: validate_package_name!() and validate_package_command_inputs!() thoroughly validate inputs (lib/react_on_rails/utils.rb:297-331)
  • Shellwords.escape: Properly escapes shell arguments before command construction (lib/react_on_rails/utils.rb:369, 392)
  • Input validation: Uses strict regex patterns matching npm package name spec
  • Private methods: Validation methods are appropriately marked as private_class_method

4. Excellent User Experience

  • Package manager detection: Automatically detects yarn/npm/pnpm/bun via packageManager field + lock files
  • Clear error messages: All validation errors include the exact command to fix the issue
  • Environment-aware: Node renderer validation is strict in dev, permissive in production
  • Fail-fast: Boot-time validation catches issues immediately

🔍 Code Quality Observations

Very Good

  1. Version normalization (checkProtocolVersionHandler.ts):

    • Handles Ruby gem format (4.0.0.rc.1) vs NPM format (4.0.0-rc.1)
    • Case-insensitive, whitespace-trimmed
    • Well-tested with 6 comprehensive tests
  2. Semver wildcard detection (version_checker.rb:268-295):

    • Comprehensive coverage of all npm semver patterns
    • Split into logical helper methods for clarity
    • Handles special strings (latest, next, canary, beta, alpha, rc)
  3. Release script (rakelib/release.rake):

    • Atomic version updates across 6 files
    • Good validation of arguments
    • Clear dry-run support
    • Verdaccio testing support

🐛 Potential Issues / Suggestions

Minor Issues

  1. Double negation could be clearer (version_checker.rb:132)

    def validate_pro_package_has_pro_gem!(is_pro_gem, has_pro_package)
      return unless !is_pro_gem && has_pro_package  # Could be: return if is_pro_gem || !has_pro_package

    This works but return if is_pro_gem || !has_pro_package is more readable.

  2. Potential race condition in package manager detection

    • detect_package_manager() reads package.json twice (once in detect_package_manager_from_package_json, potentially again in validation)
    • Consider memoizing the result or passing parsed JSON to avoid TOCTOU issues
    • Impact: Low (mainly a performance optimization, not a security issue)
  3. Missing validation for empty dependencies object

    • NodePackageVersion#raw (version_checker.rb:236) checks for parsed.key?("dependencies") but doesn't validate it's a Hash
    • If someone sets "dependencies": null or "dependencies": [], it could raise an exception
    • Suggestion: Add parsed["dependencies"].is_a?(Hash) check

Questions for Maintainer

  1. Breaking change consideration: The new boot-time validation will cause apps with misconfigured package.json to fail to start. Is this documented in the upgrade guide?

  2. Rails initializer ordering: The validation runs on config.after_initialize. Could this interact poorly with other gems that also validate at boot time?

  3. Performance: The validation reads and parses package.json on every boot. For large monorepos, is this acceptable? Consider caching in production.

📊 Performance Considerations

Good:

  • Validation only runs once at boot (via Rails initializer)
  • Package manager detection has multiple fallbacks

Potential optimization:

  • Memoize package.json parsing to avoid reading the file multiple times during validation
  • Consider skipping validation in production if package was validated during deployment

🔒 Security Review - Excellent ✅

The PR demonstrates excellent security practices:

  1. Input validation is thorough and follows best practices
  2. Command injection protection is properly implemented with both validation and escaping
  3. Regex patterns use proper anchors (\A and \z) to prevent bypass
  4. No dynamic code execution or unsafe file operations
  5. Proper error handling without exposing sensitive paths

📝 Documentation

Strengths:

  • Excellent PR description with clear examples
  • Updated contributor documentation (releasing.md, CONTRIBUTING.md)
  • Clear inline comments explaining complex logic

Suggestions:

  • Consider adding a migration guide for users upgrading from older versions
  • Document the new boot-time validation behavior in the main README

🎯 Recommendations

Before Merge:

  1. Address the double negation in validate_pro_package_has_pro_gem! for clarity
  2. Add null/array check for dependencies object in NodePackageVersion#raw
  3. Run full linting: Ensure bundle exec rubocop passes with zero offenses (per CLAUDE.md)

Future Enhancements (not blocking):

  1. Consider memoizing package.json parsing for performance
  2. Add upgrade guide to CHANGELOG.md
  3. Consider adding a REACT_ON_RAILS_SKIP_VERSION_CHECK env var for advanced users who know what they're doing

✅ Final Verdict

LGTM with minor suggestions - This is high-quality, production-ready code that significantly improves the project. The architecture is sound, testing is thorough, and security is well-handled.

Recommended action: Merge after addressing the minor clarity issues mentioned above.


Review completed by Claude Code
Review time: 2025-10-27
Files reviewed: 24 changed files, 1,800+ additions

@AbanoubGhadban AbanoubGhadban removed the request for review from alexeyr-ci October 27, 2025 11:34
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 (3)
docs/contributor-info/releasing.md (3)

63-64: Fix list formatting for PRIVATE packages.

Both items are rendered on one line. Split into proper list items for readability.

Apply this diff:

-**PRIVATE (GitHub Packages):** 4. **@shakacode-tools/react-on-rails-pro-node-renderer** - NPM package 5. **react_on_rails_pro** - RubyGem
+**PRIVATE (GitHub Packages):**
+
+4. **@shakacode-tools/react-on-rails-pro-node-renderer** - NPM package
+5. **react_on_rails_pro** - RubyGem

160-169: Add language tags to fenced code blocks.

The .npmrc snippet (line 162) and the export command (line 168) code blocks need language identifiers for proper rendering and to satisfy markdownlint.

Apply this diff:

   - Add to `~/.npmrc`:
-  ```
+  ```ini
   //npm.pkg.github.com/:_authToken=<TOKEN>
   always-auth=true
  • Set environment variable:
  • export GITHUB_TOKEN=<TOKEN>

---

`180-182`: **Add language tag to fenced code block.**

The credentials file snippet is missing a language identifier, triggering MD040 linting failures.




Apply this diff:

```diff
   - Add to `~/.gem/credentials`:
-  ```
+  ```yaml
   :github: Bearer <GITHUB_TOKEN>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>docs/contributor-info/releasing.md (1)</summary><blockquote>

`3-3`: **Consider clarifying "unified versioning" terminology.**

The phrase "using unified versioning" followed by "We keep the version numbers in sync" is somewhat redundant. Consider being more explicit about what this means.



For example:

```diff
-We're releasing this as a unified release with 5 packages total. We keep the version numbers in sync across all packages using unified versioning.
+We're releasing this as a unified release with 5 packages total. All packages share the same version number and are released together (unified versioning).
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (4)

63-80: Cache by normalized gem version to dedupe logs and comparisons.

Currently the cache keys on the raw gemVersion, causing duplicate work/logs for equivalent variants (case/whitespace/prerelease dot vs hyphen). Key by normalized value.

-    // Check cache first
-    let versionsMatch = versionCache.get(gemVersion);
-    let justCached = false;
-
-    // If not in cache, perform comparison and cache the result
-    if (versionsMatch === undefined) {
-      const normalizedGemVersion = normalizeVersion(gemVersion);
-      const normalizedPackageVersion = normalizeVersion(packageJson.version);
-      versionsMatch = normalizedGemVersion === normalizedPackageVersion;
+    const normalizedGemVersion = normalizeVersion(gemVersion);
+    // Check cache first using normalized key
+    let versionsMatch = versionCache.get(normalizedGemVersion);
+    let justCached = false;
+
+    // If not in cache, perform comparison and cache the result
+    if (versionsMatch === undefined) {
+      const normalizedPackageVersion = normalizeVersion(packageJson.version);
+      versionsMatch = normalizedGemVersion === normalizedPackageVersion;
 
       // Clear cache if it exceeds max size to prevent unbounded growth
       if (versionCache.size >= VERSION_CACHE_MAX_SIZE) {
         versionCache.clear();
       }
 
-      versionCache.set(gemVersion, versionsMatch);
+      versionCache.set(normalizedGemVersion, versionsMatch);
       justCached = true;
     }

11-15: Make cache-eviction comment match behavior.

Comment says “exceeds 10” but code clears at >= 10. Clarify to avoid confusion.

-// Cache is cleared when it exceeds 10 entries to prevent unbounded growth
+// Cache is cleared when it reaches VERSION_CACHE_MAX_SIZE entries to prevent unbounded growth

1-4: Fix JSDoc module name typo.

- * @module worker/checkProtocVersionHandler
+ * @module worker/checkProtocolVersionHandler

91-93: Include env context in the production warning for easier debugging.

-          log.warn(mismatchMessage);
+          log.warn(
+            `${mismatchMessage} (railsEnv=${railsEnv ?? 'undefined'}, NODE_ENV=${process.env.NODE_ENV ?? 'undefined'})`
+          );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6b3636 and d16d32e.

📒 Files selected for processing (3)
  • docs/contributor-info/releasing.md (3 hunks)
  • lib/react_on_rails/version_checker.rb (4 hunks)
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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:

  • docs/contributor-info/releasing.md
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
{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:

  • lib/react_on_rails/version_checker.rb
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
🧠 Learnings (1)
📚 Learning: 2025-10-23T17:22:01.064Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.064Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/version_checker.rb
🧬 Code graph analysis (2)
lib/react_on_rails/version_checker.rb (1)
lib/react_on_rails/utils.rb (3)
  • package_manager_install_exact_command (364-381)
  • react_on_rails_pro? (216-224)
  • package_manager_remove_command (387-404)
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (1)
react_on_rails_pro/packages/node-renderer/src/worker/types.ts (1)
  • FastifyRequest (11-11)
🪛 markdownlint-cli2 (0.18.1)
docs/contributor-info/releasing.md

180-180: 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). (12)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: markdown-link-check
  • GitHub Check: claude-review
🔇 Additional comments (9)
lib/react_on_rails/version_checker.rb (8)

20-33: LGTM: Well-structured validation orchestration.

The new entry point properly orchestrates all compatibility checks in a logical sequence. The documentation clearly explains what each validation enforces.


37-58: LGTM: Clear error messaging with actionable remediation.

The validation correctly detects missing package.json and provides package-manager-aware installation commands for both base and Pro scenarios.


60-154: LGTM: Comprehensive package-gem compatibility validation.

The four-tier validation correctly enforces:

  1. At least one package must be present
  2. Mutual exclusivity of base and Pro packages (Pro supersedes base)
  3. Pro gem requires Pro package
  4. Pro package requires Pro gem (with fallback option to base)

All error messages provide actionable, package-manager-aware remediation steps.


156-178: LGTM: Proper exact-version enforcement.

The validation correctly skips legitimate cases (nil, local paths/URLs) and enforces exact versioning with clear error messaging that lists prohibited semver syntax.


180-201: LGTM: Version match validation is sound.

Correctly compares parsed version components for exact match, with appropriate edge-case handling and actionable error messaging.


230-266: LGTM: Improved package detection with appropriate precedence.

The refactored raw method correctly:

  • Uses shared JSON parsing
  • Prioritizes Pro over base package (correct precedence)
  • Logs a warning for missing packages (hard errors are raised in validation methods)

The new helper methods (react_on_rails_package?, react_on_rails_pro_package?, package_name) provide clean, testable abstractions.


268-295: Comprehensive semver range detection.

The granular helpers correctly identify all forms of non-exact versions:

  • Special strings (latest, next, canary, etc.)
  • Wildcard/x-ranges (*, x, X in various positions)
  • Range operators (^, ~, >, <)
  • Range syntax (" - ", " || ")

Minor observation: Line 272 treats blank as a wildcard, which is pragmatic for validation purposes but semantically a blank value is more "invalid/missing" than a "wildcard." This is acceptable as a defensive check.


328-349: LGTM: Robust JSON parsing with excellent error guidance.

The memoized parsing includes comprehensive error handling that provides:

  • Exact file location
  • Original error message
  • Common JSON syntax issues to check

This will significantly improve the debugging experience for malformed package.json files.

react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (1)

26-36: normalizeVersion implementation looks solid.

Good handling of prerelease normalization and casing; simple and readable.

Comment on lines +9 to +10
const NODE_ENV = process.env.NODE_ENV || 'production';

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 | 🔴 Critical

🧩 Analysis chain

Avoid defaulting NODE_ENV to 'production' and avoid import-time capture.

Defaulting to production can mask dev mismatches; capturing at import time makes tests brittle. Compute from env at request time and keep your OR semantics.

-const NODE_ENV = process.env.NODE_ENV || 'production';
+// Read NODE_ENV at request time to reflect the current environment.
-      const isProduction = railsEnv === 'production' || NODE_ENV === 'production';
+      const isProduction = railsEnv === 'production' || process.env.NODE_ENV === 'production';

Please run your node-renderer tests with scenarios where:

  • railsEnv='development', NODE_ENV unset
  • railsEnv='development', NODE_ENV='production'
  • railsEnv unset, NODE_ENV='production'
    to confirm the intended permissive/strict behavior.

Also applies to: 84-85


🏁 Script executed:

#!/bin/bash
# Examine the target file to understand current implementation
find . -name "checkProtocolVersionHandler.ts" -type f | head -5

Length of output: 85


🏁 Script executed:

#!/bin/bash
# View the file content, focusing on lines around 9-10 and 84-85
cat react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts

Length of output: 4077


🏁 Script executed:

#!/bin/bash
# Search for all usages of NODE_ENV in the codebase
rg -n "NODE_ENV" --type ts --type js -C 2

Length of output: 121634


🏁 Script executed:

#!/bin/bash
# Look for test files related to checkProtocolVersionHandler
find . -name "*checkProtocolVersionHandler*" -o -name "*test*" -o -name "*spec*" | grep -i "checkprotocol\|worker" | head -20

Length of output: 197


🏁 Script executed:

#!/bin/bash
# Examine the test file
cat react_on_rails_pro/packages/node-renderer/tests/worker.test.ts | head -100

Length of output: 3448


🏁 Script executed:

#!/bin/bash
# Check if NODE_ENV const is used anywhere else in the handler file
grep -n "NODE_ENV" react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts

Length of output: 142


🏁 Script executed:

#!/bin/bash
# Look for any tests that modify NODE_ENV or test environment behavior
rg -n "NODE_ENV\s*=" react_on_rails_pro/packages/node-renderer/tests/ --type ts

Length of output: 0


Remove the import-time NODE_ENV capture and read from environment at request time.

The concern is valid: capturing process.env.NODE_ENV at import time freezes its value, and defaulting to 'production' can mask development scenarios. This is especially problematic when NODE_ENV is unset—the code should use development behavior by default, not production.

At line 84, change from reading the captured NODE_ENV constant to reading process.env.NODE_ENV directly at request time to reflect the current environment state and avoid surprising defaults.

-const NODE_ENV = process.env.NODE_ENV || 'production';
+// Read NODE_ENV at request time to reflect the current environment.
-      const isProduction = railsEnv === 'production' || NODE_ENV === 'production';
+      const isProduction = railsEnv === 'production' || process.env.NODE_ENV === 'production';
🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
around lines 9-10 and at line 84, remove the import-time capture const NODE_ENV
= process.env.NODE_ENV || 'production'; and instead read process.env.NODE_ENV at
request time where the code currently uses the NODE_ENV constant (line 84);
change the logic to treat an unset NODE_ENV as development behavior (e.g.,
default to 'development' or branch on falsy to development) so the environment
is evaluated per-request rather than frozen at module import.

Comment on lines +45 to +46
const { protocolVersion: reqProtocolVersion, gemVersion, railsEnv } = req.body as RequestBody;

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 | 🔴 Critical

Prevent crash when req.body is undefined (safe destructuring).

Destructuring req.body directly can throw if body is missing. Use a safe default to avoid runtime errors.

-  const { protocolVersion: reqProtocolVersion, gemVersion, railsEnv } = req.body as RequestBody;
+  const {
+    protocolVersion: reqProtocolVersion,
+    gemVersion,
+    railsEnv,
+  } = (req.body as Partial<RequestBody>) ?? {};
🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
around lines 45 to 46, destructuring req.body directly can throw when body is
undefined; change to safely destructure from a default empty object (and adjust
types if needed) so reqProtocolVersion, gemVersion and railsEnv are read from
req.body || {} to avoid runtime crashes when req.body is missing.

…ing error message enhancements, strict version validation, and command injection protection.
@claude
Copy link

claude bot commented Oct 27, 2025

Pull Request Review: Unify release scripts and add strict version validation

I've completed a comprehensive review of this PR. Overall, this is excellent work that significantly improves the React on Rails release process and runtime safety. The implementation is thorough, well-tested, and follows best practices.

✅ Strengths

1. Unified Release Script (Part 1)

  • Atomic version updates: All 6 version files updated together eliminates version drift
  • Excellent command interface: Support for semver bumps (patch/minor/major) plus explicit versions
  • Testing support: Verdaccio integration and dry-run mode are great additions
  • Auto Ruby switching: Smart detection and switching of Ruby versions is a quality-of-life improvement
  • Clear documentation: The updated docs/contributor-info/releasing.md is comprehensive and well-organized

2. Strict Version Validation (Part 2)

  • Fail-fast approach: Catching misconfigurations at boot time prevents obscure runtime errors
  • Excellent error messages: Clear, actionable errors with package-manager-specific commands
  • Comprehensive coverage: Handles all major misconfiguration scenarios:
    • Missing package.json
    • Both packages installed
    • Pro gem with base package
    • Pro package without Pro gem
    • Semver wildcards
    • Version mismatches
  • DRY implementation: Good use of helper methods (package_installed?, package_name, etc.)
  • Security hardening: Input validation and Shellwords.escape prevent command injection

3. Node Renderer Version Validation (Part 3)

  • Smart environment-aware behavior: Strict in dev (412 error), permissive in prod (warning)
  • Version normalization: Handles Ruby gem vs NPM format differences (4.0.0.rc.14.0.0-rc.1)
  • Performance optimization: Version cache prevents repeated normalization and log spam
  • Clean implementation: Caching with justCached flag ensures warnings only logged once

4. Test Coverage

  • Comprehensive Ruby tests: 53 tests with 9 new test cases for validation scenarios
  • Thorough TypeScript tests: 17 tests (11 updated + 6 new) for node renderer validation
  • Good fixtures: Added 3 new fixture files for testing Pro package scenarios
  • Zero RuboCop offenses: Code quality maintained

🔍 Code Quality Observations

Security ✅

  • Command injection protection: Proper use of Shellwords.escape in lib/react_on_rails/utils.rb:287-404
  • Input validation: validate_package_name! and validate_package_command_inputs! prevent unsafe inputs
  • Regex validation: Package names and versions validated against safe patterns

Performance ✅

  • Caching strategy: Version cache in node renderer prevents redundant normalization
  • Cache size management: 10-entry limit prevents unbounded growth
  • Early returns: Validation methods use early returns to avoid unnecessary checks

Maintainability ✅

  • Clear separation of concerns: Each validation in its own method
  • Consistent error format: All errors follow the same structure with clear fixes
  • Package manager abstraction: Detection logic cleanly separated from command generation
  • Good naming: Methods like validate_pro_gem_uses_pro_package! are self-documenting

📝 Minor Suggestions

1. Release Script - Ruby Version Detection (rakelib/task_helpers.rb:91-101)

The current implementation correctly handles most Ruby version formats. Consider this optional enhancement for patchlevel suffixes:

match = output.strip.match(/ruby\s+([\d.]+)(?:p\d+)?(?:-([a-zA-Z0-9.]+))?/)
version = match && [match[1], match[2]].compact.join("-")

This would handle ruby 3.2.1p20 more explicitly (though current code works fine).

2. Package Manager Detection (lib/react_on_rails/utils.rb:287-356)

Excellent implementation! The dual detection (packageManager field + lock files) follows Node.js Corepack standards. The fallback to Yarn is reasonable given the project's history.

3. Version Normalization (checkProtocolVersionHandler.ts:26-35)

Current normalization handles the documented cases well. Consider adding support for prerelease without dots (e.g., 4.0.0.rc14.0.0-rc.1) if this format is used:

normalized = normalized
  .replace(/^(\d+\.\d+\.\d+)\.([a-z]+)\.(\d+)$/, '$1-$2.$3')
  .replace(/^(\d+\.\d+\.\d+)\.([a-z]+)(\d+)$/, '$1-$2.$3');

4. Error Message Consistency (lib/react_on_rails/version_checker.rb)

All error messages are excellent and actionable. One minor enhancement: the messages could include which package manager was detected to help users understand why a particular command is suggested.

🎯 Testing Recommendations

Additional Test Cases to Consider:

  1. Package manager detection edge cases:

    • Multiple lock files present
    • Malformed packageManager field in package.json
  2. Version normalization edge cases:

    • Version without prerelease dot: 4.0.0.rc1
    • Mixed case in prerelease: 4.0.0-RC.1
    • Extra whitespace: 4.0.0
  3. Node renderer validation:

    • Missing railsEnv field (should default to dev behavior)
    • Both railsEnv='production' AND NODE_ENV='development'

🔒 Security Assessment

No security concerns identified

  • Input validation is comprehensive and prevents command injection
  • Regex patterns are safe and don't cause ReDoS
  • File operations use proper path validation
  • Error messages don't expose sensitive information

📊 Performance Assessment

No performance concerns

  • Boot-time validation runs once during initialization
  • Version cache prevents redundant processing
  • File reads are minimal and cached appropriately
  • No N+1 queries or inefficient loops

🎨 Best Practices Adherence

Follows all CLAUDE.md requirements:

  • Code formatted by RuboCop and Prettier
  • Files end with newlines
  • No manual formatting
  • Clear, descriptive commit messages
  • Comprehensive documentation updates

📚 Documentation Quality

Excellent documentation updates:

  • docs/contributor-info/releasing.md: Complete rewrite with examples and troubleshooting
  • react_on_rails_pro/CONTRIBUTING.md: Updated to reference unified script
  • Clear migration notes for users and contributors
  • Good use of examples and command syntax

🎉 Final Assessment

Overall Rating: ⭐⭐⭐⭐⭐ (5/5)

This PR represents a significant improvement to the React on Rails project:

  1. Unified versioning eliminates complexity - Single source of truth for all packages
  2. Fail-fast validation prevents runtime issues - Users get immediate, actionable feedback
  3. Environment-aware runtime checks - Balances strictness in dev with flexibility in prod
  4. Excellent test coverage - 70+ tests ensure reliability
  5. Security hardened - Command injection protection throughout
  6. Well documented - Clear guides for both users and contributors

Breaking Changes Assessment

The breaking changes are well-justified and properly communicated:

  1. Boot-time validation - Prevents silent failures, clear migration path
  2. Exact versions required - Best practice for production dependencies
  3. Node renderer validation - Only affects Pro users, permissive in production

The CHANGELOG clearly documents migration steps and the error messages provide exact commands to fix issues.

🚀 Recommendation

APPROVE - This PR is ready to merge pending CI/CD checks.

The implementation is solid, well-tested, properly documented, and follows all project conventions. The benefits (unified versioning, fail-fast validation, better DX) far outweigh the minor breaking changes, which are well-communicated and easily resolved.

Great work! 🎊


Reviewed Files:

  • lib/react_on_rails/version_checker.rb (260 lines changed)
  • lib/react_on_rails/utils.rb (120 lines added)
  • lib/react_on_rails/engine.rb (10 lines added)
  • rakelib/release.rake (147 additions, 53 deletions)
  • rakelib/task_helpers.rb (68 additions)
  • react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts (88 additions)
  • spec/react_on_rails/version_checker_spec.rb (300 additions)
  • react_on_rails_pro/packages/node-renderer/tests/worker.test.ts (149 additions)
  • docs/contributor-info/releasing.md (126 additions, 17 deletions)

Test Results:

  • Ruby: 53 tests passing, 0 RuboCop offenses
  • TypeScript: 17 tests passing (11 updated + 6 new)

@AbanoubGhadban AbanoubGhadban merged commit 0d87ea7 into master Oct 27, 2025
22 of 23 checks passed
@AbanoubGhadban AbanoubGhadban deleted the strict-version-validation branch October 27, 2025 12:50
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.

Unify release scripts and automate version management across all packages

3 participants