From cc003c7c5afe3c394d32b82e8d7dbd0ae6b035aa Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 19 Jun 2026 17:46:05 -0500 Subject: [PATCH 1/2] Fix host tests silently not running on Node 24 (Percy Chromium hang) Percy bundles its own Chromium and downloads + unzips it on first use via extract-zip, whose extraction hangs indefinitely on Node 24.x. `percy exec` runs the wrapped test command only after its browser starts, so the hang left the host suite never running: no TAP, no testem, no junit/host-N.xml. Each shard's junit upload then warned "No files were found" and the merge job failed at `ls all-host-reports` with nothing to merge. Point Percy at the system Chrome via PERCY_BROWSER_EXECUTABLE (honored by @percy/core, which then skips the download), reusing the path already resolved for PUPPETEER_EXECUTABLE_PATH. Also assert junit/host-N.xml exists after the suite step, so a suite that never runs fails the shard loudly instead of reporting green with zero tests executed. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci-host.yaml | 10 ++++++++++ mise-tasks/lib/env-vars.sh | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/.github/workflows/ci-host.yaml b/.github/workflows/ci-host.yaml index 727995b2ef..0596dd6821 100644 --- a/.github/workflows/ci-host.yaml +++ b/.github/workflows/ci-host.yaml @@ -623,6 +623,16 @@ jobs: DBUS_SYSTEM_BUS_ADDRESS: unix:path=/run/dbus/system_bus_socket working-directory: packages/host + # The shard's pass/fail must reflect the test suite actually running, + # not the exit code of whatever wraps it. testem's xunit reporter + # writes junit/host-N.xml only once the suite runs to completion, so + # a missing or empty report means the suite never ran (e.g. its + # wrapper wedged before launch) — fail loudly here rather than + # report a green shard that executed zero tests. + - name: Assert junit report was produced + if: ${{ !cancelled() }} + run: test -s junit/host-${{ matrix.shardIndex }}.xml + - name: Upload junit report to GitHub Actions Artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ !cancelled() }} diff --git a/mise-tasks/lib/env-vars.sh b/mise-tasks/lib/env-vars.sh index 4c3c111d23..2dcd94be14 100755 --- a/mise-tasks/lib/env-vars.sh +++ b/mise-tasks/lib/env-vars.sh @@ -248,6 +248,17 @@ else export PUPPETEER_EXECUTABLE_PATH="/Applications/Chromium.app/Contents/MacOS/Chromium" fi fi + + # Percy bundles its own Chromium and downloads + unzips it on first use + # (@percy/core install.js) via extract-zip, whose extraction step hangs + # indefinitely on Node 24.x — `percy exec` then never reaches the test + # command it wraps, so the host suite never runs and no junit report is + # written. @percy/core honors PERCY_BROWSER_EXECUTABLE and skips the + # download when it points at an existing binary, so reuse the system + # Chrome already resolved above. + if [ -z "${PERCY_BROWSER_EXECUTABLE:-}" ] && [ -n "${PUPPETEER_EXECUTABLE_PATH:-}" ]; then + export PERCY_BROWSER_EXECUTABLE="$PUPPETEER_EXECUTABLE_PATH" + fi fi # Trust the mkcert root CA in Node clients regardless of env-mode vs From 777b9f65cb801645910ca6b84ceda84b6b989c5a Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 19 Jun 2026 18:06:49 -0500 Subject: [PATCH 2/2] Set PERCY_BROWSER_EXECUTABLE in env mode too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host-test job runs with BOXEL_ENVIRONMENT=ci, so env-vars.sh takes the env-mode branch and skipped the PERCY_BROWSER_EXECUTABLE assignment, which sat in the standard-mode else — Percy kept downloading its own Chromium and the Node 24 extract-zip hang could still produce zero-test shards. Factor the system-Chrome detection into a helper and set PERCY_BROWSER_EXECUTABLE outside both branches, reusing PUPPETEER_EXECUTABLE_PATH when standard mode set it and otherwise detecting the system Chrome directly. The env-mode prerender's own browser selection is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- mise-tasks/lib/env-vars.sh | 59 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/mise-tasks/lib/env-vars.sh b/mise-tasks/lib/env-vars.sh index 2dcd94be14..0e06581e55 100755 --- a/mise-tasks/lib/env-vars.sh +++ b/mise-tasks/lib/env-vars.sh @@ -42,6 +42,21 @@ export PRERENDER_MULTIPLEX="${PRERENDER_MULTIPLEX:-1}" export WORKER_HIGH_PRIORITY_COUNT="${WORKER_HIGH_PRIORITY_COUNT:-0}" export WORKER_ALL_PRIORITY_COUNT="${WORKER_ALL_PRIORITY_COUNT:-1}" +# Echo the first installed system Chrome/Chromium, or nothing. Lets tooling +# reuse an already-present browser instead of downloading its own. Explicit +# checks (not a for-loop) so the macOS path's embedded space doesn't get +# word-split — env-vars.sh runs under whatever shell mise invokes, and POSIX +# sh handles backslash-escapes in for-loop word lists inconsistently. +_boxel_resolve_chrome() { + if [ -x /usr/bin/google-chrome ]; then echo /usr/bin/google-chrome + elif [ -x /usr/bin/google-chrome-stable ]; then echo /usr/bin/google-chrome-stable + elif [ -x /usr/bin/chromium ]; then echo /usr/bin/chromium + elif [ -x /usr/bin/chromium-browser ]; then echo /usr/bin/chromium-browser + elif [ -x "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then echo "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + elif [ -x "/Applications/Chromium.app/Contents/MacOS/Chromium" ]; then echo "/Applications/Chromium.app/Contents/MacOS/Chromium" + fi +} + if [ -n "${BOXEL_ENVIRONMENT:-}" ]; then ENV_SLUG=$(compute_env_slug "$BOXEL_ENVIRONMENT") export ENV_SLUG @@ -230,35 +245,27 @@ else # keep the bundled puppeteer chromium — they'll see the hang stall # longer until vite's optimizer cache warms up. if [ -z "${PUPPETEER_EXECUTABLE_PATH:-}" ]; then - # Explicit checks (not a for-loop) so the macOS path's embedded space - # doesn't get word-split by /bin/sh — env-vars.sh runs under whatever - # shell mise invokes, and POSIX sh handles backslash-escapes in for- - # loop word lists inconsistently across implementations. - if [ -x /usr/bin/google-chrome ]; then - export PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome - elif [ -x /usr/bin/google-chrome-stable ]; then - export PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable - elif [ -x /usr/bin/chromium ]; then - export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium - elif [ -x /usr/bin/chromium-browser ]; then - export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser - elif [ -x "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then - export PUPPETEER_EXECUTABLE_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" - elif [ -x "/Applications/Chromium.app/Contents/MacOS/Chromium" ]; then - export PUPPETEER_EXECUTABLE_PATH="/Applications/Chromium.app/Contents/MacOS/Chromium" - fi + _boxel_chrome="$(_boxel_resolve_chrome)" + [ -n "$_boxel_chrome" ] && export PUPPETEER_EXECUTABLE_PATH="$_boxel_chrome" + unset _boxel_chrome fi +fi - # Percy bundles its own Chromium and downloads + unzips it on first use - # (@percy/core install.js) via extract-zip, whose extraction step hangs - # indefinitely on Node 24.x — `percy exec` then never reaches the test - # command it wraps, so the host suite never runs and no junit report is - # written. @percy/core honors PERCY_BROWSER_EXECUTABLE and skips the - # download when it points at an existing binary, so reuse the system - # Chrome already resolved above. - if [ -z "${PERCY_BROWSER_EXECUTABLE:-}" ] && [ -n "${PUPPETEER_EXECUTABLE_PATH:-}" ]; then - export PERCY_BROWSER_EXECUTABLE="$PUPPETEER_EXECUTABLE_PATH" +# Percy bundles its own Chromium and downloads + unzips it on first use +# (@percy/core install.js) via extract-zip, whose extraction step hangs +# indefinitely on Node 24.x — `percy exec` then never reaches the test +# command it wraps, so the host suite never runs and no junit report is +# written. @percy/core honors PERCY_BROWSER_EXECUTABLE and skips the download +# when it points at an existing binary. This sits outside the env/standard +# branches above because the host-test job runs in env mode (BOXEL_ENVIRONMENT +# set), whose branch resolves no Chrome path. Reuse PUPPETEER_EXECUTABLE_PATH +# when standard mode already set it, otherwise detect the system Chrome. +if [ -z "${PERCY_BROWSER_EXECUTABLE:-}" ]; then + _boxel_percy_chrome="${PUPPETEER_EXECUTABLE_PATH:-$(_boxel_resolve_chrome)}" + if [ -n "$_boxel_percy_chrome" ] && [ -x "$_boxel_percy_chrome" ]; then + export PERCY_BROWSER_EXECUTABLE="$_boxel_percy_chrome" fi + unset _boxel_percy_chrome fi # Trust the mkcert root CA in Node clients regardless of env-mode vs