Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
# 2.0.0") to the v2 TOML engine (control-socket daemon, ~/.boi/v2). The v3.0.0
# release supersedes the old 2.0.0 line (V1 lives only in git history now).

BOI_VERSION=v3.3.1
BOI_VERSION=v3.3.2
HARNESS_VERSION=v0.8.0
25 changes: 20 additions & 5 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,16 @@ install_or_upgrade_boi() {

# Fast path: the machine-owned build already provides the pinned version.
# (Also makes repeated install.sh runs — e.g. from test suites — no-ops.)
# `boi_bin` must be a REAL FILE, not a symlink (FIX-017, 2026-06-17): the old
# layout symlinked `boi_bin` → the cargo build output and rebuilt it in place,
# so a rebuild overwrote the very Mach-O the running daemon was mapped from →
# macOS AMFI invalidated the live process's code signature and SIGKILLed it
# ("Code Signature Invalid"). We now deploy a real-file copy via atomic
# rename. `[ ! -L ]` forces a one-time migration off any pre-existing symlink.
# `|| true` inside the substitution: a present-but-unrunnable binary (e.g.
# interrupted build) must fall through to the rebuild below, not errexit
# the whole installer.
if [ -x "$boi_bin" ] && \
[ "$(readlink "$boi_bin" 2>/dev/null)" = "$boi_build/target/release/boi" ]; then
if [ -x "$boi_bin" ] && [ ! -L "$boi_bin" ]; then
local current
current="v$("$boi_bin" --version 2>/dev/null | awk '/^boi /{print $2}' | tail -1 || true)"
if [ "$current" = "$BOI_VERSION" ]; then
Expand Down Expand Up @@ -418,9 +423,19 @@ install_or_upgrade_boi() {
tail -20 "$build_log" >&2 || true
return
}
# Symlink binary
ln -sf "$boi_build/target/release/boi" "$boi_bin"
echo " BOI $BOI_VERSION built and linked ✓"
# Deploy as a STABLE real file via atomic rename — never symlink to (or
# overwrite in place) the build output the running daemon is mapped from
# (FIX-017). The copy gets boi_bin a fresh inode; rename(2) is atomic, so
# a live daemon keeps its old inode alive until its next restart instead
# of being AMFI-SIGKILLed ("Code Signature Invalid"). Temp on the SAME
# filesystem (sibling path) so the rename cannot fall back to a copy.
local boi_tmp="$boi_bin.new.$$"
cp -f "$boi_build/target/release/boi" "$boi_tmp" && chmod +x "$boi_tmp" && mv -f "$boi_tmp" "$boi_bin" || {
echo " BOI: failed to install built binary to $boi_bin" >&2
rm -f "$boi_tmp" 2>/dev/null || true
return
}
echo " BOI $BOI_VERSION built and installed (atomic) ✓"
else
echo " ⚠️ Rust/cargo not found — cannot build BOI binary"
echo " Install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
Expand Down
Loading