2020-06-29 15:13:24 +00:00
|
|
|
/**
|
|
|
|
* Copyright 2020 Google Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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-09-15 19:41:03 +00:00
|
|
|
import https, { RequestOptions } from 'https';
|
2020-06-29 15:13:24 +00:00
|
|
|
import ProgressBar from 'progress';
|
2021-09-15 19:41:03 +00:00
|
|
|
import URL from 'url';
|
2020-10-13 15:19:26 +00:00
|
|
|
import puppeteer from '../node.js';
|
2020-10-12 06:56:29 +00:00
|
|
|
import { PUPPETEER_REVISIONS } from '../revisions.js';
|
2020-10-13 15:19:26 +00:00
|
|
|
import { PuppeteerNode } from './Puppeteer.js';
|
2021-09-15 19:41:03 +00:00
|
|
|
import createHttpsProxyAgent, {
|
|
|
|
HttpsProxyAgentOptions,
|
|
|
|
} from 'https-proxy-agent';
|
|
|
|
import { getProxyForUrl } from 'proxy-from-env';
|
2020-06-29 15:13:24 +00:00
|
|
|
|
|
|
|
const supportedProducts = {
|
|
|
|
chrome: 'Chromium',
|
|
|
|
firefox: 'Firefox Nightly',
|
|
|
|
} as const;
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
function getProduct(input: string): 'chrome' | 'firefox' {
|
|
|
|
if (input !== 'chrome' && input !== 'firefox') {
|
|
|
|
throw new Error(`Unsupported product ${input}`);
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
2021-05-12 16:43:05 +00:00
|
|
|
export async function downloadBrowser(): Promise<void> {
|
2020-06-29 15:13:24 +00:00
|
|
|
const downloadHost =
|
|
|
|
process.env.PUPPETEER_DOWNLOAD_HOST ||
|
|
|
|
process.env.npm_config_puppeteer_download_host ||
|
|
|
|
process.env.npm_package_config_puppeteer_download_host;
|
2022-01-28 09:38:36 +00:00
|
|
|
const product = getProduct(
|
2020-06-29 15:13:24 +00:00
|
|
|
process.env.PUPPETEER_PRODUCT ||
|
2022-01-28 09:38:36 +00:00
|
|
|
process.env.npm_config_puppeteer_product ||
|
|
|
|
process.env.npm_package_config_puppeteer_product ||
|
|
|
|
'chrome'
|
|
|
|
);
|
2020-08-10 08:37:31 +00:00
|
|
|
const downloadPath =
|
|
|
|
process.env.PUPPETEER_DOWNLOAD_PATH ||
|
|
|
|
process.env.npm_config_puppeteer_download_path ||
|
|
|
|
process.env.npm_package_config_puppeteer_download_path;
|
2020-10-13 15:19:26 +00:00
|
|
|
const browserFetcher = (puppeteer as PuppeteerNode).createBrowserFetcher({
|
2020-06-29 15:13:24 +00:00
|
|
|
product,
|
|
|
|
host: downloadHost,
|
2020-08-10 08:37:31 +00:00
|
|
|
path: downloadPath,
|
2020-06-29 15:13:24 +00:00
|
|
|
});
|
|
|
|
const revision = await getRevision();
|
|
|
|
await fetchBinary(revision);
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
async function getRevision(): Promise<string> {
|
2020-06-29 15:13:24 +00:00
|
|
|
if (product === 'chrome') {
|
|
|
|
return (
|
|
|
|
process.env.PUPPETEER_CHROMIUM_REVISION ||
|
|
|
|
process.env.npm_config_puppeteer_chromium_revision ||
|
|
|
|
PUPPETEER_REVISIONS.chromium
|
|
|
|
);
|
|
|
|
} else if (product === 'firefox') {
|
2020-10-13 15:19:26 +00:00
|
|
|
(puppeteer as PuppeteerNode)._preferredRevision =
|
|
|
|
PUPPETEER_REVISIONS.firefox;
|
2020-08-10 08:22:31 +00:00
|
|
|
return getFirefoxNightlyVersion().catch((error) => {
|
2020-06-29 15:13:24 +00:00
|
|
|
console.error(error);
|
|
|
|
process.exit(1);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw new Error(`Unsupported product ${product}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
function fetchBinary(revision: string) {
|
2020-06-29 15:13:24 +00:00
|
|
|
const revisionInfo = browserFetcher.revisionInfo(revision);
|
|
|
|
|
|
|
|
// Do nothing if the revision is already downloaded.
|
|
|
|
if (revisionInfo.local) {
|
|
|
|
logPolitely(
|
|
|
|
`${supportedProducts[product]} is already in ${revisionInfo.folderPath}; skipping download.`
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Override current environment proxy settings with npm configuration, if any.
|
|
|
|
const NPM_HTTPS_PROXY =
|
|
|
|
process.env.npm_config_https_proxy || process.env.npm_config_proxy;
|
|
|
|
const NPM_HTTP_PROXY =
|
|
|
|
process.env.npm_config_http_proxy || process.env.npm_config_proxy;
|
|
|
|
const NPM_NO_PROXY = process.env.npm_config_no_proxy;
|
|
|
|
|
|
|
|
if (NPM_HTTPS_PROXY) process.env.HTTPS_PROXY = NPM_HTTPS_PROXY;
|
|
|
|
if (NPM_HTTP_PROXY) process.env.HTTP_PROXY = NPM_HTTP_PROXY;
|
|
|
|
if (NPM_NO_PROXY) process.env.NO_PROXY = NPM_NO_PROXY;
|
|
|
|
|
|
|
|
function onSuccess(localRevisions: string[]): void {
|
2022-05-05 07:33:19 +00:00
|
|
|
logPolitely(
|
|
|
|
`${supportedProducts[product]} (${revisionInfo.revision}) downloaded to ${revisionInfo.folderPath}`
|
|
|
|
);
|
2020-06-29 15:13:24 +00:00
|
|
|
localRevisions = localRevisions.filter(
|
|
|
|
(revision) => revision !== revisionInfo.revision
|
|
|
|
);
|
|
|
|
const cleanupOldVersions = localRevisions.map((revision) =>
|
|
|
|
browserFetcher.remove(revision)
|
|
|
|
);
|
|
|
|
Promise.all([...cleanupOldVersions]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function onError(error: Error) {
|
|
|
|
console.error(
|
|
|
|
`ERROR: Failed to set up ${supportedProducts[product]} r${revision}! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to skip download.`
|
|
|
|
);
|
|
|
|
console.error(error);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
let progressBar: ProgressBar | null = null;
|
2020-06-29 15:13:24 +00:00
|
|
|
let lastDownloadedBytes = 0;
|
2022-01-28 09:38:36 +00:00
|
|
|
function onProgress(downloadedBytes: number, totalBytes: number) {
|
2020-06-29 15:13:24 +00:00
|
|
|
if (!progressBar) {
|
|
|
|
progressBar = new ProgressBar(
|
|
|
|
`Downloading ${
|
|
|
|
supportedProducts[product]
|
|
|
|
} r${revision} - ${toMegabytes(totalBytes)} [:bar] :percent :etas `,
|
|
|
|
{
|
|
|
|
complete: '=',
|
|
|
|
incomplete: ' ',
|
|
|
|
width: 20,
|
|
|
|
total: totalBytes,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const delta = downloadedBytes - lastDownloadedBytes;
|
|
|
|
lastDownloadedBytes = downloadedBytes;
|
|
|
|
progressBar.tick(delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
return browserFetcher
|
|
|
|
.download(revisionInfo.revision, onProgress)
|
|
|
|
.then(() => browserFetcher.localRevisions())
|
|
|
|
.then(onSuccess)
|
|
|
|
.catch(onError);
|
|
|
|
}
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
function toMegabytes(bytes: number) {
|
2020-06-29 15:13:24 +00:00
|
|
|
const mb = bytes / 1024 / 1024;
|
|
|
|
return `${Math.round(mb * 10) / 10} Mb`;
|
|
|
|
}
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
async function getFirefoxNightlyVersion(): Promise<string> {
|
2021-09-15 19:41:03 +00:00
|
|
|
const firefoxVersionsUrl =
|
2020-08-10 08:22:31 +00:00
|
|
|
'https://product-details.mozilla.org/1.0/firefox_versions.json';
|
|
|
|
|
2021-09-15 19:41:03 +00:00
|
|
|
const proxyURL = getProxyForUrl(firefoxVersionsUrl);
|
|
|
|
|
|
|
|
const requestOptions: RequestOptions = {};
|
|
|
|
|
|
|
|
if (proxyURL) {
|
|
|
|
const parsedProxyURL = URL.parse(proxyURL);
|
|
|
|
|
|
|
|
const proxyOptions = {
|
|
|
|
...parsedProxyURL,
|
|
|
|
secureProxy: parsedProxyURL.protocol === 'https:',
|
|
|
|
} as HttpsProxyAgentOptions;
|
|
|
|
|
|
|
|
requestOptions.agent = createHttpsProxyAgent(proxyOptions);
|
|
|
|
requestOptions.rejectUnauthorized = false;
|
|
|
|
}
|
|
|
|
|
2022-01-28 09:38:36 +00:00
|
|
|
const promise = new Promise<string>((resolve, reject) => {
|
2020-06-29 15:13:24 +00:00
|
|
|
let data = '';
|
2020-08-10 08:22:31 +00:00
|
|
|
logPolitely(
|
2021-09-15 19:41:03 +00:00
|
|
|
`Requesting latest Firefox Nightly version from ${firefoxVersionsUrl}`
|
2020-08-10 08:22:31 +00:00
|
|
|
);
|
2020-06-29 15:13:24 +00:00
|
|
|
https
|
2021-09-15 19:41:03 +00:00
|
|
|
.get(firefoxVersionsUrl, requestOptions, (r) => {
|
2022-01-28 09:38:36 +00:00
|
|
|
if (r.statusCode && r.statusCode >= 400)
|
2020-06-29 15:13:24 +00:00
|
|
|
return reject(new Error(`Got status code ${r.statusCode}`));
|
|
|
|
r.on('data', (chunk) => {
|
|
|
|
data += chunk;
|
|
|
|
});
|
|
|
|
r.on('end', () => {
|
|
|
|
try {
|
|
|
|
const versions = JSON.parse(data);
|
|
|
|
return resolve(versions.FIREFOX_NIGHTLY);
|
|
|
|
} catch {
|
|
|
|
return reject(new Error('Firefox version not found'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.on('error', reject);
|
|
|
|
});
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-12 16:43:05 +00:00
|
|
|
export function logPolitely(toBeLogged: unknown): void {
|
2022-01-28 09:38:36 +00:00
|
|
|
const logLevel = process.env.npm_config_loglevel || '';
|
2020-06-29 15:13:24 +00:00
|
|
|
const logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel) > -1;
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
if (!logLevelDisplay) console.log(toBeLogged);
|
|
|
|
}
|