Re-sign electrobun's CLI binary so the app actually launches

`bun run start` printed nothing and exited 0 without ever opening a window.
electrobun's prebuilt CLI is a Bun single-file executable whose payload is
appended after the linker ad-hoc signed it, so `codesign -v` reports "code or
signature have been modified" and the macOS kernel SIGKILLs it at exec. The
failure was invisible because bin/electrobun.cjs spawns the real binary and
forwards `process.exit(code || 0)` — a signal death has `code === null`.

Confirmed upstream, not local corruption: a fresh download from the GitHub
release hashes identical to the shipped copy and fails the same check. Both
the darwin-arm64 and darwin-x64 CLI tarballs are affected.

scripts/fix-electrobun-cli.ts re-seals the binary with `codesign --force
--sign -`. It runs from postinstall AND ahead of every electrobun invocation,
because the wrapper downloads the CLI lazily on first use rather than at
install time — postinstall alone would no-op on a fresh checkout. When the
binary is missing it provokes that download first, so a clean tree self-heals.
It also fixes .cache/electrobun, which the wrapper restores bin/ from, and
release.ts re-signs the Rosetta x64 CLI on download and on cache reuse.

Idempotent (~0.3s when already valid) and a no-op off macOS. Verified from a
deleted-binary state: `bun run start` downloads, re-signs and launches.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-09-22 13:10:28 +02:00
co-authored by Claude
parent fc03fa6216
commit 6c05e39ce1
5 changed files with 88 additions and 7 deletions
+9 -1
View File
@@ -9,6 +9,7 @@
import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { ensureSigned } from "./fix-electrobun-cli";
const ROOT = path.join(import.meta.dir, "..");
const TARGETS = ["mac-arm64", "mac-x64", "win-x64"] as const;
@@ -51,6 +52,7 @@ const viaRosetta = requested === "mac-x64" && process.arch === "arm64";
// ---- prepare: version, icons, UI bundle ----
run(["bun", "scripts/gen-version.ts"]);
run(["bun", "scripts/fix-electrobun-cli.ts"]);
run(["bun", "scripts/gen-icons.ts"]);
run(["bun", "run", "--cwd", "ui", "build"]);
@@ -64,7 +66,12 @@ async function x64Cli(): Promise<string> {
const pkg = await Bun.file(path.join(ROOT, "node_modules", "electrobun", "package.json")).json();
const dir = path.join(os.homedir(), ".cache", "miniscp", `electrobun-cli-x64-${pkg.version}`);
const bin = path.join(dir, "electrobun");
if (existsSync(bin)) return bin;
// Same broken upstream ad-hoc signature as the arm64 CLI (see
// scripts/fix-electrobun-cli.ts) — unsigned it is SIGKILLed on exec.
if (existsSync(bin)) {
if (!ensureSigned(bin)) fail(`x64 CLI at ${bin} has an unusable code signature`);
return bin;
}
console.log(`[release] downloading electrobun x64 CLI ${pkg.version} (one-time)…`);
mkdirSync(dir, { recursive: true });
const url = `https://github.com/blackboardsh/electrobun/releases/download/v${pkg.version}/electrobun-cli-darwin-x64.tar.gz`;
@@ -73,6 +80,7 @@ async function x64Cli(): Promise<string> {
run(["tar", "-xzf", tarball, "-C", dir]);
if (!existsSync(bin)) fail(`x64 CLI tarball did not contain "electrobun" (${dir})`);
run(["chmod", "+x", bin]);
if (!ensureSigned(bin)) fail(`x64 CLI at ${bin} has an unusable code signature`);
return bin;
}