Files
descrybe/scripts/build.mjs
T

148 lines
4.3 KiB
JavaScript
Raw Normal View History

2026-08-10 02:04:45 +02:00
#!/usr/bin/env node
/**
* Production build with auto host detection (Windows/Linux/macOS, RAM, CPU).
* Web then Go — never parallel. Caps cores so rebuilds do not pin 100%.
*
* Env (optional overrides):
* BUILD_PROFILE=low|medium|high
* BUILD_NODE_HEAP_MB / BUILD_GO_P / BUILD_GO_MAXPROCS / BUILD_GO_MEMLIMIT
* BUILD_LOW_MEM=0|1
* BUILD_SKIP_WEB=1 / BUILD_SKIP_GO=1 / BUILD_GO_ALL=1
* BUILD_BIN_DIR (default <repo>/bin)
*
* Usage:
* node scripts/build.mjs
* npm run build | build:web | build:api
*/
import { spawn } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
detectHostProfile,
formatHostNote,
withGoLowMem,
withNodeHeap,
} from "./lowmem-env.mjs";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const isWin = process.platform === "win32";
const exe = isWin ? ".exe" : "";
const args = new Set(process.argv.slice(2));
const onlyWeb = args.has("--web") || args.has("web");
const onlyGo = args.has("--api") || args.has("api") || args.has("--go") || args.has("go");
const wantHelp = args.has("-h") || args.has("--help");
if (wantHelp) {
console.log(`Usage: node scripts/build.mjs [--web|--api]
Auto-detects OS/RAM/CPU and scales Node heap + Go -p / GOMAXPROCS.
Alias: npm run build | build:web | build:api`);
process.exit(0);
}
const profile = detectHostProfile();
function section(title) {
console.log(`\n==> ${title}`);
}
function run(command, cmdArgs, { cwd = root, env = process.env, label, shell } = {}) {
return new Promise((resolve, reject) => {
const child = spawn(command, cmdArgs, {
cwd,
env,
stdio: "inherit",
shell: shell ?? (isWin && command === "npm"),
});
child.on("error", reject);
child.on("exit", (code, signal) => {
if (signal) {
reject(new Error(`${label ?? command}: killed by ${signal}`));
return;
}
if (code !== 0) {
reject(new Error(`${label ?? command}: exit ${code ?? 1}`));
return;
}
resolve();
});
});
}
async function buildWeb() {
section("Web (Vite / SvelteKit)");
const env = withNodeHeap(process.env, profile.nodeHeapMb, profile.uvThreadpool);
console.log(
`NOTE NODE_OPTIONS max-old-space-size=${profile.nodeHeapMb} UV_THREADPOOL_SIZE=${env.UV_THREADPOOL_SIZE} BUILD_LOW_MEM=${env.BUILD_LOW_MEM}`,
);
await run(
"npm",
["run", "build", "--workspace=web"],
{ env, label: "web build" },
);
}
async function buildGo() {
section("API + worker build");
const apiDir = path.join(root, "apps", "api");
const binDir = path.resolve(root, process.env.BUILD_BIN_DIR || "bin");
fs.mkdirSync(binDir, { recursive: true });
const env = withGoLowMem(process.env, profile);
const apiOut = path.join(binDir, `api${exe}`);
const workerOut = path.join(binDir, `worker${exe}`);
const p = profile.goP;
console.log(
`NOTE GOMAXPROCS=${env.GOMAXPROCS}` +
(env.GOMEMLIMIT ? ` GOMEMLIMIT=${env.GOMEMLIMIT}` : "") +
(env.GOGC ? ` GOGC=${env.GOGC}` : "") +
` go -p ${p}`,
);
// Always sequential api then worker — parallel binaries double peak RAM.
const ldflags = "-s -w";
await run(
"go",
["build", `-p=${p}`, "-trimpath", `-ldflags=${ldflags}`, "-o", apiOut, "./cmd/api"],
{ cwd: apiDir, env, label: "go build api", shell: false },
);
console.log(`OK ${path.relative(root, apiOut)}`);
await run(
"go",
["build", `-p=${p}`, "-trimpath", `-ldflags=${ldflags}`, "-o", workerOut, "./cmd/worker"],
{ cwd: apiDir, env, label: "go build worker", shell: false },
);
console.log(`OK ${path.relative(root, workerOut)}`);
if (process.env.BUILD_GO_ALL === "1") {
section("Go packages (go build ./...)");
await run(
"go",
["build", `-p=${p}`, "./..."],
{ cwd: apiDir, env, label: "go build ./..." },
);
}
}
async function main() {
section("Host");
console.log(`NOTE ${formatHostNote(profile)}`);
const skipWeb = process.env.BUILD_SKIP_WEB === "1" || onlyGo;
const skipGo = process.env.BUILD_SKIP_GO === "1" || onlyWeb;
if (!skipWeb) await buildWeb();
if (!skipGo) await buildGo();
section("Done");
console.log(`Build finished (tier=${profile.tier}, sequential web→go).`);
}
main().catch((err) => {
console.error(`FAIL ${err?.message || err}`);
process.exit(1);
});