// Maintainer-only build. The resulting two-binary bundle needs no extra host runtime. import { spawnSync } from "node:child_process"; import { createHash } from "node:crypto"; import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const repository = process.env.THT_BUILD_SOURCE_ROOT ? resolve(process.env.THT_BUILD_SOURCE_ROOT) : resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const backend = join(repository, "backend"); const native = `${process.platform === "win32" ? "windows" : process.platform}-${process.arch === "x64" ? "amd64" : process.arch}`; const targets = { "windows-amd64": ["windows", "amd64", "bun-windows-x64"], "darwin-amd64": ["darwin", "amd64", "bun-darwin-x64"], "darwin-arm64": ["darwin", "arm64", "bun-darwin-arm64"], "linux-amd64": ["linux", "amd64", "bun-linux-x64"], "linux-arm64": ["linux", "arm64", "bun-linux-arm64"], }; const requested = process.argv.slice(2); const selected = requested.length === 1 && requested[0] === "--all" ? Object.keys(targets) : requested.length ? requested : [native]; if (selected.some((target) => !targets[target])) { console.error(`Usage: npm run build:workspace-tools -- [${Object.keys(targets).join("|")}|--all]`); process.exit(2); } function run(command, args, cwd = backend, env = process.env) { const result = spawnSync(command, args, { cwd, env, stdio: "inherit" }); if (result.error || result.status !== 0) throw new Error(`Build failed: ${command}`); } const revision = spawnSync("git", ["rev-parse", "HEAD"], { cwd: repository, encoding: "utf8" }); if (revision.status !== 0) throw new Error("Cannot read build revision"); const commit = revision.stdout.trim(); const buildTime = process.env.THT_BUILD_TIME ?? new Date().toISOString(); const releaseVersion = process.env.THT_BUILD_VERSION ?? "0.0.0-dev"; if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?$/.test(releaseVersion) || !Number.isFinite(Date.parse(buildTime))) throw new Error("Invalid build identity"); const module = "github.com/aritmolab/thothii/tools/tht/internal/version"; const bunPackage = JSON.parse(readFileSync(join(backend, "node_modules", "bun", "package.json"), "utf8")); const bun = join(backend, "node_modules", "bun", bunPackage.bin.bun); for (const target of selected) { const [os, arch, bunTarget] = targets[target]; const output = join(repository, "dist", "workspace-tools", target); mkdirSync(output, { recursive: true }); const extension = os === "windows" ? ".exe" : ""; const names = [`tht${extension}`, `tht-workspace-documents${extension}`]; run(bun, ["build", "src/workspace-documents-cli.ts", "--compile", `--target=${bunTarget}`, "--outfile", join(output, names[1])]); run("go", ["build", "-trimpath", "-ldflags", `-s -w -X ${module}.semanticVersion=${releaseVersion} -X ${module}.commit=${commit} -X ${module}.buildTime=${buildTime}`, "-o", join(output, names[0]), "./cmd/tht"], join(repository, "tools", "tht"), { ...process.env, CGO_ENABLED: "0", GOOS: os, GOARCH: arch }); const hashes = names.map((name) => `${createHash("sha256").update(readFileSync(join(output, name))).digest("hex")} ${name}\n`).join(""); writeFileSync(join(output, "SHA256SUMS"), hashes); writeFileSync(join(output, "build.json"), JSON.stringify({ version: releaseVersion, commit, buildTime, target, bun: JSON.parse(readFileSync(join(backend, "package.json"), "utf8")).devDependencies.bun }, null, 2) + "\n"); console.log(output); }