Files
scriptc/scripts/sandbox-config.mjs
Chris Tate 9d30b56c6b Speed up compiler startup and trim release runtime packs
- Cache host libc detection and load TypeScript 5 only when needed.
- Exclude debug payloads from release runtime packs and vendor archives.
- Preserve native self-hosting, portable diagnostics, and reliable Sandbox execution.
2026-09-29 21:41:39 -05:00

273 lines
9.2 KiB
JavaScript

import { loadEnvFile } from "node:process";
import { fileURLToPath } from "node:url";
const customImageExample = "vcr.vercel.com/<team>/<project>/<repository>:<tag>";
export const defaultSandboxImage = "vercel/sandbox/universal";
const localEnv = fileURLToPath(new URL("../.env.local", import.meta.url));
let loadedLocalEnv = false;
function loadLocalEnv() {
if (loadedLocalEnv) return;
loadedLocalEnv = true;
try {
// Node preserves variables already present in the process environment,
// allowing an agent or shell to override values from this local file.
loadEnvFile(localEnv);
} catch (error) {
if (error?.code !== "ENOENT") throw error;
}
}
export function sandboxRunnerConfig(env) {
loadLocalEnv();
const source = env ?? process.env;
const sandboxTimeout = source.SCRIPTC_SANDBOX_TIMEOUT ?? "45m";
const duration = /^(\d+)\s*(ms|s|m|h)$/.exec(sandboxTimeout.trim());
const units = { ms: 1, s: 1000, m: 60_000, h: 3_600_000 };
const sandboxTimeoutMs = duration ? Number(duration[1]) * units[duration[2]] : 0;
if (!Number.isSafeInteger(sandboxTimeoutMs) || sandboxTimeoutMs <= 0 || sandboxTimeoutMs > 2_147_483_647) {
throw new Error("SCRIPTC_SANDBOX_TIMEOUT must be a positive duration such as 45m or 2h");
}
return {
vcpus: source.SCRIPTC_SANDBOX_VCPUS ?? "8",
testWorkers: source.SCRIPTC_TEST_WORKERS ?? "4",
localTestWorkers: source.SCRIPTC_LOCAL_TEST_WORKERS ?? "2",
localCaseShards: source.SCRIPTC_LOCAL_CASE_SHARDS ?? "2",
sandboxTimeout,
sandboxTimeoutMs,
};
}
/** Select Sandbox credentials and scope without coupling either to the image.
* OIDC is project-scoped and takes precedence over legacy access tokens and
* their explicit environment scope. */
export function sandboxVercelConfig(env) {
loadLocalEnv();
const source = env ?? process.env;
const oidcToken = source.VERCEL_OIDC_TOKEN?.trim();
const configuredTeam = source.VERCEL_TEAM_ID?.trim();
const configuredProject = source.VERCEL_PROJECT_ID?.trim();
if (!oidcToken && Boolean(configuredTeam) !== Boolean(configuredProject)) {
throw new Error(
"VERCEL_TEAM_ID and VERCEL_PROJECT_ID must be set together; " +
"Sandbox scope is independent of SCRIPTC_SANDBOX_IMAGE",
);
}
const team = oidcToken ? undefined : configuredTeam;
const project = oidcToken ? undefined : configuredProject;
const accessToken = source.VERCEL_TOKEN?.trim() || source.VERCEL_AUTH_TOKEN?.trim();
const accessTokenSource = source.VERCEL_TOKEN?.trim()
? "VERCEL_TOKEN"
: source.VERCEL_AUTH_TOKEN?.trim()
? "VERCEL_AUTH_TOKEN"
: undefined;
const authSource = oidcToken
? "VERCEL_OIDC_TOKEN"
: accessToken
? accessTokenSource
: "Vercel CLI login";
if (accessToken && !oidcToken && (!team || !project)) {
throw new Error(
`${authSource} requires VERCEL_TEAM_ID and VERCEL_PROJECT_ID for Sandbox access`,
);
}
return {
accessToken,
accessTokenSource,
authSource,
authToken: oidcToken || accessToken,
oidc: Boolean(oidcToken),
project,
scopeArgs: team && project ? ["--scope", team, "--project", project] : [],
scopeSource: oidcToken
? "VERCEL_OIDC_TOKEN claims"
: team && project
? "VERCEL_TEAM_ID + VERCEL_PROJECT_ID"
: "linked/default Vercel CLI project",
team,
};
}
/** Build a child environment that makes the selected authentication source
* unambiguous. The Vercel wrapper otherwise prefers a stored/access token to
* VERCEL_OIDC_TOKEN when both are available. */
export function sandboxVercelEnvironment(config, env) {
const childEnv = { ...(env ?? process.env) };
if (config.authToken) childEnv.VERCEL_AUTH_TOKEN = config.authToken;
if (config.oidc) delete childEnv.VERCEL_TOKEN;
return childEnv;
}
/** Adapt the selected Sandbox credential to the legacy VCR command surface.
* Unlike the Sandbox CLI, `vercel vcr` rejects JWTs in VERCEL_TOKEN and cannot
* infer registry scope from OIDC claims. OIDC therefore supplies only VCR's
* scope; authentication uses an explicit access token or the Vercel CLI login. */
export function sandboxVcrConfig(config, env) {
const childEnv = { ...(env ?? process.env) };
delete childEnv.VERCEL_AUTH_TOKEN;
let authSource = "Vercel CLI login";
const vcrAccessToken = /^\w+$/.test(config.accessToken ?? "")
? config.accessToken
: undefined;
if (config.accessToken && !vcrAccessToken && !config.oidc) {
throw new Error(
`${config.accessTokenSource} is not a VCR-compatible access token; ` +
"use VERCEL_TOKEN with a classic access token or run `vercel login`",
);
}
if (vcrAccessToken) {
childEnv.VERCEL_TOKEN = vcrAccessToken;
authSource = config.accessTokenSource;
} else {
delete childEnv.VERCEL_TOKEN;
}
if (config.team && config.project) {
return {
authSource,
env: childEnv,
scopeArgs: ["--scope", config.team, "--project", config.project],
scopeSource: "VERCEL_TEAM_ID + VERCEL_PROJECT_ID",
};
}
if (!config.oidc) {
return {
authSource,
env: childEnv,
scopeArgs: [],
scopeSource: "linked/default Vercel CLI project",
};
}
let claims;
try {
const segments = config.authToken?.split(".") ?? [];
if (segments.length !== 3 || !segments[1]) throw new Error("token is not a JWT");
claims = JSON.parse(Buffer.from(segments[1], "base64url").toString("utf8"));
} catch (cause) {
throw new Error(
"VERCEL_OIDC_TOKEN must be a JWT with owner_id and project_id claims for VCR access",
{ cause },
);
}
const team = typeof claims.owner_id === "string" ? claims.owner_id.trim() : "";
const project = typeof claims.project_id === "string" ? claims.project_id.trim() : "";
if (!team || !project) {
throw new Error(
"VERCEL_OIDC_TOKEN must contain owner_id and project_id claims for VCR access",
);
}
return {
authSource,
env: childEnv,
scopeArgs: ["--scope", team, "--project", project],
scopeSource: "VERCEL_OIDC_TOKEN claims",
};
}
export function sandboxBootstrapCommand(customImage) {
if (customImage) return undefined;
return {
install: {
args: ["scripts/sandbox-bootstrap.sh"],
command: "sh",
workdir: "/workspace",
},
prepareWorkspace: {
args: [
"-c",
"sudo mkdir -p /workspace && sudo chown \"$(id -u):$(id -g)\" /workspace",
],
command: "sh",
workdir: "/vercel",
},
};
}
/** Reserve one Vitest worker for each concurrently scheduled side process
* without ever starving the case-sharded corpus or exceeding the configured
* per-Sandbox worker budget. Extra side processes share the reserved slots. */
export function sandboxTestWorkerAllocation(workerCount, sideTaskCount) {
if (!Number.isInteger(workerCount) || workerCount < 1) {
throw new Error("workerCount must be a positive integer");
}
if (!Number.isInteger(sideTaskCount) || sideTaskCount < 0) {
throw new Error("sideTaskCount must be a non-negative integer");
}
const sideConcurrency = Math.min(sideTaskCount, Math.max(0, workerCount - 1));
return {
caseWorkers: workerCount - sideConcurrency,
sideConcurrency,
};
}
export function sandboxImageConfig(env) {
loadLocalEnv();
const source = env ?? process.env;
const configuredReference = source.SCRIPTC_SANDBOX_IMAGE?.trim();
if (!configuredReference) {
return {
custom: false,
reference: defaultSandboxImage,
sandboxImage: defaultSandboxImage,
};
}
const match = /^vcr\.vercel\.com\/([^/]+)\/([^/]+)\/([^/:]+):([^/:]+)$/.exec(
configuredReference,
);
if (!match) {
throw new Error(
`SCRIPTC_SANDBOX_IMAGE must be a fully qualified VCR image (${customImageExample})`,
);
}
const [, , , repository, tag] = match;
return {
custom: true,
reference: configuredReference,
repository,
tag,
// Preserve the full reference: Sandbox scope comes from Vercel auth,
// never from path segments in this image name.
sandboxImage: configuredReference,
};
}
export function requiredSandboxImageConfig(env) {
const config = sandboxImageConfig(env);
const reference = (env ?? process.env).SCRIPTC_SANDBOX_IMAGE?.trim();
if (!reference) {
throw new Error(
`SCRIPTC_SANDBOX_IMAGE is required to build an image (for example: ${customImageExample})`,
);
}
return config;
}
/** A prepared snapshot reuses the same pinned tools as a custom image.
* The runner still replaces the source tree and rebuilds native artifacts. */
export function sandboxTestSourceConfig(env) {
loadLocalEnv();
const source = env ?? process.env;
const snapshot = source.SCRIPTC_SANDBOX_SNAPSHOT?.trim();
if (snapshot) {
if (source.SCRIPTC_SANDBOX_IMAGE?.trim()) {
throw new Error("Set only one of SCRIPTC_SANDBOX_SNAPSHOT and SCRIPTC_SANDBOX_IMAGE");
}
if (!/^snap_[A-Za-z0-9]+$/.test(snapshot)) {
throw new Error("SCRIPTC_SANDBOX_SNAPSHOT must be a Sandbox snapshot ID (snap_...)");
}
return { prepared: true, reference: snapshot, description: "prepared snapshot", createArgs: ["--snapshot", snapshot] };
}
const image = sandboxImageConfig(source);
return {
prepared: image.custom,
reference: image.sandboxImage,
description: image.custom ? "custom VCR image" : "managed fallback image",
createArgs: ["--image", image.sandboxImage],
};
}