mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: implement --dry-run for npx playwright install (#17158)
* feat: implement `--dry-run` for `npx playwright install` The `--dry-run` command prints URLs for browsers to be installed. Demo output: ``` browser: chromium version 106.0.5249.21 Install location: /Users/andreylushnikov/Library/Caches/ms-playwright/chromium-1023 Download url: https://playwright.azureedge.net/builds/chromium/1023/chromium-mac-arm64.zip Download fallback 1: https://playwright-akamai.azureedge.net/builds/chromium/1023/chromium-mac-arm64.zip Download fallback 2: https://playwright-verizon.azureedge.net/builds/chromium/1023/chromium-mac-arm64.zip browser: firefox version 104.0 Install location: /Users/andreylushnikov/Library/Caches/ms-playwright/firefox-1350 Download url: https://playwright.azureedge.net/builds/firefox/1350/firefox-mac-11-arm64.zip Download fallback 1: https://playwright-akamai.azureedge.net/builds/firefox/1350/firefox-mac-11-arm64.zip Download fallback 2: https://playwright-verizon.azureedge.net/builds/firefox/1350/firefox-mac-11-arm64.zip browser: webkit version 16.0 Install location: /Users/andreylushnikov/Library/Caches/ms-playwright/webkit-1714 Download url: https://playwright.azureedge.net/builds/webkit/1714/webkit-mac-12-arm64.zip Download fallback 1: https://playwright-akamai.azureedge.net/builds/webkit/1714/webkit-mac-12-arm64.zip Download fallback 2: https://playwright-verizon.azureedge.net/builds/webkit/1714/webkit-mac-12-arm64.zip browser: ffmpeg Install location: /Users/andreylushnikov/Library/Caches/ms-playwright/ffmpeg-1007 Download url: https://playwright.azureedge.net/builds/ffmpeg/1007/ffmpeg-mac-arm64.zip Download fallback 1: https://playwright-akamai.azureedge.net/builds/ffmpeg/1007/ffmpeg-mac-arm64.zip Download fallback 2: https://playwright-verizon.azureedge.net/builds/ffmpeg/1007/ffmpeg-mac-arm64.zip ``` Fixes #16926
This commit is contained in:
parent
3708ba7a1f
commit
b0ff4f58ce
@ -32,9 +32,7 @@ import type { Page } from '../client/page';
|
|||||||
import type { BrowserType } from '../client/browserType';
|
import type { BrowserType } from '../client/browserType';
|
||||||
import type { BrowserContextOptions, LaunchOptions } from '../client/types';
|
import type { BrowserContextOptions, LaunchOptions } from '../client/types';
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { getPlaywrightVersion } from '../common/userAgent';
|
|
||||||
import { wrapInASCIIBox, isLikelyNpxGlobal, assert } from '../utils';
|
import { wrapInASCIIBox, isLikelyNpxGlobal, assert } from '../utils';
|
||||||
import { spawnAsync } from '../utils/spawnAsync';
|
|
||||||
import { launchGridAgent } from '../grid/gridAgent';
|
import { launchGridAgent } from '../grid/gridAgent';
|
||||||
import type { GridFactory } from '../grid/gridServer';
|
import type { GridFactory } from '../grid/gridServer';
|
||||||
import { GridServer } from '../grid/gridServer';
|
import { GridServer } from '../grid/gridServer';
|
||||||
@ -120,8 +118,9 @@ program
|
|||||||
.command('install [browser...]')
|
.command('install [browser...]')
|
||||||
.description('ensure browsers necessary for this version of Playwright are installed')
|
.description('ensure browsers necessary for this version of Playwright are installed')
|
||||||
.option('--with-deps', 'install system dependencies for browsers')
|
.option('--with-deps', 'install system dependencies for browsers')
|
||||||
|
.option('--dry-run', 'do not execute installation, only print information')
|
||||||
.option('--force', 'force reinstall of stable browser channels')
|
.option('--force', 'force reinstall of stable browser channels')
|
||||||
.action(async function(args: string[], options: { withDeps?: boolean, force?: boolean }) {
|
.action(async function(args: string[], options: { withDeps?: boolean, force?: boolean, dryRun?: boolean }) {
|
||||||
if (isLikelyNpxGlobal()) {
|
if (isLikelyNpxGlobal()) {
|
||||||
console.error(wrapInASCIIBox([
|
console.error(wrapInASCIIBox([
|
||||||
`WARNING: It looks like you are running 'npx playwright install' without first`,
|
`WARNING: It looks like you are running 'npx playwright install' without first`,
|
||||||
@ -143,27 +142,26 @@ program
|
|||||||
].join('\n'), 1));
|
].join('\n'), 1));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!args.length) {
|
const hasNoArguments = !args.length;
|
||||||
const executables = registry.defaultExecutables();
|
const executables = hasNoArguments ? registry.defaultExecutables() : checkBrowsersToInstall(args);
|
||||||
if (options.withDeps)
|
if (options.withDeps)
|
||||||
await registry.installDeps(executables, false);
|
await registry.installDeps(executables, !!options.dryRun);
|
||||||
await registry.install(executables, false /* forceReinstall */);
|
if (options.dryRun) {
|
||||||
|
for (const executable of executables) {
|
||||||
|
const version = executable.browserVersion ? `version ` + executable.browserVersion : '';
|
||||||
|
console.log(`browser: ${executable.name}${version ? ' ' + version : ''}`);
|
||||||
|
console.log(` Install location: ${executable.directory ?? '<system>'}`);
|
||||||
|
if (executable.downloadURLs?.length) {
|
||||||
|
const [url, ...fallbacks] = executable.downloadURLs;
|
||||||
|
console.log(` Download url: ${url}`);
|
||||||
|
for (let i = 0; i < fallbacks.length; ++i)
|
||||||
|
console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
|
||||||
|
}
|
||||||
|
console.log(``);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const installDockerImage = args.some(arg => arg === 'docker-image');
|
const forceReinstall = hasNoArguments ? false : !!options.force;
|
||||||
args = args.filter(arg => arg !== 'docker-image');
|
await registry.install(executables, forceReinstall);
|
||||||
if (installDockerImage) {
|
|
||||||
const imageName = `mcr.microsoft.com/playwright:v${getPlaywrightVersion()}-focal`;
|
|
||||||
const { code } = await spawnAsync('docker', ['pull', imageName], { stdio: 'inherit' });
|
|
||||||
if (code !== 0) {
|
|
||||||
console.log('Failed to pull docker image');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const executables = checkBrowsersToInstall(args);
|
|
||||||
if (options.withDeps)
|
|
||||||
await registry.installDeps(executables, false);
|
|
||||||
await registry.install(executables, !!options.force /* forceReinstall */);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Failed to install browsers\n${e}`);
|
console.log(`Failed to install browsers\n${e}`);
|
||||||
|
|||||||
@ -310,6 +310,8 @@ 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[],
|
||||||
|
browserVersion?: string,
|
||||||
executablePathOrDie(sdkLanguage: string): string;
|
executablePathOrDie(sdkLanguage: string): string;
|
||||||
executablePath(sdkLanguage: string): string | undefined;
|
executablePath(sdkLanguage: string): string | undefined;
|
||||||
validateHostRequirements(sdkLanguage: string): Promise<void>;
|
validateHostRequirements(sdkLanguage: string): Promise<void>;
|
||||||
@ -376,7 +378,9 @@ 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']),
|
||||||
_install: () => this._downloadExecutable(chromium, chromiumExecutable, DOWNLOAD_PATHS['chromium'][hostPlatform], 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(chromium),
|
||||||
|
browserVersion: chromium.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(chromium, chromiumExecutable),
|
||||||
_dependencyGroup: 'chromium',
|
_dependencyGroup: 'chromium',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -392,7 +396,9 @@ export class Registry {
|
|||||||
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-with-symbols', chromiumWithSymbolsExecutable, chromiumWithSymbols.installByDefault, sdkLanguage),
|
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-with-symbols', chromiumWithSymbolsExecutable, chromiumWithSymbols.installByDefault, sdkLanguage),
|
||||||
installType: chromiumWithSymbols.installByDefault ? 'download-by-default' : 'download-on-demand',
|
installType: chromiumWithSymbols.installByDefault ? 'download-by-default' : 'download-on-demand',
|
||||||
validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumWithSymbols.dir, ['chrome-linux'], [], ['chrome-win']),
|
validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumWithSymbols.dir, ['chrome-linux'], [], ['chrome-win']),
|
||||||
_install: () => this._downloadExecutable(chromiumWithSymbols, chromiumWithSymbolsExecutable, DOWNLOAD_PATHS['chromium-with-symbols'][hostPlatform], 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(chromiumWithSymbols),
|
||||||
|
browserVersion: chromiumWithSymbols.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(chromiumWithSymbols, chromiumWithSymbolsExecutable),
|
||||||
_dependencyGroup: 'chromium',
|
_dependencyGroup: 'chromium',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -408,7 +414,9 @@ 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']),
|
||||||
_install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable, DOWNLOAD_PATHS['chromium-tip-of-tree'][hostPlatform], 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(chromiumTipOfTree),
|
||||||
|
browserVersion: chromiumTipOfTree.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable),
|
||||||
_dependencyGroup: 'chromium',
|
_dependencyGroup: 'chromium',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -492,7 +500,9 @@ 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']),
|
||||||
_install: () => this._downloadExecutable(firefox, firefoxExecutable, DOWNLOAD_PATHS['firefox'][hostPlatform], 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(firefox),
|
||||||
|
browserVersion: firefox.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(firefox, firefoxExecutable),
|
||||||
_dependencyGroup: 'firefox',
|
_dependencyGroup: 'firefox',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -508,7 +518,9 @@ 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']),
|
||||||
_install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable, DOWNLOAD_PATHS['firefox-beta'][hostPlatform], 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(firefoxBeta),
|
||||||
|
browserVersion: firefoxBeta.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable),
|
||||||
_dependencyGroup: 'firefox',
|
_dependencyGroup: 'firefox',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -534,7 +546,9 @@ 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'], ['']),
|
||||||
_install: () => this._downloadExecutable(webkit, webkitExecutable, DOWNLOAD_PATHS['webkit'][hostPlatform], 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(webkit),
|
||||||
|
browserVersion: webkit.browserVersion,
|
||||||
|
_install: () => this._downloadExecutable(webkit, webkitExecutable),
|
||||||
_dependencyGroup: 'webkit',
|
_dependencyGroup: 'webkit',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -550,7 +564,8 @@ 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(),
|
||||||
_install: () => this._downloadExecutable(ffmpeg, ffmpegExecutable, DOWNLOAD_PATHS['ffmpeg'][hostPlatform], 'PLAYWRIGHT_FFMPEG_DOWNLOAD_HOST'),
|
downloadURLs: this._downloadURLs(ffmpeg),
|
||||||
|
_install: () => this._downloadExecutable(ffmpeg, ffmpegExecutable),
|
||||||
_dependencyGroup: 'tools',
|
_dependencyGroup: 'tools',
|
||||||
_isHermeticInstallation: true,
|
_isHermeticInstallation: true,
|
||||||
});
|
});
|
||||||
@ -727,17 +742,33 @@ export class Registry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath: string | undefined, downloadPathTemplate: string | undefined, downloadHostEnv: string) {
|
private _downloadURLs(descriptor: BrowsersJSONDescriptor): string[] {
|
||||||
if (!downloadPathTemplate || !executablePath)
|
const downloadPathTemplate: string|undefined = (DOWNLOAD_PATHS as any)[descriptor.name][hostPlatform];
|
||||||
throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`);
|
if (!downloadPathTemplate)
|
||||||
if (hostPlatform === 'generic-linux' || hostPlatform === 'generic-linux-arm64')
|
return [];
|
||||||
logPolitely('BEWARE: your OS is not officially supported by Playwright; downloading fallback build.');
|
|
||||||
const downloadPath = util.format(downloadPathTemplate, descriptor.revision);
|
const downloadPath = util.format(downloadPathTemplate, descriptor.revision);
|
||||||
|
|
||||||
let downloadURLs = PLAYWRIGHT_CDN_MIRRORS.map(mirror => `${mirror}/${downloadPath}`) ;
|
let downloadURLs = PLAYWRIGHT_CDN_MIRRORS.map(mirror => `${mirror}/${downloadPath}`) ;
|
||||||
|
let downloadHostEnv;
|
||||||
|
if (descriptor.name.startsWith('chromium'))
|
||||||
|
downloadHostEnv = 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST';
|
||||||
|
else if (descriptor.name.startsWith('firefox'))
|
||||||
|
downloadHostEnv = 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST';
|
||||||
|
else if (descriptor.name.startsWith('webkit'))
|
||||||
|
downloadHostEnv = 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST';
|
||||||
|
|
||||||
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}`];
|
downloadURLs = [`${customHostOverride}/${downloadPath}`];
|
||||||
|
return downloadURLs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath: string | undefined) {
|
||||||
|
const downloadURLs = this._downloadURLs(descriptor);
|
||||||
|
if (!downloadURLs.length || !executablePath)
|
||||||
|
throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`);
|
||||||
|
if (hostPlatform === 'generic-linux' || hostPlatform === 'generic-linux-arm64')
|
||||||
|
logPolitely('BEWARE: your OS is not officially supported by Playwright; downloading fallback build.');
|
||||||
|
|
||||||
const displayName = descriptor.name.split('-').map(word => {
|
const displayName = descriptor.name.split('-').map(word => {
|
||||||
return word === 'ffmpeg' ? 'FFMPEG' : word.charAt(0).toUpperCase() + word.slice(1);
|
return word === 'ffmpeg' ? 'FFMPEG' : word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user