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..45fa1c72c --- /dev/null +++ b/scripts/patchright-single-version.test.ts @@ -0,0 +1,61 @@ +// 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. 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 +// @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 + ); +}); + +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"); +}); 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",