44#
55# Usage:
66# script/validate-precompile-output <output_file>
7+ #
8+ # # When piping, MUST use pipefail to catch rake failures:
9+ # set -o pipefail
710# RAILS_ENV=production rake assets:precompile 2>&1 | script/validate-precompile-output -
811#
12+ # # Or use tee to capture output and check exit code separately:
13+ # RAILS_ENV=production rake assets:precompile 2>&1 | tee output.txt
14+ # if [ ${PIPESTATUS[0]} -ne 0 ]; then echo "Rake failed!"; exit 1; fi
15+ # script/validate-precompile-output output.txt
16+ #
917# Exit codes:
1018# 0 - All checks passed
1119# 1 - One or more failure patterns detected
1220#
1321# This script checks for known issues that don't cause precompile to fail
1422# but indicate bugs like duplicate task execution or missing dependencies.
1523#
24+ # Environment variables:
25+ # MIN_EXPECTED_LINES - Minimum lines expected in output (default: 10)
26+ # WARNING_THRESHOLD - Warning count threshold before alerting (default: 20)
27+ #
1628# See: https://github.com/shakacode/react_on_rails/issues/2081
1729
1830set -e
@@ -24,14 +36,39 @@ if [ "$OUTPUT_FILE" = "-" ]; then
2436 TEMP_FILE=$( mktemp)
2537 cat > " $TEMP_FILE "
2638 OUTPUT_FILE=" $TEMP_FILE "
27- trap " rm -f $TEMP_FILE " EXIT
39+ trap ' rm -f " $TEMP_FILE"' EXIT
2840fi
2941
3042if [ ! -f " $OUTPUT_FILE " ]; then
3143 echo " Error: Output file '$OUTPUT_FILE ' not found"
3244 exit 1
3345fi
3446
47+ # Check for empty or suspiciously small output
48+ # A successful precompile produces substantial output (webpack stats, asset listing, etc.)
49+ # Empty or very small output usually indicates the command failed before producing anything.
50+ OUTPUT_SIZE=$( wc -c < " $OUTPUT_FILE " | tr -d ' ' )
51+ OUTPUT_LINES=$( wc -l < " $OUTPUT_FILE " | tr -d ' ' )
52+
53+ if [ " $OUTPUT_SIZE " -eq 0 ]; then
54+ echo " Error: Output file is empty."
55+ echo " This usually means the precompile command failed before producing any output."
56+ echo " Check that all dependencies are installed (bundle install, pnpm install)."
57+ exit 1
58+ fi
59+
60+ # Minimum expected lines for a successful precompile (webpack output alone is 50+ lines)
61+ MIN_EXPECTED_LINES=" ${MIN_EXPECTED_LINES:- 10} "
62+ if [ " $OUTPUT_LINES " -lt " $MIN_EXPECTED_LINES " ]; then
63+ echo " Warning: Output file has only $OUTPUT_LINES lines (expected at least $MIN_EXPECTED_LINES )."
64+ echo " This may indicate the precompile failed early. Output contents:"
65+ echo " ---"
66+ cat " $OUTPUT_FILE "
67+ echo " ---"
68+ echo " "
69+ echo " If this is expected, set MIN_EXPECTED_LINES=0 to disable this check."
70+ fi
71+
3572echo " Validating assets:precompile output..."
3673echo " "
3774
@@ -57,8 +94,14 @@ report_warning() {
5794 fi
5895}
5996
97+ # ==============================================================================
6098# Pattern 1: Duplicate webpack compilation (indicates rake tasks running twice)
61- # Look for webpack's "Compiled successfully" message which appears once per compilation
99+ # ==============================================================================
100+ # Prevents: Rails::Engine loading rake tasks twice, causing double webpack builds
101+ # History: PR #2052 fixed this - Engine's rake_tasks block was explicitly loading
102+ # tasks from lib/tasks/, but Rails::Engine already does this automatically.
103+ # Symptom: "Compiled successfully" appears 2+ times in output, doubling build time.
104+ # See: https://github.com/shakacode/react_on_rails/pull/2052
62105if grep -q " Compiled successfully" " $OUTPUT_FILE " ; then
63106 COMPILE_SUCCESS_COUNT=$( grep -c " Compiled successfully" " $OUTPUT_FILE " || true)
64107 if [ " $COMPILE_SUCCESS_COUNT " -gt 1 ]; then
@@ -70,7 +113,12 @@ if grep -q "Compiled successfully" "$OUTPUT_FILE"; then
70113 fi
71114fi
72115
73- # Pattern 2: Duplicate task execution messages (generate_packs)
116+ # ==============================================================================
117+ # Pattern 2: Duplicate task execution (react_on_rails rake tasks)
118+ # ==============================================================================
119+ # Prevents: Same root cause as Pattern 1 - rake tasks executing twice.
120+ # Symptom: react_on_rails:generate_packs or react_on_rails:locale appear multiple times.
121+ # These tasks generate files, so running twice wastes time and may cause issues.
74122if grep -q " react_on_rails:generate_packs" " $OUTPUT_FILE " ; then
75123 GENERATE_PACKS_COUNT=$( grep -c " react_on_rails:generate_packs" " $OUTPUT_FILE " || true)
76124 if [ " $GENERATE_PACKS_COUNT " -gt 1 ]; then
@@ -81,7 +129,7 @@ if grep -q "react_on_rails:generate_packs" "$OUTPUT_FILE"; then
81129 fi
82130fi
83131
84- # Pattern 2b: Duplicate locale generation
132+ # Pattern 2b: Duplicate locale generation (same root cause as 2a)
85133if grep -q " react_on_rails:locale" " $OUTPUT_FILE " ; then
86134 LOCALE_COUNT=$( grep -c " react_on_rails:locale" " $OUTPUT_FILE " || true)
87135 if [ " $LOCALE_COUNT " -gt 1 ]; then
@@ -92,7 +140,7 @@ if grep -q "react_on_rails:locale" "$OUTPUT_FILE"; then
92140 fi
93141fi
94142
95- # Pattern 2c: Duplicate webpack builds (check "Built at:" messages )
143+ # Pattern 2c: Duplicate webpack builds (alternative detection via "Built at:" timestamp )
96144BUILT_AT_COUNT=$( grep -c " Built at:" " $OUTPUT_FILE " || true)
97145if [ " $BUILT_AT_COUNT " -gt 1 ]; then
98146 report_error " Detected $BUILT_AT_COUNT webpack builds (expected 1). Tasks may be running twice."
@@ -101,63 +149,112 @@ if [ "$BUILT_AT_COUNT" -gt 1 ]; then
101149 FAILURES_FOUND=1
102150fi
103151
104- # Pattern 3: Module not found errors
105- if grep -Ei " module not found|cannot find module|can't resolve" " $OUTPUT_FILE " ; then
152+ # ==============================================================================
153+ # Pattern 3: Module resolution errors
154+ # ==============================================================================
155+ # Prevents: Missing npm packages or incorrect import paths that precompile may not fail on.
156+ # Important: Uses strict matching to avoid false positives from deprecation warnings
157+ # or informational messages that contain similar phrases.
158+ # Only matches when preceded by ERROR, Error:, or webpack's "Module not found: Error:" format.
159+ if grep -Ei " (^|\s)(ERROR|Error:).*module not found|(^|\s)(ERROR|Error:).*cannot find module|(^|\s)(ERROR|Error:).*can't resolve|Module not found: Error:" " $OUTPUT_FILE " ; then
106160 report_error " Module resolution errors detected in precompile output."
107161 echo " Sample matching lines:"
108- grep -Ei " module not found|cannot find module|can't resolve" " $OUTPUT_FILE " | head -3
162+ grep -Ei " (^|\s)(ERROR|Error:).* module not found|(^|\s)(ERROR|Error:).* cannot find module|(^|\s)(ERROR|Error:).* can't resolve|Module not found: Error: " " $OUTPUT_FILE " | head -3
109163 FAILURES_FOUND=1
110164fi
111165
112- # Pattern 3b: ENOENT errors (missing files/directories)
166+ # Pattern 3b: ENOENT errors (missing files/directories during build )
113167if grep -q " Error: ENOENT" " $OUTPUT_FILE " ; then
114168 report_error " Missing file or directory errors detected."
115169 echo " Sample matching lines:"
116170 grep -n " Error: ENOENT" " $OUTPUT_FILE " | head -3
117171 FAILURES_FOUND=1
118172fi
119173
120- # Pattern 4: Webpack build errors (use specific webpack error markers)
121- if grep -Ei " webpack.*error|failed to compile|compilation failed|ERROR in" " $OUTPUT_FILE " ; then
174+ # ==============================================================================
175+ # Pattern 4: Webpack compilation errors
176+ # ==============================================================================
177+ # Prevents: Silent webpack build failures that don't cause precompile to exit non-zero.
178+ # Important: Uses specific error markers to avoid false positives from benign text
179+ # like "webpack handles errors gracefully" in documentation or comments.
180+ # Matches:
181+ # - "^ERROR in ./": Webpack's standard error format for file-specific errors
182+ # - "failed to compile": Direct compilation failure message
183+ # - "compilation failed": Explicit failure message
184+ # - "Module build failed": Loader/build errors (e.g., babel, css-loader failures)
185+ if grep -Ei " ^ERROR in [./]|failed to compile|compilation failed|Module build failed" " $OUTPUT_FILE " ; then
122186 report_error " Webpack compilation errors detected."
123187 echo " Sample matching lines:"
124- grep -Ei " webpack.*error |failed to compile|compilation failed|ERROR in " " $OUTPUT_FILE " | head -3
188+ grep -Ei " ^ERROR in [./] |failed to compile|compilation failed|Module build failed " " $OUTPUT_FILE " | head -3
125189 FAILURES_FOUND=1
126190fi
127191
128- # Pattern 5: Ruby/Rails errors during precompile (match error class format)
192+ # ==============================================================================
193+ # Pattern 5: Ruby/Rails errors during precompile
194+ # ==============================================================================
195+ # Prevents: Ruby exceptions that are caught/logged but don't fail the rake task.
196+ # Matches specific exception class names to avoid false positives.
129197if grep -E " (NameError|LoadError|NoMethodError|SyntaxError):" " $OUTPUT_FILE " ; then
130198 report_error " Ruby errors detected during precompile."
131199 echo " Sample matching lines:"
132200 grep -E " (NameError|LoadError|NoMethodError|SyntaxError):" " $OUTPUT_FILE " | head -3
133201 FAILURES_FOUND=1
134202fi
135203
136- # Pattern 6: Asset pipeline errors
204+ # Pattern 5b: Bundler/Gem errors (missing or version mismatch)
205+ # Detects errors like "Could not find gem 'shakapacker (= 9.4.0)'"
206+ # These indicate bundle install wasn't run or Gemfile.lock is out of sync.
207+ if grep -Ei " Could not find gem|Run \` bundle install\` |Bundler::GemNotFound|Gem::MissingSpecError" " $OUTPUT_FILE " ; then
208+ report_error " Bundler/gem dependency errors detected."
209+ echo " This usually means 'bundle install' needs to be run."
210+ echo " Sample matching lines:"
211+ grep -Ei " Could not find gem|Run \` bundle install\` |Bundler::GemNotFound|Gem::MissingSpecError" " $OUTPUT_FILE " | head -3
212+ FAILURES_FOUND=1
213+ fi
214+
215+ # ==============================================================================
216+ # Pattern 6: Sprockets/Asset pipeline errors
217+ # ==============================================================================
218+ # Prevents: Missing assets that Sprockets can't find or undeclared asset dependencies.
137219if grep -Ei " Sprockets::FileNotFound|Asset.*was not declared" " $OUTPUT_FILE " ; then
138220 report_error " Asset pipeline errors detected."
139221 echo " Sample matching lines:"
140222 grep -Ei " Sprockets::FileNotFound|Asset.*was not declared" " $OUTPUT_FILE " | head -3
141223 FAILURES_FOUND=1
142224fi
143225
226+ # ==============================================================================
144227# Pattern 7: Memory issues
228+ # ==============================================================================
229+ # Prevents: OOM kills that can happen silently with webpack on large codebases.
230+ # Note: "killed" alone may have false positives; consider tightening if needed.
145231if grep -Ei " javascript heap out of memory|killed|out of memory" " $OUTPUT_FILE " ; then
146232 report_error " Memory-related errors detected."
147233 echo " Sample matching lines:"
148234 grep -Ei " javascript heap out of memory|killed|out of memory" " $OUTPUT_FILE " | head -3
149235 FAILURES_FOUND=1
150236fi
151237
152- # Pattern 8: Check for warnings that might indicate problems
238+ # ==============================================================================
239+ # Pattern 8: Warning count threshold (advisory, does not fail)
240+ # ==============================================================================
241+ # Purpose: Alert when warnings spike, which may indicate new issues worth investigating.
242+ # Threshold is configurable via WARNING_THRESHOLD env var.
243+ # Default of 20 chosen based on typical precompile output having ~10-15 benign warnings
244+ # from node deprecations and peer dependency notices.
245+ WARNING_THRESHOLD=" ${WARNING_THRESHOLD:- 20} "
153246WARNING_COUNT=$( grep -ci " warning" " $OUTPUT_FILE " || true)
154- if [ " $WARNING_COUNT " -gt 10 ]; then
155- report_warning " High number of warnings detected: $WARNING_COUNT warnings found. Please review."
247+ if [ " $WARNING_COUNT " -gt " $WARNING_THRESHOLD " ]; then
248+ report_warning " High number of warnings detected: $WARNING_COUNT warnings found (threshold: $WARNING_THRESHOLD ) . Please review."
156249 echo " Sample warnings:"
157250 grep -i " warning" " $OUTPUT_FILE " | head -5
158251fi
159252
160- # Pattern 9: Deprecation warnings (log for visibility, don't fail)
253+ # ==============================================================================
254+ # Pattern 9: Deprecation warnings (advisory, does not fail)
255+ # ==============================================================================
256+ # Purpose: Track deprecations that may cause future breakage after upgrades.
257+ # Rails deprecations are logged with "DEPRECATION" prefix.
161258DEPRECATION_COUNT=$( grep -c " DEPRECATION" " $OUTPUT_FILE " || true)
162259if [ " $DEPRECATION_COUNT " -gt 0 ]; then
163260 report_warning " Found $DEPRECATION_COUNT deprecation warnings. These may indicate future breakage."
0 commit comments