From 4c7343fe96c63fe9542f48ef326df65be23d67cc Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 1 Jul 2021 17:14:04 -0700 Subject: [PATCH] chore: move installDeps to Registry (#7431) This is an effort to consolidate all handling of browser binaries in a single place. --- src/cli/cli.ts | 8 +-- src/install/installDeps.ts | 69 ------------------- ...alidateDependencies.ts => dependencies.ts} | 42 +++++++++++ src/utils/registry.ts | 21 +++++- src/utils/ubuntuVersion.ts | 22 ++++-- 5 files changed, 84 insertions(+), 78 deletions(-) delete mode 100644 src/install/installDeps.ts rename src/utils/{validateDependencies.ts => dependencies.ts} (91%) diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 048c685509..d600a9269a 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -31,8 +31,7 @@ import { Page } from '../client/page'; import { BrowserType } from '../client/browserType'; import { BrowserContextOptions, LaunchOptions } from '../client/types'; import { spawn } from 'child_process'; -import { installDeps } from '../install/installDeps'; -import { allBrowserNames, BrowserName } from '../utils/registry'; +import { allBrowserNames, BrowserName, Registry } from '../utils/registry'; import * as utils from '../utils/utils'; const SCRIPTS_DIRECTORY = path.join(__dirname, '..', '..', 'bin'); @@ -182,9 +181,10 @@ async function installBrowserChannel(channel: BrowserChannel) { program .command('install-deps [browserType...]') .description('install dependencies necessary to run browsers (will ask for sudo permissions)') - .action(async function(browserType) { + .action(async function(browserTypes) { try { - await installDeps(browserType); + // TODO: verify the list and print supported browserTypes in the error message. + await Registry.currentPackageRegistry().installDeps(browserTypes); } catch (e) { console.log(`Failed to install browser dependencies\n${e}`); process.exit(1); diff --git a/src/install/installDeps.ts b/src/install/installDeps.ts deleted file mode 100644 index 2badc4cc80..0000000000 --- a/src/install/installDeps.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import childProcess from 'child_process'; -import os from 'os'; -import path from 'path'; -import { getUbuntuVersion } from '../utils/ubuntuVersion'; -import * as utils from '../utils/utils'; - -const { deps } = require('../nativeDeps'); - -const SCRIPTS_DIRECTORY = path.join(__dirname, '..', '..', 'bin'); - -export async function installDeps(browserTypes: string[]) { - if (!browserTypes.length) - browserTypes = ['chromium', 'firefox', 'webkit']; - if (os.platform() === 'win32') { - if (browserTypes.includes('chromium')) { - const {code} = await utils.spawnAsync('powershell.exe', [path.join(SCRIPTS_DIRECTORY, 'install_media_pack.ps1')], { cwd: SCRIPTS_DIRECTORY, stdio: 'inherit' }); - if (code !== 0) - throw new Error('Failed to install windows dependencies!'); - } - return; - } - if (os.platform() !== 'linux') - return; - browserTypes.push('tools'); - - const ubuntuVersion = await getUbuntuVersion(); - if (ubuntuVersion !== '18.04' && ubuntuVersion !== '20.04' && ubuntuVersion !== '21.04') { - console.warn('Cannot install dependencies for this linux distribution!'); // eslint-disable-line no-console - return; - } - - const libraries: string[] = []; - for (const browserType of browserTypes) { - if (ubuntuVersion === '18.04') - libraries.push(...deps['bionic'][browserType]); - else if (ubuntuVersion === '20.04') - libraries.push(...deps['focal'][browserType]); - else if (ubuntuVersion === '21.04') - libraries.push(...deps['hirsute'][browserType]); - } - const uniqueLibraries = Array.from(new Set(libraries)); - console.log('Installing Ubuntu dependencies...'); // eslint-disable-line no-console - const commands: string[] = []; - commands.push('apt-get update'); - commands.push(['apt-get', 'install', '-y', '--no-install-recommends', - ...uniqueLibraries, - ].join(' ')); - const isRoot = (process.getuid() === 0); - const child = isRoot ? - childProcess.spawn('sh', ['-c', `${commands.join('; ')}`], { stdio: 'inherit' }) : - childProcess.spawn('sudo', ['--', 'sh', '-c', `${commands.join('; ')}`], { stdio: 'inherit' }); - await new Promise(f => child.on('exit', f)); -} diff --git a/src/utils/validateDependencies.ts b/src/utils/dependencies.ts similarity index 91% rename from src/utils/validateDependencies.ts rename to src/utils/dependencies.ts index ddd702648a..b94ad03e2f 100644 --- a/src/utils/validateDependencies.ts +++ b/src/utils/dependencies.ts @@ -17,9 +17,12 @@ import fs from 'fs'; import path from 'path'; import * as os from 'os'; +import childProcess from 'child_process'; import { getUbuntuVersion } from './ubuntuVersion'; import * as utils from './utils'; +const BIN_DIRECTORY = path.join(__dirname, '..', '..', 'bin'); + const checkExecutable = (filePath: string) => fs.promises.access(filePath, fs.constants.X_OK).then(() => true).catch(e => false); function isSupportedWindowsVersion(): boolean { @@ -32,6 +35,45 @@ function isSupportedWindowsVersion(): boolean { return major > 6 || (major === 6 && minor > 1); } +export async function installDependenciesWindows(targets: Set<'chromium' | 'firefox' | 'webkit' | 'tools'>) { + if (targets.has('chromium')) { + const {code} = await utils.spawnAsync('powershell.exe', [path.join(BIN_DIRECTORY, 'install_media_pack.ps1')], { cwd: BIN_DIRECTORY, stdio: 'inherit' }); + if (code !== 0) + throw new Error('Failed to install windows dependencies!'); + } +} + +export async function installDependenciesLinux(targets: Set<'chromium' | 'firefox' | 'webkit' | 'tools'>) { + const ubuntuVersion = await getUbuntuVersion(); + if (ubuntuVersion !== '18.04' && ubuntuVersion !== '20.04' && ubuntuVersion !== '21.04') { + console.warn('Cannot install dependencies for this linux distribution!'); // eslint-disable-line no-console + return; + } + + const libraries: string[] = []; + const { deps } = require('../nativeDeps'); + for (const target of targets) { + if (ubuntuVersion === '18.04') + libraries.push(...deps['bionic'][target]); + else if (ubuntuVersion === '20.04') + libraries.push(...deps['focal'][target]); + else if (ubuntuVersion === '21.04') + libraries.push(...deps['hirsute'][target]); + } + const uniqueLibraries = Array.from(new Set(libraries)); + console.log('Installing Ubuntu dependencies...'); // eslint-disable-line no-console + const commands: string[] = []; + commands.push('apt-get update'); + commands.push(['apt-get', 'install', '-y', '--no-install-recommends', + ...uniqueLibraries, + ].join(' ')); + const isRoot = (process.getuid() === 0); + const child = isRoot ? + childProcess.spawn('sh', ['-c', `${commands.join('; ')}`], { stdio: 'inherit' }) : + childProcess.spawn('sudo', ['--', 'sh', '-c', `${commands.join('; ')}`], { stdio: 'inherit' }); + await new Promise(f => child.on('exit', f)); +} + export async function validateDependenciesWindows(windowsExeAndDllDirectories: string[]) { const directoryPaths = windowsExeAndDllDirectories; const lddPaths: string[] = []; diff --git a/src/utils/registry.ts b/src/utils/registry.ts index a1a1aafdbf..7a8a6627ae 100644 --- a/src/utils/registry.ts +++ b/src/utils/registry.ts @@ -20,7 +20,7 @@ import path from 'path'; import * as util from 'util'; import { getUbuntuVersion, getUbuntuVersionSync } from './ubuntuVersion'; import { assert, getFromENV, getAsBooleanFromENV } from './utils'; -import { validateDependenciesLinux, validateDependenciesWindows } from './validateDependencies'; +import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies'; export type BrowserName = 'chromium'|'chromium-with-symbols'|'webkit'|'firefox'|'firefox-beta'|'ffmpeg'; export const allBrowserNames: Set = new Set(['chromium', 'chromium-with-symbols', 'webkit', 'firefox', 'ffmpeg', 'firefox-beta']); @@ -374,4 +374,23 @@ export class Registry { return await validateDependenciesWindows(windowsExeAndDllDirectories); } } + + async installDeps(browserNames: BrowserName[]) { + const targets = new Set<'chromium' | 'firefox' | 'webkit' | 'tools'>(); + if (!browserNames.length) + browserNames = this.installByDefault(); + for (const browserName of browserNames) { + if (browserName === 'chromium' || browserName === 'chromium-with-symbols') + targets.add('chromium'); + if (browserName === 'firefox' || browserName === 'firefox-beta') + targets.add('firefox'); + if (browserName === 'webkit') + targets.add('webkit'); + } + targets.add('tools'); + if (os.platform() === 'win32') + return await installDependenciesWindows(targets); + if (os.platform() === 'linux') + return await installDependenciesLinux(targets); + } } diff --git a/src/utils/ubuntuVersion.ts b/src/utils/ubuntuVersion.ts index a8b893770e..74015b2195 100644 --- a/src/utils/ubuntuVersion.ts +++ b/src/utils/ubuntuVersion.ts @@ -18,7 +18,21 @@ import fs from 'fs'; import * as os from 'os'; +let ubuntuVersionCached: string | undefined; + export async function getUbuntuVersion(): Promise { + if (ubuntuVersionCached === undefined) + ubuntuVersionCached = await getUbuntuVersionAsyncInternal(); + return ubuntuVersionCached; +} + +export function getUbuntuVersionSync(): string { + if (ubuntuVersionCached === undefined) + ubuntuVersionCached = getUbuntuVersionSyncInternal(); + return ubuntuVersionCached; +} + +async function getUbuntuVersionAsyncInternal(): Promise { if (os.platform() !== 'linux') return ''; let osReleaseText = await fs.promises.readFile('/etc/upstream-release/lsb-release', 'utf8').catch(e => ''); @@ -26,10 +40,10 @@ export async function getUbuntuVersion(): Promise { osReleaseText = await fs.promises.readFile('/etc/os-release', 'utf8').catch(e => ''); if (!osReleaseText) return ''; - return getUbuntuVersionInternal(osReleaseText); + return parseUbuntuVersion(osReleaseText); } -export function getUbuntuVersionSync(): string { +function getUbuntuVersionSyncInternal(): string { if (os.platform() !== 'linux') return ''; try { @@ -40,13 +54,13 @@ export function getUbuntuVersionSync(): string { osReleaseText = fs.readFileSync('/etc/os-release', 'utf8'); if (!osReleaseText) return ''; - return getUbuntuVersionInternal(osReleaseText); + return parseUbuntuVersion(osReleaseText); } catch (e) { return ''; } } -function getUbuntuVersionInternal(osReleaseText: string): string { +function parseUbuntuVersion(osReleaseText: string): string { const fields = new Map(); for (const line of osReleaseText.split('\n')) { const tokens = line.split('=');