Skip to content

ci: fix visual regression tests — provision a pinned Chrome for Puppeteer + Percy#3255

Merged
misama-ct merged 10 commits into
mainfrom
ci/cache-puppeteer-browser
Jun 11, 2026
Merged

ci: fix visual regression tests — provision a pinned Chrome for Puppeteer + Percy#3255
misama-ct merged 10 commits into
mainfrom
ci/cache-puppeteer-browser

Conversation

@misama-ct

@misama-ct misama-ct commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Problem

The build_lint_and_test job's visual-regression step started capturing 0 snapshots, so Percy builds came back "manually failed" / "no snapshots uploaded." The last healthy run was build 13795 (May 27, 81 snapshots); every run after that captured nothing — with no relevant change in this repo.

Causes

Three independent things broke, all external to our source (the first two surfaced first; the third only became visible once the browsers were available again):

  1. Puppeteer's Chrome no longer installed. puppeteer browsers install chrome (Chrome-for-Testing 127.0.6533.88, via @puppeteer/browsers@2.3.0) silently stopped installing, so jest-puppeteer's globalSetup failed with Could not find Chrome (ver. 127.0.6533.88).
  2. Percy's discovery Chromium download hung. @percy/core@1.31.13 pins Chromium snapshot revision 1300309 and auto-downloads it; that revision was pruned from the public Chromium snapshots bucket, so the download went from ~4s (May 27) to a permanent hang (this.executable = executable || (await install.chromium())).
  3. Two visual specs crash Chrome's sandbox on ubuntu-24.04. rich-text-input and localized-rich-text-input launch their own Puppeteer browser (not the jest-puppeteer global one) without --no-sandbox. On ubuntu-24.04, AppArmor restricts unprivileged user namespaces, so those launches die with "No usable sandbox". They had only ever passed because of a now-removed aa-exec --profile=chrome wrapper.

Solution

Provision one explicit, pinned Chrome and use it for everything; stop relying on the tools' own (now-broken) downloads.

  • browser-actions/setup-chrome (SHA-pinned) installs 127.0.6533.88 — the build puppeteer@22.15.0 targets and the one the Percy baseline was captured with. chrome-version: stable was rejected: it pulled Chrome 149 (~22 majors newer).
  • Point both tools at it: PUPPETEER_EXECUTABLE_PATH (read by jest-puppeteer.config.js) and PERCY_BROWSER_EXECUTABLE. This removes both failing downloads (Setup CI #1, Setup release script #2).
  • Add --no-sandbox/--disable-setuid-sandbox to the two self-launching rich-text specs, matching jest-puppeteer.config.js used by the other 68 specs. Fixes chore: upgrade storybook to alpha.18 #3 without a privileged runner sysctl, on any OS.
  • Drop the now-redundant Install Puppeteer's Chrome step and the aa-exec wrapper. Stay on ubuntu-24.04 (no longer needs either, and avoids the ubuntu-22.04 deprecation track).

Verification

Green run on this branch → Percy build 13802:

  • Test Suites: 71 passed, 71 total (the two rich-text suites pass again)
  • Provisioned Chrome 127.0.6533.88, 81 snapshots (matches the May-27 baseline count)
  • Finalized build #13802exited with status: 0
  • percy/ui-kit: pass"automatically approved, no visual changes found" (Chrome 127 renders identically to the baseline — no baseline shift)

Notes / follow-ups

  • The commit history records the full investigation (a since-abandoned cache/skip-download approach, the ~/.cache/puppeteer husk, a ubuntu-22.04 pin, a brief unpinned-Chrome attempt). The net change is only the items under Solution.
  • Reproducibility now hinges on the pinned chrome-versionbump it in lockstep with puppeteer.

The build_lint_and_test job cached only the pnpm store. Puppeteer downloads
Chrome to ~/.cache/puppeteer (outside node_modules / the store), and pnpm's
side-effects cache records the postinstall as already-run on a store cache hit,
so the browser binary was never preserved — the explicit `puppeteer browsers
install chrome` step re-fetched it every run. When the runner image shipped a
stale/partial ~/.cache/puppeteer for the pinned Chrome build, @puppeteer/browsers
refused to overwrite it and failed with "the browser folder exists but the
executable is missing", blocking the job.

Relocate the browser to a workspace-local PUPPETEER_CACHE_DIR and cache that
directory (keyed on the lockfile). The install step is now a no-op on a cache
hit and a clean download into an empty dir on a miss, so it can't collide with a
partially-populated system cache. No `rm -rf`, and the binary is actually
preserved across runs.
@misama-ct misama-ct requested a review from a team as a code owner June 11, 2026 05:50
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ea2055c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ui-kit Ready Ready Preview, Comment Jun 11, 2026 10:35am

Request Review

Resolve the installed puppeteer version and use it as the cache key, instead of
the lockfile hash + restore-keys. The Chrome build is pinned transitively by the
puppeteer dependency, so a Puppeteer bump now changes the key and forces a clean
re-download, while same-version runs reuse the cached binary. Dropping
restore-keys avoids ever falling back to a stale-version browser, and avoids the
cache churning on unrelated lockfile changes.
Root cause of the install failure: with PUPPETEER_CACHE_DIR set, Puppeteer's
postinstall (run during `pnpm install` under the restored pnpm store) leaves a
partial Chrome — the version folder without the executable. @puppeteer/browsers
2.3.0 then refuses to repair it and fails the explicit install with "the browser
folder exists but the executable is missing".

Set PUPPETEER_SKIP_DOWNLOAD=true for the install step so downloadBrowser() returns
before touching the cache dir (verified against puppeteer 22.15.0's
node/install.js). The explicit `puppeteer browsers install chrome` step — which
does not consult that flag — remains the sole, authoritative installer into the
version-keyed cache dir.
Reverts the cache/relocation/skip-download experiments back to main's working
flow. Adds a single step to remove a partial ~/.cache/puppeteer (browser folder
without the executable) that the runner image can ship — @puppeteer/browsers
refuses to repair an incomplete install (same check in 2.3.0 through 2.13.2), so
a version bump can't fix it. Clearing it lets the postinstall download a clean
Chrome.
The ubuntu-24.04 runner image (~20260525) tightened AppArmor unprivileged-userns
restrictions, preventing Percy/Chromium from launching — VRT ran green but
captured 0 snapshots (Percy builds 'manually failed'). Last green VRT (81
snapshots, May 27) was on image 20260518.

Pin the job to ubuntu-22.04, which has no such restriction, and drop the now-
obsolete 24.04-specific workarounds: the aa-exec --profile=chrome wrapper (added
for 24.04 AppArmor) and the ~/.cache/puppeteer clear (the husk was a 24.04 image
artifact). This restores the pre-24.04 VRT configuration on a compatible OS.
Root cause of the 0-snapshot VRT runs: @percy/core 1.31.13 pins Chromium
snapshot revision 1300309 and auto-downloads it for asset discovery. That
revision has been pruned from the public Chromium snapshots bucket, so the
download (which took 4s on May 27) now hangs indefinitely and Percy captures no
snapshots — independent of the runner OS. Set PERCY_BROWSER_EXECUTABLE to the
runner's pre-installed Chrome so @percy/core uses it and skips the download
(verified in @percy/core dist/browser.js: executable || install.chromium()).
Both pinned browser downloads now fail in CI, yielding 0 snapshots:
- puppeteer's Chrome-for-Testing 127.0.6533.88 no longer installs (jest-puppeteer
  then can't find Chrome -> globalSetup error)
- @percy/core's Chromium snapshot 1300309 was pruned from the public bucket, so
  its auto-download hangs

Point both at the runner's pre-installed Chrome via PUPPETEER_EXECUTABLE_PATH and
PERCY_BROWSER_EXECUTABLE, and drop the now-redundant 'Install Puppeteer's Chrome'
step. jest-puppeteer.config.js already reads PUPPETEER_EXECUTABLE_PATH and runs
with --no-sandbox. Fails fast if no Chrome is found rather than silently falling
back to a broken download.
@misama-ct misama-ct changed the title ci: cache Puppeteer's Chrome instead of re-installing it every run ci: fix visual regression tests by using the runner's pre-installed Chrome Jun 11, 2026
Revert the ubuntu-22.04 pin (unnecessary: deleting the puppeteer install step
removes the ~/.cache/puppeteer husk issue, and --no-sandbox in both launchers
makes the 24.04 AppArmor workaround moot — so 24.04 works and avoids the 22.04
deprecation track).

Provision Chrome explicitly with browser-actions/setup-chrome (SHA-pinned) and
point PUPPETEER_EXECUTABLE_PATH + PERCY_BROWSER_EXECUTABLE at it, instead of
relying on the runner's rolling-latest Chrome. Using chrome-version: stable for
now; will pin a specific build for reproducible Percy snapshots once green.
…ome 127

The rich-text-input and localized-rich-text-input visualspecs launch their OWN
puppeteer browser (not the jest-puppeteer global one) and did not pass
--no-sandbox. On ubuntu-24.04 (AppArmor restricts unprivileged user namespaces)
that crashes with 'No usable sandbox', failing those 2 suites and dropping a
Percy snapshot. They previously only passed because the removed aa-exec wrapper
covered them. Add --no-sandbox/--disable-setuid-sandbox to both launches, matching
jest-puppeteer.config.js used by the other 68 specs.

Also pin setup-chrome to 127.0.6533.88 (puppeteer 22.15.0's target build and the
Percy baseline's browser) instead of 'stable' — 'stable' pulled Chrome 149.
@misama-ct misama-ct changed the title ci: fix visual regression tests by using the runner's pre-installed Chrome ci: fix visual regression tests — provision a pinned Chrome for Puppeteer + Percy Jun 11, 2026
@misama-ct misama-ct force-pushed the ci/cache-puppeteer-browser branch from 6c99829 to ea2055c Compare June 11, 2026 10:45
@misama-ct misama-ct merged commit 5d5e5d9 into main Jun 11, 2026
11 checks passed
@misama-ct misama-ct deleted the ci/cache-puppeteer-browser branch June 11, 2026 13:22
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.

3 participants