importassert from 'node:assert'; import {spawnSync} from 'node:child_process'; import {existsSync, readFileSync} from 'node:fs'; import {mkdir, unlink} from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path';
import type * as ProgressBar from 'progress'; import ProgressBarClass from 'progress';
import {
Browser,
BrowserPlatform,
downloadUrls,
} from './browser-data/browser-data.js'; import {Cache, InstalledBrowser} from './Cache.js'; import {debug} from './debug.js'; import {detectBrowserPlatform} from './detectPlatform.js'; import {unpackArchive} from './fileUtil.js'; import {downloadFile, getJSON, headHttpRequest} from './httpUtil.js';
/** *Downloadsandunpacksthebrowserarchiveaccordingtothe *{@linkInstallOptions}. * *@returnsa{@linkInstalledBrowser}instance. * *@public
*/
export function install(
options: InstallOptions & {unpack?: true},
): Promise<InstalledBrowser>; /** *Downloadsthebrowserarchiveaccordingtothe{@linkInstallOptions}without *unpacking. * *@returnstheabsolutepathtothearchive. * *@public
*/
export function install(
options: InstallOptions & {unpack: false},
): Promise<string>;
export async function install(
options: InstallOptions,
): Promise<InstalledBrowser | string> {
options.platform ??= detectBrowserPlatform();
options.unpack ??= true; if (!options.platform) { thrownew Error(
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`,
);
} const url = getDownloadUrl(
options.browser,
options.platform,
options.buildId,
options.baseUrl,
); try { return await installUrl(url, options);
} catch (err) { // If custom baseUrl is provided, do not fall back to CfT dashboard. if (options.baseUrl && !options.forceFallbackForTesting) { throw err;
}
debugInstall(`Error downloading from ${url}.`); switch (options.browser) { case Browser.CHROME: case Browser.CHROMEDRIVER: case Browser.CHROMEHEADLESSSHELL: {
debugInstall(
`Trying to find download URL via https://googlechromelabs.github.io/chrome-for-testing.`,
); interface Version {
downloads: Record<string, Array<{platform: string; url: string}>>;
} const version = (await getJSON( new URL(
`https://googlechromelabs.github.io/chrome-for-testing/${options.buildId}.json`,
),
)) as Version;
let platform = ''; switch (options.platform) { case BrowserPlatform.LINUX:
platform = 'linux64'; break; case BrowserPlatform.MAC_ARM:
platform = 'mac-arm64'; break; case BrowserPlatform.MAC:
platform = 'mac-x64'; break; case BrowserPlatform.WIN32:
platform = 'win32'; break; case BrowserPlatform.WIN64:
platform = 'win64'; break;
} const backupUrl = version.downloads[options.browser]?.find(link => { return link['platform'] === platform;
})?.url; if (backupUrl) { // If the URL is the same, skip the retry. if (backupUrl === url.toString()) { throw err;
}
debugInstall(`Falling back to downloading from ${backupUrl}.`); return await installUrl(new URL(backupUrl), options);
} throw err;
} default: throw err;
}
}
}
async function installDeps(installedBrowser: InstalledBrowser) { if (
process.platform !== 'linux' ||
installedBrowser.platform !== BrowserPlatform.LINUX
) { return;
} // Currently, only Debian-like deps are supported. const depsPath = path.join(
path.dirname(installedBrowser.executablePath), 'deb.deps',
); if (!existsSync(depsPath)) {
debugInstall(`deb.deps file was not found at ${depsPath}`); return;
} const data = readFileSync(depsPath, 'utf-8').split('\n').join(','); if (process.getuid?.() !== 0) { thrownew Error('Installing system dependencies requires root privileges');
}
let result = spawnSync('apt-get', ['-v']); if (result.status !== 0) { thrownew Error( 'Failed to install system dependencies: apt-get does not seem to be available',
);
}
debugInstall(`Trying to install dependencies: ${data}`);
result = spawnSync('apt-get', [ 'satisfy', '-y',
data, '--no-install-recommends',
]); if (result.status !== 0) { thrownew Error(
`Failed to install system dependencies: status=${result.status},error=${result.error},stdout=${result.stdout.toString('utf8')},stderr=${result.stderr.toString('utf8')}`,
);
}
debugInstall(`Installed system dependencies ${data}`);
}
async function installUrl(
url: URL,
options: InstallOptions,
): Promise<InstalledBrowser | string> {
options.platform ??= detectBrowserPlatform(); if (!options.platform) { thrownew Error(
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`,
);
}
let downloadProgressCallback = options.downloadProgressCallback; if (downloadProgressCallback === 'default') {
downloadProgressCallback = await makeProgressCallback(
options.browser,
options.buildIdAlias ?? options.buildId,
);
} const fileName = decodeURIComponent(url.toString()).split('/').pop(); assert(fileName, `A malformed download URL was found: ${url}.`); const cache = new Cache(options.cacheDir); const browserRoot = cache.browserRoot(options.browser); const archivePath = path.join(browserRoot, `${options.buildId}-${fileName}`); if (!existsSync(browserRoot)) {
await mkdir(browserRoot, {recursive: true});
}
if (!options.unpack) { if (existsSync(archivePath)) { return archivePath;
}
debugInstall(`Downloading binary from ${url}`);
debugTime('download');
await downloadFile(url, archivePath, downloadProgressCallback);
debugTimeEnd('download'); return archivePath;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.14Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.