mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(cli): honor PRODUCER_HEADLESS_SHELL_PATH in findFromEnv
`hyperframes render` picks up `PRODUCER_HEADLESS_SHELL_PATH` (engine per-worker launches read it directly, and `render.ts` even propagates the CLI-resolved executable path into it as a courtesy). But `hyperframes check` / `snapshot` / `compare` / `grade-compare` all route through `openSettledCompositionPage` → `ensureBrowser` → `findFromEnv`, and `findFromEnv` only knew the CLI-native name `HYPERFRAMES_BROWSER_PATH`. Field report — #hyperframes-cli-feedback ts=1784095034 (win32/x64, CLI 0.7.58): the cached `chrome-headless-shell 152.0.7928.2` crashed with `Failed to launch the browser process: Code: 3221225595` (`STATUS_STACK_BUFFER_OVERRUN`). Setting `PRODUCER_HEADLESS_SHELL_PATH` to system Chrome unblocked `render`, but `check` still crashed on the broken cached shell because it never read that env var. Docs and deployment manifests (`skills/hyperframes-animation/adapters/ typegpu.md`, `packages/gcp-cloud-run/Dockerfile`, `examples/k8s-jobs/ Dockerfile.example`) all instruct users to set `PRODUCER_HEADLESS_SHELL_PATH`, so the escape hatch is documentation-blessed but was silently half-implemented on the CLI side. Alias it in `findFromEnv`. Tiebreak matches `render.ts:1479` — `HYPERFRAMES_BROWSER_PATH` wins when both are set. This is the CLI side of the symmetry #2459 is closing on the engine (engine gaining `HYPERFRAMES_BROWSER_PATH` honoring); the two make the alias coherent both directions. - Sibling to #2443 (surfaces `HYPERFRAMES_BROWSER_PATH` on download failures). - Not the same class as #2040 (arm64 pin), #2078 (SIGTRAP), or #2082 (launch crash rewrap) — those are download / launch fixes; this is the env-var alias gap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c54bf13fb4
commit
72d30cda8d
@@ -171,6 +171,7 @@ describe("findBrowser — cache resolution", () => {
|
||||
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
|
||||
Object.defineProperty(process, "arch", { value: "x64", configurable: true });
|
||||
delete process.env["HYPERFRAMES_BROWSER_PATH"];
|
||||
delete process.env["PRODUCER_HEADLESS_SHELL_PATH"];
|
||||
installChildProcessMocks();
|
||||
});
|
||||
|
||||
@@ -593,6 +594,54 @@ describe("findBrowser — cache resolution", () => {
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Sibling env-var alias for the CLI resolver. The engine layer already
|
||||
// honors `PRODUCER_HEADLESS_SHELL_PATH` (see
|
||||
// `packages/engine/src/services/browserManager.ts`), and docs
|
||||
// (skills/hyperframes-animation/adapters/typegpu.md,
|
||||
// packages/gcp-cloud-run/Dockerfile, examples/k8s-jobs/Dockerfile.example)
|
||||
// all instruct users to set that name. Before this alias, `hyperframes
|
||||
// check`/`snapshot`/`compare` — which all route through `openSettledCompositionPage`
|
||||
// → `ensureBrowser` → `findFromEnv` — silently ignored a documented escape
|
||||
// hatch that `render` had honored, so a user with a broken pinned build
|
||||
// (win32/x64 STATUS_STACK_BUFFER_OVERRUN 3221225595, #hyperframes-cli-feedback
|
||||
// ts=1784095034) could render successfully but check would still crash on
|
||||
// the cached headless-shell. The alias closes that direction of the
|
||||
// symmetry (the engine side is being closed by #2459).
|
||||
it("resolves via PRODUCER_HEADLESS_SHELL_PATH when HYPERFRAMES_BROWSER_PATH is unset", async () => {
|
||||
const directShell = "/opt/chrome-headless-shell/chrome-headless-shell";
|
||||
installFsMocks({ existing: new Set([directShell]) });
|
||||
installPuppeteerBrowsersMock();
|
||||
process.env["PRODUCER_HEADLESS_SHELL_PATH"] = directShell;
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
const { findBrowser, _resetSystemFallbackWarnForTests } = await import("./manager.js");
|
||||
_resetSystemFallbackWarnForTests();
|
||||
const result = await findBrowser();
|
||||
|
||||
expect(result?.executablePath).toBe(directShell);
|
||||
expect(result?.source).toBe("env");
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("prefers HYPERFRAMES_BROWSER_PATH over PRODUCER_HEADLESS_SHELL_PATH when both are set", async () => {
|
||||
// Tiebreak matches `render.ts` — the CLI-native name canonicalizes; the
|
||||
// engine name is a compatibility alias. If both are set the caller almost
|
||||
// certainly meant the CLI-native one.
|
||||
const hfPath = "/opt/hf/chrome-headless-shell";
|
||||
const producerPath = "/opt/producer/chrome-headless-shell";
|
||||
installFsMocks({ existing: new Set([hfPath, producerPath]) });
|
||||
installPuppeteerBrowsersMock();
|
||||
process.env["HYPERFRAMES_BROWSER_PATH"] = hfPath;
|
||||
process.env["PRODUCER_HEADLESS_SHELL_PATH"] = producerPath;
|
||||
|
||||
const { findBrowser, _resetSystemFallbackWarnForTests } = await import("./manager.js");
|
||||
_resetSystemFallbackWarnForTests();
|
||||
const result = await findBrowser();
|
||||
|
||||
expect(result?.executablePath).toBe(hfPath);
|
||||
expect(result?.source).toBe("env");
|
||||
});
|
||||
|
||||
it("does NOT warn on macOS when falling back to system Chrome", async () => {
|
||||
// macOS Chrome still works fine for the screenshot path and the perf
|
||||
// claims around BeginFrame are Linux-only — keep the warning Linux-scoped
|
||||
@@ -716,6 +765,7 @@ describe("downloadBrowser — install failure surfaces HYPERFRAMES_BROWSER_PATH
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
delete process.env["HYPERFRAMES_BROWSER_PATH"];
|
||||
delete process.env["PRODUCER_HEADLESS_SHELL_PATH"];
|
||||
installChildProcessMocks();
|
||||
});
|
||||
|
||||
|
||||
@@ -250,8 +250,26 @@ function whichBinary(name: string): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
// Env-var aliases for a caller-supplied browser executable path. The CLI-native
|
||||
// name `HYPERFRAMES_BROWSER_PATH` is the canonical spelling (documented via the
|
||||
// download-failure hint added in #2443). `PRODUCER_HEADLESS_SHELL_PATH` is the
|
||||
// engine-side name that per-worker render launches already honor (see
|
||||
// `packages/engine/src/services/browserManager.ts` and `render.ts` which even
|
||||
// propagates the CLI-resolved executable into it). Docs in
|
||||
// `skills/hyperframes-animation/adapters/typegpu.md`,
|
||||
// `packages/gcp-cloud-run/Dockerfile`, and `examples/k8s-jobs/Dockerfile.example`
|
||||
// all instruct users to set `PRODUCER_HEADLESS_SHELL_PATH`, so field reports
|
||||
// (e.g. `#hyperframes-cli-feedback` ts=1784095034 on win32/x64) hit the case
|
||||
// where `render` completes via that env var while `check`, `snapshot`, and
|
||||
// `compare` all ignore it and crash on the cached headless-shell instead.
|
||||
// Alias them here so every consumer of `openSettledCompositionPage` (which
|
||||
// calls `ensureBrowser` → `findFromEnv`) picks up the same escape hatch.
|
||||
// Tiebreak: HYPERFRAMES_BROWSER_PATH wins when both are set, matching the
|
||||
// CLI-native canonicalization in `render.ts` (which only sets
|
||||
// `PRODUCER_HEADLESS_SHELL_PATH` if not already present).
|
||||
function findFromEnv(): BrowserResult | undefined {
|
||||
const envPath = process.env["HYPERFRAMES_BROWSER_PATH"];
|
||||
const envPath =
|
||||
process.env["HYPERFRAMES_BROWSER_PATH"] ?? process.env["PRODUCER_HEADLESS_SHELL_PATH"];
|
||||
if (envPath && existsSync(envPath)) {
|
||||
return { executablePath: envPath, source: "env" };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user