From 218b5e7b3cb6f3065e417049368c3d8e99b0ae9e Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 25 Nov 2025 21:13:22 -1000 Subject: [PATCH 1/4] Add CI workflow to check assets:precompile output for failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2081 This adds a new GitHub Actions workflow that runs `RAILS_ENV=production bin/rake assets:precompile` on the dummy app and checks the output for known failure patterns: - Duplicate webpack compilations (tasks running twice) - Duplicate rake task execution (e.g., generate_packs running multiple times) - Module not found / cannot resolve errors - Webpack compilation errors - Ruby errors (NameError, LoadError, etc.) - Asset pipeline errors - Memory issues (heap out of memory) - High warning counts (>10 warnings triggers a warning annotation) The workflow runs on PRs and master pushes, uses the same change detection as other CI jobs, and uploads the precompile output as an artifact for debugging when failures occur. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/precompile-check.yml | 226 +++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 .github/workflows/precompile-check.yml diff --git a/.github/workflows/precompile-check.yml b/.github/workflows/precompile-check.yml new file mode 100644 index 0000000000..74de40609b --- /dev/null +++ b/.github/workflows/precompile-check.yml @@ -0,0 +1,226 @@ +name: Assets Precompile Check + +on: + push: + branches: + - 'master' + pull_request: + paths-ignore: + - '**.md' + - 'docs/**' + - 'react_on_rails_pro/**' + workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false + +jobs: + detect-changes: + permissions: + contents: read + actions: read + runs-on: ubuntu-22.04 + outputs: + docs_only: ${{ steps.detect.outputs.docs_only }} + run_dummy_tests: ${{ steps.detect.outputs.run_dummy_tests }} + has_full_ci_label: ${{ steps.check-label.outputs.result }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + persist-credentials: false + - name: Check for full-ci label + id: check-label + uses: ./.github/actions/check-full-ci-label + - name: Detect relevant changes + id: detect + run: | + if [ "${{ inputs.force_run }}" = "true" ] || [ "${{ steps.check-label.outputs.result }}" = "true" ]; then + echo "run_dummy_tests=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" + script/ci-changes-detector "$BASE_REF" + shell: bash + - name: Guard docs-only master pushes + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: ./.github/actions/ensure-master-docs-safety + with: + docs-only: ${{ steps.detect.outputs.docs_only }} + previous-sha: ${{ github.event.before }} + + precompile-check: + needs: detect-changes + # Skip only if: master push AND docs-only changes + # Otherwise run if: on master OR workflow_dispatch OR dummy tests needed + if: | + !( + github.event_name == 'push' && + github.ref == 'refs/heads/master' && + needs.detect-changes.outputs.docs_only == 'true' + ) && ( + github.ref == 'refs/heads/master' || + github.event_name == 'workflow_dispatch' || + needs.detect-changes.outputs.run_dummy_tests == 'true' + ) + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + bundler: 2.5.9 + # libyaml-dev is needed for psych v5 + - name: Fix dependency for libyaml-dev + run: sudo apt install libyaml-dev + - name: Setup Node + uses: ./.github/actions/setup-node-with-retry + with: + node-version: '22' + - name: Setup pnpm + uses: pnpm/action-setup@v4 + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV" + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Print system information + run: | + echo "Linux release: "; cat /etc/issue + echo "Current user: "; whoami + echo "Current directory: "; pwd + echo "Ruby version: "; ruby -v + echo "Node version: "; node -v + echo "pnpm version: "; pnpm --version + echo "Bundler version: "; bundle --version + - name: Install Node modules with pnpm for renderer package + run: | + pnpm install --frozen-lockfile + pnpm add -g yalc + - name: yalc publish for react-on-rails + run: cd packages/react-on-rails && yalc publish + - name: yalc add react-on-rails + run: cd react_on_rails/spec/dummy && yalc add react-on-rails + - name: Install Node modules with pnpm for dummy app + run: cd react_on_rails/spec/dummy && pnpm install --ignore-workspace + - name: Save dummy app ruby gems to cache + uses: actions/cache@v4 + with: + path: react_on_rails/spec/dummy/vendor/bundle + key: dummy-app-gem-cache-${{ hashFiles('react_on_rails/spec/dummy/Gemfile.lock') }}-precompile + - name: Install Ruby Gems for dummy app + run: | + cd react_on_rails/spec/dummy + bundle lock --add-platform 'x86_64-linux' + if ! bundle check --path=vendor/bundle; then + bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 + fi + - name: Run assets:precompile and check output + run: | + cd react_on_rails/spec/dummy + + echo "Running RAILS_ENV=production bin/rake assets:precompile..." + echo "==========================================" + + # Run precompile and capture both stdout and stderr + RAILS_ENV=production bin/rake assets:precompile 2>&1 | tee precompile_output.txt + + echo "==========================================" + echo "Precompile finished. Checking output for known issues..." + echo "" + + # Check for known failure patterns + FAILURES_FOUND=0 + + # Pattern 1: Duplicate webpack compilation (indicates rake tasks running twice) + WEBPACK_COMPILE_COUNT=$(grep -c "Compiled" precompile_output.txt || true) + if [ "$WEBPACK_COMPILE_COUNT" -gt 1 ]; then + # Check if these are genuinely different compilations or duplicates + # If the same "Compiled" message appears multiple times for the same bundle, it's a problem + DUPLICATE_COMPILES=$(sort precompile_output.txt | uniq -d | grep -c "Compiled" || true) + if [ "$DUPLICATE_COMPILES" -gt 0 ]; then + echo "::error::FAILURE: Detected duplicate webpack compilations. Tasks may be running twice." + echo " Found $DUPLICATE_COMPILES duplicate compilation messages." + FAILURES_FOUND=1 + fi + fi + + # Pattern 2: Duplicate task execution messages + if grep -q "react_on_rails:generate_packs" precompile_output.txt; then + GENERATE_PACKS_COUNT=$(grep -c "react_on_rails:generate_packs" precompile_output.txt || true) + if [ "$GENERATE_PACKS_COUNT" -gt 1 ]; then + echo "::error::FAILURE: react_on_rails:generate_packs task ran $GENERATE_PACKS_COUNT times (should only run once)." + FAILURES_FOUND=1 + fi + fi + + # Pattern 3: Module not found errors + if grep -Ei "module not found|cannot find module|can't resolve" precompile_output.txt; then + echo "::error::FAILURE: Module resolution errors detected in precompile output." + FAILURES_FOUND=1 + fi + + # Pattern 4: Webpack build errors + if grep -Ei "error in |failed to compile|compilation failed" precompile_output.txt; then + echo "::error::FAILURE: Webpack compilation errors detected." + FAILURES_FOUND=1 + fi + + # Pattern 5: Ruby/Rails errors during precompile + if grep -Ei "NameError|LoadError|NoMethodError|SyntaxError" precompile_output.txt; then + echo "::error::FAILURE: Ruby errors detected during precompile." + FAILURES_FOUND=1 + fi + + # Pattern 6: Asset pipeline errors + if grep -Ei "sprockets::filenotfound|asset .* was not declared" precompile_output.txt; then + echo "::error::FAILURE: Asset pipeline errors detected." + FAILURES_FOUND=1 + fi + + # Pattern 7: Memory issues + if grep -Ei "javascript heap out of memory|killed|out of memory" precompile_output.txt; then + echo "::error::FAILURE: Memory-related errors detected." + FAILURES_FOUND=1 + fi + + # Pattern 8: Check for warnings that might indicate problems + WARNING_COUNT=$(grep -ci "warning" precompile_output.txt || true) + if [ "$WARNING_COUNT" -gt 10 ]; then + echo "::warning::High number of warnings detected: $WARNING_COUNT warnings found. Please review." + fi + + if [ "$FAILURES_FOUND" -eq 1 ]; then + echo "" + echo "==========================================" + echo "PRECOMPILE CHECK FAILED" + echo "==========================================" + echo "Review the output above for details." + exit 1 + fi + + echo "" + echo "==========================================" + echo "PRECOMPILE CHECK PASSED" + echo "==========================================" + echo "No known failure patterns detected in precompile output." + - name: Upload precompile output + if: always() + uses: actions/upload-artifact@v4 + with: + name: precompile-output-${{ github.run_id }} + path: react_on_rails/spec/dummy/precompile_output.txt + retention-days: 7 From 416099890ab88803216ae944048498b63a7b28a5 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 25 Nov 2025 21:46:16 -1000 Subject: [PATCH 2/4] Add generate_packs step before assets:precompile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The precompile check needs to run react_on_rails:generate_packs first to generate the server-bundle-generated.js file that webpack imports. This matches what other CI jobs do before running webpack builds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/precompile-check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/precompile-check.yml b/.github/workflows/precompile-check.yml index 74de40609b..7501a10b51 100644 --- a/.github/workflows/precompile-check.yml +++ b/.github/workflows/precompile-check.yml @@ -128,6 +128,8 @@ jobs: if ! bundle check --path=vendor/bundle; then bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 fi + - name: Generate file system-based packs + run: cd react_on_rails/spec/dummy && RAILS_ENV=production bundle exec rake react_on_rails:generate_packs - name: Run assets:precompile and check output run: | cd react_on_rails/spec/dummy From dc4c429c83fe09f8f2dcf7c373bb873c25cf729d Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 25 Nov 2025 21:51:45 -1000 Subject: [PATCH 3/4] Add ReScript build step before assets:precompile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dummy app uses ReScript components that need to be compiled before webpack can bundle them. This adds `pnpm run build:rescript` to compile HelloWorldReScript.res.js and other ReScript files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/precompile-check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/precompile-check.yml b/.github/workflows/precompile-check.yml index 7501a10b51..d1e1dd1f7d 100644 --- a/.github/workflows/precompile-check.yml +++ b/.github/workflows/precompile-check.yml @@ -128,6 +128,8 @@ jobs: if ! bundle check --path=vendor/bundle; then bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3 fi + - name: Build ReScript files + run: cd react_on_rails/spec/dummy && pnpm run build:rescript - name: Generate file system-based packs run: cd react_on_rails/spec/dummy && RAILS_ENV=production bundle exec rake react_on_rails:generate_packs - name: Run assets:precompile and check output From 7e8a89258526d7a5be8ba885df29eb8cdfe6f5b7 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 25 Nov 2025 22:04:25 -1000 Subject: [PATCH 4/4] Address PR review feedback for precompile check workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes based on CodeRabbit review comments: 1. Capture rake exit code with PIPESTATUS to detect silent failures - Added set -o pipefail to propagate pipe failures - Check PIPESTATUS[0] after tee to catch rake failures 2. Improved duplicate webpack compilation detection - Changed pattern from generic "Compiled" to "Compiled successfully" - Simplified logic to count successful compilations directly - Added line numbers to matching output for debugging 3. Improved webpack error pattern detection - Changed from overly broad "error in" to more specific patterns - Now matches: webpack.*error, failed to compile, compilation failed, ERROR in 4. Better Ruby error detection - Pattern now matches error class format with colon (e.g., "NameError:") - Reduces false positives from log messages 5. Fixed asset pipeline error pattern - Use proper casing for Sprockets::FileNotFound 6. Added sample output for all error patterns - Each error now shows the first few matching lines for easier debugging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/precompile-check.yml | 48 +++++++++++++++++++------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/.github/workflows/precompile-check.yml b/.github/workflows/precompile-check.yml index d1e1dd1f7d..f2f6a3492b 100644 --- a/.github/workflows/precompile-check.yml +++ b/.github/workflows/precompile-check.yml @@ -140,7 +140,10 @@ jobs: echo "==========================================" # Run precompile and capture both stdout and stderr + # Use pipefail to catch rake failures even when piped through tee + set -o pipefail RAILS_ENV=production bin/rake assets:precompile 2>&1 | tee precompile_output.txt + PRECOMPILE_EXIT=${PIPESTATUS[0]} echo "==========================================" echo "Precompile finished. Checking output for known issues..." @@ -149,15 +152,20 @@ jobs: # Check for known failure patterns FAILURES_FOUND=0 + # Check if rake command itself failed + if [ "$PRECOMPILE_EXIT" -ne 0 ]; then + echo "::error::Precompile command failed with exit code $PRECOMPILE_EXIT" + FAILURES_FOUND=1 + fi + # Pattern 1: Duplicate webpack compilation (indicates rake tasks running twice) - WEBPACK_COMPILE_COUNT=$(grep -c "Compiled" precompile_output.txt || true) - if [ "$WEBPACK_COMPILE_COUNT" -gt 1 ]; then - # Check if these are genuinely different compilations or duplicates - # If the same "Compiled" message appears multiple times for the same bundle, it's a problem - DUPLICATE_COMPILES=$(sort precompile_output.txt | uniq -d | grep -c "Compiled" || true) - if [ "$DUPLICATE_COMPILES" -gt 0 ]; then - echo "::error::FAILURE: Detected duplicate webpack compilations. Tasks may be running twice." - echo " Found $DUPLICATE_COMPILES duplicate compilation messages." + # Look for webpack's "Compiled successfully" message which appears once per compilation + if grep -q "Compiled successfully" precompile_output.txt; then + COMPILE_SUCCESS_COUNT=$(grep -c "Compiled successfully" precompile_output.txt || true) + if [ "$COMPILE_SUCCESS_COUNT" -gt 1 ]; then + echo "::error::FAILURE: Detected $COMPILE_SUCCESS_COUNT webpack compilations (expected 1). Tasks may be running twice." + echo " Matching lines:" + grep -n "Compiled successfully" precompile_output.txt | head -5 FAILURES_FOUND=1 fi fi @@ -167,6 +175,8 @@ jobs: GENERATE_PACKS_COUNT=$(grep -c "react_on_rails:generate_packs" precompile_output.txt || true) if [ "$GENERATE_PACKS_COUNT" -gt 1 ]; then echo "::error::FAILURE: react_on_rails:generate_packs task ran $GENERATE_PACKS_COUNT times (should only run once)." + echo " Matching lines:" + grep -n "react_on_rails:generate_packs" precompile_output.txt FAILURES_FOUND=1 fi fi @@ -174,30 +184,40 @@ jobs: # Pattern 3: Module not found errors if grep -Ei "module not found|cannot find module|can't resolve" precompile_output.txt; then echo "::error::FAILURE: Module resolution errors detected in precompile output." + echo " Sample matching lines:" + grep -Ei "module not found|cannot find module|can't resolve" precompile_output.txt | head -3 FAILURES_FOUND=1 fi - # Pattern 4: Webpack build errors - if grep -Ei "error in |failed to compile|compilation failed" precompile_output.txt; then + # Pattern 4: Webpack build errors (use specific webpack error markers) + if grep -Ei "webpack.*error|failed to compile|compilation failed|ERROR in" precompile_output.txt; then echo "::error::FAILURE: Webpack compilation errors detected." + echo " Sample matching lines:" + grep -Ei "webpack.*error|failed to compile|compilation failed|ERROR in" precompile_output.txt | head -3 FAILURES_FOUND=1 fi - # Pattern 5: Ruby/Rails errors during precompile - if grep -Ei "NameError|LoadError|NoMethodError|SyntaxError" precompile_output.txt; then + # Pattern 5: Ruby/Rails errors during precompile (match error class format) + if grep -E "(NameError|LoadError|NoMethodError|SyntaxError):" precompile_output.txt; then echo "::error::FAILURE: Ruby errors detected during precompile." + echo " Sample matching lines:" + grep -E "(NameError|LoadError|NoMethodError|SyntaxError):" precompile_output.txt | head -3 FAILURES_FOUND=1 fi # Pattern 6: Asset pipeline errors - if grep -Ei "sprockets::filenotfound|asset .* was not declared" precompile_output.txt; then + if grep -Ei "Sprockets::FileNotFound|Asset.*was not declared" precompile_output.txt; then echo "::error::FAILURE: Asset pipeline errors detected." + echo " Sample matching lines:" + grep -Ei "Sprockets::FileNotFound|Asset.*was not declared" precompile_output.txt | head -3 FAILURES_FOUND=1 fi # Pattern 7: Memory issues if grep -Ei "javascript heap out of memory|killed|out of memory" precompile_output.txt; then echo "::error::FAILURE: Memory-related errors detected." + echo " Sample matching lines:" + grep -Ei "javascript heap out of memory|killed|out of memory" precompile_output.txt | head -3 FAILURES_FOUND=1 fi @@ -205,6 +225,8 @@ jobs: WARNING_COUNT=$(grep -ci "warning" precompile_output.txt || true) if [ "$WARNING_COUNT" -gt 10 ]; then echo "::warning::High number of warnings detected: $WARNING_COUNT warnings found. Please review." + echo " Sample warnings:" + grep -i "warning" precompile_output.txt | head -5 fi if [ "$FAILURES_FOUND" -eq 1 ]; then