From bbc1a4fea0b067aa71e412007366a494ea7e5f44 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Wed, 23 Mar 2022 15:06:14 -0600 Subject: [PATCH] fix: print all missing libraries when used on unsupported linux distro (#12966) This patch: - Adds 3 new host platform types: * `generic-linux` and `generic-linux-arm64` for the unsupported linux distributions * `` for non-supported OS versions - Prints a warning when downloading Ubuntu browser builds on unsupported Linux distribution - Makes sure launch doctor prints all missing shared libraries on unknown Linux distributions - Also prints an `apt` command as an alternative to Playwright CLI dependency installation. --- .../playwright-core/src/utils/dependencies.ts | 51 ++++++++----------- .../playwright-core/src/utils/registry.ts | 20 ++++++++ packages/playwright-core/src/utils/utils.ts | 10 ++-- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/playwright-core/src/utils/dependencies.ts b/packages/playwright-core/src/utils/dependencies.ts index c0c5171ff6..9740c533bb 100644 --- a/packages/playwright-core/src/utils/dependencies.ts +++ b/packages/playwright-core/src/utils/dependencies.ts @@ -155,9 +155,9 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir const lddPaths: string[] = []; for (const directoryPath of directoryPaths) lddPaths.push(...(await executablesOrSharedLibraries(directoryPath))); - const allMissingDeps = await Promise.all(lddPaths.map(lddPath => missingFileDependencies(lddPath, directoryPaths))); + const missingDepsPerFile = await Promise.all(lddPaths.map(lddPath => missingFileDependencies(lddPath, directoryPaths))); const missingDeps: Set = new Set(); - for (const deps of allMissingDeps) { + for (const deps of missingDepsPerFile) { for (const dep of deps) missingDeps.add(dep); } @@ -165,6 +165,7 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir missingDeps.add(dep); if (!missingDeps.size) return; + const allMissingDeps = new Set(missingDeps); // Check Ubuntu version. const missingPackages = new Set(); @@ -182,41 +183,33 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir } const maybeSudo = (process.getuid() !== 0) && os.platform() !== 'win32' ? 'sudo ' : ''; - // Happy path: known dependencies are missing for browsers. - // Suggest installation with a Playwright CLI. + const errorLines = [ + `Host system is missing dependencies to run browsers.`, + ]; if (missingPackages.size && !missingDeps.size) { - throw new Error('\n' + utils.wrapInASCIIBox([ - `Host system is missing a few dependencies to run browsers.`, + // Happy path: known dependencies are missing for browsers. + // Suggest installation with a Playwright CLI. + errorLines.push(...[ `Please install them with the following command:`, ``, ` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, 'install-deps')}`, ``, + `Alternatively, use Aptitude:`, + ` ${maybeSudo}apt-get install ${[...missingPackages].join('\\\n ')}`, + ``, `<3 Playwright Team`, - ].join('\n'), 1)); + ]); + } else { + // Unhappy path: we either run on unknown distribution, or we failed to resolve all missing + // libraries to package names. + // Print missing libraries only: + errorLines.push(...[ + `Missing libraries:`, + ...[...allMissingDeps].map(dep => ' ' + dep), + ]); } - // Unhappy path - unusual distribution configuration. - let missingPackagesMessage = ''; - if (missingPackages.size) { - missingPackagesMessage = [ - ` Install missing packages with:`, - ` ${maybeSudo}apt-get install ${[...missingPackages].join('\\\n ')}`, - ``, - ``, - ].join('\n'); - } - - let missingDependenciesMessage = ''; - if (missingDeps.size) { - const header = missingPackages.size ? `Missing libraries we didn't find packages for:` : `Missing libraries are:`; - missingDependenciesMessage = [ - ` ${header}`, - ` ${[...missingDeps].join('\n ')}`, - ``, - ].join('\n'); - } - - throw new Error('Host system is missing dependencies!\n\n' + missingPackagesMessage + missingDependenciesMessage); + throw new Error('\n' + utils.wrapInASCIIBox(errorLines.join('\n'), 1)); } function isSharedLib(basename: string) { diff --git a/packages/playwright-core/src/utils/registry.ts b/packages/playwright-core/src/utils/registry.ts index 55c24c2186..33cbd34c64 100644 --- a/packages/playwright-core/src/utils/registry.ts +++ b/packages/playwright-core/src/utils/registry.ts @@ -53,6 +53,9 @@ const EXECUTABLE_PATHS = { const DOWNLOAD_PATHS = { 'chromium': { + '': undefined, + 'generic-linux': 'builds/chromium/%s/chromium-linux.zip', + 'generic-linux-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu18.04': 'builds/chromium/%s/chromium-linux.zip', 'ubuntu20.04': 'builds/chromium/%s/chromium-linux.zip', 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', @@ -67,6 +70,9 @@ const DOWNLOAD_PATHS = { 'win64': 'builds/chromium/%s/chromium-win64.zip', }, 'chromium-with-symbols': { + '': undefined, + 'generic-linux': 'builds/chromium/%s/chromium-with-symbols-linux.zip', + 'generic-linux-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', 'ubuntu18.04': 'builds/chromium/%s/chromium-with-symbols-linux.zip', 'ubuntu20.04': 'builds/chromium/%s/chromium-with-symbols-linux.zip', 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', @@ -81,6 +87,9 @@ const DOWNLOAD_PATHS = { 'win64': 'builds/chromium/%s/chromium-with-symbols-win64.zip', }, 'firefox': { + '': undefined, + 'generic-linux': 'builds/firefox/%s/firefox-ubuntu-20.04.zip', + 'generic-linux-arm64': 'builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip', 'ubuntu18.04': 'builds/firefox/%s/firefox-ubuntu-18.04.zip', 'ubuntu20.04': 'builds/firefox/%s/firefox-ubuntu-20.04.zip', 'ubuntu18.04-arm64': undefined, @@ -95,6 +104,9 @@ const DOWNLOAD_PATHS = { 'win64': 'builds/firefox/%s/firefox-win64.zip', }, 'firefox-beta': { + '': undefined, + 'generic-linux': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip', + 'generic-linux-arm64': undefined, 'ubuntu18.04': 'builds/firefox-beta/%s/firefox-beta-ubuntu-18.04.zip', 'ubuntu20.04': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip', 'ubuntu18.04-arm64': undefined, @@ -109,6 +121,9 @@ const DOWNLOAD_PATHS = { 'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip', }, 'webkit': { + '': undefined, + 'generic-linux': 'builds/webkit/%s/webkit-ubuntu-20.04.zip', + 'generic-linux-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip', 'ubuntu18.04': 'builds/webkit/%s/webkit-ubuntu-18.04.zip', 'ubuntu20.04': 'builds/webkit/%s/webkit-ubuntu-20.04.zip', 'ubuntu18.04-arm64': undefined, @@ -123,6 +138,9 @@ const DOWNLOAD_PATHS = { 'win64': 'builds/webkit/%s/webkit-win64.zip', }, 'ffmpeg': { + '': undefined, + 'generic-linux': 'builds/ffmpeg/%s/ffmpeg-linux.zip', + 'generic-linux-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', 'ubuntu18.04': 'builds/ffmpeg/%s/ffmpeg-linux.zip', 'ubuntu20.04': 'builds/ffmpeg/%s/ffmpeg-linux.zip', 'ubuntu18.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', @@ -593,6 +611,8 @@ export class Registry { private async _downloadExecutable(descriptor: BrowsersJSONDescriptor, executablePath: string | undefined, downloadPathTemplate: string | undefined, downloadHostEnv: string) { if (!downloadPathTemplate || !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 Ubuntu build as a fallback.'); const downloadHost = (downloadHostEnv && getFromENV(downloadHostEnv)) || getFromENV('PLAYWRIGHT_DOWNLOAD_HOST') || diff --git a/packages/playwright-core/src/utils/utils.ts b/packages/playwright-core/src/utils/utils.ts index 7be6c8b27a..b7a3995772 100644 --- a/packages/playwright-core/src/utils/utils.ts +++ b/packages/playwright-core/src/utils/utils.ts @@ -508,7 +508,9 @@ export type HostPlatform = 'win64' | 'mac11' | 'mac11-arm64' | 'mac12' | 'mac12-arm64' | 'ubuntu18.04' | 'ubuntu18.04-arm64' | - 'ubuntu20.04' | 'ubuntu20.04-arm64'; + 'ubuntu20.04' | 'ubuntu20.04-arm64' | + 'generic-linux' | 'generic-linux-arm64' | + ''; export const hostPlatform = ((): HostPlatform => { const platform = os.platform(); @@ -534,15 +536,17 @@ export const hostPlatform = ((): HostPlatform => { return macVersion as HostPlatform; } if (platform === 'linux') { - const ubuntuVersion = getUbuntuVersionSync(); const archSuffix = os.arch() === 'arm64' ? '-arm64' : ''; + const ubuntuVersion = getUbuntuVersionSync(); + if (!ubuntuVersion) + return ('generic-linux' + archSuffix) as HostPlatform; if (parseInt(ubuntuVersion, 10) <= 19) return ('ubuntu18.04' + archSuffix) as HostPlatform; return ('ubuntu20.04' + archSuffix) as HostPlatform; } if (platform === 'win32') return 'win64'; - return platform as HostPlatform; + return ''; })(); export function wrapInASCIIBox(text: string, padding = 0): string {