fix(cli): the Windows Chrome check reads the file version, not chrome.exe --version (#6459)

* fix(cli): the Windows Chrome check launches Chrome instead of trusting chrome.exe --version

* refactor(cli): shorten the Windows Chrome version comment to fit the comment ratchet

* test(cli): pin the platform in the Unix Chrome preflight tests so Windows runs them as written

* fix(cli): the Windows Chrome check reads the file version, not chrome.exe --version
This commit is contained in:
Miguel Ángel
2026-10-26 15:39:09 -04:00
committed by GitHub
parent 74905aeff6
commit 8f618c5ffe
2 changed files with 78 additions and 0 deletions
@@ -34,7 +34,11 @@ describe("runEnvironmentChecks", () => {
const originalFfmpegPath = process.env.HYPERFRAMES_FFMPEG_PATH;
const originalFfprobePath = process.env.HYPERFRAMES_FFPROBE_PATH;
const realPlatform = process.platform;
beforeEach(() => {
// The `--version` tests below describe the Unix path; the Windows block sets its own platform.
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
process.env.HYPERFRAMES_FFMPEG_PATH = process.execPath;
process.env.HYPERFRAMES_FFPROBE_PATH = process.execPath;
runProcess.mockReset();
@@ -42,6 +46,7 @@ describe("runEnvironmentChecks", () => {
});
afterEach(() => {
Object.defineProperty(process, "platform", { value: realPlatform, configurable: true });
if (originalFfmpegPath === undefined) delete process.env.HYPERFRAMES_FFMPEG_PATH;
else process.env.HYPERFRAMES_FFMPEG_PATH = originalFfmpegPath;
if (originalFfprobePath === undefined) delete process.env.HYPERFRAMES_FFPROBE_PATH;
@@ -211,6 +216,48 @@ describe("runEnvironmentChecks", () => {
expect(result.browserInstall).toMatchObject({ pathAscii: true });
});
describe("on Windows", () => {
beforeEach(() => {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
});
it("reads Chrome's version from the file, never from a `--version` run that exits 21", async () => {
runProcess.mockImplementation((binaryPath: string, args: string[]) => {
if (binaryPath === process.execPath && args[0] === "--version")
throw Object.assign(new Error("exit 21"), { status: 21 });
if (binaryPath === "powershell.exe")
return Promise.resolve({ stdout: "131.0.6778.85\r\n", stderr: "" });
return Promise.resolve({ stdout: "ffmpeg version 7.1.1\n", stderr: "" });
});
const result = await runEnvironmentChecks({
includeBrowser: true,
browserPath: process.execPath,
});
expect(result.outcomes.find((outcome) => outcome.name === "Chrome")).toMatchObject({
ok: true,
versionMajor: 131,
});
});
it("still reports Chrome usable when its version cannot be read", async () => {
runProcess.mockImplementation((binaryPath: string) => {
if (binaryPath === "powershell.exe") throw new Error("powershell unavailable");
return Promise.resolve({ stdout: "ffmpeg version 7.1.1\n", stderr: "" });
});
const result = await runEnvironmentChecks({
includeBrowser: true,
browserPath: process.execPath,
});
const chrome = result.outcomes.find((outcome) => outcome.name === "Chrome");
expect(chrome?.ok).toBe(true);
expect(chrome?.versionMajor).toBeUndefined();
});
});
it("reports Chrome as not found (no throw) when browser discovery throws on a corrupt cache", async () => {
const spy = vi.spyOn(manager, "findBrowser").mockRejectedValue(
Object.assign(new Error("ENOTDIR: not a directory, scandir 'chrome-headless-shell'"), {
+31
View File
@@ -255,6 +255,34 @@ function chromeLaunchFailureDetails(error: unknown): string {
.join(", ");
}
// Windows chrome.exe prints nothing for --version and can exit 21 while rendering fine: read the file's version.
async function windowsChromeVersionMajor(
executablePath: string,
signal?: AbortSignal,
): Promise<number | undefined> {
try {
const { stdout } = await runCancellableProcess(
"powershell.exe",
[
"-NoProfile",
"-NonInteractive",
"-Command",
"(Get-Item -LiteralPath $env:HF_CHROME).VersionInfo.ProductVersion",
],
{
signal,
timeoutMs: 10_000,
maxBufferBytes: 4 * 1024,
env: { ...process.env, HF_CHROME: executablePath },
},
);
return extractMajorVersion(stdout);
} catch {
if (signal?.aborted) signal.throwIfAborted();
return undefined;
}
}
async function chromeLaunchOutcome(
executablePath: string,
found: EnvironmentCheckOutcome,
@@ -262,6 +290,9 @@ async function chromeLaunchOutcome(
): Promise<EnvironmentCheckOutcome> {
const libraries = await chromeSharedLibOutcome(executablePath, found, signal);
if (!libraries.ok) return libraries;
if (process.platform === "win32") {
return { ...found, versionMajor: await windowsChromeVersionMajor(executablePath, signal) };
}
try {
const { stdout } = await runCancellableProcess(executablePath, ["--version"], {
signal,