chore: move extracting of browser out-of-process (#23739)

https://github.com/microsoft/playwright/issues/23729
This commit is contained in:
Max Schmitt 2023-06-16 20:40:15 +02:00 committed by GitHub
parent de422b5afb
commit b44723708c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 24 deletions

View File

@ -19,15 +19,14 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import childProcess from 'child_process';
import { getUserAgent } from '../../utils/userAgent';
import { existsAsync } from '../../utils/fileUtils';
import { debugLogger } from '../../common/debugLogger';
import { extract } from '../../zipBundle';
import { ManualPromise } from '../../utils/manualPromise';
import { colors } from '../../utilsBundle';
import { browserDirectoryToMarkerFilePath } from '.';
export async function downloadBrowserWithProgressBar(title: string, browserDirectory: string, executablePath: string | undefined, downloadURLs: string[], downloadFileName: string, downloadConnectionTimeout: number): Promise<boolean> {
if (await existsAsync(browserDirectory)) {
if (await existsAsync(browserDirectoryToMarkerFilePath(browserDirectory))) {
// Already downloaded.
debugLogger.log('install', `${title} is already downloaded.`);
return false;
@ -40,24 +39,20 @@ export async function downloadBrowserWithProgressBar(title: string, browserDirec
debugLogger.log('install', `downloading ${title} - attempt #${attempt}`);
const url = downloadURLs[(attempt - 1) % downloadURLs.length];
logPolitely(`Downloading ${title}` + colors.dim(` from ${url}`));
const { error } = await downloadFileOutOfProcess(url, zipPath, getUserAgent(), downloadConnectionTimeout);
const { error } = await downloadBrowserWithProgressBarOutOfProcess(title, browserDirectory, url, zipPath, executablePath, downloadConnectionTimeout);
if (!error) {
debugLogger.log('install', `SUCCESS downloading ${title}`);
debugLogger.log('install', `SUCCESS installing ${title}`);
break;
}
if (await existsAsync(zipPath))
await fs.promises.unlink(zipPath);
if (await existsAsync(browserDirectory))
await fs.promises.rmdir(browserDirectory, { recursive: true });
const errorMessage = error?.message || '';
debugLogger.log('install', `attempt #${attempt} - ERROR: ${errorMessage}`);
if (attempt >= retryCount)
throw error;
}
debugLogger.log('install', `extracting archive`);
debugLogger.log('install', `-- zip: ${zipPath}`);
debugLogger.log('install', `-- location: ${browserDirectory}`);
await extract(zipPath, { dir: browserDirectory });
if (executablePath) {
debugLogger.log('install', `fixing permissions at ${executablePath}`);
await fs.promises.chmod(executablePath, 0o755);
}
} catch (e) {
debugLogger.log('install', `FAILED installation ${title} with error: ${e}`);
process.exitCode = 1;
@ -75,8 +70,8 @@ export async function downloadBrowserWithProgressBar(title: string, browserDirec
* Thats why we execute it in a separate process and check manually if the destination file exists.
* https://github.com/microsoft/playwright/issues/17394
*/
function downloadFileOutOfProcess(url: string, destinationPath: string, userAgent: string, downloadConnectionTimeout: number): Promise<{ error: Error | null }> {
const cp = childProcess.fork(path.join(__dirname, 'oopDownloadMain.js'), [url, destinationPath, userAgent, String(downloadConnectionTimeout)]);
function downloadBrowserWithProgressBarOutOfProcess(title: string, browserDirectory: string, url: string, zipPath: string, executablePath: string | undefined, downloadConnectionTimeout: number): Promise<{ error: Error | null }> {
const cp = childProcess.fork(path.join(__dirname, 'oopDownloadBrowserMain.js'), [title, browserDirectory, url, zipPath, executablePath || '', String(downloadConnectionTimeout)]);
const promise = new ManualPromise<{ error: Error | null }>();
cp.on('message', (message: any) => {
if (message?.method === 'log')
@ -87,8 +82,8 @@ function downloadFileOutOfProcess(url: string, destinationPath: string, userAgen
promise.resolve({ error: new Error(`Download failure, code=${code}`) });
return;
}
if (!fs.existsSync(destinationPath))
promise.resolve({ error: new Error(`Download failure, ${destinationPath} does not exist`) });
if (!fs.existsSync(browserDirectoryToMarkerFilePath(browserDirectory)))
promise.resolve({ error: new Error(`Download failure, ${browserDirectoryToMarkerFilePath(browserDirectory)} does not exist`) });
else
promise.resolve({ error: null });
});

View File

@ -839,7 +839,6 @@ export class Registry {
await downloadBrowserWithProgressBar(title, descriptor.dir, executablePath, downloadURLs, downloadFileName, downloadConnectionTimeout).catch(e => {
throw new Error(`Failed to download ${title}, caused by\n${e.stack}`);
});
await fs.promises.writeFile(markerFilePath(descriptor.dir), '');
}
private async _installMSEdgeChannel(channel: 'msedge'|'msedge-beta'|'msedge-dev', scripts: Record<'linux' | 'darwin' | 'win32', string>) {
@ -920,7 +919,7 @@ export class Registry {
(browserName === 'webkit' && browserRevision >= 1307) ||
// All new applications have a marker file right away.
(browserName !== 'firefox' && browserName !== 'chromium' && browserName !== 'webkit');
if (!shouldHaveMarkerFile || (await existsAsync(markerFilePath(usedBrowserPath))))
if (!shouldHaveMarkerFile || (await existsAsync(browserDirectoryToMarkerFilePath(usedBrowserPath))))
usedBrowserPaths.add(usedBrowserPath);
}
} catch (e) {
@ -942,7 +941,7 @@ export class Registry {
}
}
function markerFilePath(browserDirectory: string): string {
export function browserDirectoryToMarkerFilePath(browserDirectory: string): string {
return path.join(browserDirectory, 'INSTALLATION_COMPLETE');
}

View File

@ -18,6 +18,9 @@ import fs from 'fs';
import { progress as ProgressBar } from '../../utilsBundle';
import { httpRequest } from '../../utils/network';
import { ManualPromise } from '../../utils/manualPromise';
import { extract } from '../../zipBundle';
import { getUserAgent } from '../../utils/userAgent';
import { browserDirectoryToMarkerFilePath } from '.';
type OnProgressCallback = (downloadedBytes: number, totalBytes: number) => void;
type DownloadFileLogger = (message: string) => void;
@ -140,13 +143,24 @@ function toMegabytes(bytes: number) {
}
async function main() {
const [url, destination, userAgent, downloadConnectionTimeout] = process.argv.slice(2);
await downloadFile(url, destination, {
const log = (message: string) => process.send?.({ method: 'log', params: { message } });
const [title, browserDirectory, url, zipPath, executablePath, downloadConnectionTimeout] = process.argv.slice(2);
await downloadFile(url, zipPath, {
progressCallback: getDownloadProgress(),
userAgent,
log: message => process.send?.({ method: 'log', params: { message } }),
userAgent: getUserAgent(),
log,
connectionTimeout: +downloadConnectionTimeout,
});
log(`SUCCESS downloading ${title}`);
log(`extracting archive`);
log(`-- zip: ${zipPath}`);
log(`-- location: ${browserDirectory}`);
await extract(zipPath, { dir: browserDirectory });
if (executablePath) {
log(`fixing permissions at ${executablePath}`);
await fs.promises.chmod(executablePath, 0o755);
}
await fs.promises.writeFile(browserDirectoryToMarkerFilePath(browserDirectory), '');
}
main().catch(error => {