mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
120 lines
3.0 KiB
TypeScript
120 lines
3.0 KiB
TypeScript
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { spawnSync } from "node:child_process";
|
|
import crypto from "node:crypto";
|
|
|
|
const PLAN_FILE = "railpack-plan.json";
|
|
const FRONTEND_IMAGE = "ghcr.io/railwayapp/railpack:railpack-frontend";
|
|
|
|
// Parse command line arguments
|
|
const args = process.argv.slice(2);
|
|
if (args.length === 0) {
|
|
console.error("Please provide a directory path");
|
|
process.exit(1);
|
|
}
|
|
|
|
const dir = args[0];
|
|
const envArgs = args.slice(1).filter((arg) => arg.startsWith("--env"));
|
|
|
|
// Create temp directory to save the build plan to
|
|
const randId = Math.random().toString(36).slice(2);
|
|
const tmpDir = join("/tmp", "railpack-" + randId);
|
|
const planDir = join(tmpDir, "plan");
|
|
mkdirSync(planDir, { recursive: true });
|
|
|
|
const cleanup = () => {
|
|
if (existsSync(tmpDir)) {
|
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
}
|
|
};
|
|
|
|
process.on("exit", cleanup);
|
|
process.on("SIGINT", () => {
|
|
cleanup();
|
|
process.exit();
|
|
});
|
|
|
|
// Generate build plan
|
|
console.log(`Generating build plan for ${dir}`);
|
|
const planResult = spawnSync(
|
|
"go",
|
|
["run", "cmd/cli/main.go", "plan", dir, "--format", "json"],
|
|
{
|
|
stdio: ["inherit", "pipe", "inherit"],
|
|
}
|
|
);
|
|
|
|
if (planResult.status !== 0) {
|
|
console.error("Failed to generate build plan");
|
|
process.exit(1);
|
|
}
|
|
|
|
const planPath = join(planDir, PLAN_FILE);
|
|
writeFileSync(planPath, planResult.stdout);
|
|
|
|
// Parse all env vars so that we can use them as secrets
|
|
const envVars: Record<string, string> = {};
|
|
const secretArgs: string[] = [];
|
|
|
|
// Find all env args and their values
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (args[i] === "--env" && i + 1 < args.length) {
|
|
const nameValue = args[i + 1];
|
|
const [name, value] = nameValue.split("=");
|
|
if (name && value) {
|
|
envVars[name] = value;
|
|
secretArgs.push(`--secret=id=${name},env=${name}`);
|
|
}
|
|
i++; // Skip the next argument since we've processed it
|
|
}
|
|
}
|
|
|
|
// Pipe buildctl and docker load together
|
|
const buildctlArgs = [
|
|
"build",
|
|
`--local`,
|
|
`context=${dir}`,
|
|
`--local`,
|
|
`dockerfile=${planDir}`,
|
|
"--frontend=gateway.v0",
|
|
"--opt",
|
|
`source=${FRONTEND_IMAGE}`,
|
|
"--output",
|
|
"type=docker,name=test",
|
|
...secretArgs,
|
|
];
|
|
|
|
// Options that are passed to our custom frontend
|
|
const cacheKey = dir;
|
|
buildctlArgs.push("--opt", `cache-key=${cacheKey}`);
|
|
|
|
if (Object.keys(envVars).length > 0) {
|
|
const secretsHash = crypto
|
|
.createHash("sha256")
|
|
.update(Object.values(envVars).sort().join(""))
|
|
.digest("hex");
|
|
buildctlArgs.push("--opt", `secrets-hash=${secretsHash}`);
|
|
}
|
|
|
|
console.log(`Executing buildctl\n ${buildctlArgs.join(" ")}`);
|
|
|
|
const buildctl = spawnSync("buildctl", buildctlArgs, {
|
|
stdio: ["inherit", "pipe", "inherit"],
|
|
env: { ...process.env, ...envVars },
|
|
});
|
|
|
|
if (buildctl.status !== 0) {
|
|
console.error("buildctl command failed");
|
|
process.exit(1);
|
|
}
|
|
|
|
const dockerLoad = spawnSync("docker", ["load"], {
|
|
input: buildctl.stdout,
|
|
stdio: ["pipe", "inherit", "inherit"],
|
|
});
|
|
|
|
if (dockerLoad.status !== 0) {
|
|
console.error("docker load failed");
|
|
process.exit(1);
|
|
}
|