Files
oh-my-pi/packages/stats/scripts/generate-client-bundle.ts
T
can1357 35aa15ae94 fix(stats): generated tar bundle for compiled dashboard assets
Generate a tar.gz bundle from stats dist/client during binary builds, embed it into the compiled binary, and extract/serve it at runtime in compiled mode instead of reading //root/client directly.

Updated CI and build:binary flow to generate/reset the embedded bundle around compilation.

Fixes #61
2026-02-15 07:23:40 +01:00

83 lines
2.5 KiB
TypeScript

#!/usr/bin/env bun
import { $ } from "bun";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";
const GENERATED_FILE = path.join("src", "embedded-client.generated.ts");
const DIST_CLIENT_DIR = path.join("dist", "client");
function placeholderContent(): string {
return `/**
* Embedded stats dashboard bundle for compiled binaries.
*
* This file is generated by \`bun --cwd=packages/stats scripts/generate-client-bundle.ts\` during
* binary builds. The checked-in value is intentionally empty.
*/
export const EMBEDDED_CLIENT_ARCHIVE_TAR_GZ_BASE64 = "";
`;
}
async function collectFiles(dir: string): Promise<string[]> {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files: string[] = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...(await collectFiles(fullPath)));
} else if (entry.isFile()) {
files.push(fullPath);
}
}
files.sort((a, b) => a.localeCompare(b));
return files;
}
async function buildArchiveBase64(dir: string): Promise<string> {
const files = await collectFiles(dir);
const entries: Record<string, Uint8Array> = {};
for (const filePath of files) {
const relativePath = path.relative(dir, filePath).split(path.sep).join("/");
entries[relativePath] = await fs.readFile(filePath);
}
const tempArchivePath = path.join(
os.tmpdir(),
`omp-stats-client-${Bun.hash(Date.now().toString() + Math.random().toString(16)).toString(16)}.tar.gz`,
);
try {
await Bun.Archive.write(tempArchivePath, entries, { compress: "gzip" });
const archiveBytes = await Bun.file(tempArchivePath).bytes();
return Buffer.from(archiveBytes).toString("base64");
} finally {
await fs.rm(tempArchivePath, { force: true });
}
}
async function writeGeneratedBundle(base64Bundle: string): Promise<void> {
const content = `/**
* Embedded stats dashboard bundle for compiled binaries.
*
* Generated by \`bun --cwd=packages/stats scripts/generate-client-bundle.ts\`.
*/
export const EMBEDDED_CLIENT_ARCHIVE_TAR_GZ_BASE64 = ${JSON.stringify(base64Bundle)};
`;
await Bun.write(GENERATED_FILE, content);
}
async function main(): Promise<void> {
if (process.argv.includes("--reset")) {
await Bun.write(GENERATED_FILE, placeholderContent());
console.log(`Reset ${GENERATED_FILE}`);
return;
}
await $`bun run build`;
const archiveBase64 = await buildArchiveBase64(DIST_CLIENT_DIR);
await writeGeneratedBundle(archiveBase64);
console.log(`Generated ${GENERATED_FILE}`);
}
await main();