import { access, cp, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { constants } from "node:fs"; import os from "node:os"; import path from "node:path"; import { spawn } from "node:child_process"; const repoRoot = path.resolve(import.meta.dirname, "../.."); const litmusDir = path.join(repoRoot, "vendor", "litmus"); const binaryPath = path.join(litmusDir, "litmus"); const releaseVersion = "0.17"; const releaseUrl = `https://notroj.github.io/litmus/litmus-${releaseVersion}.tar.gz`; async function exists(filePath: string): Promise { try { await access(filePath, constants.F_OK); return true; } catch { return false; } } function run(command: string, args: string[], cwd: string): Promise { return new Promise((resolve, reject) => { const child = spawn(command, args, { cwd, stdio: "inherit" }); child.on("error", reject); child.on("exit", (code) => { if (code === 0) resolve(); else reject(new Error(`${command} ${args.join(" ")} exited with ${code}`)); }); }); } async function downloadReleaseTarball(): Promise { const tempDir = await mkdtemp(path.join(os.tmpdir(), "dab-litmus-setup-")); try { const archivePath = path.join(tempDir, `litmus-${releaseVersion}.tar.gz`); const extractedDir = path.join(tempDir, `litmus-${releaseVersion}`); const response = await fetch(releaseUrl); if (!response.ok) throw new Error(`Unable to download ${releaseUrl}: ${response.status}`); const body = await response.arrayBuffer(); await writeFile(archivePath, new Uint8Array(body)); await run("tar", ["-xzf", archivePath, "-C", tempDir], repoRoot); await rm(litmusDir, { recursive: true, force: true }); await mkdir(path.dirname(litmusDir), { recursive: true }); await cp(extractedDir, litmusDir, { recursive: true }); } finally { await rm(tempDir, { recursive: true, force: true }); } } async function main(): Promise { if (await exists(binaryPath)) { console.log(binaryPath); return; } await mkdir(path.dirname(litmusDir), { recursive: true }); if (!(await exists(litmusDir))) { await downloadReleaseTarball(); } if (!(await exists(path.join(litmusDir, "configure")))) { await downloadReleaseTarball(); } if (await exists(path.join(litmusDir, "configure"))) { await run("./configure", [], litmusDir); } await run("make", [], litmusDir); if (!(await exists(binaryPath))) { throw new Error("litmus build completed, but vendor/litmus/litmus was not found"); } console.log(binaryPath); } main().catch((error) => { console.error( "Unable to build litmus. Install git, make, a C compiler, autoconf/automake, and neon development headers.", ); console.error(error instanceof Error ? error.message : String(error)); process.exit(1); });