chore: enable ESRP CDN

This commit is contained in:
Max Schmitt 2024-06-25 12:33:05 +02:00
parent a6b6b243d0
commit 98bcf84bc6
4 changed files with 23 additions and 70 deletions

View file

@ -149,12 +149,8 @@ program
const version = executable.browserVersion ? `version ` + executable.browserVersion : ''; const version = executable.browserVersion ? `version ` + executable.browserVersion : '';
console.log(`browser: ${executable.name}${version ? ' ' + version : ''}`); console.log(`browser: ${executable.name}${version ? ' ' + version : ''}`);
console.log(` Install location: ${executable.directory ?? '<system>'}`); console.log(` Install location: ${executable.directory ?? '<system>'}`);
if (executable.downloadURLs?.length) { if (executable.downloadURL)
const [url, ...fallbacks] = executable.downloadURLs; console.log(` Download url: ${executable.downloadURL}`);
console.log(` Download url: ${url}`);
for (let i = 0; i < fallbacks.length; ++i)
console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
}
console.log(``); console.log(``);
} }
} else { } else {

View file

@ -27,7 +27,7 @@ import { browserDirectoryToMarkerFilePath } from '.';
import { getUserAgent } from '../../utils/userAgent'; import { getUserAgent } from '../../utils/userAgent';
import type { DownloadParams } from './oopDownloadBrowserMain'; import type { DownloadParams } from './oopDownloadBrowserMain';
export async function downloadBrowserWithProgressBar(title: string, browserDirectory: string, executablePath: string | undefined, downloadURLs: string[], downloadFileName: string, downloadConnectionTimeout: number): Promise<boolean> { export async function downloadBrowserWithProgressBar(title: string, browserDirectory: string, executablePath: string | undefined, downloadURLs: string, downloadFileName: string, downloadConnectionTimeout: number): Promise<boolean> {
if (await existsAsync(browserDirectoryToMarkerFilePath(browserDirectory))) { if (await existsAsync(browserDirectoryToMarkerFilePath(browserDirectory))) {
// Already downloaded. // Already downloaded.
debugLogger.log('install', `${title} is already downloaded.`); debugLogger.log('install', `${title} is already downloaded.`);
@ -39,9 +39,8 @@ export async function downloadBrowserWithProgressBar(title: string, browserDirec
const retryCount = 3; const retryCount = 3;
for (let attempt = 1; attempt <= retryCount; ++attempt) { for (let attempt = 1; attempt <= retryCount; ++attempt) {
debugLogger.log('install', `downloading ${title} - attempt #${attempt}`); debugLogger.log('install', `downloading ${title} - attempt #${attempt}`);
const url = downloadURLs[(attempt - 1) % downloadURLs.length]; logPolitely(`Downloading ${title}` + colors.dim(` from ${downloadURLs}`));
logPolitely(`Downloading ${title}` + colors.dim(` from ${url}`)); const { error } = await downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, downloadURLs, zipPath, executablePath, downloadConnectionTimeout);
const { error } = await downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url, zipPath, executablePath, downloadConnectionTimeout);
if (!error) { if (!error) {
debugLogger.log('install', `SUCCESS installing ${title}`); debugLogger.log('install', `SUCCESS installing ${title}`);
break; break;
@ -96,6 +95,7 @@ function downloadBrowserWithProgressBarOutOfProcess(title: string, browserDirect
promise.resolve({ error }); promise.resolve({ error });
}); });
debugLogger.log('install', `running download:`); debugLogger.log('install', `running download:`);
debugLogger.log('install', `-- from url: ${url}`); debugLogger.log('install', `-- from url: ${url}`);
debugLogger.log('install', `-- to location: ${zipPath}`); debugLogger.log('install', `-- to location: ${zipPath}`);

View file

@ -36,19 +36,7 @@ import { debugLogger } from '../../utils/debugLogger';
const PACKAGE_PATH = path.join(__dirname, '..', '..', '..'); const PACKAGE_PATH = path.join(__dirname, '..', '..', '..');
const BIN_PATH = path.join(__dirname, '..', '..', '..', 'bin'); const BIN_PATH = path.join(__dirname, '..', '..', '..', 'bin');
const PLAYWRIGHT_CDN_MIRRORS = [ const PLAYWRIGHT_CDN_MIRROR = 'https://playwright.azureedge.net';
'https://playwright.azureedge.net',
'https://playwright-akamai.azureedge.net',
'https://playwright-verizon.azureedge.net',
];
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];
if (cdn !== process.env.PW_TEST_CDN_THAT_SHOULD_WORK)
PLAYWRIGHT_CDN_MIRRORS[i] = cdn + '.does-not-resolve.playwright.dev';
}
}
const EXECUTABLE_PATHS = { const EXECUTABLE_PATHS = {
'chromium': { 'chromium': {
@ -360,7 +348,7 @@ export interface Executable {
browserName: BrowserName | undefined; browserName: BrowserName | undefined;
installType: 'download-by-default' | 'download-on-demand' | 'install-script' | 'none'; installType: 'download-by-default' | 'download-on-demand' | 'install-script' | 'none';
directory: string | undefined; directory: string | undefined;
downloadURLs?: string[], downloadURL?: string,
browserVersion?: string, browserVersion?: string,
executablePathOrDie(sdkLanguage: string): string; executablePathOrDie(sdkLanguage: string): string;
executablePath(sdkLanguage: string): string | undefined; executablePath(sdkLanguage: string): string | undefined;
@ -428,7 +416,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage),
installType: chromium.installByDefault ? 'download-by-default' : 'download-on-demand', installType: chromium.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']), _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']),
downloadURLs: this._downloadURLs(chromium), downloadURL: this._downloadURL(chromium),
browserVersion: chromium.browserVersion, browserVersion: chromium.browserVersion,
_install: () => this._downloadExecutable(chromium, chromiumExecutable), _install: () => this._downloadExecutable(chromium, chromiumExecutable),
_dependencyGroup: 'chromium', _dependencyGroup: 'chromium',
@ -446,7 +434,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-tip-of-tree', chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-tip-of-tree', chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage),
installType: chromiumTipOfTree.installByDefault ? 'download-by-default' : 'download-on-demand', installType: chromiumTipOfTree.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']), _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']),
downloadURLs: this._downloadURLs(chromiumTipOfTree), downloadURL: this._downloadURL(chromiumTipOfTree),
browserVersion: chromiumTipOfTree.browserVersion, browserVersion: chromiumTipOfTree.browserVersion,
_install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable), _install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable),
_dependencyGroup: 'chromium', _dependencyGroup: 'chromium',
@ -532,7 +520,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox', firefoxExecutable, firefox.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox', firefoxExecutable, firefox.installByDefault, sdkLanguage),
installType: firefox.installByDefault ? 'download-by-default' : 'download-on-demand', installType: firefox.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefox.dir, ['firefox'], [], ['firefox']), _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefox.dir, ['firefox'], [], ['firefox']),
downloadURLs: this._downloadURLs(firefox), downloadURL: this._downloadURL(firefox),
browserVersion: firefox.browserVersion, browserVersion: firefox.browserVersion,
_install: () => this._downloadExecutable(firefox, firefoxExecutable), _install: () => this._downloadExecutable(firefox, firefoxExecutable),
_dependencyGroup: 'firefox', _dependencyGroup: 'firefox',
@ -550,7 +538,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox-beta', firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox-beta', firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage),
installType: firefoxBeta.installByDefault ? 'download-by-default' : 'download-on-demand', installType: firefoxBeta.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxBeta.dir, ['firefox'], [], ['firefox']), _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxBeta.dir, ['firefox'], [], ['firefox']),
downloadURLs: this._downloadURLs(firefoxBeta), downloadURL: this._downloadURL(firefoxBeta),
browserVersion: firefoxBeta.browserVersion, browserVersion: firefoxBeta.browserVersion,
_install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable), _install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable),
_dependencyGroup: 'firefox', _dependencyGroup: 'firefox',
@ -578,7 +566,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('webkit', webkitExecutable, webkit.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('webkit', webkitExecutable, webkit.installByDefault, sdkLanguage),
installType: webkit.installByDefault ? 'download-by-default' : 'download-on-demand', installType: webkit.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'webkit', webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']), _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'webkit', webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']),
downloadURLs: this._downloadURLs(webkit), downloadURL: this._downloadURL(webkit),
browserVersion: webkit.browserVersion, browserVersion: webkit.browserVersion,
_install: () => this._downloadExecutable(webkit, webkitExecutable), _install: () => this._downloadExecutable(webkit, webkitExecutable),
_dependencyGroup: 'webkit', _dependencyGroup: 'webkit',
@ -596,7 +584,7 @@ export class Registry {
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('ffmpeg', ffmpegExecutable, ffmpeg.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('ffmpeg', ffmpegExecutable, ffmpeg.installByDefault, sdkLanguage),
installType: ffmpeg.installByDefault ? 'download-by-default' : 'download-on-demand', installType: ffmpeg.installByDefault ? 'download-by-default' : 'download-on-demand',
_validateHostRequirements: () => Promise.resolve(), _validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(ffmpeg), downloadURL: this._downloadURL(ffmpeg),
_install: () => this._downloadExecutable(ffmpeg, ffmpegExecutable), _install: () => this._downloadExecutable(ffmpeg, ffmpegExecutable),
_dependencyGroup: 'tools', _dependencyGroup: 'tools',
_isHermeticInstallation: true, _isHermeticInstallation: true,
@ -611,7 +599,7 @@ export class Registry {
executablePathOrDie: () => '', executablePathOrDie: () => '',
installType: 'download-on-demand', installType: 'download-on-demand',
_validateHostRequirements: () => Promise.resolve(), _validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(android), downloadURL: this._downloadURL(android),
_install: () => this._downloadExecutable(android), _install: () => this._downloadExecutable(android),
_dependencyGroup: 'tools', _dependencyGroup: 'tools',
_isHermeticInstallation: true, _isHermeticInstallation: true,
@ -828,14 +816,14 @@ export class Registry {
await fs.promises.writeFile(markerFile, '').catch(() => {}); await fs.promises.writeFile(markerFile, '').catch(() => {});
} }
private _downloadURLs(descriptor: BrowsersJSONDescriptor): string[] { private _downloadURL(descriptor: BrowsersJSONDescriptor): string | undefined {
const paths = (DOWNLOAD_PATHS as any)[descriptor.name]; const paths = (DOWNLOAD_PATHS as any)[descriptor.name];
const downloadPathTemplate: string|undefined = paths[hostPlatform] || paths['<unknown>']; const downloadPathTemplate: string|undefined = paths[hostPlatform] || paths['<unknown>'];
if (!downloadPathTemplate) if (!downloadPathTemplate)
return []; return undefined;
const downloadPath = util.format(downloadPathTemplate, descriptor.revision); const downloadPath = util.format(downloadPathTemplate, descriptor.revision);
let downloadURLs = PLAYWRIGHT_CDN_MIRRORS.map(mirror => `${mirror}/${downloadPath}`) ; let downloadURL = `${PLAYWRIGHT_CDN_MIRROR}/download/playwright/${downloadPath}`;
let downloadHostEnv; let downloadHostEnv;
if (descriptor.name.startsWith('chromium')) if (descriptor.name.startsWith('chromium'))
downloadHostEnv = 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST'; downloadHostEnv = 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST';
@ -846,13 +834,13 @@ export class Registry {
const customHostOverride = (downloadHostEnv && getFromENV(downloadHostEnv)) || getFromENV('PLAYWRIGHT_DOWNLOAD_HOST'); const customHostOverride = (downloadHostEnv && getFromENV(downloadHostEnv)) || getFromENV('PLAYWRIGHT_DOWNLOAD_HOST');
if (customHostOverride) if (customHostOverride)
downloadURLs = [`${customHostOverride}/${downloadPath}`]; downloadURL = `${customHostOverride}/${downloadPath}`;
return downloadURLs; return downloadURL;
} }
private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath?: string) { private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath?: string) {
const downloadURLs = this._downloadURLs(descriptor); const downloadURL = this._downloadURL(descriptor);
if (!downloadURLs.length) if (!downloadURL)
throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`); throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`);
if (!isOfficiallySupportedPlatform) if (!isOfficiallySupportedPlatform)
logPolitely(`BEWARE: your OS is not officially supported by Playwright; downloading fallback build for ${hostPlatform}.`); logPolitely(`BEWARE: your OS is not officially supported by Playwright; downloading fallback build for ${hostPlatform}.`);
@ -867,7 +855,7 @@ export class Registry {
const downloadFileName = `playwright-download-${descriptor.name}-${hostPlatform}-${descriptor.revision}.zip`; const downloadFileName = `playwright-download-${descriptor.name}-${hostPlatform}-${descriptor.revision}.zip`;
const downloadConnectionTimeoutEnv = getFromENV('PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT'); const downloadConnectionTimeoutEnv = getFromENV('PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT');
const downloadConnectionTimeout = +(downloadConnectionTimeoutEnv || '0') || 30_000; const downloadConnectionTimeout = +(downloadConnectionTimeoutEnv || '0') || 30_000;
await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURLs, downloadFileName, downloadConnectionTimeout).catch(e => { await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURL, downloadFileName, downloadConnectionTimeout).catch(e => {
throw new Error(`Failed to download ${title}, caused by\n${e.stack}`); throw new Error(`Failed to download ${title}, caused by\n${e.stack}`);
}); });
} }

View file

@ -18,39 +18,8 @@ import http from 'http';
import net from 'net'; import net from 'net';
import type { AddressInfo } from 'net'; import type { AddressInfo } from 'net';
const CDNS = [
'https://playwright.azureedge.net',
'https://playwright-akamai.azureedge.net',
'https://playwright-verizon.azureedge.net',
];
const DL_STAT_BLOCK = /^.*from url: (.*)$\n^.*to location: (.*)$\n^.*response status code: (.*)$\n^.*total bytes: (\d+)$\n^.*download complete, size: (\d+)$\n^.*SUCCESS downloading (\w+) .*$/gm;
const parsedDownloads = (rawLogs: string) => {
const out: { url: string, status: number, name: string }[] = [];
for (const match of rawLogs.matchAll(DL_STAT_BLOCK)) {
const [, url, /* filepath */, status, /* size */, /* receivedBytes */, name] = match;
out.push({ url, status: Number.parseInt(status, 10), name: name.toLocaleLowerCase() });
}
return out;
};
test.use({ isolateBrowsers: true }); test.use({ isolateBrowsers: true });
for (const cdn of CDNS) {
test(`playwright cdn failover should work (${cdn})`, async ({ exec, installedSoftwareOnDisk }) => {
await exec('npm i playwright');
const result = await exec('npx playwright install', { env: { PW_TEST_CDN_THAT_SHOULD_WORK: cdn, DEBUG: 'pw:install' } });
expect(result).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', 'firefox', 'webkit']);
expect(await installedSoftwareOnDisk()).toEqual(['chromium', 'ffmpeg', 'firefox', 'webkit']);
const dls = parsedDownloads(result);
for (const software of ['chromium', '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');
});
}
test(`playwright cdn should race with a timeout`, async ({ exec }) => { test(`playwright cdn should race with a timeout`, async ({ exec }) => {
const server = http.createServer(() => {}); const server = http.createServer(() => {});
await new Promise<void>(resolve => server.listen(0, resolve)); await new Promise<void>(resolve => server.listen(0, resolve));