diff --git a/packages/playwright-core/src/server/registry/browserFetcher.ts b/packages/playwright-core/src/server/registry/browserFetcher.ts index 5ab80f472c..23f924dc7a 100644 --- a/packages/playwright-core/src/server/registry/browserFetcher.ts +++ b/packages/playwright-core/src/server/registry/browserFetcher.ts @@ -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 { - 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 }); }); diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 18f2992bd9..3ebab52660 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -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'); } diff --git a/packages/playwright-core/src/server/registry/oopDownloadMain.ts b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts similarity index 85% rename from packages/playwright-core/src/server/registry/oopDownloadMain.ts rename to packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts index 013fbbe18d..3ff5db3661 100644 --- a/packages/playwright-core/src/server/registry/oopDownloadMain.ts +++ b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts @@ -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 => {