88aa88f605
The vendored markit engine kept `mupdf` external, but a single-file `bun --compile` binary has no node_modules to resolve it from, so the standalone binary aborted at startup with `Cannot find package 'mupdf'` — the otherwise-lazy import is resolved eagerly at boot. Bundle mupdf and embed its WASM blob (scripts/embed-mupdf-wasm.ts, reset after the build); npm and source installs still load mupdf from node_modules. Import mupdf lazily inside the PDF converter so the bundled markit chunk's init stays synchronous: mupdf's top-level await otherwise made the chunk init async and bun's compiled bundler failed to await it through the barrel, exposing the converters before their module-level const tables initialized (undefined EXTENSIONS). Also keeps the ~10MB wasm off non-PDF document conversions.
210 lines
6.6 KiB
TypeScript
210 lines
6.6 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
import * as fs from "node:fs/promises";
|
|
import * as path from "node:path";
|
|
|
|
interface BinaryTarget {
|
|
id: string;
|
|
platform: string;
|
|
arch: string;
|
|
target: string;
|
|
outfile: string;
|
|
}
|
|
|
|
const repoRoot = path.join(import.meta.dir, "..");
|
|
const binariesDir = path.join(repoRoot, "packages", "coding-agent", "binaries");
|
|
const entrypoint = "./packages/coding-agent/src/cli.ts";
|
|
// Legacy extension shims and package barrels in the build argv below are still
|
|
// explicit `--compile` entrypoints because they are reached via computed bunfs
|
|
// paths. Worker threads spawn `new Worker(Bun.main, { argv })` — they re-enter
|
|
// the binary's own entry module — so no separate worker modules are compiled.
|
|
const isDryRun = process.argv.includes("--dry-run");
|
|
const targets: BinaryTarget[] = [
|
|
{
|
|
id: "darwin-arm64",
|
|
platform: "darwin",
|
|
arch: "arm64",
|
|
target: "bun-darwin-arm64",
|
|
outfile: "packages/coding-agent/binaries/omp-darwin-arm64",
|
|
},
|
|
{
|
|
id: "darwin-x64",
|
|
platform: "darwin",
|
|
arch: "x64",
|
|
target: "bun-darwin-x64",
|
|
outfile: "packages/coding-agent/binaries/omp-darwin-x64",
|
|
},
|
|
{
|
|
id: "linux-x64",
|
|
platform: "linux",
|
|
arch: "x64",
|
|
target: "bun-linux-x64-baseline",
|
|
outfile: "packages/coding-agent/binaries/omp-linux-x64",
|
|
},
|
|
{
|
|
id: "linux-arm64",
|
|
platform: "linux",
|
|
arch: "arm64",
|
|
target: "bun-linux-arm64",
|
|
outfile: "packages/coding-agent/binaries/omp-linux-arm64",
|
|
},
|
|
{
|
|
id: "win32-x64",
|
|
platform: "win32",
|
|
arch: "x64",
|
|
target: "bun-windows-x64-modern",
|
|
outfile: "packages/coding-agent/binaries/omp-windows-x64.exe",
|
|
},
|
|
];
|
|
|
|
function parseRequestedTargets(): Set<string> | null {
|
|
const flagIndex = process.argv.findIndex(arg => arg === "--targets");
|
|
const flagValue =
|
|
flagIndex >= 0
|
|
? process.argv[flagIndex + 1]
|
|
: process.argv.find(arg => arg.startsWith("--targets="))?.split("=", 2)[1] ?? Bun.env.RELEASE_TARGETS;
|
|
|
|
if (!flagValue) {
|
|
return null;
|
|
}
|
|
|
|
return new Set(
|
|
flagValue
|
|
.split(",")
|
|
.map(value => value.trim())
|
|
.filter(Boolean),
|
|
);
|
|
}
|
|
|
|
function shouldAdhocSignDarwinBinary(target: BinaryTarget): boolean {
|
|
return target.platform === "darwin" && process.platform === "darwin";
|
|
}
|
|
|
|
async function runCommand(command: string[], cwd: string, env: NodeJS.ProcessEnv = Bun.env): Promise<void> {
|
|
const proc = Bun.spawn(command, {
|
|
cwd,
|
|
env,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
const exitCode = await proc.exited;
|
|
if (exitCode !== 0) {
|
|
throw new Error(`Command failed with exit code ${exitCode}: ${command.join(" ")}`);
|
|
}
|
|
}
|
|
|
|
async function embedNative(target: BinaryTarget): Promise<void> {
|
|
if (isDryRun) {
|
|
console.log(`DRY RUN bun --cwd=packages/natives run embed:native [${target.platform}/${target.arch}]`);
|
|
return;
|
|
}
|
|
|
|
await runCommand(["bun", "--cwd=packages/natives", "run", "embed:native"], repoRoot, {
|
|
...Bun.env,
|
|
TARGET_PLATFORM: target.platform,
|
|
TARGET_ARCH: target.arch,
|
|
});
|
|
}
|
|
|
|
async function buildBinary(target: BinaryTarget): Promise<void> {
|
|
console.log(`Building ${target.outfile}...`);
|
|
await embedNative(target);
|
|
if (isDryRun) {
|
|
console.log(`DRY RUN bun build --compile --no-compile-autoload-bunfig --no-compile-autoload-dotenv --no-compile-autoload-tsconfig --no-compile-autoload-package-json --minify-identifiers --keep-names --define process.env.PI_COMPILED="true" --root . --target=${target.target} ${entrypoint} --outfile ${target.outfile}`);
|
|
return;
|
|
}
|
|
|
|
const buildEnv = shouldAdhocSignDarwinBinary(target)
|
|
? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" }
|
|
: Bun.env;
|
|
await runCommand(
|
|
[
|
|
"bun",
|
|
"build",
|
|
"--compile",
|
|
"--no-compile-autoload-bunfig",
|
|
"--no-compile-autoload-dotenv",
|
|
"--no-compile-autoload-tsconfig",
|
|
"--no-compile-autoload-package-json",
|
|
"--minify-identifiers",
|
|
"--keep-names",
|
|
"--define",
|
|
'process.env.PI_COMPILED="true"',
|
|
"--root",
|
|
".",
|
|
"--target",
|
|
target.target,
|
|
entrypoint,
|
|
"--outfile",
|
|
target.outfile,
|
|
],
|
|
repoRoot,
|
|
buildEnv,
|
|
);
|
|
|
|
// Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
|
|
if (shouldAdhocSignDarwinBinary(target)) {
|
|
await runCommand(["codesign", "--force", "--sign", "-", path.join(repoRoot, target.outfile)], repoRoot);
|
|
}
|
|
}
|
|
|
|
async function generateBundle(): Promise<void> {
|
|
if (isDryRun) {
|
|
console.log("DRY RUN bun --cwd=packages/stats scripts/generate-client-bundle.ts --generate");
|
|
console.log("DRY RUN bun --cwd=packages/coding-agent scripts/generate-docs-index.ts --generate");
|
|
console.log("DRY RUN bun --cwd=packages/coding-agent scripts/embed-mupdf-wasm.ts --generate");
|
|
return;
|
|
}
|
|
await runCommand(["bun", "--cwd=packages/stats", "scripts/generate-client-bundle.ts", "--generate"], repoRoot);
|
|
await runCommand(["bun", "--cwd=packages/coding-agent", "scripts/generate-docs-index.ts", "--generate"], repoRoot);
|
|
await runCommand(["bun", "--cwd=packages/coding-agent", "scripts/embed-mupdf-wasm.ts", "--generate"], repoRoot);
|
|
}
|
|
|
|
async function resetArtifacts(): Promise<void> {
|
|
if (isDryRun) {
|
|
console.log("DRY RUN bun --cwd=packages/natives run embed:native --reset");
|
|
console.log("DRY RUN bun --cwd=packages/stats scripts/generate-client-bundle.ts --reset");
|
|
console.log("DRY RUN bun --cwd=packages/coding-agent scripts/generate-docs-index.ts --reset");
|
|
console.log("DRY RUN bun --cwd=packages/coding-agent scripts/embed-mupdf-wasm.ts --reset");
|
|
return;
|
|
}
|
|
await runCommand(["bun", "--cwd=packages/natives", "run", "embed:native", "--reset"], repoRoot);
|
|
await runCommand(["bun", "--cwd=packages/stats", "scripts/generate-client-bundle.ts", "--reset"], repoRoot);
|
|
await runCommand(["bun", "--cwd=packages/coding-agent", "scripts/generate-docs-index.ts", "--reset"], repoRoot);
|
|
await runCommand(["bun", "--cwd=packages/coding-agent", "scripts/embed-mupdf-wasm.ts", "--reset"], repoRoot);
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const requestedTargets = parseRequestedTargets();
|
|
const selectedTargets = requestedTargets
|
|
? targets.filter(target => requestedTargets.has(target.id))
|
|
: targets;
|
|
|
|
if (requestedTargets) {
|
|
const unknownTargets = [...requestedTargets].filter(
|
|
requestedTarget => !targets.some(target => target.id === requestedTarget),
|
|
);
|
|
if (unknownTargets.length > 0) {
|
|
throw new Error(`Unknown release target(s): ${unknownTargets.join(", ")}`);
|
|
}
|
|
}
|
|
|
|
if (selectedTargets.length === 0) {
|
|
throw new Error("No release targets selected.");
|
|
}
|
|
|
|
await fs.mkdir(binariesDir, { recursive: true });
|
|
// Generate inside the try so resetArtifacts() always restores the empty
|
|
// checked-in placeholders, even if a generate or build step throws.
|
|
try {
|
|
await generateBundle();
|
|
for (const target of selectedTargets) {
|
|
await buildBinary(target);
|
|
}
|
|
} finally {
|
|
await resetArtifacts();
|
|
}
|
|
}
|
|
|
|
await main();
|