204 lines · 5.6 KB · JavaScript
RawDownload
import { spawnSync } from "node:child_process";import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";import path from "node:path";import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url);const __dirname = path.dirname(__filename);const packageDir = path.resolve(__dirname, "..");const cacheDir = path.join(packageDir, ".cache", "playwright-system-libs");const browsersCacheDir = path.join(packageDir, ".cache", "ms-playwright");const debDir = path.join(cacheDir, "deb");const extractedDir = path.join(cacheDir, "root");const libraryDirs = [	path.join(extractedDir, "usr", "lib", "x86_64-linux-gnu"),	path.join(extractedDir, "lib", "x86_64-linux-gnu"),];const systemLibraryDirs = [	"/usr/lib/x86_64-linux-gnu",	"/lib/x86_64-linux-gnu",	"/usr/lib",	"/lib",]; const linuxRuntimePackages = [	{ debPrefix: "libatk1.0-0t64_", specs: ["libatk1.0-0t64"] },	{ debPrefix: "libatk-bridge2.0-0t64_", specs: ["libatk-bridge2.0-0t64"] },	{ debPrefix: "libatspi2.0-0t64_", specs: ["libatspi2.0-0t64"] },	{ debPrefix: "libxcomposite1_", specs: ["libxcomposite1"] },	{ debPrefix: "libxdamage1_", specs: ["libxdamage1"] },	{ debPrefix: "libxfixes3_", specs: ["libxfixes3"] },	{ debPrefix: "libxrandr2_", specs: ["libxrandr2"] },	{ debPrefix: "libxkbcommon0_", specs: ["libxkbcommon0"] },	{		debPrefix: "libasound2t64_",		specs: ["libasound2t64", "libasound2t64=1.2.11-1build2"],	},	{		debPrefix: "libgbm1_",		specs: ["libgbm1", "libgbm1=24.0.5-1ubuntu1"],	},	{		debPrefix: "libdrm2_",		specs: ["libdrm2", "libdrm2=2.4.120-2build1"],	},	{ debPrefix: "libwayland-server0_", specs: ["libwayland-server0"] },	{ debPrefix: "libxcb-randr0_", specs: ["libxcb-randr0"] },	{ debPrefix: "libxi6_", specs: ["libxi6"] },]; const requiredLibraries = [	"libatk-1.0.so.0",	"libatk-bridge-2.0.so.0",	"libatspi.so.0",	"libXcomposite.so.1",	"libXdamage.so.1",	"libXfixes.so.3",	"libXrandr.so.2",	"libasound.so.2",	"libgbm.so.1",	"libxkbcommon.so.0",	"libdrm.so.2",	"libwayland-server.so.0",	"libxcb-randr.so.0",	"libXi.so.6",]; function run(command, args, options = {}) {	const result = spawnSync(command, args, {		cwd: options.cwd ?? packageDir,		env: options.env ?? process.env,		stdio: options.stdio ?? "inherit",		encoding: "utf8",	});	if (result.status !== 0) {		const commandLine = [command, ...args].join(" ");		const error = new Error(`Command failed (${result.status}): ${commandLine}`);		error.stdout = result.stdout ?? "";		error.stderr = result.stderr ?? "";		throw error;	}	return result;} function tryRun(command, args, options = {}) {	return spawnSync(command, args, {		cwd: options.cwd ?? packageDir,		env: options.env ?? process.env,		stdio: options.stdio ?? "pipe",		encoding: "utf8",	});} function libraryPresentInDirs(name, dirs) {	return dirs.some((dir) => existsSync(path.join(dir, name)));} function cachedLibraryPresent(name) {	return libraryPresentInDirs(name, libraryDirs);} function systemLibraryPresent(name) {	return libraryPresentInDirs(name, systemLibraryDirs);} function headlessShellPresent() {	if (!existsSync(browsersCacheDir)) {		return false;	} 	for (const entry of readdirSync(browsersCacheDir)) {		if (!entry.startsWith("chromium_headless_shell-")) {			continue;		} 		const executablePath = path.join(			browsersCacheDir,			entry,			"chrome-headless-shell-linux64",			"chrome-headless-shell",		);		if (existsSync(executablePath)) {			return true;		}	} 	return false;} function ensurePlaywrightBrowser(env) {	if (headlessShellPresent()) {		return;	} 	mkdirSync(browsersCacheDir, { recursive: true });	run("pnpm", ["exec", "playwright", "install", "--only-shell", "chromium"], {		env,	});} function ensureBrowserRuntimeLibraries() {	if (process.platform !== "linux") {		return [];	}	if (requiredLibraries.every(systemLibraryPresent)) {		return [];	}	if (requiredLibraries.every(cachedLibraryPresent)) {		return libraryDirs.filter(existsSync);	} 	mkdirSync(debDir, { recursive: true });	rmSync(extractedDir, { recursive: true, force: true });	mkdirSync(extractedDir, { recursive: true }); 	for (const pkg of linuxRuntimePackages) {		let debFile = readdirSync(debDir).find((entry) => entry.startsWith(pkg.debPrefix) && entry.endsWith(".deb"));		if (!debFile) {			let lastFailure = null;			for (const spec of pkg.specs) {				const result = tryRun("apt-get", ["download", spec], { cwd: debDir });				if (result.status === 0) {					debFile = readdirSync(debDir).find(						(entry) => entry.startsWith(pkg.debPrefix) && entry.endsWith(".deb"),					);					lastFailure = null;					break;				}				lastFailure = result;			}			if (!debFile) {				const failureText = lastFailure					? [lastFailure.stdout, lastFailure.stderr].filter(Boolean).join("\n")					: "unknown apt-get failure";				throw new Error(`Unable to download ${pkg.debPrefix}: ${failureText}`.trim());			}		}		run("dpkg-deb", ["-x", path.join(debDir, debFile), extractedDir], { stdio: "inherit" });	} 	const missing = requiredLibraries.filter((library) => !cachedLibraryPresent(library));	if (missing.length > 0) {		throw new Error(`Missing extracted browser runtime libraries: ${missing.join(", ")}`);	} 	return libraryDirs.filter(existsSync);} function main() {	const extraLibraryDirs = ensureBrowserRuntimeLibraries();	const env = {		...process.env,		PLAYWRIGHT_BROWSERS_PATH: browsersCacheDir,		LD_LIBRARY_PATH: [...extraLibraryDirs, process.env.LD_LIBRARY_PATH]			.filter(Boolean)			.join(":"),	}; 	ensurePlaywrightBrowser(env);	run("pnpm", ["exec", "playwright", "test", "--project=chromium", "--workers=1"], {		env,	});} main();