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:
@@ -0,0 +1,46 @@
|
||||
// Re-signs electrobun's prebuilt CLI binary, which ships with a BROKEN ad-hoc
|
||||
// signature: it is a Bun single-file executable whose payload is appended after
|
||||
// the linker signed it, so `codesign -v` reports "code or signature have been
|
||||
// modified" and the macOS kernel SIGKILLs it at exec. The failure is invisible:
|
||||
// electrobun's bin/electrobun.cjs wrapper spawns the real binary and forwards
|
||||
// `process.exit(code || 0)` — a signal death has code === null, so `bun run
|
||||
// start` printed nothing and exited 0 while never launching the app.
|
||||
// `codesign --force --sign -` re-seals it ad-hoc and it runs normally.
|
||||
//
|
||||
// The binary is downloaded LAZILY by that wrapper on first use, not by install,
|
||||
// so this runs both from postinstall and ahead of every electrobun invocation.
|
||||
// Idempotent, and a no-op off macOS.
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = path.join(import.meta.dir, "..");
|
||||
|
||||
/** true if `bin` has (or now has) a signature the kernel will accept. */
|
||||
export function ensureSigned(bin: string): boolean {
|
||||
if (process.platform !== "darwin" || !existsSync(bin)) return true;
|
||||
const valid = () => Bun.spawnSync(["codesign", "-v", bin], { stderr: "ignore" }).success;
|
||||
if (valid()) return true;
|
||||
const signed = Bun.spawnSync(["codesign", "--force", "--sign", "-", bin], { stderr: "pipe" });
|
||||
if (!signed.success || !valid()) {
|
||||
console.warn(`[electrobun-cli] could not re-sign ${path.relative(ROOT, bin)} — the app will not launch`);
|
||||
return false;
|
||||
}
|
||||
console.log(`[electrobun-cli] re-signed ${path.relative(ROOT, bin)} (upstream ad-hoc signature is invalid)`);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const pkgDir = path.join(ROOT, "node_modules", "electrobun");
|
||||
const bin = path.join(pkgDir, "bin", "electrobun");
|
||||
|
||||
// The wrapper downloads the CLI on first run and only then execs it, so one
|
||||
// throwaway invocation leaves a downloaded (still unsigned) binary behind
|
||||
// even though that exec is killed.
|
||||
if (process.platform === "darwin" && existsSync(pkgDir) && !existsSync(bin)) {
|
||||
Bun.spawnSync(["bunx", "electrobun", "--version"], { cwd: ROOT, stdout: "ignore", stderr: "ignore" });
|
||||
}
|
||||
|
||||
// .cache is what the wrapper restores bin/ from, so both copies need fixing.
|
||||
const ok = [bin, path.join(pkgDir, ".cache", "electrobun")].map(ensureSigned).every(Boolean);
|
||||
if (!ok) process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user