mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-15 20:53:23 +02:00
fix(runner): bound Rosetta interpreter build probes at 30 seconds
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
d3077a061f
commit
bd44f41e9e
4 files changed
+36
-4
No files matched your search
@@ -0,0 +1,6 @@
|
||||
/** Build-time probes only; provider/session admission deadlines are unchanged. */
|
||||
export function buildNodeStartupTimeout(platform = process.platform, architecture = process.arch) {
|
||||
// A freshly copied x64 Node can spend more than 10s in Rosetta's first launch.
|
||||
// Keep this bounded and target-specific instead of weakening other probes.
|
||||
return platform === "darwin" && architecture === "x64" ? 30_000 : 10_000;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { buildNodeStartupTimeout } from "./build-node-startup-timeout.mjs";
|
||||
|
||||
test("cold Rosetta Node startup has a bounded build-only allowance", () => {
|
||||
assert.equal(buildNodeStartupTimeout("darwin", "x64"), 30_000);
|
||||
for (const [platform, architecture] of [["darwin", "arm64"], ["linux", "x64"], ["linux", "arm64"], ["win32", "x64"]]) {
|
||||
assert.equal(buildNodeStartupTimeout(platform, architecture), 10_000);
|
||||
}
|
||||
assert.equal(buildNodeStartupTimeout(), buildNodeStartupTimeout(process.platform, process.arch));
|
||||
});
|
||||
|
||||
test("both pack and private Pi Node probes use the allowance without changing other operation deadlines", () => {
|
||||
const pack = readFileSync(new URL("./build-provider-pack.mjs", import.meta.url), "utf8");
|
||||
const pi = readFileSync(new URL("./materialize-pi-distribution.mjs", import.meta.url), "utf8");
|
||||
assert.match(pack, /spawnSync\(stableNodeCommand, \["--version"\], \{[^}]*env: \{[^}]*\}[^}]*timeout: buildNodeStartupTimeout\(\)/);
|
||||
for (const probe of ['run(node, ["--version"]', 'run(node, ["-p", "process.versions.undici"]', 'run(copiedNode, ["--version"]']) {
|
||||
const line = pi.split("\n").find(line => line.includes(probe));
|
||||
assert.ok(line?.includes("timeout: buildNodeStartupTimeout()"), probe);
|
||||
}
|
||||
assert.match(pi, /run\("git", \["apply", "--check", patchPath\], \{[^}]*timeout: 10_000/);
|
||||
assert.match(pi, /run\("\/usr\/bin\/otool", \["-L", copiedNode\], \{ env: \{\}, timeout: 10_000/);
|
||||
});
|
||||
@@ -19,6 +19,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parseProviderPackArguments, materializeCandidateProviderPack } from "./candidate-provider-pack.mjs";
|
||||
import { buildNodeStartupTimeout } from "./build-node-startup-timeout.mjs";
|
||||
|
||||
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const workspaceRoot = resolve(packageRoot, "../..");
|
||||
@@ -165,7 +166,7 @@ try {
|
||||
copyFileSync(process.execPath, stableNodeCommand);
|
||||
chmodSync(stableNodeCommand, 0o755);
|
||||
const relocatedNode = spawnSync(stableNodeCommand, ["--version"], {
|
||||
cwd: temporaryRoot, env: { PATH: "/usr/bin:/bin" }, encoding: "utf8", timeout: 10_000,
|
||||
cwd: temporaryRoot, env: { PATH: "/usr/bin:/bin" }, encoding: "utf8", timeout: buildNodeStartupTimeout(),
|
||||
});
|
||||
if (relocatedNode.status !== 0 || relocatedNode.stdout.trim() !== `v${process.versions.node}`) {
|
||||
throw new Error("Provider pack Node is not portable after relocation; build with a standalone Node distribution");
|
||||
|
||||
@@ -8,6 +8,7 @@ import { promisify } from "node:util";
|
||||
import { gunzipSync } from "node:zlib";
|
||||
import { PI_DISTRIBUTION_CLOSURE_SHA256 } from "../src/drivers/acpx/pi-closure-pins.ts";
|
||||
import { PI_NODE_DISTRIBUTIONS, PI_NODE_VERSION } from "../src/drivers/acpx/pi-node-pins.ts";
|
||||
import { buildNodeStartupTimeout } from "./build-node-startup-timeout.mjs";
|
||||
import { QUALIFIED_ACPX_PROFILES } from "../src/drivers/acpx/qualified-profiles.ts";
|
||||
import { inventoryPiRuntimeFiles, verifyPiRuntimeManifest } from "../src/drivers/acpx/pi-verified-runtime.ts";
|
||||
|
||||
@@ -208,9 +209,9 @@ export async function materializePiDistribution({ outputRoot, nodeExecutable, np
|
||||
node = join(nodeRoot, "node");
|
||||
}
|
||||
if (hash(await readFile(node)) !== nodePin.executableSha256 || (await lstat(node)).size !== nodePin.executableSize) throw new Error("Pi Node executable does not match its target release pin");
|
||||
const version = (await run(node, ["--version"], { env: {}, timeout: 10_000 })).stdout.trim();
|
||||
const version = (await run(node, ["--version"], { env: {}, timeout: buildNodeStartupTimeout() })).stdout.trim();
|
||||
if (version !== `v${PI_NODE_VERSION}`) throw new Error("Pi distribution requires exact pinned Node version");
|
||||
if ((await run(node, ["-p", "process.versions.undici"], { env: {}, timeout: 10_000 })).stdout.trim() !== PI_DISTRIBUTION_PINS.nodeBundledUndici) throw new Error("Pi Node bundled Undici differs from its reviewed security pin");
|
||||
if ((await run(node, ["-p", "process.versions.undici"], { env: {}, timeout: buildNodeStartupTimeout() })).stdout.trim() !== PI_DISTRIBUTION_PINS.nodeBundledUndici) throw new Error("Pi Node bundled Undici differs from its reviewed security pin");
|
||||
await mkdir(runtimeRoot);
|
||||
await Promise.all(["package.json", "package-lock.json"].map((name) => copyFile(join(lockDirectory, name), join(runtimeRoot, name))));
|
||||
await writeFile(join(runtimeRoot, ".npmrc"), "registry=https://registry.npmjs.org/\nignore-scripts=true\naudit=false\nfund=false\n");
|
||||
@@ -244,7 +245,7 @@ export async function materializePiDistribution({ outputRoot, nodeExecutable, np
|
||||
? (await run("/usr/bin/otool", ["-L", copiedNode], { env: {}, timeout: 10_000 })).stdout
|
||||
: (await run("ldd", [copiedNode], { env: { PATH: "/usr/bin:/bin" }, timeout: 10_000 })).stdout;
|
||||
assertPiNodeSystemDependencies(dependencyListing, process.platform);
|
||||
if ((await run(copiedNode, ["--version"], { env: {}, timeout: 10_000 })).stdout.trim() !== version) throw new Error("Copied Pi Node cannot execute after relocation");
|
||||
if ((await run(copiedNode, ["--version"], { env: {}, timeout: buildNodeStartupTimeout() })).stdout.trim() !== version) throw new Error("Copied Pi Node cannot execute after relocation");
|
||||
await rm(buildHome, { recursive: true });
|
||||
// npm-generated .bin links and hidden lock metadata are not package payload
|
||||
// files. No launch uses PATH; excluding them makes the closure regular-only.
|
||||
|
||||
Reference in new issue
Block a user