Skip to content

Conversation

@ihabadham
Copy link
Collaborator

@ihabadham ihabadham commented Jan 9, 2026

Summary

  • Wrap bundler commands in install_generator.rb with Bundler.with_unbundled_env to prevent BUNDLE_GEMFILE inheritance from the parent process
  • Fixes "injected gems" conflicts when running bundle exec rails g react_on_rails:install

Problem

When executing bundle exec rails g react_on_rails:install, the generator's internal bundler commands inherit the parent process's BUNDLE_GEMFILE environment variable, causing gem version conflicts with the error:

"[!] There was an error parsing injected gems: You cannot specify the same gem twice with different version requirements."

Root Cause

Three system() calls in install_generator.rb execute bundler commands without isolating the bundler environment:

  • system("bundle add shakapacker --strict")
  • system("bundle install")
  • system("bundle exec rails shakapacker:install")

Solution

Wrap bundler commands using Bundler.with_unbundled_env:

success = Bundler.with_unbundled_env { system("bundle add shakapacker --strict") }

This is the official solution documented by:

The pattern already exists in this codebase in process_manager.rb and pack_generator.rb.

Test plan

  • Verified fix clears BUNDLE_GEMFILE in child processes
  • Verified bundler discovers correct Gemfile in target app directory
  • RuboCop passes with no offenses

Fixes #2287

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Ensure bundler commands run in an unbundled context so installation tasks target the generated app’s Gemfile/Gemfile.lock, preventing environment leakage and improving example app generation reliability.
    • Added clarifying comments about the bundler execution context.
  • Tests

    • Added regression tests to verify bundler environment isolation and that local Gemfile/Gemfile.lock checks ignore inherited bundler environment variables.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Generator subprocesses that run bundler commands now execute inside Bundler.with_unbundled_env and Gemfile/Gemfile.lock checks always target the generated app’s files at project root, preventing inheritance of the parent process's BUNDLE_GEMFILE.

Changes

Cohort / File(s) Summary
Install generator
react_on_rails/lib/generators/react_on_rails/install_generator.rb
Switch Gemfile/Gemfile.lock checks to project-root files; wrap bundle add, bundle install, and bundle exec rails shakapacker:install calls in Bundler.with_unbundled_env; add comments referencing issue #2287.
Rake helper for examples
react_on_rails/rakelib/shakapacker_examples.rake
Replace direct rake shakapacker:install and generator invocations with unbundled_sh_in_dir wrappers so bundle/rake run against the generated app's Gemfile/Gemfile.lock.
Tests
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
Add regression tests ("bundler environment isolation") verifying Bundler.with_unbundled_env is used and that local Gemfile/Gemfile.lock checks do not rely on inherited BUNDLE_GEMFILE.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer / Rails generator
    participant Gen as InstallGenerator
    participant Bundler as Bundler.with_unbundled_env
    participant Shell as subprocess (bundle / rake)
    participant FS as Target app filesystem (Gemfile, Gemfile.lock)

    Dev->>Gen: run react_on_rails:install
    Gen->>FS: read project-root `Gemfile.lock` / `Gemfile`
    Gen->>Bundler: enter unbundled block
    Bundler->>Shell: run `bundle add shakapacker --strict`
    Shell->>FS: update target `Gemfile`/`Gemfile.lock`
    Bundler->>Shell: run `bundle install`
    Bundler->>Shell: run `bundle exec rails shakapacker:install`
    Shell->>FS: run shakapacker installer in target app
    Bundler-->>Gen: return success/failure
    Gen-->>Dev: complete with status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped from parent env to the app at my feet,
Cleared BUNDLE_GEMFILE so installs are neat.
Bundler unbound, commands find their home,
No more injected gems making me roam. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately summarizes the main change: fixing the generator's inheritance of BUNDLE_GEMFILE from the parent process, which is the core issue addressed.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #2287: wraps bundler calls in Bundler.with_unbundled_env, updates Gemfile/Gemfile.lock discovery, and adds comprehensive regression tests.
Out of Scope Changes check ✅ Passed All changes directly support the primary objective of isolating bundler environment; modifications to both install_generator.rb and shakapacker_examples.rake apply the same isolation pattern consistently across the codebase.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ihabadham/fix/generator-bundle-gemfile-inheritance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@claude
Copy link

claude bot commented Jan 9, 2026

Code Review - PR #2288

Summary: This PR fixes a legitimate bug where the install generator inherits BUNDLE_GEMFILE from parent processes, causing "injected gems" conflicts. The fix is clean, well-documented, and follows existing patterns in the codebase.


Strengths

1. Problem Correctly Identified

  • The issue is real and appears in CI logs ("injected gems" errors)
  • Root cause analysis is accurate: system() calls inherit environment variables
  • The PR description provides excellent context with links to Bundler documentation

2. Solution Follows Established Patterns

The fix uses Bundler.with_unbundled_env, which is already used elsewhere in the codebase:

  • lib/react_on_rails/dev/process_manager.rb:136
  • lib/react_on_rails/dev/pack_generator.rb:179
  • rakelib/task_helpers.rb

3. Minimal, Focused Change

  • Only 3 lines changed (exactly what's needed)
  • No unnecessary refactoring or scope creep
  • Clear diff that's easy to review

4. Code Quality

  • ✅ RuboCop passes
  • ✅ Consistent with project style
  • ✅ Clean, readable code

⚠️ Areas for Improvement

1. Bundler Version Compatibility (CRITICAL)

Issue: The code directly calls Bundler.with_unbundled_env, but other parts of the codebase use a compatibility wrapper.

In process_manager.rb:134-142, there's a with_unbundled_context helper that handles older Bundler versions:

def with_unbundled_context(&block)
  if Bundler.respond_to?(:with_unbundled_env)
    Bundler.with_unbundled_env(&block)
  elsif Bundler.respond_to?(:with_clean_env)
    Bundler.with_clean_env(&block)  # Legacy Bundler API
  else
    yield  # Fallback for very old versions
  end
end

Why this matters:

  • with_unbundled_env was added in Bundler 2.1.0 (Jan 2020)
  • Older Bundler versions use with_clean_env (deprecated but still works)
  • Ruby 3.0+ is required by this gem, which typically comes with Bundler 2.2+, so this is low risk
  • However, users can install older Bundler versions independently

Recommendation: Consider one of these approaches:

Option A (Recommended): Extract the compatibility helper to a shared module:

# In a new file: lib/react_on_rails/bundler_helper.rb
module ReactOnRails
  module BundlerHelper
    def with_unbundled_context(&block)
      if Bundler.respond_to?(:with_unbundled_env)
        Bundler.with_unbundled_env(&block)
      elsif Bundler.respond_to?(:with_clean_env)
        Bundler.with_clean_env(&block)
      else
        yield
      end
    end
    module_function :with_unbundled_context
  end
end

# Then use it in install_generator.rb:
include ReactOnRails::BundlerHelper
success = with_unbundled_context { system("bundle add shakapacker --strict") }

Option B (Acceptable): Document why direct usage is safe:

# Bundler.with_unbundled_env is available in Bundler 2.1.0+ (Jan 2020)
# Ruby 3.0+ (our minimum) typically ships with Bundler 2.2+
success = Bundler.with_unbundled_env { system("bundle add shakapacker --strict") }

Option C (Acceptable for now): Leave as-is and document that the gem requires Bundler 2.1+


2. Test Coverage

Issue: The existing test suite doesn't verify that Bundler.with_unbundled_env is actually called or that the environment is properly isolated.

Current tests (install_generator_spec.rb):

  • Test file generation and content
  • Test option flags (--redux, --typescript, --rspack)
  • Test helper messages
  • Do NOT test the Bundler isolation behavior

Recommendation: Add a test to verify the fix:

# In react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb

context "bundler environment isolation" do
  let(:install_generator) { described_class.new }

  it "runs bundle commands with unbundled environment" do
    # Mock system calls to prevent actual execution
    allow(install_generator).to receive(:shakapacker_in_gemfile?).and_return(false)
    
    # Verify that system() is called within Bundler.with_unbundled_env
    expect(Bundler).to receive(:with_unbundled_env).at_least(:once).and_call_original
    expect(install_generator).to receive(:system).with("bundle add shakapacker --strict").and_return(true)
    
    install_generator.send(:ensure_shakapacker_in_gemfile)
  end

  it "clears BUNDLE_GEMFILE in child processes" do
    # Set a fake BUNDLE_GEMFILE to simulate parent process
    ENV['BUNDLE_GEMFILE'] = '/fake/parent/Gemfile'
    
    bundler_env_in_block = nil
    Bundler.with_unbundled_env do
      bundler_env_in_block = ENV['BUNDLE_GEMFILE']
    end
    
    expect(bundler_env_in_block).to be_nil
    
    # Cleanup
    ENV.delete('BUNDLE_GEMFILE')
  end
end

This test would:

  • Verify Bundler.with_unbundled_env is actually called
  • Confirm the environment is properly isolated
  • Prevent future regressions

3. Minor: Error Handling

Observation: The current code correctly checks success return values:

success = Bundler.with_unbundled_env { system("bundle add shakapacker --strict") }
return if success  # Early return on failure

This is good! The error handling already exists via handle_shakapacker_gemfile_error and handle_shakapacker_install_error.

No change needed, but worth noting that the fix preserves existing error handling behavior.


🔒 Security Review

No security concerns

  • The change doesn't introduce new attack vectors
  • Bundler.with_unbundled_env is a standard, safe Bundler API
  • No user input is passed to system() calls
  • The commands are hardcoded and safe

🚀 Performance Review

No performance impact

  • Bundler.with_unbundled_env is a lightweight wrapper that temporarily modifies ENV
  • The bundler commands themselves are the slow operations (unchanged)
  • No additional I/O or computation introduced

📋 Testing Recommendations

Before Merge:

  1. Manual verification (as noted in PR description):

    cd react_on_rails/spec/dummy
    bundle exec rails g react_on_rails:install
    # Should complete without "injected gems" errors
  2. CI verification:

    • Wait for CI to pass (currently in progress)
    • Check that "injected gems" errors no longer appear in logs

After Merge (optional enhancements):

  1. Add test coverage for Bundler isolation (see suggestion above)
  2. Consider extracting compatibility helper if supporting older Bundler versions is desired

🎯 Overall Assessment

Rating: ✅ APPROVED with minor suggestions

This is a solid bug fix that:

  • ✅ Solves a real problem affecting CI and potentially users
  • ✅ Uses the correct Bundler API for environment isolation
  • ✅ Follows existing patterns in the codebase
  • ✅ Has minimal scope and clear intent
  • ✅ Includes excellent documentation in PR description

Minor improvements suggested:

  • ⚠️ Consider Bundler version compatibility (low priority, low risk)
  • 💡 Add test coverage for the fix (nice-to-have)

Recommendation: Merge after CI passes. The compatibility concern is low-risk given Ruby 3.0+ requirement, but could be addressed in a follow-up PR if needed.


📚 Additional Context

Per CLAUDE.md project guidelines:

  • ✅ This is a user-visible bug fix → Should be documented in CHANGELOG.md
  • ✅ RuboCop passes (required before merge)
  • ✅ Follows existing code patterns
  • ✅ Minimal, focused change (good!)

Great work @ihabadham! 🎉

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Fixes BUNDLE_GEMFILE inheritance from parent process by wrapping bundler commands with Bundler.with_unbundled_env.

  • Correctly wraps three system() calls with Bundler.with_unbundled_env to prevent environment variable pollution
  • Follows the documented solution from Bundler and Rails issues
  • Successfully prevents "injected gems" conflicts during generator execution

Minor improvement opportunity: The codebase has an established pattern in process_manager.rb and pack_generator.rb that uses a with_unbundled_context helper method with fallback logic for older Bundler versions. Consider using the same pattern for consistency, though Bundler.with_unbundled_env is available in all supported Bundler versions (2.x+).

Confidence Score: 4/5

  • This PR is safe to merge - it solves a real bug with a well-established solution
  • The fix correctly addresses the BUNDLE_GEMFILE inheritance issue using the officially documented solution. The implementation is straightforward and matches patterns already used elsewhere in the codebase. Minor style inconsistency with existing with_unbundled_context helper pattern prevents a perfect score, but doesn't affect functionality.
  • No files require special attention - the change is minimal and localized

Important Files Changed

File Analysis

Filename Score Overview
react_on_rails/lib/generators/react_on_rails/install_generator.rb 4/5 Wraps bundler commands with Bundler.with_unbundled_env to prevent BUNDLE_GEMFILE inheritance. Minor inconsistency with existing codebase pattern for backward compatibility.

Sequence Diagram

sequenceDiagram
    participant User
    participant Rails as Rails CLI
    participant Generator as InstallGenerator
    participant Bundler
    participant System as System Shell
    
    User->>Rails: bundle exec rails g react_on_rails:install
    Note over Rails,Generator: Parent process has<br/>BUNDLE_GEMFILE set
    
    Rails->>Generator: run_generators()
    Generator->>Generator: ensure_shakapacker_installed()
    
    alt Shakapacker not in Gemfile
        Generator->>Bundler: with_unbundled_env
        Bundler->>Bundler: Clear BUNDLE_GEMFILE<br/>and other env vars
        Bundler->>System: system("bundle add shakapacker --strict")
        Note over System: Discovers Gemfile in<br/>current directory,<br/>NOT parent's Gemfile
        System-->>Bundler: Success
        Bundler-->>Generator: Success
    end
    
    Generator->>Bundler: with_unbundled_env
    Bundler->>Bundler: Clear BUNDLE_GEMFILE
    Bundler->>System: system("bundle install")
    System-->>Bundler: Success
    Bundler-->>Generator: Success
    
    Generator->>Bundler: with_unbundled_env
    Bundler->>Bundler: Clear BUNDLE_GEMFILE
    Bundler->>System: system("bundle exec rails shakapacker:install")
    System-->>Bundler: Success
    Bundler-->>Generator: Success
    
    Generator-->>User: Installation complete
Loading

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

Caution

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

⚠️ Outside diff range comments (1)
react_on_rails/lib/generators/react_on_rails/install_generator.rb (1)

220-225: Verify that Gemfile checks work correctly when parent sets BUNDLE_GEMFILE.

The methods shakapacker_in_gemfile_text? (line 234) and shakapacker_in_lockfile? (line 220) read ENV["BUNDLE_GEMFILE"] to locate the Gemfile. When BUNDLE_GEMFILE is set to an absolute path pointing outside the target app (e.g., /parent/Gemfile), these methods will incorrectly check the parent's files instead of the target app's:

  1. If shakapacker exists in parent's Gemfile, these checks return true
  2. ensure_shakapacker_in_gemfile returns early without adding shakapacker to the target app's Gemfile
  3. Later bundler commands may fail because shakapacker is missing from the target app

This is an edge case (requires BUNDLE_GEMFILE to point elsewhere), but possible in CI environments or monorepos. Consider using explicit paths "./Gemfile" and "./Gemfile.lock" in generator context, since generators should always operate on the target application's files.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 619cb78 and d4f7d8e.

📒 Files selected for processing (1)
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rb: Run bundle exec rubocop and fix ALL violations before every commit/push
Run bundle exec rubocop (MANDATORY) before every commit to ensure zero offenses

Files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
🧠 Learnings (11)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /CHANGELOG_PRO.md : Update `/CHANGELOG_PRO.md` for user-visible changes in the Pro-only react_on_rails_pro gem and npm packages

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to sig/react_on_rails/**/*.rbs : RBS signatures in `sig/react_on_rails/` should be added for new Ruby files in `lib/react_on_rails/`, included in Steepfile, validated with `bundle exec rake rbs:validate`, and type-checked with `bundle exec rake rbs:steep`

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes in the open-source react_on_rails gem and npm package (features, bug fixes, breaking changes, deprecations, performance improvements)

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to lib/react_on_rails/engine.rb : In Rails Engine code, DO NOT use `rake_tasks` block to explicitly load rake files from `lib/tasks/` - Rails::Engine automatically loads them. Only use `rake_tasks` block if tasks are in non-standard location, need programmatic generation, or need to pass context

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: In IDE configuration, exclude these directories to prevent slowdowns: /coverage, /tmp, /gen-examples, /packages/react-on-rails/lib, /node_modules, /react_on_rails/spec/dummy/node_modules, /react_on_rails/spec/dummy/tmp, /react_on_rails/spec/dummy/app/assets/webpack, /react_on_rails/spec/dummy/log, /react_on_rails/spec/dummy/e2e/playwright-report, /react_on_rails/spec/dummy/test-results

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 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:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-09-29T14:45:42.687Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to react_on_rails/spec/dummy/e2e/playwright/app_commands/**/*.rb : Create custom Rails app commands in `e2e/playwright/app_commands/` directory with .rb files for reusable helpers in Playwright tests

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Shakapacker configuration should be tested by creating debug scripts in dummy app root (debug-webpack-rules.js, debug-webpack-with-config.js) to inspect webpack rules before committing configuration changes

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
⏰ 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). (4)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: Greptile Review
  • GitHub Check: examples (3.4, latest)
  • GitHub Check: claude-review
🔇 Additional comments (2)
react_on_rails/lib/generators/react_on_rails/install_generator.rb (2)

266-266: LGTM! Correctly prevents BUNDLE_GEMFILE inheritance.

The Bundler.with_unbundled_env wrapper ensures the child process discovers the correct Gemfile in the target app directory rather than inheriting the parent process's BUNDLE_GEMFILE. This prevents "injected gems" conflicts as described in issue #2287.


277-277: LGTM! Both bundler commands properly isolated.

Both bundle install and bundle exec rails shakapacker:install are correctly wrapped with Bundler.with_unbundled_env, ensuring they operate on the target app's Gemfile context rather than inheriting the parent's bundler environment.

Also applies to: 284-284

@claude
Copy link

claude bot commented Jan 9, 2026

Code Review - PR #2288

Summary

This PR fixes a critical bug where the install generator inherits BUNDLE_GEMFILE from the parent process, causing 'injected gems' conflicts. The fix wraps bundler commands with Bundler.with_unbundled_env to isolate the generator bundler context.

✅ Strengths

1. Correct Root Cause Analysis

The issue is well-diagnosed: system() calls inherit environment variables including BUNDLE_GEMFILE. Wrapping with Bundler.with_unbundled_env is the canonical solution per Bundler documentation.

2. Consistent with Existing Patterns

The implementation follows established patterns in process_manager.rb, pack_generator.rb, and task_helpers.rb.

3. Good Backward Compatibility

The with_unbundled_context helper supports both modern (with_unbundled_env) and legacy (with_clean_env) Bundler APIs with graceful fallback.

4. DRY Principle

Creating the helper avoids repeating API version checking logic three times.

5. Rake Task Fix

The change from sh_in_dir to unbundled_sh_in_dir in shakapacker_examples.rake:122 is correct - example generation should use the example app's Gemfile.

🔍 Observations & Suggestions

1. Minor Code Duplication

The with_unbundled_context helper is duplicated in install_generator.rb and pack_generator.rb with slight differences. Consider extracting to a shared module (non-blocking).

2. Test Coverage Gap

The existing install_generator_spec.rb does not test bundler environment isolation. Consider adding tests to verify BUNDLE_GEMFILE is not inherited (non-blocking - the fix addresses a real CI failure).

3. Documentation

The comment in shakapacker_examples.rake:120-121 is excellent. Similar comments in install_generator.rb would help future maintainers.

🚀 Performance & Security

  • Performance: No impact, negligible overhead during generator execution only
  • Security: Positive impact - prevents gem version conflicts and potential confusion attacks

🎯 Final Verdict: Approve with minor suggestions ✅

This PR:

  • ✅ Fixes a real bug causing CI failures
  • ✅ Uses the correct, documented solution
  • ✅ Follows existing codebase patterns
  • ✅ Includes proper backward compatibility
  • ✅ Has clear commit messages and PR description

Suggested follow-ups (non-blocking):

  1. Extract with_unbundled_context to a shared module
  2. Add test coverage for bundler environment isolation
  3. Add explanatory comments near each with_unbundled_context usage

Great work fixing this issue! 🎉

@claude
Copy link

claude bot commented Jan 9, 2026

Code Review: PR #2288 - Fix generator inheriting BUNDLE_GEMFILE from parent process

✅ Overall Assessment

Excellent fix! This PR correctly addresses a real issue with Bundler environment inheritance and implements the solution consistently across the codebase.


🎯 Strengths

  1. Correct Problem Diagnosis: The root cause analysis is spot-on. Running bundle exec rails g react_on_rails:install was indeed inheriting BUNDLE_GEMFILE from the parent process, causing gem conflicts.

  2. Well-Documented Solution: The inline comments reference the specific issue (Generator inherits BUNDLE_GEMFILE from parent process, causing 'injected gems' conflicts #2287) and explain why Bundler.with_unbundled_env is needed, which helps future maintainers.

  3. Consistent with Existing Patterns: The fix aligns with existing usage in:

    • task_helpers.rb:36 - unbundled_sh_in_dir helper
    • task_helpers.rb:89 - detect_bundler_ruby_version
    • process_manager.rb:136 - Process spawning
    • pack_generator.rb:179 - Pack generation
  4. Comprehensive Test Coverage: The test suite includes:

    • Unit tests verifying with_unbundled_env is called
    • Integration test demonstrating the actual behavior of clearing BUNDLE_GEMFILE
    • Good use of mocking to prevent actual command execution during tests
  5. Fixes Both Locations: Updated both install_generator.rb (user-facing) and shakapacker_examples.rake (internal tooling), ensuring consistency.


⚠️ Minor Consideration: Backward Compatibility

Observation: Other parts of the codebase use a backward-compatible wrapper for older Bundler versions:

# From process_manager.rb:134-142
def with_unbundled_context(&block)
  if Bundler.respond_to?(:with_unbundled_env)
    Bundler.with_unbundled_env(&block)
  elsif Bundler.respond_to?(:with_clean_env)
    Bundler.with_clean_env(&block)  # Deprecated in Bundler 2.1+
  else
    yield  # Fallback for very old versions
  end
end

Analysis:

  • with_unbundled_env was introduced in Bundler 2.1.0 (Jan 2020)
  • The gemspec requires Ruby >= 3.0.0, which typically ships with Bundler >= 2.2
  • The minimum supported Rails version is 5.2, which works with modern Bundler

Verdict: This is NOT a blocker. The direct use of Bundler.with_unbundled_env is acceptable because:

  1. All supported Ruby versions (3.0+) include compatible Bundler versions
  2. The backward-compatible pattern exists in process_manager.rb and pack_generator.rb for historical reasons
  3. Generators run in user environments where modern Bundler is virtually guaranteed

If you wanted to be extra cautious, you could extract the backward-compatible helper from process_manager.rb into a shared module, but it's not necessary given the target environment.


📝 Code Quality

  • ✅ RuboCop passes (as stated in PR)
  • ✅ Clear, explanatory comments
  • ✅ Proper test coverage with regression tests
  • ✅ Consistent formatting and style
  • ✅ No security concerns
  • ✅ No performance issues

🧪 Test Coverage

The tests are well-structured:

  1. Unit tests verify the method calls happen correctly
  2. Integration test (lines 359-377) demonstrates actual ENV clearing behavior
  3. Proper cleanup of environment variables in tests

Minor suggestion: The integration test manually manages ENV cleanup. Consider using RSpec's around hook pattern for safer cleanup:

around do |example|
  original = ENV.fetch("BUNDLE_GEMFILE", nil)
  example.run
ensure
  original ? ENV["BUNDLE_GEMFILE"] = original : ENV.delete("BUNDLE_GEMFILE")
end

But the current approach is also fine.


🎓 Educational Value

This PR demonstrates excellent problem-solving:

  1. Clear reproduction of the issue
  2. Research of official documentation (Bundler issues, Rails issues)
  3. Finding existing patterns in the codebase
  4. Comprehensive testing

Great example for future contributors!


✅ Recommendation: APPROVE

This PR is ready to merge. It solves a real user-facing issue with a well-tested, idiomatic solution that follows established patterns in the codebase.

Nice work! 🎉

@claude
Copy link

claude bot commented Jan 9, 2026

Code Review for PR #2288: Fix generator inheriting BUNDLE_GEMFILE from parent process

✅ Summary

This PR correctly addresses issue #2287 by wrapping bundler commands in Bundler.with_unbundled_env to prevent BUNDLE_GEMFILE inheritance. The solution is well-implemented, properly tested, and follows established patterns in the codebase.


🎯 Code Quality & Best Practices

Strengths:

  • Follows existing patterns: Uses the same Bundler.with_unbundled_env pattern already established in process_manager.rb and pack_generator.rb
  • Clear documentation: Inline comments reference the issue number and explain the rationale
  • Consistent implementation: All three bundler subprocess calls are properly wrapped
  • Test coverage: Comprehensive regression tests added to prevent future breakage
  • Proper scope: Changes are minimal and focused on the specific problem

Minor observation:

  • 💡 Consistency consideration: process_manager.rb and pack_generator.rb use a with_unbundled_context helper method that falls back to with_clean_env for older Bundler versions. However, since React on Rails likely requires a modern Bundler version, the direct use of Bundler.with_unbundled_env in the generator is acceptable and simpler.

🐛 Potential Issues

No critical issues found. The implementation is solid.

Minor edge case (already handled):

  • The tests properly verify that shakapacker_in_gemfile_text? and shakapacker_in_lockfile? now hardcode "Gemfile" and "Gemfile.lock" instead of deriving from ENV["BUNDLE_GEMFILE"]. This is correct behavior - generators should always work with the target app's Gemfile, not a parent project's.

⚡ Performance Considerations

  • No performance impact: Bundler.with_unbundled_env is a lightweight environment manipulation that only affects subprocess execution
  • No additional system calls: The change only wraps existing system() calls

🔒 Security Concerns

No security issues identified.

  • ✅ The change improves isolation between parent and child processes, which is actually a security improvement
  • ✅ No new external dependencies or unsafe operations introduced

🧪 Test Coverage

Excellent test coverage:

  1. Unit tests for with_unbundled_env usage: Verifies that Bundler.with_unbundled_env is called when running bundle commands
  2. Integration test for environment clearing: Directly tests that BUNDLE_GEMFILE is nil inside the block
  3. Regression tests for file path detection: Ensures shakapacker_in_gemfile_text? and shakapacker_in_lockfile? ignore ENV["BUNDLE_GEMFILE"]
  4. Proper test cleanup: All tests that modify ENV["BUNDLE_GEMFILE"] properly restore the original value

Suggestion for additional testing:
While the unit tests are comprehensive, consider adding an integration test that actually runs the generator in a nested bundle context to verify end-to-end behavior. However, this may be overkill given the existing test coverage.


📝 Additional Observations

Rake file changes:

  • ✅ The change from sh_in_dir to unbundled_sh_in_dir in shakapacker_examples.rake:122 is correct and consistent
  • ✅ Adding bundle exec prefix (bundle exec rake shakapacker:install instead of just rake shakapacker:install) is a good practice to ensure the correct rake version is used

Documentation:


🎉 Verdict

LGTM - Excellent work!

This PR demonstrates:

  • Clear understanding of the problem
  • Proper investigation of existing patterns
  • Comprehensive testing
  • Good documentation practices

The fix is minimal, focused, and follows Ruby/Rails best practices. No blocking issues found.


📋 Pre-merge Checklist

Before merging, ensure:

  • RuboCop passes (mentioned in PR description)
  • CI tests pass (verify all workflows succeed)
  • No conflicts with master branch

Note: According to CLAUDE.md, you should run bundle exec rubocop and ensure all files end with newlines before pushing. The PR description mentions RuboCop passes, which is good.

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

Caution

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

⚠️ Outside diff range comments (1)
react_on_rails/lib/generators/react_on_rails/install_generator.rb (1)

262-272: Add explicit require "bundler" at the top of the file.

The ensure_shakapacker_in_gemfile method (lines 262-272) and install_shakapacker method (lines 274-291) both use Bundler.with_unbundled_env without a file-level require "bundler". While this works under bundle exec, it creates brittleness for other invocation contexts. The require "bundler" at line 228 is scoped inside shakapacker_in_bundler_specs? and doesn't guarantee availability.

Proposed fix
 require "rails/generators"
 require "json"
+require "bundler"
 require_relative "generator_helper"
 require_relative "generator_messages"
 require_relative "js_dependency_manager"

Also consider using system("bundle", "add", "shakapacker", "--strict") with array arguments instead of string interpolation to avoid shell invocation semantics and align with patterns elsewhere in the codebase.

🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb (1)

333-424: Solid regression coverage for #2287 (with_unbundled_env + local Gemfile/Gemfile.lock checks).
One maintainability tweak: dedupe the repeated BUNDLE_GEMFILE save/restore using an around block (or helper) and restore via ensure for stricter isolation. Based on learnings, stubbing/restoring env vars via hooks helps avoid test cross-talk.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2ecca6 and 10211bc.

📒 Files selected for processing (2)
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rb: Run bundle exec rubocop and fix ALL violations before every commit/push
Run bundle exec rubocop (MANDATORY) before every commit to ensure zero offenses

Files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /CHANGELOG_PRO.md : Update `/CHANGELOG_PRO.md` for user-visible changes in the Pro-only react_on_rails_pro gem and npm packages
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /{CHANGELOG.md,CHANGELOG_PRO.md} : Use format `[PR 1818](https://github.com/shakacode/react_on_rails/pull/1818) by [username](https://github.com/username)` in changelog entries (no hash in PR number)
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Shakapacker configuration should be tested by creating debug scripts in dummy app root (debug-webpack-rules.js, debug-webpack-with-config.js) to inspect webpack rules before committing configuration changes
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to **/*.rb : Run `bundle exec rubocop` and fix ALL violations before every commit/push
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to **/*.rb : Run `bundle exec rubocop` (MANDATORY) before every commit to ensure zero offenses
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to sig/react_on_rails/**/*.rbs : RBS signatures in `sig/react_on_rails/` should be added for new Ruby files in `lib/react_on_rails/`, included in Steepfile, validated with `bundle exec rake rbs:validate`, and type-checked with `bundle exec rake rbs:steep`

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to react_on_rails/spec/dummy/e2e/playwright/e2e/**/*.spec.js : Use Playwright E2E tests in `react_on_rails/spec/dummy/e2e/playwright/` for React component integration testing. Tests automatically start Rails server on port 5017 before running

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to react_on_rails/spec/dummy/e2e/playwright/app_commands/**/*.rb : Create custom Rails app commands in `e2e/playwright/app_commands/` directory with .rb files for reusable helpers in Playwright tests

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: For infrastructure/config changes, perform comprehensive local testing including: finding all affected files with grep, testing build pipeline, running relevant specs, and linting everything before pushing

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: In IDE configuration, exclude these directories to prevent slowdowns: /coverage, /tmp, /gen-examples, /packages/react-on-rails/lib, /node_modules, /react_on_rails/spec/dummy/node_modules, /react_on_rails/spec/dummy/tmp, /react_on_rails/spec/dummy/app/assets/webpack, /react_on_rails/spec/dummy/log, /react_on_rails/spec/dummy/e2e/playwright-report, /react_on_rails/spec/dummy/test-results

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-02-13T14:29:49.267Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: spec/react_on_rails/utils_spec.rb:218-218
Timestamp: 2025-02-13T14:29:49.267Z
Learning: In RSpec tests, prefer using local variables over constants within test blocks to avoid constant redefinition warnings and maintain better test isolation.

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
📚 Learning: 2025-09-29T14:45:42.687Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1833
File: lib/react_on_rails/dev/process_manager.rb:72-83
Timestamp: 2025-09-29T14:45:42.687Z
Learning: In Ruby bundler contexts, when bundler intercepts system commands for executables not in the Gemfile, both version checks (like `system("foreman", "--version")`) and execution commands (like `system("foreman", "start", ...)`) fail equally, both returning false. This means availability checks using version flags accurately reflect whether execution commands will work in the current bundler context.

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2024-06-27T05:22:46.156Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1622
File: spec/dummy/spec/rake/assets_precompile_rake_spec.rb:12-12
Timestamp: 2024-06-27T05:22:46.156Z
Learning: When stubbing environment variables in RSpec tests, use `before` and `after` hooks to ensure that the original values are restored after the tests, preventing any side effects on other tests. Example provided by justin808:
```ruby
describe "My test" do
  before do
    original_value = ENV["VARIABLE_NAME"]
    allow(ENV).to receive(:[]).with("VARIABLE_NAME").and_return("stubbed_value")
  end

  after do
    allow(ENV).to receive(:[]).with("VARIABLE_NAME").and_call_original
    ENV["VARIABLE_NAME"] = original_value
  end

  it "tests something" do
    # Your test code here
  end
end
```
This practice ensures test isolation and reliability.

Applied to files:

  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /CHANGELOG_PRO.md : Update `/CHANGELOG_PRO.md` for user-visible changes in the Pro-only react_on_rails_pro gem and npm packages

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes in the open-source react_on_rails gem and npm package (features, bug fixes, breaking changes, deprecations, performance improvements)

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 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:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to {package.json,webpack.config.js,packages/*/package.json,react_on_rails_pro/package.json} : When resolving merge conflicts in build configuration files, verify file paths are correct by running `grep -r 'old/path' .` and test affected scripts like `pnpm run prepack` before continuing the merge

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Applies to lib/react_on_rails/engine.rb : In Rails Engine code, DO NOT use `rake_tasks` block to explicitly load rake files from `lib/tasks/` - Rails::Engine automatically loads them. Only use `rake_tasks` block if tasks are in non-standard location, need programmatic generation, or need to pass context

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
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:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-01-23T18:20:45.824Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1687
File: spec/dummy/package.json:0-0
Timestamp: 2025-01-23T18:20:45.824Z
Learning: When adding or updating dependencies in spec/dummy/package.json, maintain version consistency with other package.json files in the codebase to avoid potential version conflicts.

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
📚 Learning: 2025-12-19T18:57:59.314Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:57:59.314Z
Learning: Shakapacker configuration should be tested by creating debug scripts in dummy app root (debug-webpack-rules.js, debug-webpack-with-config.js) to inspect webpack rules before committing configuration changes

Applied to files:

  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: dummy-app-integration-tests (3.4, 22, latest)
  • 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: build
  • GitHub Check: pro-lint-js-and-ruby
  • GitHub Check: claude-review
🔇 Additional comments (2)
react_on_rails/lib/generators/react_on_rails/install_generator.rb (2)

220-225: Lockfile detection change is aligned with the goal; regex is OK for Bundler lockfile “specs” lines.
This now correctly ignores inherited BUNDLE_GEMFILE and only looks at the target app’s Gemfile.lock.


234-239: Gemfile detection change is aligned with the goal; avoids inherited BUNDLE_GEMFILE.
This is a clearer/safer check than relying on env-derived paths.

@claude
Copy link

claude bot commented Jan 9, 2026

Code Review - PR #2288

✅ Overall Assessment

This is an excellent fix for a legitimate bug. The implementation is clean, well-tested, and follows existing patterns in the codebase. I recommend approval with one minor suggestion for consistency.


🎯 Strengths

  1. Problem Well-Identified: The root cause analysis is spot-on. Using Bundler.with_unbundled_env is the canonical solution for this issue, as documented by both Bundler and Rails.

  2. Follows Existing Patterns: The fix mirrors the approach already used in task_helpers.rb:32-42, process_manager.rb:133-143, and pack_generator.rb:175-181, maintaining consistency across the codebase.

  3. Comprehensive Test Coverage: The test suite is thorough with 5 well-designed test cases:

    • Verifies with_unbundled_env is called for each bundler command
    • Tests actual environment variable clearing behavior
    • Confirms local Gemfile/Gemfile.lock detection regardless of BUNDLE_GEMFILE
    • Properly mocks system calls to prevent actual execution
    • Includes proper ENV cleanup in tests
  4. Clear Documentation: Comments reference the issue number and explain why, not just what.

  5. Minimal Surface Area: Changes are surgical—only touching the three system() calls that need isolation.


💡 Suggestions

1. Consider Backward Compatibility Helper (Low Priority)

The existing code in process_manager.rb:133-143 and pack_generator.rb:175-181 includes a compatibility fallback:

def with_unbundled_context(&block)
  if Bundler.respond_to?(:with_unbundled_env)
    Bundler.with_unbundled_env(&block)
  elsif Bundler.respond_to?(:with_clean_env)
    Bundler.with_clean_env(&block)
  else
    yield
  end
end

Analysis:

  • with_unbundled_env was introduced in Bundler 2.1.0 (January 2020)
  • This gem requires Ruby >= 3.0.0 (per react_on_rails.gemspec:20)
  • Ruby 3.0.0 ships with Bundler 2.2.3
  • Therefore, with_unbundled_env is always available in supported environments

Recommendation: The direct call to Bundler.with_unbundled_env is fine given the minimum Ruby version. However, using the existing with_unbundled_context helper (if extracted to a shared module) would:

  • Maintain consistency with process_manager.rb and pack_generator.rb
  • Provide defensive coding in case someone backports to older Ruby versions
  • Make the codebase more uniform

This is not blocking since the current approach works correctly for all supported versions.


🔍 Code Quality Notes

Positive:

  • ✅ Test environment cleanup is handled correctly (lines 41-47, 68-73, 89-93 in spec file)
  • ✅ Comments include issue references for future maintainers
  • ✅ The shakapacker_in_lockfile? and shakapacker_in_gemfile_text? changes correctly ignore ENV["BUNDLE_GEMFILE"]
  • ✅ Rake task changes (shakapacker_examples.rake:120, 132) use the existing unbundled_sh_in_dir helper consistently

Edge Cases Handled:

  • ENV.fetch("BUNDLE_GEMFILE", nil) correctly handles both present and absent env vars
  • ✅ System call return values are checked (success, bundle_success)
  • ✅ Error handlers (handle_shakapacker_gemfile_error, handle_shakapacker_install_error) remain in place

🧪 Testing

Test Quality: Excellent

The tests cover:

  1. Unit behavior: Each system() call wrapper
  2. Integration behavior: Actual Bundler.with_unbundled_env clearing ENV vars
  3. Regression prevention: Ensures local Gemfile is used, not inherited path

Suggestion for future enhancement (not blocking):
Consider adding an integration test that actually runs the generator in a subprocess with BUNDLE_GEMFILE set, if feasible. Current tests mock system(), which is appropriate for unit tests but doesn't catch if the system() call itself somehow re-introduces the env var.


🚀 Performance & Security

  • No performance impact: with_unbundled_env is a simple ENV manipulation
  • No security concerns: Actually improves isolation by preventing env leakage
  • No breaking changes: Behavior is identical for users without BUNDLE_GEMFILE set

📋 Checklist for Merge


🎬 Recommendation

Approve and merge once CI passes. The optional consistency suggestion can be addressed in a follow-up refactor if desired, but this PR is production-ready as-is.

Great work on the thorough investigation and clean implementation! 🎉


Files reviewed: install_generator.rb:218-287, shakapacker_examples.rake:117-132, install_generator_spec.rb:333-425

ihabadham added a commit that referenced this pull request Jan 9, 2026
Temporary duplicate of fix from PR #2288. Use unbundled context for
shakapacker:install and generator commands to prevent inheriting
parent workspace's Gemfile.lock (which has shakapacker 9.4.0).

Remove this commit after PR #2288 is merged to master.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
ihabadham added a commit that referenced this pull request Jan 9, 2026
Temporary duplicate of fix from PR #2288. Use unbundled context for
shakapacker:install and generator commands to prevent inheriting
parent workspace's Gemfile.lock (which has shakapacker 9.4.0).

Remove this commit after PR #2288 is merged to master.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@claude
Copy link

claude bot commented Jan 9, 2026

Code Review - PR #2288

Summary

This PR fixes a critical bug where the react_on_rails:install generator inherited BUNDLE_GEMFILE from the parent process, causing "injected gems" conflicts. The fix properly isolates bundler commands using Bundler.with_unbundled_env.


✅ Strengths

1. Correct Root Cause Identification

The PR correctly identifies that system() calls inherit the parent process's environment, including BUNDLE_GEMFILE. This is a well-documented Bundler issue with established solutions.

2. Consistent with Existing Patterns

The fix follows patterns already used in the codebase:

  • task_helpers.rb:32-42 - unbundled_sh_in_dir
  • process_manager.rb - bundler isolation
  • pack_generator.rb - similar isolation

3. Comprehensive Fix

The PR addresses all instances of the issue:

  • ✅ install_generator.rb - 3 bundler commands wrapped
  • ✅ shakapacker_examples.rake - example generation isolated
  • ✅ Gemfile detection methods - no longer use inherited BUNDLE_GEMFILE

4. Excellent Test Coverage

The regression tests are thorough (92 new lines):

5. Good Documentation

  • Comments explain WHY the fix is needed
  • Links to the GitHub issue for context
  • Commit messages are descriptive

🔍 Observations & Minor Suggestions

1. Simplified Approach (Good Decision)

Commit 4 simplified from a helper method with legacy fallback to direct Bundler.with_unbundled_env usage. This is correct because:

  • Ruby 3.0+ ships with Bundler 2.2.3+ which has with_unbundled_env
  • The project requires Ruby 3.2+ (per CLAUDE.md)
  • No need for dead fallback code

Suggestion: Consider adding a comment mentioning the minimum Ruby/Bundler version to prevent future additions of unnecessary fallback code.

2. Test Implementation

The tests manually restore ENV["BUNDLE_GEMFILE"] after modification. This is good hygiene and works correctly. An around block could be more RSpec-idiomatic, but this is purely stylistic.

3. Rake File Changes

The changes to shakapacker_examples.rake are essential (lines 120, 132: sh_in_dir → unbundled_sh_in_dir). This ensures example apps use their own gem versions, not the parent workspace's.

Question: Should the comments be more specific about preventing cached version conflicts to help future maintainers understand the CI failures this prevents?


🔒 Security Considerations

✅ No security concerns. The changes use official Bundler API and actually improve security by ensuring commands run in the correct context.


⚡ Performance Considerations

✅ Negligible performance impact. Bundler.with_unbundled_env just temporarily clears environment variables - no measurable overhead.


🐛 Potential Issues

None identified. The fix is targeted, well-tested, and consistent with existing patterns.


📋 Pre-Merge Checklist

Based on CLAUDE.md requirements:

  • ✅ RuboCop passes (mentioned in PR description)
  • ✅ Tests added (92 lines of regression tests)
  • ✅ Files end with newlines (required by CI)
  • ⚠️ CI Status: Please verify all CI checks are passing before merge
  • ⚠️ Local Testing: Per CLAUDE.md, ensure you've tested locally

🎯 Recommendation

APPROVE with minor suggestions

This is a high-quality fix that:

  1. Solves a real CI/production issue
  2. Uses the correct, documented solution
  3. Has comprehensive test coverage
  4. Follows existing code patterns
  5. Includes clear documentation

The minor suggestions above are optional improvements - the PR is ready to merge as-is once CI passes.


📚 References

Great work identifying and fixing this subtle but important bug! 🎉

@claude
Copy link

claude bot commented Jan 12, 2026

Code Review: Fix generator inheriting BUNDLE_GEMFILE from parent process

✅ Overall Assessment

Excellent work! This is a well-researched, properly documented fix for a legitimate bug. The PR follows best practices and aligns with existing patterns in the codebase.


🎯 Strengths

  1. Root Cause Analysis: The PR description provides clear evidence of the issue with links to CI failures and a detailed explanation of how BUNDLE_GEMFILE inheritance causes conflicts.

  2. Consistent Pattern: The fix uses Bundler.with_unbundled_env which is already established in the codebase:

    • lib/react_on_rails/dev/process_manager.rb:136
    • lib/react_on_rails/dev/pack_generator.rb:179
    • rakelib/task_helpers.rb:36,89
  3. Comprehensive Testing: The test coverage is thorough with 5 new test cases covering:

    • Environment isolation verification
    • Bundle add command unbundling
    • Bundle install command unbundling
    • Gemfile path checking independence
    • Gemfile.lock path checking independence
  4. Documentation: Added clear inline comments with issue references explaining why with_unbundled_env is needed.

  5. Consistency Fix: Also updated rakelib/shakapacker_examples.rake to use unbundled_sh_in_dir helper, which is the right abstraction.


📝 Code Quality

Generator Changes (install_generator.rb):

  • ✅ Correctly wraps all 3 bundler system calls
  • ✅ Maintains return value checking (success = Bundler.with_unbundled_env { ... })
  • ✅ Simplified path detection to always use local Gemfile/Gemfile.lock instead of ENV["BUNDLE_GEMFILE"]

Rake Task Changes (shakapacker_examples.rake):

  • ✅ Correctly switches from sh_in_dir to unbundled_sh_in_dir for generator and shakapacker commands
  • ✅ Added explanatory comments about why unbundled environment is needed

Test Coverage:

  • ✅ Tests verify actual behavior of Bundler.with_unbundled_env
  • ✅ Tests check that methods ignore BUNDLE_GEMFILE env var
  • ✅ Proper cleanup of env vars in tests

🔍 Minor Observations

  1. Test Environment Restoration: The tests properly restore the original BUNDLE_GEMFILE value after modification - good practice! ✅

  2. Backward Compatibility: The fix uses Bundler.with_unbundled_env directly without checking for respond_to? like in process_manager.rb:135-138. This is fine since:

    • with_unbundled_env was added in Bundler 2.1.0 (Jan 2020)
    • React on Rails likely requires a recent Bundler version
    • If compatibility is needed, consider using the pattern from process_manager.rb:
    if Bundler.respond_to?(:with_unbundled_env)
      Bundler.with_unbundled_env(&block)
    elsif Bundler.respond_to?(:with_clean_env)
      Bundler.with_clean_env(&block)
    end

    However, this is likely not necessary for a Rails generator that already runs in a modern bundler context.

  3. CI Failure in Examples: I see one CI failure in "examples (3.4, latest)" - this appears unrelated to your changes based on the nature of the fix. Verify if this is a pre-existing master issue.


🔒 Security Considerations

✅ No security concerns. The fix actually improves security by ensuring bundler operates on the intended Gemfile rather than an inherited one.


⚡ Performance Considerations

✅ Negligible performance impact. with_unbundled_env is lightweight (just env var manipulation).


🧪 Test Coverage Suggestions

The test coverage is already comprehensive. One optional enhancement:

Optional: Add an integration test that runs the actual generator in a bundled context to verify end-to-end behavior. However, the current unit tests are sufficient for regression prevention.


📋 Recommendations

  1. Approve and merge - this is a solid fix
  2. ⚠️ Check CI failure - Investigate if "examples (3.4, latest)" failure is related or pre-existing
  3. 📝 Consider changelog entry - This is a user-facing bug fix (generators failing in bundled contexts), so it warrants a CHANGELOG entry per CLAUDE.md guidelines

🎓 Final Thoughts

This PR demonstrates excellent engineering practices:

  • Clear problem statement with evidence
  • Thorough investigation citing similar patterns in the codebase
  • Comprehensive test coverage with proper cleanup
  • Good documentation with issue references

Great work on tracking down this subtle environment inheritance issue! 🚀

Recommendation: APPROVE with minor suggestion to verify CI failure

ihabadham added a commit that referenced this pull request Jan 24, 2026
Temporary duplicate of fix from PR #2288. Use unbundled context for
shakapacker:install and generator commands to prevent inheriting
parent workspace's Gemfile.lock (which has shakapacker 9.4.0).

Remove this commit after PR #2288 is merged to master.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@justin808 justin808 added ready-to-merge Approved and green or only rebase needed P0 Merge this week labels Feb 9, 2026
ihabadham and others added 6 commits February 8, 2026 17:35
Wrap bundler commands in install_generator.rb with
Bundler.with_unbundled_env to prevent BUNDLE_GEMFILE inheritance
from the parent process. This fixes "injected gems" conflicts
when running `bundle exec rails g react_on_rails:install`.

The issue occurs because system() calls inherit the parent's
BUNDLE_GEMFILE environment variable, causing bundler to use the
wrong Gemfile when the generator runs bundle commands.

This is a well-documented Bundler issue (rubygems/bundler#698,
rails/rails#3153) and the fix follows the official solution
recommended by Bundler maintainers since 2012.

Fixes #2287

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Change sh_in_dir to unbundled_sh_in_dir for rake shakapacker:install
to ensure the generated example app uses its own Gemfile and gem
versions, not the parent workspace's bundle context.

This fixes CI failures caused by version mismatch when the parent
workspace has a cached older version of shakapacker (e.g., 9.4.0)
while the example app installs a newer version (e.g., 9.5.0).

The fix follows the same pattern already used in this file for
shakapacker:binstubs and react_on_rails:generate_packs commands.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Extract a helper method that supports both new (with_unbundled_env)
and legacy (with_clean_env) Bundler APIs for consistency with the
pattern used in process_manager.rb and pack_generator.rb.

This ensures compatibility with older Bundler versions that don't
have the with_unbundled_env method.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove with_unbundled_context helper method with legacy Bundler fallback
- Use Bundler.with_unbundled_env directly (available in Bundler 2.1.0+,
  Ruby 3.0+ ships with Bundler 2.2.3+, so fallback is dead code)
- Add regression tests verifying BUNDLE_GEMFILE isolation (#2287)
- Matches the direct usage pattern in task_helpers.rb

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The shakapacker_in_gemfile_text? and shakapacker_in_lockfile? methods
were using ENV["BUNDLE_GEMFILE"] to determine which Gemfile to check.
This caused incorrect detection when BUNDLE_GEMFILE was inherited from
a parent process (same root cause as the subprocess fix).

Now these methods always check the local "Gemfile" and "Gemfile.lock",
which is correct since generators always operate on the target Rails app.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The generator_commands (rails generate react_on_rails:install) were
still using sh_in_dir which inherits the parent's bundler context.
This caused version mismatches when the parent workspace had a different
shakapacker version cached than the newly generated example app.

Changed to unbundled_sh_in_dir to match the pattern used for
shakapacker:install and generate_packs.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@justin808 justin808 force-pushed the ihabadham/fix/generator-bundle-gemfile-inheritance branch from b15053d to 9b34f12 Compare February 9, 2026 03:42
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

🤖 Fix all issues with AI agents
In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`:
- Around line 527-542: The tests manually save and restore ENV["BUNDLE_GEMFILE"]
inside each example which is not exception-safe; extract that logic into an
RSpec hook (use around or a before/after pair) that saves original_gemfile, sets
ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile" for the example, yields to the
example, and then restores or deletes ENV["BUNDLE_GEMFILE"] afterwards; remove
the inline save/restore and existing manual cleanup blocks around the
Bundler.with_unbundled_env checks (references: ENV["BUNDLE_GEMFILE"],
original_gemfile, and Bundler.with_unbundled_env) and apply the same hook to the
other two affected examples to avoid duplication and ensure exception-safe
cleanup.
🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb (1)

525-543: This test validates Bundler's own behavior, not your code.

Bundler.with_unbundled_env is a well-tested stdlib method. This test adds maintenance cost without exercising any project code. Consider removing it or replacing it with a test that verifies your generator method actually invokes with_unbundled_env (like the tests above already do).

Comment on lines 527 to 542
original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile"

bundler_env_in_block = nil
Bundler.with_unbundled_env do
bundler_env_in_block = ENV.fetch("BUNDLE_GEMFILE", nil)
end

expect(bundler_env_in_block).to be_nil

# Restore original value
if original_gemfile
ENV["BUNDLE_GEMFILE"] = original_gemfile
else
ENV.delete("BUNDLE_GEMFILE")
end
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

ENV restoration is not exception-safe — use around or before/after hooks.

If an expectation fails before the manual restore block, BUNDLE_GEMFILE stays polluted for subsequent tests. Extract the save/restore into an around hook (or before/after pair) so cleanup is guaranteed. This also removes significant duplication across the three tests.

♻️ Suggested refactor
   describe "bundler environment isolation" do
     let(:install_generator) { described_class.new }
+
+    around(:each) do |example|
+      original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
+      example.run
+    ensure
+      if original_gemfile
+        ENV["BUNDLE_GEMFILE"] = original_gemfile
+      else
+        ENV.delete("BUNDLE_GEMFILE")
+      end
+    end
 
     # ... (first two tests unchanged) ...
 
-    it "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" do
-      # Set a fake BUNDLE_GEMFILE to simulate parent process context
-      original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
+    it "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" do
       ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile"
 
       bundler_env_in_block = nil
       Bundler.with_unbundled_env do
         bundler_env_in_block = ENV.fetch("BUNDLE_GEMFILE", nil)
       end
 
       expect(bundler_env_in_block).to be_nil
-
-      # Restore original value
-      if original_gemfile
-        ENV["BUNDLE_GEMFILE"] = original_gemfile
-      else
-        ENV.delete("BUNDLE_GEMFILE")
-      end
     end

Apply the same cleanup removal to the other two tests.

Based on learnings: "When stubbing environment variables in RSpec tests, use before and after hooks to ensure that the original values are restored after the tests, preventing any side effects on other tests."

Also applies to: 547-566, 570-588

🤖 Prompt for AI Agents
In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`
around lines 527 - 542, The tests manually save and restore
ENV["BUNDLE_GEMFILE"] inside each example which is not exception-safe; extract
that logic into an RSpec hook (use around or a before/after pair) that saves
original_gemfile, sets ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile" for the
example, yields to the example, and then restores or deletes
ENV["BUNDLE_GEMFILE"] afterwards; remove the inline save/restore and existing
manual cleanup blocks around the Bundler.with_unbundled_env checks (references:
ENV["BUNDLE_GEMFILE"], original_gemfile, and Bundler.with_unbundled_env) and
apply the same hook to the other two affected examples to avoid duplication and
ensure exception-safe cleanup.

@justin808
Copy link
Member

@greptile, review this PR.
@claude, review this PR.

@claude
Copy link

claude bot commented Feb 9, 2026

Claude finished @justin808's task in 1m 48s —— View job


Code Review for PR #2288

Reviewing: Fix generator inheriting BUNDLE_GEMFILE from parent process

Summary

This PR correctly fixes a real issue where the React on Rails install generator inherits BUNDLE_GEMFILE from the parent process, causing "injected gems" conflicts. The implementation is clean, well-tested, and follows established patterns in the codebase.


✅ Strengths

1. Correct Problem Diagnosis

The root cause is accurately identified: system() calls in generators inherit the parent process's environment variables, including BUNDLE_GEMFILE. This causes bundler to look at the wrong Gemfile, leading to gem version conflicts.

2. Proper Solution

Using Bundler.with_unbundled_env is the canonical fix for this issue, as documented in:

3. Consistent with Existing Patterns

The fix mirrors patterns already used in this codebase:

  • rakelib/task_helpers.rb:36 - unbundled_sh_in_dir helper
  • lib/react_on_rails/dev/process_manager.rb:136 - Process spawning
  • lib/react_on_rails/dev/pack_generator.rb:179 - Pack generation

4. Comprehensive Coverage

The fix addresses all three critical locations:

  • install_generator.rb:268 - bundle add shakapacker --strict
  • install_generator.rb:280 - bundle install
  • install_generator.rb:287 - bundle exec rails shakapacker:install

Plus the rake tasks:

  • shakapacker_examples.rake:92,104 - Example generation tasks

5. Excellent Test Coverage

Added 92 lines of regression tests covering:

  • ✅ Verification that Bundler.with_unbundled_env is called for each bundler command
  • ✅ Integration test showing actual environment variable clearing
  • ✅ Tests confirming Gemfile/Gemfile.lock detection doesn't rely on inherited BUNDLE_GEMFILE
  • ✅ Proper cleanup of environment variables in tests

6. Good Documentation


🔍 Issues & Suggestions

1. Test Environment Cleanup Not Exception-Safe ⚠️ Major

Location: install_generator_spec.rb:527-542, 547-566, 570-588

Issue: The tests manually save and restore ENV["BUNDLE_GEMFILE"] inside each example:

it "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" do
  original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
  ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile"
  
  # ... test code ...
  
  # Restore original value
  if original_gemfile
    ENV["BUNDLE_GEMFILE"] = original_gemfile
  else
    ENV.delete("BUNDLE_GEMFILE")
  end
end

Problem: If an expectation fails before the restoration code runs, BUNDLE_GEMFILE remains polluted for subsequent tests.

Recommendation: Extract the save/restore logic into an RSpec hook for exception-safe cleanup:

describe "bundler environment isolation" do
  let(:install_generator) { described_class.new }
  
  around(:each) do |example|
    original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
    example.run
  ensure
    if original_gemfile
      ENV["BUNDLE_GEMFILE"] = original_gemfile
    else
      ENV.delete("BUNDLE_GEMFILE")
    end
  end
  
  # ... tests (remove manual save/restore from each one) ...
end

This follows the learning from previous reviews: "When stubbing environment variables in RSpec tests, use before and after hooks to ensure that the original values are restored after the tests."

Fix this →

2. Testing Bundler's Own Behavior 💡 Minor

Location: install_generator_spec.rb:525-543

The test "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" validates Bundler's own stdlib behavior rather than your code. This adds maintenance cost without exercising project-specific logic.

Consideration: The other two tests (lines 506-523) already verify that your generator calls with_unbundled_env. Testing Bundler's implementation is redundant.

Recommendation: Consider removing this test or replacing it with a test that verifies the generator method behavior, not Bundler's behavior.


📊 Code Quality

  • RuboCop: Passes with zero offenses (as stated in PR description)
  • Code Style: Follows established patterns
  • Comments: Clear, explanatory, includes issue references
  • Security: No concerns; actually improves isolation
  • Performance: No impact (lightweight env var manipulation)

🧪 CI Status

All checks passing (10/10 successful, 3 skipped)

  • ✅ Lint JS and Ruby
  • ✅ Generator tests
  • ✅ Rspec test for gem
  • ✅ Integration Tests
  • ✅ Assets Precompile Check
  • ✅ JS unit tests
  • ✅ Pro package tests

📝 Changed Files Analysis

install_generator.rb (+14/-11)

shakapacker_examples.rake (+6/-2)

  • ✅ Changes sh_in_dirunbundled_sh_in_dir for rake and generator commands
  • ✅ Ensures example apps use their own Gemfile, not parent workspace's
  • ✅ Good explanatory comments

install_generator_spec.rb (+92/-0)


🎯 Overall Assessment

Recommendation: APPROVE ✅ (with minor test cleanup suggested)

This is a high-quality fix that:

The suggested test improvements are non-blocking - they enhance test robustness but don't affect the correctness of the fix itself.


💡 Additional Notes

  1. Bundler Version Compatibility: The direct use of Bundler.with_unbundled_env is acceptable. This method was introduced in Bundler 2.1.0 (January 2020), and React on Rails requires Ruby 3.0+, which ships with Bundler 2.2+. No backward compatibility wrapper needed.

  2. No CHANGELOG Entry: Based on AGENTS.md guidelines, this should be added to /CHANGELOG.md as it's a user-visible bug fix. Example format:

    - Fix generator inheriting BUNDLE_GEMFILE from parent process causing "injected gems" conflicts. [PR 2288](https://github.com/shakacode/react_on_rails/pull/2288) by [ihabadham](https://github.com/ihabadham)
  3. Five Previous Reviews: This PR has received extensive review attention from CodeRabbit, Greptile, and Claude (5 previous Claude reviews!). All reviews consistently approve the fix with minor suggestions.


Great work on this fix! The implementation is solid and the testing is thorough. 🎉

@greptile-apps
Copy link

greptile-apps bot commented Feb 9, 2026

Greptile Overview

Greptile Summary

This PR hardens the react_on_rails:install generator against Bundler environment leakage by (1) running bundler subprocesses inside Bundler.with_unbundled_env, and (2) avoiding use of inherited ENV["BUNDLE_GEMFILE"] when checking Gemfile / Gemfile.lock. It also updates example-app generation tasks to run generator/rake commands in an unbundled context, and adds regression specs covering the BUNDLE_GEMFILE isolation behavior.

Main issue to address before merge: install_generator.rb now calls Bundler.with_unbundled_env in code paths that may execute before Bundler is required/defined, which can crash the generator with NameError in environments where Bundler isn’t already loaded.

Confidence Score: 3/5

  • This PR is close to safe to merge but has a likely runtime crash path if Bundler isn’t already loaded.
  • Core change is straightforward and well-covered by regression tests, but Bundler.with_unbundled_env is used without ensuring the Bundler constant is defined in install_generator.rb, which can raise NameError in some generator invocation contexts.
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb

Important Files Changed

Filename Overview
CHANGELOG.md Adds an Unreleased changelog entry noting the generator fix for inherited BUNDLE_GEMFILE.
react_on_rails/lib/generators/react_on_rails/install_generator.rb Stops using inherited ENV["BUNDLE_GEMFILE"] for Gemfile/Gemfile.lock checks and wraps bundler subprocesses in Bundler.with_unbundled_env; however, Bundler may be undefined at first use.
react_on_rails/rakelib/shakapacker_examples.rake Switches example generation commands to unbundled_sh_in_dir and uses bundle exec for shakapacker:install so the generated app’s Gemfile is used.
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb Adds regression tests ensuring Bundler subprocess commands run under with_unbundled_env and that Gemfile/Gemfile.lock checks ignore inherited BUNDLE_GEMFILE.

Sequence Diagram

sequenceDiagram
  participant Dev as Developer Shell
  participant Rails as rails g react_on_rails:install
  participant Gen as InstallGenerator
  participant Bundler as Bundler.with_unbundled_env
  participant Sys as system("bundle …")
  participant FS as Gemfile/Gemfile.lock

  Dev->>Rails: bundle exec rails g react_on_rails:install
  Rails->>Gen: run_generators
  Gen->>FS: shakapacker_in_gemfile_text?("shakapacker")
  FS-->>Gen: false (not present)
  Gen->>Bundler: with_unbundled_env { … }
  Bundler->>Sys: system("bundle add shakapacker --strict")
  Sys-->>Bundler: exit status
  Bundler-->>Gen: success?

  Gen->>Bundler: with_unbundled_env { … }
  Bundler->>Sys: system("bundle install")
  Sys-->>Bundler: exit status
  Bundler-->>Gen: success?

  Gen->>Bundler: with_unbundled_env { … }
  Bundler->>Sys: system("bundle exec rails shakapacker:install")
  Sys-->>Bundler: exit status
  Bundler-->>Gen: success?

  Note over Bundler: Clears inherited BUNDLE_GEMFILE
  Note over Gen,FS: Always checks local Gemfile/Gemfile.lock
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 515 to 522
it "clears BUNDLE_GEMFILE when running bundle install" do
# Mock the system calls to prevent actual execution
allow(install_generator).to receive(:system).and_return(true)

# Expect with_unbundled_env to be called for both bundle install and shakapacker:install
expect(Bundler).to receive(:with_unbundled_env).twice.and_yield

install_generator.send(:install_shakapacker)
Copy link

Choose a reason for hiding this comment

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

Test assumes exact call count

install_shakapacker now wraps system("bundle install") and system("bundle exec rails shakapacker:install") in Bundler.with_unbundled_env, but this spec asserts expect(Bundler).to receive(:with_unbundled_env).twice.and_yield. If any future change adds another with_unbundled_env call inside this method (or a nested call via a helper), this will fail while behavior is still correct. Consider asserting that with_unbundled_env is used for the specific commands (e.g., by expecting the wrapped system invocations) rather than an exact .twice count.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

justin808 and others added 2 commits February 8, 2026 18:27
Extract manual BUNDLE_GEMFILE save/restore into an around hook
so cleanup is guaranteed even if expectations fail. Removes
duplication across the three bundler isolation tests.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Replace exact .twice call count with have_received assertions that
  verify the specific commands are called, making the test resilient
  to future refactors (addresses Greptile review comment)
- Add CHANGELOG entry for this user-visible bug fix

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@justin808
Copy link
Member

@greptile, review this PR.
@claude, review this PR.

@claude
Copy link

claude bot commented Feb 9, 2026

Claude finished @justin808's task in 1m 49s —— View job


Code Review - PR #2288

Reviewing: Fix generator inheriting BUNDLE_GEMFILE from parent process


Summary

This PR correctly fixes a legitimate bug where the react_on_rails:install generator inherits BUNDLE_GEMFILE from the parent process, causing "injected gems" conflicts. The implementation is clean, well-tested, and follows established patterns in the codebase.

Recommendation: APPROVE ✅ (with one major issue to address in tests)


✅ Strengths

1. Correct Problem Diagnosis

The root cause is accurately identified: system() calls in generators inherit the parent process's environment variables, including BUNDLE_GEMFILE. This causes bundler to look at the wrong Gemfile, leading to gem version conflicts.

2. Proper Solution

Using Bundler.with_unbundled_env is the canonical fix, as documented in:

3. Consistent with Existing Patterns

The fix mirrors patterns already used in this codebase:

  • rakelib/task_helpers.rb:36 - unbundled_sh_in_dir helper
  • lib/react_on_rails/dev/process_manager.rb:136 - Process spawning
  • lib/react_on_rails/dev/pack_generator.rb:179 - Pack generation

4. Comprehensive Coverage

The fix addresses all three critical locations in install_generator.rb:

  • Line 268: bundle add shakapacker --strict
  • Line 280: bundle install
  • Line 287: bundle exec rails shakapacker:install

Plus the rake tasks in shakapacker_examples.rake:

  • Lines 92, 123, 133: Use unbundled_sh_in_dir for example generation

5. Excellent Test Coverage

Added 82 lines of regression tests (lines 503-580) covering:

  • ✅ Verification that Bundler.with_unbundled_env is called for each bundler command
  • ✅ Integration test showing actual environment variable clearing
  • ✅ Tests confirming Gemfile/Gemfile.lock detection doesn't rely on inherited BUNDLE_GEMFILE

6. Good Documentation


🔍 Issues & Suggestions

1. Test Environment Cleanup Not Exception-Safe ⚠️ MAJOR

Location: install_generator_spec.rb:528-579

Issue: The tests manually save and restore ENV["BUNDLE_GEMFILE"] inside each example:

it "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" do
  ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile"
  
  # ... test code ...
  
  # Restore happens AFTER expectations
  if original_gemfile
    ENV["BUNDLE_GEMFILE"] = original_gemfile
  else
    ENV.delete("BUNDLE_GEMFILE")
  end
end

Problem: If an expectation fails before the restoration code runs, BUNDLE_GEMFILE remains polluted for subsequent tests.

Good news: I see you already fixed this in the latest commit! Lines 529-538 now use an around hook:

around do |example|
  original_gemfile = ENV.fetch("BUNDLE_GEMFILE", nil)
  example.run
ensure
  if original_gemfile
    ENV["BUNDLE_GEMFILE"] = original_gemfile
  else
    ENV.delete("BUNDLE_GEMFILE")
  end
end

This is perfect! ✅ The ensure block guarantees cleanup even if tests fail.

Fix this →


2. Testing Bundler's Own Behavior 💡 Minor

Location: install_generator_spec.rb:540-548

The test "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" validates Bundler's own stdlib behavior rather than your code:

it "Bundler.with_unbundled_env clears BUNDLE_GEMFILE in block" do
  ENV["BUNDLE_GEMFILE"] = "/fake/parent/Gemfile"
  
  bundler_env_in_block = nil
  Bundler.with_unbundled_env do
    bundler_env_in_block = ENV.fetch("BUNDLE_GEMFILE", nil)
  end
  
  expect(bundler_env_in_block).to be_nil
end

Consideration: The other tests (lines 506-526) already verify that your generator calls with_unbundled_env. Testing Bundler's implementation is redundant and adds maintenance cost without exercising project-specific logic.

Recommendation: Consider removing this test or replacing it with a test that verifies generator method behavior, not Bundler's behavior. However, this is non-blocking - the test doesn't hurt anything.


📊 Code Quality

  • RuboCop: Passes with zero offenses
  • Code Style: Follows established patterns
  • Comments: Clear, explanatory, includes issue references
  • Security: No concerns; actually improves isolation
  • Performance: No impact (lightweight env var manipulation)

🧪 CI Status

All checks passing (11/11 successful, 1 skipped)

  • ✅ Lint JS and Ruby
  • ✅ Generator tests
  • ✅ Rspec test for gem
  • ✅ Integration Tests
  • ✅ Assets Precompile Check
  • ✅ JS unit tests
  • ✅ Pro package tests

📝 Changed Files Analysis

install_generator.rb (+14/-11)

shakapacker_examples.rake (+6/-2)

  • ✅ Changes sh_in_dirunbundled_sh_in_dir for rake and generator commands (lines 92, 123, 133)
  • ✅ Ensures example apps use their own Gemfile, not parent workspace's
  • ✅ Good explanatory comments (lines 90-91, 102-103, 131-132)

install_generator_spec.rb (+82/-0)

CHANGELOG.md (+4/-0)

  • ✅ Proper entry added under "Fixed" section (line 43)
  • ✅ Correct format with PR link and author attribution

🎯 Overall Assessment

This is a high-quality fix that:

The test improvements using the around hook are excellent - they enhance test robustness and follow RSpec best practices.


💡 Additional Notes

  1. Bundler Version Compatibility: The direct use of Bundler.with_unbundled_env is acceptable. This method was introduced in Bundler 2.1.0 (January 2020), and React on Rails requires Ruby 3.0+, which ships with Bundler 2.2+. No backward compatibility wrapper needed.

  2. Previous Reviews: This PR has received extensive review attention (5 previous Claude reviews, CodeRabbit, Greptile). All reviews consistently approve the fix with minor suggestions - this demonstrates strong consensus on the quality of the solution.

  3. Real-World Impact: This fix prevents a frustrating error that users encounter when running bundle exec rails g react_on_rails:install in certain environments (monorepos, CI pipelines, nested bundle contexts).


Great work on this fix! The implementation is solid and the testing is thorough. The use of the around hook for exception-safe ENV cleanup shows attention to detail and best practices. 🎉


View job

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 9, 2026

Additional Comments (1)

react_on_rails/lib/generators/react_on_rails/install_generator.rb
Bundler constant may be undefined

ensure_shakapacker_in_gemfile / install_shakapacker call Bundler.with_unbundled_env, but this file only does require "bundler" inside shakapacker_in_bundler_specs?. If those methods run before shakapacker_in_bundler_specs? is ever called (or in any environment where Bundler isn’t already loaded), this will raise NameError: uninitialized constant Bundler. Consider requiring Bundler before first use (or guarding with defined?(Bundler) / a fallback) so the generator can’t crash here.

The install generator uses Bundler.with_unbundled_env in three places
but relied on Rails boot process having already loaded Bundler. Add an
explicit require at the top of the file to make the dependency clear
and prevent a potential NameError in non-standard invocation contexts.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@justin808 justin808 merged commit 71f1033 into master Feb 9, 2026
30 checks passed
@justin808 justin808 deleted the ihabadham/fix/generator-bundle-gemfile-inheritance branch February 9, 2026 06:21
ihabadham added a commit that referenced this pull request Feb 10, 2026
Same fix as PR #2288 applied to shakapacker_in_lockfile? — always check
the target app's Gemfile.lock directly instead of deriving the path from
ENV["BUNDLE_GEMFILE"], which can point to a parent project's Gemfile
when running generators via bundle exec.

See: #2287

Co-Authored-By: Claude Opus 4.6 <[email protected]>
ihabadham added a commit that referenced this pull request Feb 11, 2026
Same fix as PR #2288 applied to shakapacker_in_lockfile? — always check
the target app's Gemfile.lock directly instead of deriving the path from
ENV["BUNDLE_GEMFILE"], which can point to a parent project's Gemfile
when running generators via bundle exec.

See: #2287

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug P0 Merge this week ready-to-merge Approved and green or only rebase needed review-needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generator inherits BUNDLE_GEMFILE from parent process, causing 'injected gems' conflicts

2 participants