mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
chore(desktop): rebuild CLI on every bundle-cli run (#999)
bundle-cli.mjs now invokes `go build` with the same ldflags as `make build` (version/commit/date) before copying the binary into resources/bin/. Running this on every `pnpm dev:desktop`, `dev:remote` and `package` guarantees the bundled CLI matches the current Go source, so you can't accidentally ship a stale binary after editing server/ code. Go's build cache makes no-op builds ~a few hundred ms. Graceful fallback preserved: if `go` is not on PATH (frontend-only contributor), we warn, skip the build, and let cli-bootstrap download the latest release at runtime. Compile errors remain fatal so broken Go code blocks dev rather than silently falling back. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,27 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
// Copies the locally-built `multica` CLI into apps/desktop/resources/bin/
|
||||
// so electron-vite (dev) and electron-builder (prod) pick it up. Desktop's
|
||||
// cli-bootstrap prefers this bundled copy over the GitHub Releases download
|
||||
// path, which keeps dev iteration fast (edit Go → make build → restart
|
||||
// Desktop) and lets the released .app ship with a CLI out of the box.
|
||||
// Builds the `multica` CLI from server/cmd/multica and copies the binary
|
||||
// into apps/desktop/resources/bin/ so electron-vite (dev) and electron-
|
||||
// builder (prod) pick it up. Running this on every dev/build/package
|
||||
// invocation guarantees the bundled CLI always matches the current Go
|
||||
// source — no more stale binary surprises. Go's build cache makes the
|
||||
// no-op case (nothing changed) effectively free.
|
||||
//
|
||||
// Graceful: if server/bin/multica is missing, prints a warning and exits
|
||||
// with status 0 so the Desktop can still boot with auto-install fallback.
|
||||
// ldflags mirror `make build` so `multica --version` reports a meaningful
|
||||
// version / commit / date.
|
||||
//
|
||||
// Graceful: if `go` is not installed (e.g. frontend-only contributor), we
|
||||
// skip the build and fall through to auto-install at runtime. A genuine
|
||||
// Go compile error is fatal — you want that to block dev, not hide.
|
||||
|
||||
import { access, chmod, copyFile, mkdir } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
import { execSync } from "node:child_process";
|
||||
import { execFileSync, execSync } from "node:child_process";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = resolve(here, "..", "..", "..");
|
||||
const serverDir = join(repoRoot, "server");
|
||||
|
||||
const binName = process.platform === "win32" ? "multica.exe" : "multica";
|
||||
const srcBinary = join(repoRoot, "server", "bin", binName);
|
||||
const srcBinary = join(serverDir, "bin", binName);
|
||||
const destDir = join(repoRoot, "apps", "desktop", "resources", "bin");
|
||||
const destBinary = join(destDir, binName);
|
||||
|
||||
function sh(cmd) {
|
||||
try {
|
||||
return execSync(cmd, { encoding: "utf-8" }).trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function hasGo() {
|
||||
try {
|
||||
execSync("go version", { stdio: "pipe" });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function exists(p) {
|
||||
try {
|
||||
await access(p, constants.F_OK);
|
||||
@@ -31,10 +54,39 @@ async function exists(p) {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasGo()) {
|
||||
const version = sh("git describe --tags --always --dirty") || "dev";
|
||||
const commit = sh("git rev-parse --short HEAD") || "unknown";
|
||||
const date = new Date().toISOString().replace(/\.\d+Z$/, "Z");
|
||||
const ldflags = `-X main.version=${version} -X main.commit=${commit} -X main.date=${date}`;
|
||||
|
||||
console.log(
|
||||
`[bundle-cli] go build → ${srcBinary} (version=${version} commit=${commit})`,
|
||||
);
|
||||
execFileSync(
|
||||
"go",
|
||||
[
|
||||
"build",
|
||||
"-ldflags",
|
||||
ldflags,
|
||||
"-o",
|
||||
join("bin", binName),
|
||||
"./cmd/multica",
|
||||
],
|
||||
{ cwd: serverDir, stdio: "inherit" },
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
"[bundle-cli] `go` not found in PATH — skipping CLI build. " +
|
||||
"Desktop will use whatever is already in resources/bin/, or fall back " +
|
||||
"to auto-installing the latest release at runtime.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await exists(srcBinary))) {
|
||||
console.warn(
|
||||
`[bundle-cli] ${srcBinary} not found — run 'make build' to bundle the CLI. ` +
|
||||
`Desktop will fall back to auto-installing the latest release at runtime.`,
|
||||
`[bundle-cli] ${srcBinary} not present — Desktop will fall back to ` +
|
||||
`auto-installing the latest release at runtime.`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -51,7 +103,7 @@ if (process.platform === "darwin") {
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch {
|
||||
// Non-fatal. Unsigned binaries still run when the parent is trusted.
|
||||
// Non-fatal. Unsigned binaries still run when the parent app is trusted.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user