update
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
#!/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);
|
||||
});
|
||||
Reference in New Issue
Block a user