fix(cli): the ignored-binary note names only the missing binary, and covers .hyperframes/bin

This commit is contained in:
Miguel Angel Simon Sierra
2026-10-29 19:20:56 -04:00
parent dbc72de868
commit 68550f635b
3 changed files with 44 additions and 13 deletions
+7 -3
View File
@@ -96,9 +96,13 @@ export function getFFmpegInstallCommand(): string | undefined {
}
export function getFFmpegInstallHint(): string {
const skipped = getSkippedX86Binary();
const base = installHint();
return skipped ? `${base} (ignored ${skipped}: x86_64 only, it cannot run on this Mac)` : base;
// Only a binary that is still missing: one that was found needs no explanation.
const ignored = (["ffmpeg", "ffprobe"] as const)
.filter((name) => !findFfBinary(name, { configuredMustExist: true }))
.map(getSkippedX86Binary)
.filter((path) => path !== undefined);
const note = ignored.map((path) => ` (ignored ${path}: x86_64 only, it cannot run on this Mac)`);
return installHint() + note.join("");
}
function installHint(): string {
+28 -2
View File
@@ -210,11 +210,14 @@ describe("findFfBinary", () => {
return { project, binary };
}
let skipped: { ffmpeg?: string; ffprobe?: string } = {};
async function findInProjectDir(
project: string,
name: "ffmpeg" | "ffprobe",
systemBinaries: string[] = [],
) {
skipped = {};
vi.stubEnv("HYPERFRAMES_FFMPEG_PATH", "");
vi.stubEnv("HYPERFRAMES_FFPROBE_PATH", "");
process.env.PATH = "";
@@ -236,8 +239,10 @@ describe("findFfBinary", () => {
return { ...mocked, default: mocked };
});
try {
const { findFfBinary } = await importFresh();
return findFfBinary(name);
const { findFfBinary, getSkippedX86Binary } = await importFresh();
const found = findFfBinary(name);
skipped = { ffmpeg: getSkippedX86Binary("ffmpeg"), ffprobe: getSkippedX86Binary("ffprobe") };
return found;
} finally {
vi.unstubAllEnvs();
rmSync(project, { recursive: true, force: true });
@@ -294,6 +299,27 @@ describe("findFfBinary", () => {
expect(await findInProjectDir(project, "ffprobe")).toBeUndefined();
});
it("names the skipped binary under its own name only", async () => {
asAppleSilicon();
const { project, binary } = installerProject("ffprobe", { content: machO(0x07) });
await findInProjectDir(project, "ffprobe");
expect(skipped).toEqual({ ffprobe: binary });
});
it("skips an x86_64 binary in the project's .hyperframes/bin", async () => {
asAppleSilicon();
const project = realpathSync(mkdtempSync(join(tmpdir(), "hf-ff-local-")));
mkdirSync(join(project, ".hyperframes", "bin"), { recursive: true });
const binary = join(project, ".hyperframes", "bin", "ffprobe");
writeFileSync(binary, machO(0x07));
chmodSync(binary, 0o755);
expect(await findInProjectDir(project, "ffprobe")).toBeUndefined();
expect(skipped.ffprobe).toBe(binary);
});
it("keeps an arm64 installer binary", async () => {
asAppleSilicon();
const { project, binary } = installerProject("ffprobe", { content: machO(0x0c) });
+9 -8
View File
@@ -1,7 +1,7 @@
import { execFileSync } from "node:child_process";
import { accessSync, closeSync, constants, existsSync, openSync, readSync } from "node:fs";
import { createRequire } from "node:module";
import { delimiter, dirname, join, resolve } from "node:path";
import { basename, delimiter, dirname, join, resolve } from "node:path";
/**
* Shared FFmpeg/FFprobe binary resolution for every package that shells out
@@ -67,15 +67,16 @@ function runsOnThisMac(candidate: string): boolean {
return true;
}
if (!isX86OnlyMachO(candidate)) return true;
skippedX86Binary ??= candidate;
const name = basename(candidate) as FfBinaryName;
if (!skippedX86Binaries.has(name)) skippedX86Binaries.set(name, candidate);
return false;
}
let skippedX86Binary: string | undefined;
const skippedX86Binaries = new Map<FfBinaryName, string>();
/** The first x86_64-only binary skipped on this Mac, for naming in the "not found" hint. */
export function getSkippedX86Binary(): string | undefined {
return skippedX86Binary;
/** The first x86_64-only `name` binary skipped on this Mac, for naming in the "not found" hint. */
export function getSkippedX86Binary(name: FfBinaryName): string | undefined {
return skippedX86Binaries.get(name);
}
function isExecutablePathCandidate(candidate: string): boolean {
@@ -143,7 +144,7 @@ function binaryFileName(name: FfBinaryName): string {
function findInProjectLocalBin(name: FfBinaryName): string | undefined {
const candidate = resolve(".hyperframes", "bin", binaryFileName(name));
return existsSync(candidate) ? candidate : undefined;
return existsSync(candidate) && runsOnThisMac(candidate) ? candidate : undefined;
}
function tryResolve(fromFile: string, request: string): string | undefined {
@@ -242,5 +243,5 @@ export function findFfBinary(
export function clearFfBinaryLookupCache(): void {
pathLookupCache.clear();
lastMissAt.clear();
skippedX86Binary = undefined;
skippedX86Binaries.clear();
}