2019-11-18 18:18:28 -08:00
|
|
|
/**
|
|
|
|
|
* Copyright 2017 Google Inc. All rights reserved.
|
|
|
|
|
* Modifications 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-02-11 06:36:15 -08:00
|
|
|
import fs from 'fs';
|
|
|
|
|
import os from 'os';
|
|
|
|
|
import path from 'path';
|
2022-10-19 13:06:35 -07:00
|
|
|
import childProcess from 'child_process';
|
2022-04-07 13:36:13 -08:00
|
|
|
import { getUserAgent } from '../../common/userAgent';
|
2022-04-07 19:18:22 -08:00
|
|
|
import { existsAsync } from '../../utils/fileUtils';
|
2022-04-07 13:36:13 -08:00
|
|
|
import { debugLogger } from '../../common/debugLogger';
|
2022-04-18 16:50:25 -08:00
|
|
|
import { extract } from '../../zipBundle';
|
2022-10-19 13:06:35 -07:00
|
|
|
import { ManualPromise } from '../../utils/manualPromise';
|
2022-04-18 10:31:58 -08:00
|
|
|
|
2022-10-19 13:06:35 -07:00
|
|
|
export async function downloadBrowserWithProgressBar(title: string, browserDirectory: string, executablePath: string, downloadURLs: string[], downloadFileName: string, downloadConnectionTimeout: number): Promise<boolean> {
|
2021-02-08 16:02:49 -08:00
|
|
|
if (await existsAsync(browserDirectory)) {
|
2020-04-24 19:14:10 -07:00
|
|
|
// Already downloaded.
|
2022-05-05 05:17:13 -06:00
|
|
|
debugLogger.log('install', `${title} is already downloaded.`);
|
2020-04-24 19:14:10 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 16:10:23 -07:00
|
|
|
const zipPath = path.join(os.tmpdir(), downloadFileName);
|
2020-03-19 11:43:35 -07:00
|
|
|
try {
|
2022-10-19 13:06:35 -07:00
|
|
|
const retryCount = 3;
|
|
|
|
|
for (let attempt = 1; attempt <= retryCount; ++attempt) {
|
|
|
|
|
debugLogger.log('install', `downloading ${title} - attempt #${attempt}`);
|
|
|
|
|
const url = downloadURLs[(attempt - 1) % downloadURLs.length];
|
|
|
|
|
const { error } = await downloadFileOutOfProcess(url, zipPath, title, getUserAgent(), downloadConnectionTimeout);
|
|
|
|
|
if (!error) {
|
|
|
|
|
debugLogger.log('install', `SUCCESS downloading ${title}`);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const errorMessage = error?.message || '';
|
|
|
|
|
debugLogger.log('install', `attempt #${attempt} - ERROR: ${errorMessage}`);
|
|
|
|
|
if (attempt >= retryCount)
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2021-05-18 17:38:02 -07:00
|
|
|
debugLogger.log('install', `extracting archive`);
|
|
|
|
|
debugLogger.log('install', `-- zip: ${zipPath}`);
|
|
|
|
|
debugLogger.log('install', `-- location: ${browserDirectory}`);
|
2021-09-27 18:58:08 +02:00
|
|
|
await extract(zipPath, { dir: browserDirectory });
|
2021-05-18 17:38:02 -07:00
|
|
|
debugLogger.log('install', `fixing permissions at ${executablePath}`);
|
2021-06-03 09:55:33 -07:00
|
|
|
await fs.promises.chmod(executablePath, 0o755);
|
2020-04-24 19:14:10 -07:00
|
|
|
} catch (e) {
|
2022-05-05 05:17:13 -06:00
|
|
|
debugLogger.log('install', `FAILED installation ${title} with error: ${e}`);
|
2020-04-24 19:14:10 -07:00
|
|
|
process.exitCode = 1;
|
|
|
|
|
throw e;
|
2020-03-19 11:43:35 -07:00
|
|
|
} finally {
|
|
|
|
|
if (await existsAsync(zipPath))
|
2021-06-03 09:55:33 -07:00
|
|
|
await fs.promises.unlink(zipPath);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
2022-05-05 05:17:13 -06:00
|
|
|
logPolitely(`${title} downloaded to ${browserDirectory}`);
|
2020-04-24 19:14:10 -07:00
|
|
|
return true;
|
2020-03-24 00:08:00 -07:00
|
|
|
}
|
|
|
|
|
|
2022-10-19 13:06:35 -07:00
|
|
|
/**
|
|
|
|
|
* Node.js has a bug where the process can exit with 0 code even though there was an uncaught exception.
|
|
|
|
|
* 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, progressBarName: string, userAgent: string, downloadConnectionTimeout: number): Promise<{ error: Error | null }> {
|
|
|
|
|
const cp = childProcess.fork(path.join(__dirname, 'oopDownloadMain.js'), [url, destinationPath, progressBarName, userAgent, String(downloadConnectionTimeout)]);
|
|
|
|
|
const promise = new ManualPromise<{ error: Error | null }>();
|
|
|
|
|
cp.on('message', (message: any) => {
|
|
|
|
|
if (message?.method === 'log')
|
|
|
|
|
debugLogger.log('install', message.params.message);
|
|
|
|
|
});
|
|
|
|
|
cp.on('exit', code => {
|
|
|
|
|
if (code !== 0) {
|
|
|
|
|
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`) });
|
|
|
|
|
else
|
|
|
|
|
promise.resolve({ error: null });
|
|
|
|
|
});
|
|
|
|
|
cp.on('error', error => {
|
|
|
|
|
promise.resolve({ error });
|
|
|
|
|
});
|
|
|
|
|
return promise;
|
|
|
|
|
}
|
2019-11-18 18:18:28 -08:00
|
|
|
|
2020-08-17 16:19:21 -07:00
|
|
|
export function logPolitely(toBeLogged: string) {
|
|
|
|
|
const logLevel = process.env.npm_config_loglevel;
|
|
|
|
|
const logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel || '') > -1;
|
|
|
|
|
|
|
|
|
|
if (!logLevelDisplay)
|
|
|
|
|
console.log(toBeLogged); // eslint-disable-line no-console
|
|
|
|
|
}
|