148 lines
4.3 KiB
JavaScript
148 lines
4.3 KiB
JavaScript
#!/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);
|
|
});
|