Skip to content

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Feb 9, 2026

Summary

  • Adds a self-guard (exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true") to the generator template's bin/shakapacker-precompile-hook, preventing duplicate execution in HMR mode (two webpack processes) regardless of Shakapacker version
  • Implements smart 3-case version warning: script+guard (silent), script without guard (suggests adding the line), direct command hook (suggests upgrade)
  • Fixes dummy app hook which was a no-op due to load not triggering __FILE__ == $PROGRAM_NAME
  • Updates extract_precompile_hook to prefer Shakapacker's public API when available
  • Adds hook_script_has_self_guard? detection method with full test coverage

Builds on #2282 (hook detection fix).

Problem

In HMR mode with SSR, Shakapacker spawns two webpack processes (wp-client and wp-server), each triggering the precompile_hook. The existing SHAKAPACKER_SKIP_PRECOMPILE_HOOK env var set by bin/dev only works on Shakapacker >= 9.4.0.

Migration for existing users

Add this line near the top of your bin/shakapacker-precompile-hook:

exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"

New installations get this automatically. The version warning will detect missing self-guards and suggest the fix.

Test plan

  • bundle exec rspec spec/react_on_rails/packer_utils_spec.rb (28 examples, 0 failures)
  • bundle exec rspec spec/react_on_rails/dev/server_manager_spec.rb (50 examples, 0 failures)
  • bundle exec rubocop on all changed files (0 offenses)
  • CI passes
  • Manual smoke test: SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true bin/shakapacker-precompile-hook exits immediately

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • More reliable precompile hook detection across setups and fewer duplicate precompile runs in HMR+SSR.
    • Clearer, context-aware precompile warnings and more consistent dummy-app behavior.
  • New Features

    • Script-based precompile hook pattern with an early self-guard to avoid double execution.
    • Extensible dev precompile pattern and detection that prefers public config when available.
  • Documentation

    • Added upgrade guides, tips, and process-manager guidance for precompile hooks.
  • Tests

    • Expanded coverage for hook detection, self-guard, and upgrade scenarios.

ihabadham and others added 5 commits February 8, 2026 19:18
The detection logic was looking for `react_on_rails:generate_packs` rake
task pattern, but the generator template uses `generate_packs_if_stale`
Ruby method directly. This mismatch caused the detection to always return
false for apps created with the generator.

Updated the regex pattern to match both valid approaches:
- The rake task: `react_on_rails:generate_packs`
- The Ruby method: `generate_packs_if_stale`

Fixes #2279

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

Co-Authored-By: Claude <[email protected]>
Address review feedback:
- Add \b word boundaries to GENERATE_PACKS_PATTERN to prevent false
  positives on substrings like 'my_generate_packs_if_stale_helper'
- Add test for direct command containing generate_packs_if_stale
- Add tests for script file reading path (both patterns)
- Add test for script file not existing

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

Co-Authored-By: Claude <[email protected]>
The method can return nil when Shakapacker is not defined or on error,
but the RBS signature declared it as returning String. Updated to String?
to match the actual implementation.

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

Co-Authored-By: Claude <[email protected]>
In HMR mode with SSR, Shakapacker spawns two webpack processes that
each trigger the precompile_hook, causing duplicate execution. The
existing SHAKAPACKER_SKIP_PRECOMPILE_HOOK env var only works on
Shakapacker >= 9.4.0.

This adds a self-guard (`exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"]`)
directly in the hook script, making it version-independent. The version
warning is now smarter with three cases: script+guard (silent),
script without guard (suggests adding the line), and direct command
hooks (suggests upgrading Shakapacker or switching to script-based hook).

Also fixes the dummy app hook which was a no-op due to load not
triggering the shared file's __FILE__ == $PROGRAM_NAME block.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@justin808 justin808 requested a review from ihabadham February 9, 2026 09:05
@greptile-apps
Copy link

greptile-apps bot commented Feb 9, 2026

Greptile Overview

Greptile Summary

This PR improves Shakapacker precompile hook reliability in SSR+HMR setups by adding a self-guard (exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true") to the generated bin/shakapacker-precompile-hook template (and dummy apps) so duplicate hook execution is prevented even on Shakapacker < 9.4.

On the Ruby side, ReactOnRails::PackerUtils now prefers Shakapacker’s public API for extracting precompile_hook, expands hook detection to match both the rake task and generator method form (generate_packs_if_stale), and adds hook_script_has_self_guard? to detect whether a script hook contains the guard line. ReactOnRails::Dev::ServerManager uses this to implement a 3-case warning: silent when guarded, actionable when the script is missing the guard, and a version/upgrade warning for direct command hooks.

Tests are expanded to cover the new hook extraction and self-guard detection behaviors, and docs/upgrading guidance is updated to recommend script-based hooks with self-guards for SSR+HMR across Shakapacker versions.

Confidence Score: 4/5

  • This PR is largely safe to merge, with one user-facing warning link that should be corrected to avoid misdirecting users.
  • Core behavior changes (self-guard in generated hook, improved hook extraction/detection) are covered by targeted specs and are straightforward. The main issue found is a hardcoded documentation URL in the new warning path that points to i18n docs instead of precompile hook guidance, which will confuse users following the recommendation.
  • react_on_rails/lib/react_on_rails/dev/server_manager.rb

Important Files Changed

Filename Overview
CHANGELOG.md Adds changelog entries for improved precompile hook detection and new self-guard behavior in HMR setups.
docs/building-features/i18n.md Documents script-based precompile hook with SHAKAPACKER_SKIP_PRECOMPILE_HOOK self-guard for SSR+HMR reliability.
docs/building-features/process-managers.md Updates process manager guidance: Shakapacker 9.4+ native skip var, and self-guarded script hook for 9.0–9.3.
docs/upgrading/upgrading-react-on-rails.md Adds upgrade section recommending self-guarded script-based precompile hooks for SSR+HMR across Shakapacker versions.
react_on_rails/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook Adds early-exit self-guard on SHAKAPACKER_SKIP_PRECOMPILE_HOOK to prevent duplicate precompile hook execution under HMR.
react_on_rails/lib/react_on_rails/dev/server_manager.rb Enhances Shakapacker version warning with 3-case logic (self-guarded script, missing-guard script, direct command); introduces a hardcoded docs link that appears unrelated.
react_on_rails/lib/react_on_rails/packer_utils.rb Adds public-API-based precompile_hook extraction, broadens generate_packs detection, and introduces self-guard detection by reading hook scripts.
react_on_rails/rakelib/rbs.rake Adjusts RBS validation command to require pathname, addressing missing dependency during signature validation.
react_on_rails/sig/react_on_rails/packer_utils.rbs Updates PackerUtils RBS: adds signatures for new methods and corrects shakapacker_precompile_hook_value to be nullable.
react_on_rails/spec/dummy/bin/shakapacker-precompile-hook Adds self-guard and fixes dummy hook execution by explicitly invoking shared functions after load; changes missing-file warning text.
react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb Expands tests for new 3-case warning behavior: direct command hook warns, missing-guard script warns, self-guarded script is silent.
react_on_rails/spec/react_on_rails/packer_utils_spec.rb Adds extensive unit tests for extract_precompile_hook, script hook parsing, and self-guard detection regex behavior.
react_on_rails/spec/support/shakapacker_precompile_hook_shared.rb Refactors shared dummy precompile hook helper: adds package manager detection (pnpm/yarn/npm) and simplifies ReScript build invocation.
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook Adds self-guard and explicitly invokes shared dummy hook helper functions after load to ensure execution.

Sequence Diagram

sequenceDiagram
  autonumber
  participant Dev as bin/dev (ReactOnRails::Dev::ServerManager)
  participant PU as ReactOnRails::PackerUtils
  participant SC as Shakapacker.config
  participant WH as Webpack process (client/server)
  participant HS as Hook script (bin/shakapacker-precompile-hook)

  Dev->>PU: shakapacker_version_requirement_met?("9.0.0")
  Dev->>PU: shakapacker_version_requirement_met?("9.4.0")
  alt Shakapacker < 9.4.0 and precompile_hook configured
    Dev->>PU: shakapacker_precompile_hook_value()
    PU->>SC: extract precompile_hook (public API if available)
    Dev->>PU: hook_script_has_self_guard?(hook_value)
    alt Hook is script path and has self-guard
      Dev-->>Dev: No warning
    else Hook is script path but missing guard
      Dev-->>Dev: Print actionable warning to add self-guard line
    else Hook is direct command
      Dev-->>Dev: Print version warning and recommend upgrade or script-based hook
    end
  end

  par Two webpack processes in SSR+HMR
    WH->>HS: execute precompile_hook
    WH->>HS: execute precompile_hook
  end
  alt ENV[SHAKAPACKER_SKIP_PRECOMPILE_HOOK] == "true"
    HS-->>WH: exit 0 (self-guard prevents duplicate work)
  else
    HS->>HS: run precompile tasks (e.g., generate_packs_if_stale)
  end
Loading

@claude
Copy link

claude bot commented Feb 9, 2026

Code review

Found 1 issue to address:

CHANGELOG.md (line 33)

Missing PR attribution for the changelog entry. According to AGENTS.md line 168, the required format is:

Format: [PR 1818](https://github.com/shakacode/react_on_rails/pull/1818) by [username](https://github.com/username) (no hash before PR number)

Current (line 33):

- **Precompile Hook Self-Guard for HMR**: Added self-guard to the generator template's `bin/shakapacker-precompile-hook` to prevent duplicate execution in HMR mode where two webpack processes (client dev-server + server watcher) each trigger the hook. The script now exits early when `SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true` is set by `bin/dev`, regardless of Shakapacker version. The version warning is now smarter: it only warns for hooks that lack the self-guard or use direct commands. **Existing users**: add `exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"` near the top of your `bin/shakapacker-precompile-hook` script.

Should end with:

... script. [PR 2388](https://github.com/shakacode/react_on_rails/pull/2388) by [justin808](https://github.com/justin808).

The same issue applies to line 34 (the "Dummy App Precompile Hook" entry should also end with the same PR attribution).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

Implements robust detection for Shakapacker precompile hooks, adds a self-guard to precompile hook scripts to avoid duplicate HMR-triggered runs, extends packer utility helpers and RBS signatures, updates server warning logic and generator templates, and adds documentation and tests for the new behaviors.

Changes

Cohort / File(s) Summary
Changelog & Docs
CHANGELOG.md, docs/building-features/i18n.md, docs/building-features/process-managers.md, docs/upgrading/upgrading-react-on-rails.md
Adds guidance and upgrade steps for script-based precompile hooks with a self-guard for SSR+HMR, documents bin/dev precompile pattern, and updates process-manager guidance and changelog notes.
Generator Templates & Binaries
react_on_rails/lib/generators/.../bin/shakapacker-precompile-hook, react_on_rails/spec/dummy/bin/shakapacker-precompile-hook, react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
Adds an early environment-based self-guard (checks SHAKAPACKER_SKIP_PRECOMPILE_HOOK) and changes loading to explicitly call shared build/generate functions to ensure deterministic execution.
Hook Detection & Utilities
react_on_rails/lib/react_on_rails/packer_utils.rb, react_on_rails/sig/react_on_rails/packer_utils.rbs
Prefer public Shakapacker API when available; add GENERATE_PACKS_PATTERN and SELF_GUARD_PATTERN; implement hook_script_has_self_guard?, script path resolution, and update RBS signatures (nullable hook value and new helpers).
Server Warning Logic
react_on_rails/lib/react_on_rails/dev/server_manager.rb
Reworks version-warning logic to distinguish direct-command hooks vs script hooks and to suppress or emit targeted warnings depending on the presence of a self-guard.
Tests & Test Support
react_on_rails/spec/react_on_rails/packer_utils_spec.rb, react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb, react_on_rails/spec/support/shakapacker_precompile_hook_shared.rb
Adds extensive specs for hook extraction, generate_packs detection, and self-guard detection; refactors test helper to detect package manager and update build_rescript flow.
RBS Validation Task
react_on_rails/rakelib/rbs.rake, react_on_rails/sig/.../packer_utils.rbs
Updates RBS validate invocation to include -r pathname; updates RBS signatures to reflect new/nullable helpers.

Sequence Diagram(s)

sequenceDiagram
  participant WebpackA as Webpack Dev (proc A)
  participant WebpackB as Webpack Dev (proc B)
  participant HookScript as bin/shakapacker-precompile-hook
  participant RailsApp as Rails (react_on_rails)

  WebpackA->>HookScript: invoke precompile hook
  HookScript->>HookScript: read SHAKAPACKER_SKIP_PRECOMPILE_HOOK
  alt skip == "true"
    HookScript-->>WebpackA: exit (skip)
  else
    HookScript->>RailsApp: load shared hook file
    HookScript->>RailsApp: call build_rescript_if_needed / generate_packs_if_needed
    HookScript-->>WebpackA: exit
  end

  WebpackB->>HookScript: concurrent invoke
  HookScript->>HookScript: read SHAKAPACKER_SKIP_PRECOMPILE_HOOK
  alt skip == "true"
    HookScript-->>WebpackB: exit (skip duplicate)
  else
    HookScript->>RailsApp: (may be skipped by guard) run tasks
    HookScript-->>WebpackB: exit
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

🐰 I hopped into the build and peeked,

A little guard so runs don't leak,
Two webpacks racing? now they'll wait,
One tiny flag saved duplicate fate,
Hooray — clean packs and fewer freaks! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add self-guard to precompile hook for HMR reliability' accurately and specifically describes the main change: introducing a self-guard mechanism to the precompile hook to prevent duplicate execution in HMR setups.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jg/self-guard-precompile-hook

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.

…manager detection

1. Add -r pathname to rbs validate command so the Pathname type used in
   packer_utils.rbs can be resolved during validation.

2. Fix precompile hook to detect the correct package manager from
   package.json's packageManager field instead of blindly preferring
   yarn. In CI, global yarn exists but the project uses pnpm, causing
   yarn to reject the packageManager: "[email protected]" field.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
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 `@CHANGELOG.md`:
- Line 34: The CHANGELOG.md entry about the dummy app precompile hook
(referencing spec/dummy/bin/shakapacker-precompile-hook and PR 2388) is a
tests-only/internal change and should not appear in the user-facing changelog;
either remove this bullet entirely or relocate it under a non-user-facing
section such as "Developer (Contributors Only)" or "Internal/Testing" so that
CHANGELOG.md only contains user-visible changes (features, bug fixes, breaking
changes, deprecations, performance improvements).
🧹 Nitpick comments (2)
react_on_rails/lib/react_on_rails/packer_utils.rb (1)

207-216: Redundant File.exist? check after resolve_hook_script_path.

resolve_hook_script_path already returns nil unless potential_path.file?, so the File.exist?(script_path) check on line 212 is redundant. Consider simplifying to just return false unless script_path.

♻️ Proposed simplification
      # Check if it's a script file path
      script_path = resolve_hook_script_path(hook_value)
-     return false unless script_path && File.exist?(script_path)
+     return false unless script_path
react_on_rails/lib/react_on_rails/dev/server_manager.rb (1)

322-341: Minor: resolve_hook_script_path called twice for the same hook value.

hook_script_has_self_guard? (line 326) internally calls resolve_hook_script_path, and then line 329 calls it again. Consider extracting the script path once and passing it through, or accepting the minor redundancy for readability.

CHANGELOG.md Outdated

- **Precompile Hook Detection**: Fixed `shakapacker_precompile_hook_configured?` always returning `false` for apps created with the React on Rails generator. The detection logic only matched the rake task pattern (`react_on_rails:generate_packs`) but the generator template uses the Ruby method (`generate_packs_if_stale`). Now correctly detects both patterns, including resolving script file contents. [PR 2282](https://github.com/shakacode/react_on_rails/pull/2282) by [ihabadham](https://github.com/ihabadham).
- **Precompile Hook Self-Guard for HMR**: Added self-guard to the generator template's `bin/shakapacker-precompile-hook` to prevent duplicate execution in HMR mode where two webpack processes (client dev-server + server watcher) each trigger the hook. The script now exits early when `SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true` is set by `bin/dev`, regardless of Shakapacker version. The version warning is now smarter: it only warns for hooks that lack the self-guard or use direct commands. **Existing users**: add `exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"` near the top of your `bin/shakapacker-precompile-hook` script. [PR 2388](https://github.com/shakacode/react_on_rails/pull/2388) by [justin808](https://github.com/justin808).
- **Dummy App Precompile Hook**: Fixed `spec/dummy/bin/shakapacker-precompile-hook` which was a no-op due to `load` not triggering the shared file's `__FILE__ == $PROGRAM_NAME` block. The hook now explicitly invokes the shared functions after loading. [PR 2388](https://github.com/shakacode/react_on_rails/pull/2388) by [justin808](https://github.com/justin808).
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Dummy app fix may not warrant a user-facing changelog entry.

The spec/dummy precompile hook fix is an internal test infrastructure change, not a user-visible bug fix. Per coding guidelines, the CHANGELOG should include user-visible changes only (features, bug fixes, breaking changes, deprecations, performance improvements) and exclude tests or refactoring. Consider moving this to a "Developer (Contributors Only)" section or removing it.

As per coding guidelines, "Update /CHANGELOG.md for user-visible changes only (features, bug fixes, breaking changes, deprecations, performance improvements) — exclude linting, formatting, refactoring, tests, or doc fixes."

🤖 Prompt for AI Agents
In `@CHANGELOG.md` at line 34, The CHANGELOG.md entry about the dummy app
precompile hook (referencing spec/dummy/bin/shakapacker-precompile-hook and PR
2388) is a tests-only/internal change and should not appear in the user-facing
changelog; either remove this bullet entirely or relocate it under a
non-user-facing section such as "Developer (Contributors Only)" or
"Internal/Testing" so that CHANGELOG.md only contains user-visible changes
(features, bug fixes, breaking changes, deprecations, performance improvements).

justin808 and others added 4 commits February 9, 2026 17:31
The "Dummy App Precompile Hook" entry was a tests-only/internal change
(fixing spec/dummy/bin/shakapacker-precompile-hook) and should not
appear in the user-facing changelog.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Avoid false positives when checking for self-guard logic by requiring an actual early-exit statement. Add coverage for public/fallback precompile hook extraction and guard detection edge cases. Also make the Pro dummy hook invoke the shared helper methods after load so behavior matches the OSS dummy hook.
Align process-manager and i18n docs with script self-guard behavior, clarify when warnings are shown, and add explicit upgrade guidance for existing SSR+HMR apps including version and CI recommendations.
- Add extract_precompile_hook and hook_contains_generate_packs? to
  packer_utils.rbs for type-checking completeness
- Fix misleading test description to accurately reflect what the test
  validates (no exit/return guard, not just comments/strings)

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

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

@claude
Copy link

claude bot commented Feb 10, 2026

Claude finished @justin808's task in 2m 8s —— View job


Code Review Complete ✅

Todo List

  • Read repository guidelines (CLAUDE.md, AGENTS.md)
  • Check CI status
  • Review core implementation files
  • Review test coverage and test files
  • Review documentation changes
  • Verify CHANGELOG format and content
  • Compile and post comprehensive review feedback

Review Summary

This is a well-implemented PR that solves a real problem (duplicate precompile hook execution in HMR mode with SSR). The implementation is thorough, well-tested, and backwards compatible. CI is mostly passing (7 in progress, 4 passed, 0 failed).

✅ Strengths

  1. Excellent test coverage: 78 total test examples across the changes (28 in packer_utils_spec.rb, 50 in server_manager_spec.rb)
  2. Smart 3-case warning logic: Distinguishes between self-guarded scripts (silent), scripts without guard (actionable warning), and direct commands (upgrade suggestion)
  3. Backwards compatible: Existing hooks continue to work; new installations get the self-guard automatically
  4. Defensive error handling: shakapacker_precompile_hook_configured? returns false on errors to fail safe
  5. Clear migration path: Documentation and warnings guide users to add the self-guard line
  6. RBS type signatures: Properly updated with nullable return types
  7. Follows repo conventions: Code style, test structure, and git workflow all follow AGENTS.md guidelines

🔍 Issues Found

1. CHANGELOG: Missing PR attribution (Already identified by previous reviewers)

Location: CHANGELOG.md:33

Per AGENTS.md:168, the format should be:

[PR 2388](https://github.com/shakacode/react_on_rails/pull/2388) by [justin808](https://github.com/justin808)

Both changelog entries (lines 33 and the removed line 34 about dummy app) need PR attribution at the end.

Status: ✅ RESOLVED - Line 34 (dummy app entry) was removed in commit 7347a95. Line 33 already has proper PR attribution.

2. Minor code redundancy in packer_utils.rb:219

Location: react_on_rails/lib/react_on_rails/packer_utils.rb:219

script_path = resolve_hook_script_path(hook_value)
return false unless script_path && File.exist?(script_path)

The File.exist?(script_path) check is redundant because resolve_hook_script_path already returns nil unless potential_path.file? (line 234). This is a nitpick and doesn't affect correctness.

Suggested simplification:

return false unless script_path

3. Duplicate resolve_hook_script_path call in server_manager.rb

Location: react_on_rails/lib/react_on_rails/dev/server_manager.rb:326-329

hook_script_has_self_guard? (line 326) internally calls resolve_hook_script_path, and then line 329 calls it again. This is a minor performance inefficiency but not a bug. Consider extracting the path once, though the current code is readable.

📝 Code Quality Observations

Strong Points:

  1. Regex patterns: GENERATE_PACKS_PATTERN and SELF_GUARD_PATTERN are well-designed with word boundaries and proper escaping
  2. Error messages: Server manager warnings (lines 332-357) are clear, actionable, and color-coded
  3. Hook detection: Properly handles both public API (precompile_hook) and private fallback (:data method)
  4. Test structure: Named subjects, clear context blocks, and comprehensive edge case coverage
  5. Generator template: The self-guard in shakapacker-precompile-hook:7 is well-commented

Documentation:

  1. Excellent migration guidance: i18n.md:80-93 provides clear tip for HMR+SSR setups with script-based hooks
  2. Process manager docs: Well-integrated into existing documentation flow
  3. Inline code comments: Appropriate level of detail without being verbose

🧪 Test Coverage Analysis

packer_utils_spec.rb (28 examples):

  • ✅ Tests both generate_packs_if_stale and react_on_rails:generate_packs patterns
  • ✅ Tests script path resolution with Rails root
  • ✅ Tests hook detection with nil, empty string, and missing Shakapacker
  • ✅ Tests self-guard detection with various patterns
  • ✅ Tests error handling (file not found, read errors)

server_manager_spec.rb (50 examples):

  • ✅ Tests precompile hook execution in all modes (dev, static, prod)
  • ✅ Tests environment variable setting (SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true)
  • ✅ Tests skip for kill/help commands
  • ✅ Tests version-specific warning behavior (9.3.0 vs 9.4.0+)
  • ✅ Tests self-guarded script detection (no warning)
  • ✅ Tests direct command hook (shows warning)

🎯 Architecture Decisions

  1. Self-guard pattern: Simple exit 0 if ENV[...] == "true" is elegant and version-agnostic
  2. Detection strategy: Regex-based detection is pragmatic and handles both method and rake task patterns
  3. Warning strategy: Three-tier approach (silent/actionable/upgrade) provides right level of guidance without noise
  4. Shared test helper: shakapacker_precompile_hook_shared.rb with explicit function invocation (lines 28-29) is a clever solution for the load vs require issue

🔒 Security Review

  • ✅ No security vulnerabilities introduced
  • ✅ Shell command execution uses Shellwords.split (line 276) for safe parsing
  • ✅ Regex patterns avoid ReDoS vulnerabilities
  • ✅ File path resolution checks potential_path.file? to prevent directory traversal

🚀 Performance Considerations

  • ✅ File reads are done sparingly and only when needed
  • ✅ Memoization used appropriately (@version_checks in packer_utils.rb:31)
  • ⚠️ Minor: Double resolve_hook_script_path call in server manager (see issue Add linting and CI scripts #3 above)

📊 CI Status

Currently: 7 in progress, 4 passed, 0 failed

  • ✅ Check Markdown Links (passed)
  • ✅ Assets Precompile Check (passed)
  • ✅ JS unit tests (passed)
  • ✅ Claude Code Review (passed, this review)
  • ⏳ React on Rails Pro tests (in progress)
  • ⏳ Generator tests (in progress)
  • ⏳ Lint JS and Ruby (in progress)
  • ⏳ Rspec test for gem (in progress)
  • ⏳ Integration Tests (in progress)

Recommendation: ✅ APPROVE (with optional minor improvements)

This PR is ready to merge. The two minor code redundancies (issues #2 and #3) are nitpicks that don't affect correctness or security. The CHANGELOG issue was already resolved in commit 7347a95.

Optional Improvements (non-blocking):

  1. Consider simplifying packer_utils.rb:219 to remove redundant File.exist? check
  2. Consider extracting resolve_hook_script_path result in server_manager.rb:326-329 to avoid duplicate call

These are purely for code cleanliness and can be addressed in a follow-up if desired.

Testing Recommendations:

Before merge, verify:

  • Manual smoke test: SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true bin/shakapacker-precompile-hook exits immediately
  • Wait for CI to complete (currently 7 jobs in progress)

Great work on this PR! The self-guard pattern is a elegant solution to the HMR duplicate execution problem, and the smart warning system will help users upgrade smoothly. The comprehensive test coverage and clear documentation make this a solid contribution.

--- | View PR branch

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.

14 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 352 to 356
puts Rainbow(" Recommendations:").cyan
puts Rainbow(" 1. Upgrade to Shakapacker 9.4.0 or later:").cyan
puts Rainbow(" bundle update shakapacker").cyan.bold
puts Rainbow(" 2. Or switch to a script-based hook with a self-guard.").cyan
puts Rainbow(" See: https://www.shakacode.com/react-on-rails/docs/building-features/i18n").cyan
Copy link

Choose a reason for hiding this comment

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

Incorrect docs link

This warning message links to the i18n docs (/docs/building-features/i18n), but the recommendation here is about migrating precompile_hook to a script-based hook with a self-guard. That link will send users to an unrelated page, so they won’t find the migration steps referenced by the warning. Please update it to point at the precompile hook guidance (e.g., the process-managers section that documents script-based hooks/self-guard).

The warning for direct-command hooks linked to the i18n docs page
instead of the process-managers page that documents script-based
hooks and the self-guard pattern.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@justin808 justin808 merged commit 024c429 into master Feb 10, 2026
25 of 26 checks passed
@justin808 justin808 deleted the jg/self-guard-precompile-hook branch February 10, 2026 08:37
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