Skip to content

Fix Env#current Rails.env call without defined?(Rails) guard#963

Merged
justin808 merged 5 commits intomainfrom
ihabadham/fix/env-rails-guard
Mar 8, 2026
Merged

Fix Env#current Rails.env call without defined?(Rails) guard#963
justin808 merged 5 commits intomainfrom
ihabadham/fix/env-rails-guard

Conversation

@ihabadham
Copy link
Copy Markdown
Contributor

@ihabadham ihabadham commented Mar 8, 2026

Summary

  • Adds defined?(Rails) guard to Shakapacker::Env#current — falls back to RAILS_ENV/RACK_ENV env vars, then Shakapacker::DEFAULT_ENV when Rails isn't loaded
  • Applies the same guard to fallback_env_warning which also called Rails.env directly
  • Adds rescue to fallback_env_warning for logger failures without full Rails (ActiveSupport::IsolatedExecutionState not available)
  • Adds specs for the non-Rails context

Related to react_on_rails#2438 — addresses the root cause of hook detection failure from bin/dev context.

Problem

Shakapacker::Env#current calls Rails.env directly with no guard:

def current
  Rails.env.presence_in(available_environments)
end

When Shakapacker.config is accessed from a non-Rails Ruby process, this raises NameError which gets rescued up the chain, causing Env#inquire to silently fall back to "production" instead of resolving the correct environment.

This was discovered via react_on_rails#2438 — React on Rails' bin/dev script intentionally skips loading Rails for startup speed, then calls Shakapacker.config.precompile_hook to detect hook configuration. The broken env resolution caused hook detection to fail silently.

Fix

Follows the same pattern established by PR #681 for Instance#initialize, and mirrors how Runner#initialize (line 239) already resolves the environment:

env: ENV["RAILS_ENV"] || ENV["NODE_ENV"] || "development"

Test plan

  • New spec env_without_rails_spec.rb — 4 examples covering RAILS_ENV fallback, RACK_ENV fallback, development default, and no-NameError guarantee
  • Existing env_spec.rb — 4 examples still pass (Rails context unchanged)
  • Existing staging_env_instance_spec.rb — 2 examples still pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved environment detection and fallback logic to more reliably determine the active environment across setups.
    • Ensured environment warning messages are emitted even when the usual framework logger is unavailable.
  • Tests

    • Added tests validating environment resolution and fallback behavior when the framework is not present.

Shakapacker::Env#current calls Rails.env directly, which raises
NameError when Shakapacker.config is accessed from a non-Rails Ruby
process (e.g., react_on_rails' bin/dev). The error gets rescued up the
chain, causing silent fallback to "production" instead of resolving the
correct environment from RAILS_ENV/RACK_ENV env vars.

This follows the same pattern established in PR #681 for
Instance#initialize, and mirrors how Runner#initialize already resolves
the environment via env vars.

Fixes #962

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

coderabbitai Bot commented Mar 8, 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

Guard Rails access in Shakapacker::Env: current prefers Rails.env only when defined/responds to env; otherwise uses ENV["RAILS_ENV"], ENV["RACK_ENV"], or Shakapacker::DEFAULT_ENV and applies presence_in(available_environments). fallback_env_warning computes env without requiring Rails and rescues logger errors. New spec covers non‑Rails scenarios.

Changes

Cohort / File(s) Summary
Environment Detection Safety
lib/shakapacker/env.rb
Guarded Rails.env access in current; fallback to ENV["RAILS_ENV"] / ENV["RACK_ENV"] / Shakapacker::DEFAULT_ENV and apply presence_in(available_environments). fallback_env_warning derives env similarly and rescues NameError/NoMethodError, using puts if the Rails logger is unavailable.
Non‑Rails Environment Tests
spec/shakapacker/env_without_rails_spec.rb
Adds RSpec validating Shakapacker::Env.inquire behavior when Rails is undefined: RAILS_ENV precedence, RACK_ENV usage, defaulting to development, and fallback to production when configured env is missing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰
I hop where code paths gently bend,
Rails may hide, but fallbacks mend.
ENV hums low, a steady guide,
No logger fright, no panic tide.
A rabbit's cheer — safe rails, stride!

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main fix: adding a defined?(Rails) guard to prevent calling Rails.env when Rails is not loaded.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ihabadham/fix/env-rails-guard

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.

Comment thread spec/shakapacker/env_without_rails_spec.rb Outdated
Comment thread spec/shakapacker/env_without_rails_spec.rb
Comment thread lib/shakapacker/env.rb
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 8, 2026

Review

The fix is correct and necessary — Shakapacker::Env#current calling Rails.env without a guard was a real bug that causes a silent "production" fallback in non-Rails contexts. The pattern follows what Instance#initialize already does and the PR description is clear.

A few issues worth addressing before merge:

Test isolation (hide_const vs stub_const("Rails", nil))

The most significant concern is the test stub. stub_const("Rails", nil) leaves the constant defined (as nil), so defined?(Rails) is still truthy inside the code under test. The guard condition in env.rb works anyway because nil.respond_to?(:env) is false, but this means the defined?(Rails) half of the guard is never actually exercised by these tests. hide_const("Rails") is the right tool here — it truly removes the constant and makes defined? return nil. See inline comment on line 32.

Duplication between current and fallback_env_warning

Both methods now contain the same two-branch env resolution logic. An empty-string inconsistency exists between them today (.presence in current, raw string in fallback_env_warning). Extracting a small private helper would eliminate the duplication and the inconsistency in one step. See inline comment on env.rb.

Style nit

The new spec uses bare describe while the rest of the suite uses RSpec.describe. See inline comment on line 4.


Overall the core change is solid. Happy to merge once the test isolation approach is addressed.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 8, 2026

Greptile Summary

This PR safely fixes a NameError crash in Shakapacker::Env#current and #fallback_env_warning when Shakapacker is called from a non-Rails context (e.g., React on Rails' bin/dev startup script).

Changes:

  • lib/shakapacker/env.rb — Both current and fallback_env_warning now guard Rails.env access with defined?(Rails) && Rails.respond_to?(:env), falling back to ENV["RAILS_ENV"] or ENV["RACK_ENV"] when Rails is unavailable.
  • spec/shakapacker/env_without_rails_spec.rb — New spec with 4 test cases verifying non-Rails environment resolution.

The pattern is consistent with existing usage in Shakapacker::Instance#initialize and Shakapacker::Doctor. Existing specs continue to pass.

Confidence Score: 5/5

  • Safe to merge. The fix is minimal, well-scoped, and consistent with established patterns in the codebase.
  • The core change is straightforward and correct — adding a simple guard to prevent calling Rails methods when Rails is not loaded. The implementation mirrors existing guards elsewhere in the codebase. New tests verify the non-Rails fallback path works correctly. All existing tests continue to pass.
  • No files require special attention.

Last reviewed commit: 79ac745

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
lib/shakapacker/env.rb (1)

29-35: Minor inconsistency with .presence usage.

Line 33 uses raw || fallback without .presence, whereas line 24 in current uses .presence. If ENV["RAILS_ENV"] is an empty string "", this would log "RAILS_ENV= environment is not defined" rather than falling back to RACK_ENV or "(unknown)".

This is a minor cosmetic inconsistency—the functionality is correct since this is only for logging. Consider adding .presence for consistency if desired.

♻️ Optional: Add `.presence` for consistency
     def fallback_env_warning
       env_value = if defined?(Rails) && Rails.respond_to?(:env)
                     Rails.env
                   else
-                    ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "(unknown)"
+                    ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "(unknown)"
                   end
       logger.info "RAILS_ENV=#{env_value} environment is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
     end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shakapacker/env.rb` around lines 29 - 35, In fallback_env_warning, ensure
the ENV lookups mirror the .presence usage from current: change how env_value is
derived in the fallback branch so ENV["RAILS_ENV"] and ENV["RACK_ENV"] use
.presence (e.g., ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence ||
"(unknown)") so empty strings fall through and the logged value is consistent
with current's behavior; update only the env_value expression inside the
fallback_env_warning method.
spec/shakapacker/env_without_rails_spec.rb (1)

31-33: Use hide_const instead of stub_const("Rails", nil) for accuracy.

The context describes "when Rails is not defined," but stub_const("Rails", nil) keeps Rails defined (as nil). After stubbing:

  • defined?(Rails) returns "constant" (truthy)
  • The test passes because nil.respond_to?(:env) returns false

Using hide_const("Rails") truly undefines the constant, making defined?(Rails) return nil (falsy), which more accurately tests the intended scenario.

♻️ Use hide_const for accurate simulation
     before do
-      stub_const("Rails", nil) if defined?(Rails)
+      hide_const("Rails") if defined?(Rails)
     end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/shakapacker/env_without_rails_spec.rb` around lines 31 - 33, Replace the
current use of stub_const("Rails", nil) in the before block with
hide_const("Rails") so the Rails constant is actually undefined during the
example; update the setup inside the before hook that references stub_const to
call hide_const("Rails") instead, ensuring defined?(Rails) returns nil and the
spec "when Rails is not defined" accurately exercises the code paths that rely
on Rails being absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/shakapacker/env.rb`:
- Around line 29-35: In fallback_env_warning, ensure the ENV lookups mirror the
.presence usage from current: change how env_value is derived in the fallback
branch so ENV["RAILS_ENV"] and ENV["RACK_ENV"] use .presence (e.g.,
ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "(unknown)") so empty
strings fall through and the logged value is consistent with current's behavior;
update only the env_value expression inside the fallback_env_warning method.

In `@spec/shakapacker/env_without_rails_spec.rb`:
- Around line 31-33: Replace the current use of stub_const("Rails", nil) in the
before block with hide_const("Rails") so the Rails constant is actually
undefined during the example; update the setup inside the before hook that
references stub_const to call hide_const("Rails") instead, ensuring
defined?(Rails) returns nil and the spec "when Rails is not defined" accurately
exercises the code paths that rely on Rails being absent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45433614-003f-4941-a58a-6a8595f370de

📥 Commits

Reviewing files that changed from the base of the PR and between 7a08a6d and 79ac745.

📒 Files selected for processing (2)
  • lib/shakapacker/env.rb
  • spec/shakapacker/env_without_rails_spec.rb

…gger

When no RAILS_ENV or RACK_ENV is set (common in non-Rails scripts), fall
back to DEFAULT_ENV ("development") instead of nil. This matches the
pattern in Runner#initialize which uses the same fallback chain.

Also add rescue to fallback_env_warning for logger failures — without
full Rails, ActiveSupport::IsolatedExecutionState may not be available,
causing the TaggedLogging logger to crash.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Comment thread spec/shakapacker/env_without_rails_spec.rb Outdated
Comment thread lib/shakapacker/env.rb
Comment thread lib/shakapacker/env.rb
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 8, 2026

Review

The core fix is correct and well-motivated — adding a defined?(Rails) guard mirrors the pattern already used in Instance#initialize (PR #681) and is the right approach for non-Rails callers.

Two issues need to be addressed before merging:

1. Tests do not actually cover the intended code path

stub_const("Rails", nil) leaves defined?(Rails) returning "constant" (the constant exists, it just holds nil). The tests pass because nil.respond_to?(:env) is false, which accidentally exercises the else-branch — but they do not test what happens when Rails is genuinely undefined. Use hide_const("Rails") instead (see inline comment).

2. The rescue block in fallback_env_warning is speculative and uses puts

The rescue catches NameError/NoMethodError from the logger just in case it breaks without Rails, but this failure mode has not been confirmed. Broad speculative rescues hide real bugs. Worse, the comment says "fall through silently" but the rescue body calls puts, which writes unconditionally to stdout — the opposite of silent. puts in a library is poor practice since callers cannot suppress or redirect it. (See inline comment.)

Minor

The env-resolution logic is duplicated between current and fallback_env_warning with subtly different fallback values (DEFAULT_ENV vs "(unknown)"). Extracting a small private resolved_env helper would keep them in sync. (See inline comment — suggestion, not a blocker.)

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
spec/shakapacker/env_without_rails_spec.rb (1)

53-55: Exercise the fallback-warning branch in this example.

With both env vars unset, current resolves to "development", and that key exists in this fixture, so fallback_env_warning never runs. That leaves the newly guarded non-Rails warning path untested. Using a missing env such as "staging" here would cover the actual regression path and verify that inquire falls back to "production" without raising.

💡 Suggested spec
-    it "does not raise NameError" do
-      stub_const("ENV", ENV.to_h.merge("RAILS_ENV" => nil, "RACK_ENV" => nil))
-      expect { Shakapacker::Env.inquire(instance) }.not_to raise_error
+    it "falls back to production without raising when the env is missing from config" do
+      stub_const("ENV", ENV.to_h.merge("RAILS_ENV" => "staging", "RACK_ENV" => nil))
+
+      resolved_env = nil
+      expect { resolved_env = Shakapacker::Env.inquire(instance) }.not_to raise_error
+      expect(resolved_env).to eq "production"
     end
If you also assert the warning emission, prefer a spy + `have_received`. As per coding guidelines, `**/*_spec.rb`: Use explicit RSpec spy assertions with `have_received`/`not_to have_received` instead of indirect counter patterns.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/shakapacker/env_without_rails_spec.rb` around lines 53 - 55, The test
should exercise the fallback-warning branch by stubbing ENV so current resolves
to a missing key (e.g., "staging") instead of defaulting to "development";
change the stub_const call used around Shakapacker::Env.inquire(instance) to set
RAILS_ENV or RACK_ENV to "staging" (or another absent fixture key) so that
Shakapacker::Env.inquire triggers the fallback to "production" and runs
fallback_env_warning and the non-Rails warning path; also, if asserting the
warning, replace indirect counters with an RSpec spy and use have_received to
assert the warning method was invoked.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/shakapacker/env.rb`:
- Around line 20-39: The fallback_env_warning should reuse the same environment
resolution logic as current to avoid mismatched/blank RAILS_ENV reporting: call
or replicate the resolver used in current (respecting presence and
available_environments) to obtain the effective env and log a neutral label
(e.g., "environment" or "Rails/Rack environment") rather than hard-coding
"RAILS_ENV="; update fallback_env_warning to derive env_value from current (or
the same ENV presence logic) and interpolate that in both logger.info and the
puts in the rescue block so the message accurately reflects the resolved
environment.

---

Nitpick comments:
In `@spec/shakapacker/env_without_rails_spec.rb`:
- Around line 53-55: The test should exercise the fallback-warning branch by
stubbing ENV so current resolves to a missing key (e.g., "staging") instead of
defaulting to "development"; change the stub_const call used around
Shakapacker::Env.inquire(instance) to set RAILS_ENV or RACK_ENV to "staging" (or
another absent fixture key) so that Shakapacker::Env.inquire triggers the
fallback to "production" and runs fallback_env_warning and the non-Rails warning
path; also, if asserting the warning, replace indirect counters with an RSpec
spy and use have_received to assert the warning method was invoked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8537ec3-47b0-4d92-904e-5c5c8813b28d

📥 Commits

Reviewing files that changed from the base of the PR and between 79ac745 and c6dff27.

📒 Files selected for processing (2)
  • lib/shakapacker/env.rb
  • spec/shakapacker/env_without_rails_spec.rb

Comment thread lib/shakapacker/env.rb
ihabadham added a commit to shakacode/react_on_rails that referenced this pull request Mar 8, 2026
resolve_hook_script_path required Rails.root to resolve hook script
paths, returning nil in bin/dev context where Rails isn't loaded. This
caused hook_configured? to return false even when the hook was properly
configured, leading to redundant pack generation after the hook already
ran.

Add project_root helper that falls back to BUNDLE_GEMFILE dirname (set
by Bundler during require "bundler/setup") or Dir.pwd when Rails.root
is unavailable.

Companion to shakacode/shakapacker#963 which fixes Shakapacker::Env to
work without Rails. Together these two fixes fully resolve #2438 without
the YAML-parsing workaround in #2561.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Use hide_const("Rails") instead of stub_const("Rails", nil) so
  defined?(Rails) is actually nil, testing the full guard path
- Add .presence to ENV lookups in fallback_env_warning to match current
- Fix comment: rescue falls back to puts, not "silently"
- Replace no-raise test with one that exercises fallback_env_warning by
  using RAILS_ENV=staging (missing from fixture config)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Comment thread lib/shakapacker/env.rb Outdated
Comment thread lib/shakapacker/env.rb
Comment thread spec/shakapacker/env_without_rails_spec.rb
Comment thread spec/shakapacker/env_without_rails_spec.rb
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 8, 2026

Review

The fix is on the right track — adding a defined?(Rails) guard is the correct approach and mirrors the pattern already used in Instance#initialize and doctor.rb. A few issues to address before merging:

🔴 Bug: .to_s on Rails.env breaks StringInquirer (see inline comment)

The most important issue. Rails.env is an ActiveSupport::StringInquirer, and instance.rb documents + depends on that:

# @return [ActiveSupport::StringInquirer] the environment
# env.development? # => true/false
# env.production?  # => true/false

The old Rails.env.presence_in(...) preserved this type. The new Rails.env.to_s strips it, so Shakapacker.env.development? would raise NoMethodError for any known environment when Rails is loaded. The .to_s call should be removed — presence_in already works correctly on a StringInquirer.

🟡 NODE_ENV not included in non-Rails fallback

The PR description notes that Runner#initialize falls back to ENV["NODE_ENV"] as well as ENV["RAILS_ENV"]. The fix here omits NODE_ENV. If there's a deliberate reason to exclude it (e.g. NODE_ENV shouldn't influence the Rails environment), please add a comment explaining why. Otherwise it looks like an oversight.

🟡 Code duplication (see inline comment)

The defined?(Rails) && Rails.respond_to?(:env) / ENV["RAILS_ENV"] / ENV["RACK_ENV"] block appears twice — once in current and once in fallback_env_warning. A small private resolved_env helper would eliminate the duplication.

🟢 Spec style + temp file path (see inline comments)

Minor: the new spec uses bare describe instead of RSpec.describe, and creates the fixture file under spec/fixtures/ via Dir.pwd rather than a proper tmpdir.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
lib/shakapacker/env.rb (1)

30-39: ⚠️ Potential issue | 🟡 Minor

Reuse the same resolver in fallback_env_warning.

Line 24 falls back to Shakapacker::DEFAULT_ENV, but Lines 33-39 still collapse that case to "(unknown)" and log it as RAILS_ENV=.... In the no-Rails/no-ENV path, the warning can still report the wrong requested environment.

💡 Proposed fix
     def current
-      env = if defined?(Rails) && Rails.respond_to?(:env)
-              Rails.env.to_s
-            else
-              ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || Shakapacker::DEFAULT_ENV
-            end
-      env.presence_in(available_environments)&.inquiry
+      resolved_env.presence_in(available_environments)&.inquiry
     end

     def fallback_env_warning
-      env_value = if defined?(Rails) && Rails.respond_to?(:env)
-                    Rails.env
-                  else
-                    ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "(unknown)"
-                  end
-      logger.info "RAILS_ENV=#{env_value} environment is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
+      env_value = resolved_env
+      logger.info "environment=#{env_value} is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
     rescue NameError, NoMethodError
-      puts "RAILS_ENV=#{env_value} environment is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
+      puts "environment=#{env_value} is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
     end
+
+    def resolved_env
+      if defined?(Rails) && Rails.respond_to?(:env)
+        Rails.env.to_s
+      else
+        ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || Shakapacker::DEFAULT_ENV
+      end
+    end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shakapacker/env.rb` around lines 30 - 39, The fallback_env_warning
currently builds env_value that uses "(unknown)" when neither Rails.env nor ENV
vars are present, causing the warning to misreport the actual fallback; update
fallback_env_warning to reuse the same resolver used elsewhere
(Shakapacker::DEFAULT_ENV / FALLBACK_ENV) by computing env_value with Rails.env
if available, else ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence ||
Shakapacker::DEFAULT_ENV (or FALLBACK_ENV) and then use that env_value in both
logger.info and the puts fallback so the logged message shows the real fallback
environment; adjust references to env_value, FALLBACK_ENV,
Shakapacker::DEFAULT_ENV, fallback_env_warning, config_path, logger and puts
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/shakapacker/env.rb`:
- Around line 21-26: The current method is returning a plain String
(Rails.env.to_s) so inquire returns that String instead of an
ActiveSupport::StringInquirer; change current to return a StringInquirer in both
branches: when Rails.env exists call Rails.env.inquiry (not to_s), and when
falling back use (ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence ||
Shakapacker::DEFAULT_ENV).to_s.inquiry (or use ActiveSupport::StringInquirer.new
on the chosen value) so that inquire (which returns current ||
FALLBACK_ENV.inquiry) always yields an object that responds to env.development?
/ env.production?; keep usage of available_environments/presence_in unchanged.

---

Duplicate comments:
In `@lib/shakapacker/env.rb`:
- Around line 30-39: The fallback_env_warning currently builds env_value that
uses "(unknown)" when neither Rails.env nor ENV vars are present, causing the
warning to misreport the actual fallback; update fallback_env_warning to reuse
the same resolver used elsewhere (Shakapacker::DEFAULT_ENV / FALLBACK_ENV) by
computing env_value with Rails.env if available, else ENV["RAILS_ENV"].presence
|| ENV["RACK_ENV"].presence || Shakapacker::DEFAULT_ENV (or FALLBACK_ENV) and
then use that env_value in both logger.info and the puts fallback so the logged
message shows the real fallback environment; adjust references to env_value,
FALLBACK_ENV, Shakapacker::DEFAULT_ENV, fallback_env_warning, config_path,
logger and puts accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9d0ea945-eb8a-428a-9d34-c4621bfa033d

📥 Commits

Reviewing files that changed from the base of the PR and between c6dff27 and 6f5efd7.

📒 Files selected for processing (2)
  • lib/shakapacker/env.rb
  • spec/shakapacker/env_without_rails_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • spec/shakapacker/env_without_rails_spec.rb

Comment thread lib/shakapacker/env.rb
- Remove unnecessary .to_s on Rails.env that was dropping the
  StringInquirer type, breaking env.development? predicates
- Wrap inquire return with .inquiry so both Rails and non-Rails
  paths always return ActiveSupport::StringInquirer
- Fix if/else/end indentation to match RuboCop variable alignment

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Comment thread lib/shakapacker/env.rb Outdated
Comment thread lib/shakapacker/env.rb
Comment thread lib/shakapacker/env.rb
Comment thread spec/shakapacker/env_without_rails_spec.rb
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 8, 2026

Review

The fix correctly addresses the root cause: Env#current calling Rails.env unconditionally in non-Rails processes. The overall approach — mirroring the guard already in Instance#initialize — is the right pattern for this codebase.

The (current || FALLBACK_ENV).inquiry change in inquire is also a latent correctness fix: the original code only called .inquiry on FALLBACK_ENV, so non-Rails paths now properly return an ActiveSupport::StringInquirer rather than a plain String, keeping the env.development? predicate interface intact.

A few things worth addressing before merging:

  1. Duplicated guard logicdefined?(Rails) && Rails.respond_to?(:env) appears identically in both current and fallback_env_warning. Extracting it to a resolve_env private helper would reduce drift risk. (See inline comment on current.)

  2. Inconsistent fallback valuefallback_env_warning falls back to "(unknown)" while current falls back to Shakapacker::DEFAULT_ENV. When no env vars are set, the warning prints RAILS_ENV=(unknown) while inquire resolves to "development" — a misleading log. (See inline comment on line 33.)

  3. Rescue scope too wide — The rescue NameError, NoMethodError wraps the full method body, including the env_value assignment. Narrowing it to just the logger.info call would be safer. (See inline comment on the rescue block.)

  4. Logger-failure path is untested — The rescue to puts fallback is the most novel part of this change, but there is no spec exercising it. Worth adding one example that stubs the logger to raise and asserts no error propagates. (See inline comment on the spec.)

No security or performance concerns.

Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
lib/shakapacker/env.rb (1)

30-34: ⚠️ Potential issue | 🟡 Minor

Use the same final default in the warning path.

When neither RAILS_ENV nor RACK_ENV is set, current still tries Shakapacker::DEFAULT_ENV before falling back. fallback_env_warning reports "(unknown)" in that same case, so the warning can disagree with the env that was actually checked.

💡 Suggested change
       env_value = if defined?(Rails) && Rails.respond_to?(:env)
         Rails.env
       else
-        ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "(unknown)"
+        ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || Shakapacker::DEFAULT_ENV
       end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shakapacker/env.rb` around lines 30 - 34, The warning message can
disagree with the actual environment check because env_value falls back to
Shakapacker::DEFAULT_ENV before "(unknown)"; update the fallback_env_warning
logic to use the same final default (Shakapacker::DEFAULT_ENV) instead of the
literal "(unknown)" so the warning matches env_value's resolution (referencing
env_value, Shakapacker::DEFAULT_ENV, and fallback_env_warning to locate and
change the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/shakapacker/env.rb`:
- Around line 30-34: The warning message can disagree with the actual
environment check because env_value falls back to Shakapacker::DEFAULT_ENV
before "(unknown)"; update the fallback_env_warning logic to use the same final
default (Shakapacker::DEFAULT_ENV) instead of the literal "(unknown)" so the
warning matches env_value's resolution (referencing env_value,
Shakapacker::DEFAULT_ENV, and fallback_env_warning to locate and change the
code).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41a75948-b0c6-4a6f-b820-2a605a02052d

📥 Commits

Reviewing files that changed from the base of the PR and between 6f5efd7 and 6d421b3.

📒 Files selected for processing (1)
  • lib/shakapacker/env.rb

The warning message used "(unknown)" when no env vars were set, but
current resolves to DEFAULT_ENV in that case. Align so the warning
accurately reports what env was attempted.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Comment thread lib/shakapacker/env.rb
Comment thread lib/shakapacker/env.rb
Comment thread lib/shakapacker/env.rb
Comment thread spec/shakapacker/env_without_rails_spec.rb
Comment thread spec/shakapacker/env_without_rails_spec.rb
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
lib/shakapacker/env.rb (1)

14-16: Cache current once in inquire.

current consults available_environments, so line 15 and line 16 currently resolve the environment twice. Reusing a local keeps the warning path and return value tied to the same lookup and avoids the extra YAML load.

♻️ Proposed change
   def inquire
-    fallback_env_warning if config_path.exist? && !current
-    (current || FALLBACK_ENV).inquiry
+    env = current
+    fallback_env_warning if config_path.exist? && !env
+    (env || FALLBACK_ENV).inquiry
   end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shakapacker/env.rb` around lines 14 - 16, The inquire method calls
current twice causing duplicate lookups; cache the result in a local variable
(e.g., env = current) at the start of inquire, call fallback_env_warning if
config_path.exist? && !env, and return (env || FALLBACK_ENV).inquiry so both the
warning path and returned value use the same lookup and avoid reloading YAML
from available_environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/shakapacker/env.rb`:
- Around line 14-16: The inquire method calls current twice causing duplicate
lookups; cache the result in a local variable (e.g., env = current) at the start
of inquire, call fallback_env_warning if config_path.exist? && !env, and return
(env || FALLBACK_ENV).inquiry so both the warning path and returned value use
the same lookup and avoid reloading YAML from available_environments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 537d8b9e-5cc9-489d-8e45-ba571b5edd4c

📥 Commits

Reviewing files that changed from the base of the PR and between 6d421b3 and 72c76a6.

📒 Files selected for processing (1)
  • lib/shakapacker/env.rb

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 8, 2026

Review: The fix is sound and addresses the root cause. Rails.env was called unconditionally in non-Rails processes. Detailed inline comments have been posted. Summary of concerns: (1) code duplication between current and fallback_env_warning - extract a resolve_env helper; (2) NODE_ENV is omitted from the fallback chain while Runner#initialize uses it - inconsistency worth aligning; (3) rescue NameError/NoMethodError is too broad and could hide real bugs - add raise if defined?(Rails) to scope it; (4) spec nits: nil ENV values are unidiomatic, and the fixture file path risks parallel-test collisions. Overall: correct and needed change, these are polish items rather than blockers.

@justin808 justin808 merged commit 7c95c45 into main Mar 8, 2026
79 checks passed
@justin808 justin808 deleted the ihabadham/fix/env-rails-guard branch March 8, 2026 22:46
justin808 added a commit that referenced this pull request Mar 8, 2026
justin808 added a commit that referenced this pull request Mar 8, 2026
## Summary

- Added [Unreleased] changelog entries for two merged PRs missing from
CHANGELOG.md:
- **PR #963**: Fixed `Env#current` crashing when Rails is not loaded
(bug fix by @ihabadham)
  - **PR #900**: Added Node package API documentation (by @justin808)
- Skipped non-user-visible PRs: #958, #957, #959

## Test plan

- [x] `yarn lint` passes
- [x] Changelog formatting matches existing conventions

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only change confined to `CHANGELOG.md`, with no runtime
or build behavior impact.
> 
> **Overview**
> Updates `CHANGELOG.md` under **[Unreleased]** to document two
previously-merged changes: a fix preventing `Env#current` from crashing
when Rails isn’t loaded, and new documentation for the Node package
JavaScript API (`docs/node_package_api.md`).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
81bf2b8. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
justin808 pushed a commit to shakacode/react_on_rails that referenced this pull request Mar 8, 2026
## Summary

- Adds `project_root` helper that resolves the project root without
requiring `Rails.root`
- Falls back to `BUNDLE_GEMFILE` dirname (reliable — always set after
`require "bundler/setup"`) then `Dir.pwd`
- `resolve_hook_script_path` now uses `project_root` instead of
hard-requiring `Rails.root`

Related to #2438 — addresses the hook detection root cause (hook not
running from `bin/dev` context), though the exact symptom reported in
that issue (pack generation failure) could not be reproduced.

## Problem

`resolve_hook_script_path` returned nil whenever `Rails.root` wasn't
available (e.g., from `bin/dev` which intentionally skips loading Rails
for startup speed). This caused `hook_configured?` to return false even
when the precompile hook was correctly configured in `shakapacker.yml`,
leading to redundant pack generation after the hook already ran.

## Companion PR

This works together with
[shakacode/shakapacker#963](shakacode/shakapacker#963),
which fixes `Shakapacker::Env#current` to resolve the environment from
`RAILS_ENV`/`RACK_ENV` env vars when Rails isn't loaded. Together, these
two fixes address the `bin/dev` hook detection issue.

With both fixes applied, `bin/dev` output shows the correct flow:
```
🔧 Running Shakapacker precompile hook...
   Command: bin/shakapacker-precompile-hook
✅ Precompile hook completed successfully
⏭️  Skipping pack generation (handled by shakapacker precompile hook: bin/shakapacker-precompile-hook)
```

## Supersedes #2561

PR #2561 worked around the Shakapacker `Env#current` bug by
re-implementing YAML config parsing. With the Shakapacker fix in place,
that workaround is no longer needed — this PR provides only the minimal
`resolve_hook_script_path` fix that was still necessary.

## Test plan

- [x] Verified `hook_configured?` returns `true` from non-Rails context
(test app with both fixes)
- [x] Verified `bin/dev` runs hook and skips redundant pack generation
- [x] Verified `BUNDLE_GEMFILE` correctly resolves project root even
when `Dir.pwd` differs

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Improvements**
* More robust project-root detection: falls back from Rails to Gemfile
directory to current working directory for clearer path resolution
across environments.
* Improved hook-script detection and reporting, with better handling of
self-guard patterns.

* **Tests**
* Added tests covering project-root detection with Rails present/absent
and various Gemfile scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
ihabadham added a commit to shakacode/react_on_rails that referenced this pull request Mar 9, 2026
…2580)

## Summary

- Reverts #2561 (squash commit 769d120), which was merged after #2568
already landed on master
- #2568 +
[shakacode/shakapacker#963](shakacode/shakapacker#963)
fix the root cause of #2438#2561's YAML-parsing fallback is redundant
- CHANGELOG entry updated to reference #2568 only

## What's removed

All of #2561's additions on top of #2568:
- `extract_precompile_hook_from_yaml` /
`extract_precompile_hook_from_shakapacker_config` split
- `extract_script_from_command` interpreter-prefix stripping
- `extract_hash_for_environment` / `current_shakapacker_environment`
helpers
- `SHAKAPACKER_CONFIG_PATH` constant, `require "erb"`, `require "yaml"`
- `rubocop:disable Metrics/ModuleLength`
- `GENERATE_PACKS_PATTERN` expansion for `generate_packs_if_needed`
- 72 lines of #2561-specific specs

## What remains (from #2568 + shakapacker#963)

- `project_root` helper (Rails.root → BUNDLE_GEMFILE → Dir.pwd fallback)
- `resolve_hook_script_path` using `project_root`
- `require "pathname"`
- 3 project_root specs
- Shakapacker `Env#current` guard (separate repo, already merged)

## Why

#2568 was designed to supersede #2561 — its PR description says so
explicitly. Both were merged, resulting in unnecessary complexity (YAML
config re-parsing, ERB evaluation, environment fallback chain) that
duplicates what Shakapacker already handles after #963.

## Test plan

- `bundle exec rspec
react_on_rails/spec/react_on_rails/packer_utils_spec.rb`
- `bundle exec rubocop react_on_rails/lib/react_on_rails/packer_utils.rb
react_on_rails/spec/react_on_rails/packer_utils_spec.rb`

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved bin/dev startup reliability with enhanced hook script path
resolution during early startup when Rails.root is unavailable.
  * Removed YAML configuration dependency, streamlining hook detection.
* Added validation checks for hook script existence to prevent runtime
errors.
  * Simplified hook script pattern matching for better clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@claude claude Bot mentioned this pull request Mar 13, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants