mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
* Improve Sandbox test portability - Decouple Sandbox scope and authentication from custom image references. - Add a pinned managed-image bootstrap with stronger preflight diagnostics. - Document and test OIDC, access-token, and fallback-image workflows. * Fix VCR OIDC authentication - Map selected credentials onto the VCR CLI token interface. - Decode OIDC project scope and cover auth paths with regression tests. * Fix Sandbox VCR authentication * Fix OIDC Sandbox scope precedence
101 lines
3.2 KiB
JavaScript
101 lines
3.2 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFile } from "node:fs/promises";
|
|
import { fileURLToPath } from "node:url";
|
|
import { spawn } from "node:child_process";
|
|
import {
|
|
requiredSandboxImageConfig,
|
|
sandboxVcrConfig,
|
|
sandboxVercelConfig,
|
|
} from "./sandbox-config.mjs";
|
|
import { linuxAmd64ManifestDigest } from "./oci-manifest.mjs";
|
|
|
|
const root = fileURLToPath(new URL("../", import.meta.url));
|
|
const { reference: image, repository, tag } = requiredSandboxImageConfig();
|
|
const vercelConfig = sandboxVercelConfig();
|
|
const vcrConfig = sandboxVcrConfig(vercelConfig);
|
|
const scopeArgs = vcrConfig.scopeArgs;
|
|
const nodeVersion = (await readFile(new URL("../.node-version", import.meta.url), "utf8")).trim();
|
|
|
|
function run(command, args, { capture = false } = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(command, args, {
|
|
cwd: root,
|
|
env: { ...vcrConfig.env, NO_UPDATE_NOTIFIER: "1" },
|
|
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
});
|
|
let stdout = "";
|
|
let stderr = "";
|
|
if (capture) {
|
|
child.stdout.setEncoding("utf8");
|
|
child.stderr.setEncoding("utf8");
|
|
child.stdout.on("data", (chunk) => (stdout += chunk));
|
|
child.stderr.on("data", (chunk) => (stderr += chunk));
|
|
}
|
|
child.on("error", reject);
|
|
child.on("exit", (code, signal) => {
|
|
if (code === 0) resolve(stdout);
|
|
else reject(new Error(`${command} exited ${signal ?? code}${stderr ? `\n${stderr.trim()}` : ""}`));
|
|
});
|
|
});
|
|
}
|
|
|
|
console.log(
|
|
`Authenticating Docker with ${vcrConfig.authSource} ` +
|
|
`(scope: ${vcrConfig.scopeSource})...`,
|
|
);
|
|
await run("vercel", ["vcr", "login", "docker", ...scopeArgs]);
|
|
|
|
console.log(`Building and pushing ${image} for linux/amd64...`);
|
|
await run("docker", [
|
|
"buildx",
|
|
"build",
|
|
"--platform",
|
|
"linux/amd64",
|
|
"--build-arg",
|
|
`NODE_VERSION=${nodeVersion}`,
|
|
"--file",
|
|
"Dockerfile.sandbox",
|
|
"--output",
|
|
`type=image,name=${image},push=true,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true`,
|
|
".",
|
|
]);
|
|
|
|
const inspection = JSON.parse(
|
|
await run(
|
|
"docker",
|
|
["buildx", "imagetools", "inspect", image, "--format", "{{json .}}"],
|
|
{ capture: true },
|
|
),
|
|
);
|
|
let amd64Digest;
|
|
try {
|
|
amd64Digest = linuxAmd64ManifestDigest(inspection);
|
|
} catch (error) {
|
|
throw new Error(`could not find the linux/amd64 manifest for ${image}`, {
|
|
cause: error,
|
|
});
|
|
}
|
|
|
|
console.log("Waiting for VCR to prepare the image for Sandbox...");
|
|
const deadline = Date.now() + 5 * 60_000;
|
|
while (Date.now() < deadline) {
|
|
const listing = JSON.parse(
|
|
await run(
|
|
"vercel",
|
|
["vcr", "image", "ls", repository, "--format", "json", ...scopeArgs],
|
|
{ capture: true },
|
|
),
|
|
);
|
|
const manifest = listing.images?.find((candidate) => candidate.manifestDigest === amd64Digest);
|
|
if (manifest?.status === "ready") {
|
|
console.log(`${repository}:${tag} is ready for Vercel Sandbox.`);
|
|
process.exit(0);
|
|
}
|
|
if (manifest?.status === "unoptimized") {
|
|
throw new Error(`${repository}:${tag} is unoptimized; Vercel Sandbox requires linux/amd64`);
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
}
|
|
|
|
throw new Error(`VCR did not prepare ${repository}:${tag} within 5 minutes`);
|