From d82bda3867fc9bbccb442ef4d544d7becccab4d4 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 20 May 2026 09:06:20 +0100 Subject: [PATCH 1/2] [ci] Cache Chrome binary across CI runs for Browser Run tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Browser Run tests in `packages/miniflare/test/plugins/browser/index.spec.ts` and the `fixtures/browser-run` fixture call into `@puppeteer/browsers` to ensure Chrome is downloaded into the global Wrangler cache. Every CI run currently re-downloads ~150 MB of Chrome from scratch because the cache directory is per-runner-instance and not shared between runs. Add an `actions/cache@v4` step keyed on the OS + the Chrome version hardcoded in `packages/miniflare/src/index.ts` (`126.0.6478.182")`, restoring/saving `~/.cache/.wrangler/chrome` (Linux), `~/Library/Caches/.wrangler/chrome` (macOS), and `~/AppData/Local/xdg.cache/.wrangler/chrome` (Windows). Benefits: - Cuts ~150 MB and the associated download time off cold CI runs. - Reduces the surface area for the intermittent partial-extraction race that surfaces as `The browser folder (...) exists but the executable (...) is missing` (see #13971 for the diagnostic that exposed this, #13980 for the in-process recovery layer). When the cache is warm and the binary is already extracted, this race can't fire at all because `install()` short-circuits. The cache step runs for the `packages-and-tools` suite on all three OSes and for the `fixtures` suite on macOS + Windows (the Browser Run fixture is excluded on Ubuntu because of AppArmor). When the Chrome version in `packages/miniflare/src/index.ts` changes, the cache key here needs to be bumped manually. A miss only triggers a fresh download — no functional impact. --- .github/workflows/test-and-check.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/test-and-check.yml b/.github/workflows/test-and-check.yml index b4b47b7e24..dd426c06b1 100644 --- a/.github/workflows/test-and-check.yml +++ b/.github/workflows/test-and-check.yml @@ -136,6 +136,32 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} + # Browser Run tests in `packages/miniflare/test/plugins/browser/index.spec.ts` + # and the `fixtures/browser-run` fixture use `@puppeteer/browsers` to + # download Chrome into the global Wrangler cache. Caching that binary + # across CI runs avoids ~150 MB of repeat downloads per run and reduces + # the surface area for the intermittent partial-extraction race that + # surfaces as `The browser folder (...) exists but the executable (...) + # is missing` (see #13971, #13980). + # + # The Browser Run fixture is skipped on Ubuntu (AppArmor), but the + # miniflare browser spec runs everywhere, so the cache is needed on all + # three OSes for `packages-and-tools` and on Windows + macOS for + # `fixtures`. + - name: Restore Chrome browser cache (Browser Run) + if: steps.changes.outputs.everything_but_markdown == 'true' && (matrix.suite == 'packages-and-tools' || (matrix.suite == 'fixtures' && matrix.os != 'ubuntu-latest')) + uses: actions/cache@v4 + with: + # The Chrome version is hardcoded in `packages/miniflare/src/index.ts` + # (search for `browserVersion:`). Bump this key when that version + # changes — a cache miss only triggers a fresh download, no + # functional impact. + key: chrome-${{ runner.os }}-126.0.6478.182 + path: | + ~/.cache/.wrangler/chrome + ~/Library/Caches/.wrangler/chrome + ~/AppData/Local/xdg.cache/.wrangler/chrome + - name: Run tests (tools only) # tools _only_ needs to be tested on Linux because they're only intended to run in CI if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.suite == 'packages-and-tools' && matrix.os == 'ubuntu-latest' From 4f6f5ce5bdc5000b553c04aa0810fd518e1abe63 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 20 May 2026 11:22:53 +0100 Subject: [PATCH 2/2] [ci] De-duplicate Chrome version used by miniflare and cache key Move the `126.0.6478.182` Chrome version literal out of `packages/miniflare/src/index.ts` into a dedicated `packages/miniflare/src/plugins/browser-rendering/browser-version.ts` module, and switch the `actions/cache` key to `hashFiles()` over that file. Bumping the constant in one place now automatically invalidates the CI cache without a separate workflow edit. --- .github/workflows/test-and-check.yml | 9 +++++---- packages/miniflare/src/index.ts | 9 ++------- .../plugins/browser-rendering/browser-version.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 packages/miniflare/src/plugins/browser-rendering/browser-version.ts diff --git a/.github/workflows/test-and-check.yml b/.github/workflows/test-and-check.yml index dd426c06b1..2bb4670273 100644 --- a/.github/workflows/test-and-check.yml +++ b/.github/workflows/test-and-check.yml @@ -152,11 +152,12 @@ jobs: if: steps.changes.outputs.everything_but_markdown == 'true' && (matrix.suite == 'packages-and-tools' || (matrix.suite == 'fixtures' && matrix.os != 'ubuntu-latest')) uses: actions/cache@v4 with: - # The Chrome version is hardcoded in `packages/miniflare/src/index.ts` - # (search for `browserVersion:`). Bump this key when that version - # changes — a cache miss only triggers a fresh download, no + # The Chrome version lives in + # `packages/miniflare/src/plugins/browser-rendering/browser-version.ts`. + # Bumping that constant invalidates this cache automatically via + # `hashFiles()` — a cache miss only triggers a fresh download, no # functional impact. - key: chrome-${{ runner.os }}-126.0.6478.182 + key: chrome-${{ runner.os }}-${{ hashFiles('packages/miniflare/src/plugins/browser-rendering/browser-version.ts') }} path: | ~/.cache/.wrangler/chrome ~/Library/Caches/.wrangler/chrome diff --git a/packages/miniflare/src/index.ts b/packages/miniflare/src/index.ts index ccdc8bdaf4..9df90d06e4 100644 --- a/packages/miniflare/src/index.ts +++ b/packages/miniflare/src/index.ts @@ -63,6 +63,7 @@ import { WORKFLOWS_PLUGIN_NAME, } from "./plugins"; import { RPC_PROXY_SERVICE_NAME } from "./plugins/assets/constants"; +import { BROWSER_VERSION } from "./plugins/browser-rendering/browser-version"; import { CUSTOM_SERVICE_KNOWN_OUTBOUND, CustomServiceKind, @@ -1550,13 +1551,7 @@ export class Miniflare { ); const { sessionId, browserProcess, startTime, wsEndpoint } = await launchBrowser({ - // Puppeteer v22.13.1 supported chrome version: - // https://pptr.dev/supported-browsers#supported-browser-version-list - // - // It should match the supported chrome version for the upstream puppeteer - // version from which @cloudflare/puppeteer branched off, which is specified in: - // https://github.com/cloudflare/puppeteer/?tab=readme-ov-file#workers-version-of-puppeteer-core - browserVersion: "126.0.6478.182", + browserVersion: BROWSER_VERSION, log: this.#log, tmpPath: this.#tmpPath, headful, diff --git a/packages/miniflare/src/plugins/browser-rendering/browser-version.ts b/packages/miniflare/src/plugins/browser-rendering/browser-version.ts new file mode 100644 index 0000000000..ea8e14f309 --- /dev/null +++ b/packages/miniflare/src/plugins/browser-rendering/browser-version.ts @@ -0,0 +1,14 @@ +/** + * The Chrome browser version downloaded by Miniflare's Browser Run binding. + * + * Puppeteer v22.13.1 supported chrome version: + * https://pptr.dev/supported-browsers#supported-browser-version-list + * + * It should match the supported chrome version for the upstream puppeteer + * version from which @cloudflare/puppeteer branched off, which is specified in: + * https://github.com/cloudflare/puppeteer/?tab=readme-ov-file#workers-version-of-puppeteer-core + * + * Bumping this value also invalidates the Chrome binary cache in + * `.github/workflows/test-and-check.yml` (which uses `hashFiles()` on this file). + */ +export const BROWSER_VERSION = "126.0.6478.182";