Skip to content

Commit edb517f

Browse files
authored
Re-sign compiled macOS binaries so bun compile output launches on macOS 27 (#984)
* fix(build): re-sign compiled macOS binary after bun compile `bun build --compile` appends the JS payload to Bun's linker-signed executable without re-signing it (oven-sh/bun#32159). The embedded signature no longer matches the file, and macOS 26+/27 SIGKILLs the binary at launch with exit 137 and no output, while `bun run start` keeps working because it never touches the signature. Ad-hoc re-sign `dist/corbits` in `build:bin` on Darwin hosts, and re-sign every `bun-darwin-*` target in the release script's compile_bin so cut releases launch on current macOS. * fix(release): fail smoke test on SIGKILLed binary The release smoke test only treated exit 126 and 127 as exec failures, so a compiled macOS binary killed by the kernel for an invalid code signature (exit 137) passed silently and would have shipped dead on arrival. Treat 137 as a fatal smoke failure.
1 parent dc46ba8 commit edb517f

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
},
3131
"scripts": {
3232
"build": "bun build ./src/index.ts --outdir ./dist --target bun --external '@opentui/core-*' && bun scripts/copy-repo-plugins.ts",
33-
"build:bin": "bun build ./src/index.ts --compile --minify --define process.env.NODE_ENV='\"production\"' --outfile ./dist/corbits && bun scripts/copy-repo-plugins.ts",
33+
"build:bin": "bun build ./src/index.ts --compile --minify --define process.env.NODE_ENV='\"production\"' --outfile ./dist/corbits && ([ \"$(uname -s)\" != Darwin ] || codesign -s - --force ./dist/corbits) && bun scripts/copy-repo-plugins.ts",
3434
"typecheck": "tsc --noEmit",
3535
"test": "bun test ./src ./tests ./evals ./scripts --randomize --seed 424242",
3636
"test:paths": "bun scripts/test-paths.ts",

scripts/release.sh

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,27 @@ host_label() {
137137
# node_modules; leaving it external makes first TUI load fail with
138138
# "Cannot find package 'react-devtools-core'".
139139
# Keep this block in sync with package.json "build:bin" when those flags change.
140+
#
141+
# macOS targets are ad-hoc re-signed after compile. `bun build --compile`
142+
# appends the JS payload to Bun's linker-signed executable without re-signing
143+
# it (oven-sh/bun#32159), so the embedded signature no longer matches the file
144+
# and macOS 26+/27 SIGKILLs the binary at launch (exit 137, no output).
145+
# `codesign` only exists on macOS hosts, which is where darwin releases are cut.
140146
compile_bin() { # compile_bin BUN_TARGET OUTFILE
141147
local target=$1 out=$2
142148
bun build ./src/index.ts --compile --target="$target" --minify \
143149
--define process.env.NODE_ENV='"production"' \
144150
--define process.env.DEV='"false"' \
145151
--outfile "$out" >/dev/null
152+
case "$target" in
153+
bun-darwin-*)
154+
if command -v codesign >/dev/null 2>&1; then
155+
codesign -s - --force "$out" >/dev/null 2>&1 \
156+
|| die "codesign failed for $target ($out)"
157+
else
158+
info "warning: codesign not available; $target binary is not re-signed and will not launch on macOS 26+"
159+
fi ;;
160+
esac
146161
}
147162

148163
# Smoke-test a freshly compiled native binary before packaging. Cross-compiled
@@ -161,7 +176,9 @@ smoke_bin() { # smoke_bin LABEL BINARY
161176
local rc=0
162177
"$bin" --__release_smoke__ >/dev/null 2>&1 || rc=$?
163178
# 126 = cannot execute, 127 = not found — real link/exec failures.
164-
if [ "$rc" -eq 126 ] || [ "$rc" -eq 127 ]; then
179+
# 137 = SIGKILL before any code ran: on macOS that is the kernel rejecting an
180+
# invalid code signature (see compile_bin), so the binary is dead on arrival.
181+
if [ "$rc" -eq 126 ] || [ "$rc" -eq 127 ] || [ "$rc" -eq 137 ]; then
165182
die "smoke: cannot execute $label binary (rc=$rc)"
166183
fi
167184
# Non-zero from "unrecognized flag" (or similar) still proves the binary ran.

0 commit comments

Comments
 (0)