qmd/scripts/test-all.mjs
Tobi Lutke c5f4217a6f
fix(cli): exit naturally so node-llama-cpp's beforeExit fires
The libggml-metal static destructor asserts on a non-empty residency-set
collection during __cxa_finalize_ranges, dumping a multi-kB GGML backtrace
after successful output (ggml-org/llama.cpp#22593, one-line fix open as
PR #22595). The assertion only trips when process.exit() skips Node's
beforeExit hook — which is exactly the hook node-llama-cpp registers to
auto-dispose its native handles.

Primary fix: finishSuccessfulCliCommand now sets process.exitCode = 0
and returns instead of calling process.exit(0). The event loop drains,
beforeExit fires, native Metal resources tear down in order, and the
process exits cleanly even without the workaround env var.

Defense-in-depth retained: bin/qmd and scripts/test-all.mjs still export
GGML_METAL_NO_RESIDENCY=1 on darwin for error paths and tests that
terminate via process.exit(). Opt back in with QMD_METAL_KEEP_RESIDENCY=1.

Also: correct upstream issue refs (was #17869 → now #22593/#22595).
Add scripts/repro-metal-rsets-crash.mjs minimal reproduction.
2026-05-28 17:23:31 -07:00

39 lines
1.8 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
const root = fileURLToPath(new URL("..", import.meta.url));
// Mirror bin/qmd's darwin Metal residency mitigation for test subprocesses.
// libggml-metal asserts on a non-empty residency set during its static
// destructor (ggml-org/llama.cpp#22593, fix open as #22595) and dumps a
// multi-kB backtrace at process exit even when tests pass. The env var must
// be set BEFORE the subprocess starts because libggml-metal reads it via
// libc getenv at module-load time. Opt out with QMD_METAL_KEEP_RESIDENCY=1.
const darwinMetalEnv =
process.platform === "darwin" && process.env.QMD_METAL_KEEP_RESIDENCY !== "1"
? { GGML_METAL_NO_RESIDENCY: "1" }
: {};
function run(label, command, args, options = {}) {
console.log(`==> ${label}`);
const { env: extraEnv, ...spawnOptions } = options;
const result = spawnSync(command, args, {
cwd: root,
stdio: "inherit",
shell: process.platform === "win32",
env: { ...process.env, ...darwinMetalEnv, ...(extraEnv ?? {}) },
...spawnOptions,
});
if (result.status !== 0) {
console.error(`Test task failed: ${label}`);
process.exit(result.status ?? 1);
}
}
run("TypeScript build typecheck", process.execPath, [join(root, "node_modules", "typescript", "bin", "tsc"), "-p", "tsconfig.build.json", "--noEmit"]);
run("Vitest suite under Node", process.execPath, [join(root, "node_modules", "vitest", "vitest.mjs"), "run", "--reporter=verbose", "--testTimeout", "60000", "test/"], { env: { CI: "true" } });
run("Bun test suite", "bun", ["test", "--timeout", "60000", "--preload", "./src/test-preload.ts", "test/"], { env: { CI: "true" } });
run("Package smoke", process.execPath, ["scripts/package-smoke.mjs"]);