progress bar on builds (fuck you golang)

This commit is contained in:
KingKDot
2026-03-07 18:15:01 -06:00
parent 7b6fcf0132
commit b07f944370
+163 -14
View File
@@ -33,6 +33,35 @@ function resolveClientModuleDir(rootDir: string): string | null {
return null;
}
async function estimateBuildPackageCount(clientDir: string): Promise<number> {
try {
const result = await $`go list -deps -f '{{if not .Standard}}{{.ImportPath}}{{end}}' ./cmd/agent`
.cwd(clientDir)
.quiet()
.nothrow();
if (result.exitCode !== 0) {
return 0;
}
const packages = result.stdout
.toString()
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0);
return new Set(packages).size;
} catch {
return 0;
}
}
function renderProgressBar(percent: number, width = 24): string {
const clamped = Math.max(0, Math.min(100, percent));
const filled = Math.round((clamped / 100) * width);
return `[${"#".repeat(filled)}${"-".repeat(Math.max(0, width - filled))}]`;
}
type BuildProcessConfig = {
platforms: string[];
serverUrl?: string;
@@ -245,35 +274,150 @@ export async function startBuildProcess(
sendToStream({ type: "output", text: "Client printing disabled (noprint tag)\n", level: "info" });
}
const totalPackages = await estimateBuildPackageCount(clientDir);
if (totalPackages > 0) {
sendToStream({
type: "output",
text: `Estimated compile units: ${totalPackages} packages\n`,
level: "info",
});
}
try {
const buildTool = config.obfuscate ? "garble" : "go";
const verboseArg = "-x -v ";
const tagArg = config.noPrinting ? "-tags noprint " : "";
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${verboseArg}${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
logger.info(`[build:${buildId.substring(0, 8)}] Environment: GOOS=${os} GOARCH=${actualArch} CGO_ENABLED=${env.CGO_ENABLED} CC=${env.CC || "<default>"}`);
sendToStream({ type: "output", text: "Build trace enabled (-x -v)\n", level: "info" });
const planCmd = config.obfuscate
? (config.noPrinting
? (ldflags
? $`garble build -n -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -n -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
: (ldflags
? $`garble build -n -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -n -o ${outDir}/${outputName} ./cmd/agent`))
: (config.noPrinting
? (ldflags
? $`go build -n -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -n -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
: (ldflags
? $`go build -n -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -n -o ${outDir}/${outputName} ./cmd/agent`));
const planResult = await planCmd.env(env).cwd(clientDir).quiet().nothrow();
const plannedSteps = planResult.stdout
.toString()
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0).length;
if (plannedSteps > 0) {
sendToStream({
type: "output",
text: `Planned build steps (-n): ${plannedSteps}\n`,
level: "info",
});
}
const buildCmd = config.obfuscate
? (config.noPrinting
? (ldflags
? $`garble build -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
? $`garble build -x -v -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -x -v -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
: (ldflags
? $`garble build -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -o ${outDir}/${outputName} ./cmd/agent`))
? $`garble build -x -v -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`garble build -x -v -o ${outDir}/${outputName} ./cmd/agent`))
: (config.noPrinting
? (ldflags
? $`go build -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
? $`go build -x -v -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -x -v -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
: (ldflags
? $`go build -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -o ${outDir}/${outputName} ./cmd/agent`));
? $`go build -x -v -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
: $`go build -x -v -o ${outDir}/${outputName} ./cmd/agent`));
const proc = buildCmd.env(env).cwd(clientDir).nothrow();
for await (const line of proc.lines()) {
sendToStream({ type: "output", text: line + "\n", level: "info" });
const compiledPackages = new Set<string>();
const packageLinePattern = /^[A-Za-z0-9._~\-]+(?:\/[A-Za-z0-9._~\-]+)+$/;
let executedSteps = 0;
let lastBarPercent = -1;
const phaseStartedAt = Date.now();
const emitBuildProgressBar = (force = false) => {
const packagePercent =
totalPackages > 0
? Math.floor((compiledPackages.size / Math.max(1, totalPackages)) * 100)
: -1;
const stepPercent =
plannedSteps > 0
? Math.floor((executedSteps / Math.max(1, plannedSteps)) * 100)
: -1;
let percent = Math.max(packagePercent, stepPercent);
if (percent < 0) {
const elapsedMs = Date.now() - phaseStartedAt;
percent = Math.min(90, Math.floor(elapsedMs / 1500));
}
percent = Math.max(0, Math.min(99, percent));
if (!force && percent === lastBarPercent) {
return;
}
lastBarPercent = percent;
let detail = "working";
if (totalPackages > 0) {
detail = `pkgs ${Math.min(compiledPackages.size, totalPackages)}/${totalPackages}`;
} else if (plannedSteps > 0) {
detail = `steps ${Math.min(executedSteps, plannedSteps)}/${plannedSteps}`;
}
sendToStream({
type: "status",
text: `Compiling ${platform}: ${renderProgressBar(percent)} ${percent}% (${detail})`,
});
};
emitBuildProgressBar(true);
const progressBarTimer = setInterval(() => emitBuildProgressBar(false), 1000);
let result: any;
try {
for await (const line of proc.lines()) {
sendToStream({ type: "output", text: line + "\n", level: "info" });
if (line.trim().length > 0) {
executedSteps += 1;
}
const trimmed = line.trim();
if (packageLinePattern.test(trimmed) && !compiledPackages.has(trimmed)) {
compiledPackages.add(trimmed);
if (totalPackages > 0) {
const percent = Math.min(99, Math.floor((compiledPackages.size / totalPackages) * 100));
sendToStream({
type: "status",
text: `Compiling ${platform}: ${compiledPackages.size}/${totalPackages} packages (${percent}%)`,
});
} else {
sendToStream({
type: "status",
text: `Compiling ${platform}: ${compiledPackages.size} packages`,
});
}
}
emitBuildProgressBar(false);
}
result = await proc;
} finally {
clearInterval(progressBarTimer);
}
const result = await proc;
logger.info(`[build:${buildId.substring(0, 8)}] Process exited with code: ${result.exitCode}`);
if (result.exitCode !== 0) {
@@ -286,6 +430,11 @@ export async function startBuildProcess(
throw new Error(`Build failed for ${platform}`);
}
sendToStream({
type: "status",
text: `Compiling ${platform}: ${renderProgressBar(100)} 100% (complete)`,
});
const filePath = `${outDir}/${outputName}`;
const file = Bun.file(filePath);
const size = file.size;
@@ -297,7 +446,7 @@ export async function startBuildProcess(
size,
});
} catch (err: any) {
const errorMsg = `Γ£ù Failed to build ${platform}: ${err.message || err}\n`;
const errorMsg = `[ERROR] Failed to build ${platform}: ${err.message || err}\n`;
logger.error(`[build:${buildId.substring(0, 8)}] ${errorMsg.trim()}`);
sendToStream({ type: "output", text: errorMsg, level: "error" });
throw err;
@@ -306,7 +455,7 @@ export async function startBuildProcess(
build.status = "completed";
logger.info(`[build:${buildId.substring(0, 8)}] Build completed successfully! Built ${build.files.length} file(s)`);
sendToStream({ type: "output", text: `\nΓ£ô Build completed successfully!\n`, level: "success" });
sendToStream({ type: "output", text: `\n[OK] Build completed successfully!\n`, level: "success" });
sendToStream({ type: "complete", success: true, files: build.files, buildId, expiresAt: build.expiresAt });
saveBuild({