From 993c6935050074470d60a488a1af8a5793e99e59 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 16 Dec 2025 09:42:48 +0100 Subject: [PATCH 1/6] chore: fallback to Google mirror --- .../src/server/registry/index.ts | 110 +++++++++++------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 1c4777a19be71..d9999f86df67f 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -47,6 +47,11 @@ const PLAYWRIGHT_CDN_MIRRORS = [ 'https://cdn.playwright.dev', // Hit the Storage Bucket directly ]; +const CHROME_FOR_TESTING_CDN_MIRRORS = [ + 'https://cdn.playwright.dev/chrome-for-testing-public', + 'https://storage.googleapis.com/chrome-for-testing-public', +]; + if (process.env.PW_TEST_CDN_THAT_SHOULD_WORK) { for (let i = 0; i < PLAYWRIGHT_CDN_MIRRORS.length; i++) { const cdn = PLAYWRIGHT_CDN_MIRRORS[i]; @@ -125,69 +130,80 @@ const EXECUTABLE_PATHS = { }, }; -type DownloadPaths = Record; +type DownloadPathFunction = (params: BrowsersJSONDescriptor) => { path: string, mirrors: string[] }; + +function cftUrl(suffix: string): DownloadPathFunction { + return ({ browserVersion }) => { + return { + path: `${browserVersion}/${suffix}`, + mirrors: CHROME_FOR_TESTING_CDN_MIRRORS, + }; + }; +} + +type DownloadPaths = Record; const DOWNLOAD_PATHS: Record = { 'chromium': { '': undefined, 'ubuntu18.04-x64': undefined, - 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-linux.zip', - 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-linux.zip', - 'ubuntu24.04-x64': 'builds/chromium/%s/chromium-linux.zip', + 'ubuntu20.04-x64': cftUrl('linux64/chrome-linux64.zip'), + 'ubuntu22.04-x64': cftUrl('linux64/chrome-linux64.zip'), + 'ubuntu24.04-x64': cftUrl('linux64/chrome-linux64.zip'), 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu24.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', - 'debian11-x64': 'builds/chromium/%s/chromium-linux.zip', + 'debian11-x64': cftUrl('linux64/chrome-linux64.zip'), 'debian11-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', - 'debian12-x64': 'builds/chromium/%s/chromium-linux.zip', + 'debian12-x64': cftUrl('linux64/chrome-linux64.zip'), 'debian12-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', - 'debian13-x64': 'builds/chromium/%s/chromium-linux.zip', + 'debian13-x64': cftUrl('linux64/chrome-linux64.zip'), 'debian13-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', - 'mac10.13': 'builds/chromium/%s/chromium-mac.zip', - 'mac10.14': 'builds/chromium/%s/chromium-mac.zip', - 'mac10.15': 'builds/chromium/%s/chromium-mac.zip', - 'mac11': 'builds/chromium/%s/chromium-mac.zip', - 'mac11-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac12': 'builds/chromium/%s/chromium-mac.zip', - 'mac12-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac13': 'builds/chromium/%s/chromium-mac.zip', - 'mac13-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac14': 'builds/chromium/%s/chromium-mac.zip', - 'mac14-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac15': 'builds/chromium/%s/chromium-mac.zip', - 'mac15-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'win64': 'builds/chromium/%s/chromium-win64.zip', + 'mac10.13': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac10.14': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac10.15': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac11': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac11-arm64': cftUrl('mac-arm64/chrome-mac-arm64.zip'), + 'mac12': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac12-arm64': cftUrl('mac-arm64/chrome-mac-arm64.zip'), + 'mac13': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac13-arm64': cftUrl('mac-arm64/chrome-mac-arm64.zip'), + 'mac14': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac14-arm64': cftUrl('mac-arm64/chrome-mac-arm64.zip'), + 'mac15': cftUrl('mac-x64/chrome-mac-x64.zip'), + 'mac15-arm64': cftUrl('mac-arm64/chrome-mac-arm64.zip'), + 'win64': cftUrl('win64/chrome-win64.zip'), }, 'chromium-headless-shell': { '': undefined, 'ubuntu18.04-x64': undefined, - 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', - 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', - 'ubuntu24.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'ubuntu20.04-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), + 'ubuntu22.04-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), + 'ubuntu24.04-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'ubuntu24.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', - 'debian11-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'debian11-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), 'debian11-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', - 'debian12-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'debian12-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), 'debian12-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', - 'debian13-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'debian13-x64': cftUrl('linux64/chrome-headless-shell-linux64.zip'), 'debian13-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'mac10.13': undefined, 'mac10.14': undefined, 'mac10.15': undefined, - 'mac11': 'builds/chromium/%s/chromium-headless-shell-mac.zip', - 'mac11-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac12': 'builds/chromium/%s/chromium-headless-shell-mac.zip', - 'mac12-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac13': 'builds/chromium/%s/chromium-headless-shell-mac.zip', - 'mac13-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac14': 'builds/chromium/%s/chromium-headless-shell-mac.zip', - 'mac14-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac15': 'builds/chromium/%s/chromium-headless-shell-mac.zip', - 'mac15-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'win64': 'builds/chromium/%s/chromium-headless-shell-win64.zip', + 'mac11': cftUrl('mac-x64/chrome-headless-shell-mac-x64.zip'), + 'mac11-arm64': cftUrl('mac-arm64/chrome-headless-shell-mac-arm64.zip'), + 'mac12': cftUrl('mac-x64/chrome-headless-shell-mac-x64.zip'), + 'mac12-arm64': cftUrl('mac-arm64/chrome-headless-shell-mac-arm64.zip'), + 'mac13': cftUrl('mac-x64/chrome-headless-shell-mac-x64.zip'), + 'mac13-arm64': cftUrl('mac-arm64/chrome-headless-shell-mac-arm64.zip'), + 'mac14': cftUrl('mac-x64/chrome-headless-shell-mac-x64.zip'), + 'mac14-arm64': cftUrl('mac-arm64/chrome-headless-shell-mac-arm64.zip'), + 'mac15': cftUrl('mac-x64/chrome-headless-shell-mac-x64.zip'), + 'mac15-arm64': cftUrl('mac-arm64/chrome-headless-shell-mac-arm64.zip'), + 'win64': cftUrl('win64/chrome-headless-shell-win64.zip'), }, 'chromium-tip-of-tree': { '': undefined, @@ -1216,12 +1232,19 @@ export class Registry { private _downloadURLs(descriptor: BrowsersJSONDescriptor): string[] { const paths = (DOWNLOAD_PATHS as any)[descriptor.name]; - const downloadPathTemplate: string|undefined = paths[hostPlatform] || paths['']; + const downloadPathTemplate: string|DownloadPathFunction|undefined = paths[hostPlatform] || paths['']; if (!downloadPathTemplate) return []; - const downloadPath = util.format(downloadPathTemplate, descriptor.revision); + let downloadPath: string; + let mirrors = PLAYWRIGHT_CDN_MIRRORS; + if (typeof downloadPathTemplate === 'function') { + const result = downloadPathTemplate(descriptor); + downloadPath = result.path; + mirrors = result.mirrors || mirrors; + } else { + downloadPath = util.format(downloadPathTemplate, descriptor.revision); + } - let downloadURLs = PLAYWRIGHT_CDN_MIRRORS.map(mirror => `${mirror}/${downloadPath}`) ; let downloadHostEnv; if (descriptor.name.startsWith('chromium')) downloadHostEnv = 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST'; @@ -1232,8 +1255,9 @@ export class Registry { const customHostOverride = (downloadHostEnv && getFromENV(downloadHostEnv)) || getFromENV('PLAYWRIGHT_DOWNLOAD_HOST'); if (customHostOverride) - downloadURLs = [`${customHostOverride}/${downloadPath}`]; - return downloadURLs; + mirrors = [customHostOverride]; + + return mirrors.map(mirror => `${mirror}/${downloadPath}`); } private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath?: string) { From f911c81b8ff27e2a2f90d45122e4a5c5defa1265 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 16 Dec 2025 09:45:13 +0100 Subject: [PATCH 2/6] simplify --- .../playwright-core/src/server/registry/index.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index d9999f86df67f..89c32c9a7aa85 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -47,11 +47,6 @@ const PLAYWRIGHT_CDN_MIRRORS = [ 'https://cdn.playwright.dev', // Hit the Storage Bucket directly ]; -const CHROME_FOR_TESTING_CDN_MIRRORS = [ - 'https://cdn.playwright.dev/chrome-for-testing-public', - 'https://storage.googleapis.com/chrome-for-testing-public', -]; - if (process.env.PW_TEST_CDN_THAT_SHOULD_WORK) { for (let i = 0; i < PLAYWRIGHT_CDN_MIRRORS.length; i++) { const cdn = PLAYWRIGHT_CDN_MIRRORS[i]; @@ -136,7 +131,10 @@ function cftUrl(suffix: string): DownloadPathFunction { return ({ browserVersion }) => { return { path: `${browserVersion}/${suffix}`, - mirrors: CHROME_FOR_TESTING_CDN_MIRRORS, + mirrors: [ + 'https://cdn.playwright.dev/chrome-for-testing-public', + 'https://storage.googleapis.com/chrome-for-testing-public', + ], }; }; } @@ -1236,13 +1234,14 @@ export class Registry { if (!downloadPathTemplate) return []; let downloadPath: string; - let mirrors = PLAYWRIGHT_CDN_MIRRORS; + let mirrors: string[]; if (typeof downloadPathTemplate === 'function') { const result = downloadPathTemplate(descriptor); downloadPath = result.path; - mirrors = result.mirrors || mirrors; + mirrors = result.mirrors; } else { downloadPath = util.format(downloadPathTemplate, descriptor.revision); + mirrors = PLAYWRIGHT_CDN_MIRRORS; } let downloadHostEnv; From e331343ab3aeab4721d76911f83ab479dbe1e0d6 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 16 Dec 2025 12:12:53 +0100 Subject: [PATCH 3/6] fix tests --- tests/installation/playwright-cdn.spec.ts | 2 +- tests/installation/playwright-cli-install-should-work.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/installation/playwright-cdn.spec.ts b/tests/installation/playwright-cdn.spec.ts index ac03a4b92e42b..c7a9321711a85 100644 --- a/tests/installation/playwright-cdn.spec.ts +++ b/tests/installation/playwright-cdn.spec.ts @@ -46,7 +46,7 @@ for (const cdn of CDNS) { expect(result).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]); await checkInstalledSoftwareOnDisk((['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware])); const dls = parsedDownloads(result); - for (const software of ['chromium', 'ffmpeg', 'firefox', 'webkit']) + for (const software of ['ffmpeg', 'firefox', 'webkit']) expect(dls).toContainEqual({ status: 200, name: software, url: expect.stringContaining(cdn) }); await exec('node sanity.js playwright chromium firefox webkit'); await exec('node esm-playwright.mjs'); diff --git a/tests/installation/playwright-cli-install-should-work.spec.ts b/tests/installation/playwright-cli-install-should-work.spec.ts index 2667564b93f1e..cb9c13b2bd3ac 100755 --- a/tests/installation/playwright-cli-install-should-work.spec.ts +++ b/tests/installation/playwright-cli-install-should-work.spec.ts @@ -85,7 +85,7 @@ test('install command should work with mirror that uses chunked encoding', async await exec('npm i playwright'); const server = http.createServer(async (req, res) => { try { - const upstream = await fetch('https://cdn.playwright.dev/dbazure/download/playwright' + req.url); + const upstream = await fetch('https://cdn.playwright.dev/chrome-for-testing-public' + req.url); const headers = new Headers(upstream.headers); headers.delete('content-length'); res.writeHead(upstream.status, Object.fromEntries(headers)); From 86ae89a0a97a45270977645100cddb0ae196a5fa Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 16 Dec 2025 12:43:00 +0100 Subject: [PATCH 4/6] fix test --- .../installation/playwright-cli-install-should-work.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/installation/playwright-cli-install-should-work.spec.ts b/tests/installation/playwright-cli-install-should-work.spec.ts index cb9c13b2bd3ac..5dbe64c6da559 100755 --- a/tests/installation/playwright-cli-install-should-work.spec.ts +++ b/tests/installation/playwright-cli-install-should-work.spec.ts @@ -85,7 +85,11 @@ test('install command should work with mirror that uses chunked encoding', async await exec('npm i playwright'); const server = http.createServer(async (req, res) => { try { - const upstream = await fetch('https://cdn.playwright.dev/chrome-for-testing-public' + req.url); + const upstream = await fetch( + req.url.startsWith('builds/') + ? 'https://cdn.playwright.dev/dbazure/download/playwright' + req.url + : 'https://cdn.playwright.dev/chrome-for-testing-public' + req.url + ); const headers = new Headers(upstream.headers); headers.delete('content-length'); res.writeHead(upstream.status, Object.fromEntries(headers)); From 6c78ada707cfa721de5ebdf375e4776ce3440bd8 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 16 Dec 2025 17:24:20 +0100 Subject: [PATCH 5/6] fix once more --- tests/installation/playwright-cli-install-should-work.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/installation/playwright-cli-install-should-work.spec.ts b/tests/installation/playwright-cli-install-should-work.spec.ts index 5dbe64c6da559..48f7dabc785d8 100755 --- a/tests/installation/playwright-cli-install-should-work.spec.ts +++ b/tests/installation/playwright-cli-install-should-work.spec.ts @@ -86,7 +86,7 @@ test('install command should work with mirror that uses chunked encoding', async const server = http.createServer(async (req, res) => { try { const upstream = await fetch( - req.url.startsWith('builds/') + req.url.startsWith('/builds/') ? 'https://cdn.playwright.dev/dbazure/download/playwright' + req.url : 'https://cdn.playwright.dev/chrome-for-testing-public' + req.url ); From 1855eb920358313597cc7874e0ab4068e8064ad1 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Thu, 18 Dec 2025 11:52:34 +0100 Subject: [PATCH 6/6] dont fallback to bucket --- packages/playwright-core/src/server/registry/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 89c32c9a7aa85..2bd817776a80d 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -133,7 +133,6 @@ function cftUrl(suffix: string): DownloadPathFunction { path: `${browserVersion}/${suffix}`, mirrors: [ 'https://cdn.playwright.dev/chrome-for-testing-public', - 'https://storage.googleapis.com/chrome-for-testing-public', ], }; };