From 967458fbfd33fed6ae876ac057fb77aeb56de76a Mon Sep 17 00:00:00 2001 From: Tim Nunamaker Date: Thu, 5 Nov 2026 22:05:33 -0600 Subject: [PATCH 1/3] deps: resolve one Patchright version across the workspace The vendored @pdpp/polyfill-connectors tarball pins patchright 1.63.0 exactly, while the root and reference-implementation pin 1.64.1. npm installed both, so connector code (acquireIsolatedBrowser) launched the nested 1.63.0 and needed Chromium revision 1243, while the Dockerfile and `patchright install` fetch the 1.64.1 revision (1248). Add a scoped npm override that points the tarball's patchright at the root version, so one patchright and one patchright-core remain. The tarball is unchanged: the freshness check requires it to match a rebuild of the pinned upstream commit. The lockfile loses the two nested entries and nothing else. scripts/patchright-single-version.test.ts fails on any extra Patchright copy in package-lock.json, or one that differs from the version the image installs browsers for. Assisted-by: AI Signed-off-by: Tim Nunamaker --- package-lock.json | 29 ---------- package.json | 3 ++ .../runtime/browser-executable.ts | 2 +- scripts/patchright-single-version.test.ts | 53 +++++++++++++++++++ vite.config.ts | 1 + 5 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 scripts/patchright-single-version.test.ts diff --git a/package-lock.json b/package-lock.json index e109d1536..423ddca02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3672,35 +3672,6 @@ "resolved": "packages/polyfill-connectors", "link": true }, - "node_modules/@pdpp/polyfill-connectors/node_modules/patchright": { - "version": "1.63.0", - "resolved": "https://registry.npmjs.org/patchright/-/patchright-1.63.0.tgz", - "integrity": "sha512-WVXhmNhAJ5I+CmcHEu/I3KMKhOlQaKS1XJsk+5nRIypKJ3Dq9IaQP29p4vB3I1FWCTarUXVRj02Na30wGcuztQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "patchright-core": "1.63.0" - }, - "bin": { - "patchright": "cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@pdpp/polyfill-connectors/node_modules/patchright-core": { - "version": "1.63.0", - "resolved": "https://registry.npmjs.org/patchright-core/-/patchright-core-1.63.0.tgz", - "integrity": "sha512-nuRJVTMVKoOFzY8vmwFqJZR3F0trUCKiPzDSgCOPq/yHHTefm9czILV23dXJsi5mvS7sCy7U1CqG7edA/21DtQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "patchright-core": "cli.js" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/@pdpp/polyfill-connectors/node_modules/ws": { "version": "8.21.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", diff --git a/package.json b/package.json index 0d4373aaa..8dc95b939 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,9 @@ "@huggingface/transformers": { "sharp": "^0.35.4" }, + "@pdpp/polyfill-connectors": { + "patchright": "$patchright" + }, "qs": "^6.15.2" } } diff --git a/reference-implementation/runtime/browser-executable.ts b/reference-implementation/runtime/browser-executable.ts index 0f6a8974d..bc336e0e4 100644 --- a/reference-implementation/runtime/browser-executable.ts +++ b/reference-implementation/runtime/browser-executable.ts @@ -39,7 +39,7 @@ export const IMAGE_BROWSERS_PATH = "/opt/patchright-browsers"; */ export const BROWSERS_PATH_ENV_KEYS = ["PLAYWRIGHT_BROWSERS_PATH", "PATCHRIGHT_BROWSERS_PATH"] as const; -/** The only variable patchright-core 1.63.0 reads for the browsers directory. */ +/** The only variable patchright-core reads for the browsers directory. */ const PATCHRIGHT_READS_KEY = "PLAYWRIGHT_BROWSERS_PATH"; const LINUX_EXECUTABLE_DIRS: Readonly> = { diff --git a/scripts/patchright-single-version.test.ts b/scripts/patchright-single-version.test.ts new file mode 100644 index 000000000..f1c51ad66 --- /dev/null +++ b/scripts/patchright-single-version.test.ts @@ -0,0 +1,53 @@ +// Copyright The PDP-Connect Contributors +// SPDX-License-Identifier: Apache-2.0 + +// One Patchright for the whole workspace. The Dockerfile installs the browser +// revision of reference-implementation's exact `patchright` pin, and +// runtime/browser-executable.ts checks for that revision. A second copy of +// Patchright in the lockfile (for example one pinned by the vendored +// @pdpp/polyfill-connectors tarball) launches a different Chromium revision +// than the one the image installs. The root `overrides` entry for +// @pdpp/polyfill-connectors keeps the tree on one version. + +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { test } from "vitest"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +function readJson(relativePath: string) { + return JSON.parse(readFileSync(path.join(root, relativePath), "utf8")); +} + +function lockedPatchrightEntries(): Array<{ key: string; version: string }> { + const lock = readJson("package-lock.json"); + return Object.entries<{ version?: string }>(lock.packages) + .filter(([key]) => /(^|\/)node_modules\/patchright(-core)?$/.test(key)) + .map(([key, entry]) => ({ key, version: String(entry.version) })); +} + +test("package-lock.json resolves every patchright and patchright-core copy to the image's pin", () => { + const pin = readJson("reference-implementation/package.json").dependencies.patchright; + assert.match(pin, /^\d+\.\d+\.\d+$/, "reference-implementation must pin patchright exactly"); + + const entries = lockedPatchrightEntries(); + assert.ok( + entries.some((entry) => entry.key === "node_modules/patchright"), + "lockfile has no top-level patchright entry" + ); + assert.ok( + entries.some((entry) => entry.key === "node_modules/patchright-core"), + "lockfile has no top-level patchright-core entry" + ); + const stray = entries.filter((entry) => entry.version !== pin); + assert.deepEqual(stray, [], `lockfile entries not on patchright ${pin}`); +}); + +test("the root patchright dependency matches the reference-implementation pin", () => { + assert.equal( + readJson("package.json").dependencies.patchright, + readJson("reference-implementation/package.json").dependencies.patchright + ); +}); diff --git a/vite.config.ts b/vite.config.ts index 9f0206674..bddc64bf1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -63,6 +63,7 @@ export default defineConfig(({ mode }) => ({ "scripts/mutation-falsification/*.test.ts", "scripts/check-dockerfile-copy-paths.test.ts", "scripts/chrome-distrust-policy.test.ts", + "scripts/patchright-single-version.test.ts", "scripts/check-flyio-deploy-env.test.ts", "scripts/check-railway-deploy-env.test.ts", "scripts/check-railway-ghcr-public.test.ts", From 539bbb66af2c6f295dc337c381e9f3d9112a0482 Mon Sep 17 00:00:00 2001 From: Tim Nunamaker Date: Thu, 5 Nov 2026 22:11:52 -0600 Subject: [PATCH 2/3] test: pin the Patchright override and the Dockerfile install path The single-version guard checked the lockfile outcome only. Deleting the root overrides entry would pass until the next lock regeneration nests the tarball pin again, so assert the entry itself. Also check that the Dockerfile installs browsers for reference-implementation's exact patchright pin, which the guard's header comment relies on. Assisted-by: AI Signed-off-by: Tim Nunamaker --- scripts/patchright-single-version.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/patchright-single-version.test.ts b/scripts/patchright-single-version.test.ts index f1c51ad66..3edc6dc63 100644 --- a/scripts/patchright-single-version.test.ts +++ b/scripts/patchright-single-version.test.ts @@ -51,3 +51,17 @@ test("the root patchright dependency matches the reference-implementation pin", readJson("reference-implementation/package.json").dependencies.patchright ); }); + +test("the root overrides keep the vendored connectors package on the root patchright", () => { + // Without this entry the lockfile test above still passes until the next + // regeneration, when npm would nest the tarball's own pin again. + assert.equal(readJson("package.json").overrides?.["@pdpp/polyfill-connectors"]?.patchright, "$patchright"); +}); + +test("the Dockerfile installs browsers for reference-implementation's patchright pin", () => { + const dockerfile = readFileSync(path.join(root, "Dockerfile"), "utf8"); + assert.match(dockerfile, /COPY reference-implementation\/package\.json \/tmp\/reference-implementation-package\.json/); + assert.match(dockerfile, /reference-implementation-package\.json'.*packageJson\.dependencies\.patchright/); + assert.match(dockerfile, /npm install [^\n]*"patchright@\$\{PATCHRIGHT_VERSION\}"/); + assert.match(dockerfile, /npx patchright install [^\n]*chromium/); +}); From 3aaef24081dd5fcc23a7feb3a6d7ac4d0cf31753 Mon Sep 17 00:00:00 2001 From: Tim Nunamaker Date: Thu, 5 Nov 2026 23:01:45 -0600 Subject: [PATCH 3/3] test: drop Dockerfile substring check from patchright version test The test matched four source substrings in the Dockerfile, so commented lines passed it and equivalent rewrites failed it. It said nothing about the built image. The docker-images workflow already builds the core image and launches Patchright in it (core-headed-patchright-runtime-oracle), so the substring check is removed rather than replaced. Assisted-by: AI Signed-off-by: Tim Nunamaker --- scripts/patchright-single-version.test.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/patchright-single-version.test.ts b/scripts/patchright-single-version.test.ts index 3edc6dc63..45fa1c72c 100644 --- a/scripts/patchright-single-version.test.ts +++ b/scripts/patchright-single-version.test.ts @@ -3,7 +3,9 @@ // One Patchright for the whole workspace. The Dockerfile installs the browser // revision of reference-implementation's exact `patchright` pin, and -// runtime/browser-executable.ts checks for that revision. A second copy of +// runtime/browser-executable.ts checks for that revision. Whether the built +// image really launches that browser is covered by the docker-images workflow, +// not here. A second copy of // Patchright in the lockfile (for example one pinned by the vendored // @pdpp/polyfill-connectors tarball) launches a different Chromium revision // than the one the image installs. The root `overrides` entry for @@ -57,11 +59,3 @@ test("the root overrides keep the vendored connectors package on the root patchr // regeneration, when npm would nest the tarball's own pin again. assert.equal(readJson("package.json").overrides?.["@pdpp/polyfill-connectors"]?.patchright, "$patchright"); }); - -test("the Dockerfile installs browsers for reference-implementation's patchright pin", () => { - const dockerfile = readFileSync(path.join(root, "Dockerfile"), "utf8"); - assert.match(dockerfile, /COPY reference-implementation\/package\.json \/tmp\/reference-implementation-package\.json/); - assert.match(dockerfile, /reference-implementation-package\.json'.*packageJson\.dependencies\.patchright/); - assert.match(dockerfile, /npm install [^\n]*"patchright@\$\{PATCHRIGHT_VERSION\}"/); - assert.match(dockerfile, /npx patchright install [^\n]*chromium/); -});