From ee7dc7879cf12f33dd8101958e10888e58183368 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 6 Aug 2026 19:38:44 -0400 Subject: [PATCH] Gate the Chromium WPT parity legs in CI on Playwright's pinned build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt the webcrypto sibling's pattern: the parity browser legs always launch Playwright's own Chromium build, pinned by playwright-core's version in the parity lockfile, so losses-chromium.js measures one engine everywhere — local runs and CI alike — and a Chromium behavior shift arrives only with a deliberate playwright bump, never with a runner-image update. run-browser.mjs drops the system-Chrome discovery (findChrome stays with the conformance jco-browser leg, which prefers the runner's Chrome on purpose). The conformance job installs the pinned browser with a cache keyed on the parity lockfile (restore-keys let a bump download only what changed; --with-deps only on a cache miss), and conformance-checks gains the wpt::parity-chromium step. Verified: just wpt::parity-chromium green on the pinned build (Chrome for Testing 151 / chromium-1234) with the same empty loss set; just ci green end to end. Closes #35 --- .github/justfile | 1 + .github/workflows/ci.yml | 23 ++++++++++++++++++++++ js/componentize/wpt/README.md | 9 ++++++--- js/componentize/wpt/justfile | 6 ++++-- js/componentize/wpt/parity/run-browser.mjs | 13 +++++------- scripts/chrome.mjs | 7 ++++--- 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/.github/justfile b/.github/justfile index 092b842..023a96b 100644 --- a/.github/justfile +++ b/.github/justfile @@ -34,4 +34,5 @@ rust-checks: conformance-checks: @just gha::_step conformance-ct::all @just gha::_step wpt::parity + @just gha::_step wpt::parity-chromium @just gha::_step demo::test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55e0e3d..5f33f40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,29 @@ jobs: with: path: ${{ steps.pnpm-store.outputs.path }} key: pnpm-store-v2-${{ runner.os }}-${{ hashFiles('conformance/driver-ct/jco/pnpm-lock.yaml', 'examples/jco-demo/pnpm-lock.yaml', 'js/componentize/wpt/parity/pnpm-lock.yaml') }} + # The WPT parity Chromium leg always runs Playwright's own build, + # pinned by playwright-core's version in the parity lockfile, so + # losses-chromium.js measures one engine everywhere. The cache key + # follows that lockfile; restore-keys lets a playwright bump start + # from the previous browsers and download only what changed. + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('js/componentize/wpt/parity/pnpm-lock.yaml') }} + restore-keys: playwright-${{ runner.os }}- + - name: Install Playwright Chromium + # On a cache hit the install only verifies the browser is present; + # --with-deps (an apt run) is needed only when the browser was + # actually (re)downloaded. + run: | + cd js/componentize/wpt/parity + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then + npx playwright-core install chromium + else + npx playwright-core install --with-deps chromium + fi - run: just gha::conformance-checks # The jco-browser adapter finds the runner's preinstalled Chrome via # its candidate paths; CHROME_PATH overrides if ever needed. diff --git a/js/componentize/wpt/README.md b/js/componentize/wpt/README.md index 89aecf4..03bc8e8 100644 --- a/js/componentize/wpt/README.md +++ b/js/componentize/wpt/README.md @@ -31,9 +31,12 @@ unbundled. Each engine ratchets separately (`parity/losses-chromium.js`, re-recorded with `just wpt::update-losses-chromium`): a loss set is a fact about one engine's baseline. -The Chromium gate is not yet in CI: it needs a Chrome 137+ binary on the -runner. Test names are engine-independent by construction — `wpt-env.js` -shadows `location` with a fixed stub in every leg, so names never embed a +The Chromium legs always run Playwright's own build, pinned by +playwright-core's version in the parity lockfile, so the loss set +measures one engine everywhere — local runs and CI alike; a Chromium +behavior shift arrives only with a deliberate playwright bump. Test +names are engine-independent by construction — `wpt-env.js` shadows +`location` with a fixed stub in every leg, so names never embed a per-run origin. ## Vendoring policy diff --git a/js/componentize/wpt/justfile b/js/componentize/wpt/justfile index ffd0a9b..0bb1c43 100644 --- a/js/componentize/wpt/justfile +++ b/js/componentize/wpt/justfile @@ -30,8 +30,10 @@ update-losses: _artifacts # baseline measures Chromium's own WebSocket, the round trip runs the # browser-profile transpile against websocket-jco in the environment it # targets. Ratchets separately in parity/losses-chromium.js — a loss set -# is a fact about one engine's baseline. Not yet in CI: needs a Chrome -# 137+ binary on the runner. +# is a fact about one engine's baseline. The browser is always +# Playwright's pinned Chromium build (install once with +# `npx playwright-core install --with-deps chromium` from parity/), so +# the loss set measures one engine everywhere. parity-chromium: _artifacts-web {{timeout_cmd}} node {{root}}/js/componentize/wpt/parity/run-browser.mjs diff --git a/js/componentize/wpt/parity/run-browser.mjs b/js/componentize/wpt/parity/run-browser.mjs index a1e4241..cef6623 100644 --- a/js/componentize/wpt/parity/run-browser.mjs +++ b/js/componentize/wpt/parity/run-browser.mjs @@ -14,7 +14,11 @@ // echo server directly: WebSocket is not subject to CORS, and a localhost // `http:` page may open `ws:` connections. // -// jco's async ABI needs JSPI; Chrome ships it enabled from 137 onward. +// The browser is always Playwright's own Chromium build, pinned by +// playwright-core's version (the parity lockfile), so losses-chromium.js +// measures one engine everywhere — local runs and CI alike. Install it +// once with `npx playwright-core install --with-deps chromium` (from this +// directory). jco's async ABI needs JSPI; Chromium ships it. // // Usage: node run-browser.mjs [--update] @@ -26,7 +30,6 @@ import { fileURLToPath } from "node:url"; import { chromium } from "playwright-core"; -import { findChrome } from "../../../../scripts/chrome.mjs"; import { spawnEchod } from "../../../../conformance/server/echod.mjs"; const HERE = dirname(fileURLToPath(import.meta.url)); @@ -99,18 +102,12 @@ async function main() { throw new Error(`missing browser-profile transpile in ${join(HERE, "generated-web")}; run "npm run transpile:web" first`); } - const executablePath = await findChrome(); - if (!executablePath) { - throw new Error("no Chrome/Chromium binary found; set CHROME_PATH to a Chrome 137+ executable"); - } - const echod = await spawnEchod(join(REPO_ROOT, "target", "debug", "conformance-echod")); const server = await startServer(); const base = `http://127.0.0.1:${server.address().port}`; process.stderr.write(`echo server at ${echod.base}; page served from ${base}\n`); const browser = await chromium.launch({ - executablePath, headless: true, args: ["--no-sandbox", "--disable-dev-shm-usage"], }); diff --git a/scripts/chrome.mjs b/scripts/chrome.mjs index 8800026..23cad2c 100644 --- a/scripts/chrome.mjs +++ b/scripts/chrome.mjs @@ -1,6 +1,7 @@ -// Chrome/Chromium discovery shared by every headless-browser runner (the -// conformance jco-browser leg and the WPT parity browser legs). -// Node-only. +// Chrome/Chromium discovery for the conformance jco-browser leg, which +// prefers a system Chrome. (The WPT parity browser legs do not use this: +// they always launch Playwright's own pinned build, so the recorded loss +// set measures one engine everywhere.) Node-only. import { access, readdir } from "node:fs/promises"; import { join } from "node:path";