chore: move installDeps to Registry (#7431)

This is an effort to consolidate all handling of browser binaries in a single place.
This commit is contained in:
Dmitry Gozman 2021-07-01 17:14:04 -07:00 committed by GitHub
parent e19d509c32
commit 4c7343fe96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 78 deletions

View file

@ -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);

View file

@ -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));
}

View file

@ -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[] = [];

View file

@ -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<BrowserName> = 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);
}
}

View file

@ -18,7 +18,21 @@
import fs from 'fs';
import * as os from 'os';
let ubuntuVersionCached: string | undefined;
export async function getUbuntuVersion(): Promise<string> {
if (ubuntuVersionCached === undefined)
ubuntuVersionCached = await getUbuntuVersionAsyncInternal();
return ubuntuVersionCached;
}
export function getUbuntuVersionSync(): string {
if (ubuntuVersionCached === undefined)
ubuntuVersionCached = getUbuntuVersionSyncInternal();
return ubuntuVersionCached;
}
async function getUbuntuVersionAsyncInternal(): Promise<string> {
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<string> {
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('=');