86b9d069b1
1145 narrowed local-release to install-only (binary + codesign-heal wrapper) and removed the cargo build + gateway hot-restart steps that the script used to do. That broke the "rebuild the gateway" muscle memory: running script/local-release no longer rebuilt or restarted anything, just re-installed the same binary. Restore the build + restart logic while keeping 1145's wrapper: - `cargo build --release --bin huskies` before install - Snapshot the prior binary to ~/bin/huskies-bin.prev for rollback - Print PREV → NEW version delta after install - Detect a running `huskies .*--gateway` process and SSH-safe-restart it (kill descendants depth-first, then nohup the wrapper from the detached subshell) - Wait up to 10s for the new gateway PID to appear; on timeout, roll back to the previous binary and try to relaunch it - Refuse to restart when more than one --gateway process matches, so we don't kill the wrong tree - `--skip-check` bypasses script/check for already-verified changes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
166 lines
5.4 KiB
Bash
Executable File
166 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build huskies, install (codesign-heal wrapper + underlying binary), and if a
|
|
# gateway is running on this host, hot-restart it detached from the current shell
|
|
# so SSH disconnect — e.g. when redeploying from a phone — doesn't kill it.
|
|
#
|
|
# Skips the restart silently if no gateway is running. Errors loudly if more
|
|
# than one matches, so we don't restart the wrong one.
|
|
#
|
|
# Pass --skip-check to bypass `script/check` (useful for docs / build-script
|
|
# changes you've already verified).
|
|
#
|
|
# On relaunch failure the previous binary is restored from
|
|
# ~/bin/huskies-bin.prev and re-launched, so a bad deploy doesn't leave the
|
|
# host without a working gateway.
|
|
#
|
|
# After a `cp` or download the binary loses its ad-hoc signature and macOS
|
|
# SIGKILLs it silently on Apple Silicon. The wrapper at ~/bin/huskies re-signs
|
|
# the underlying binary at ~/bin/huskies-bin whenever codesign validation
|
|
# fails, then execs it. Normal launches (already signed) are zero-overhead.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
LOG_DIR="${HUSKIES_LOG_DIR:-$PROJECT_ROOT/logs}"
|
|
GATEWAY_PATTERN='huskies .*--gateway'
|
|
BIN_DIR="${HOME}/bin"
|
|
UNDERLYING="${BIN_DIR}/huskies-bin"
|
|
WRAPPER="${BIN_DIR}/huskies"
|
|
PREV_BIN="${BIN_DIR}/huskies-bin.prev"
|
|
NEW_BIN="${PROJECT_ROOT}/target/release/huskies"
|
|
|
|
SKIP_CHECK=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--skip-check) SKIP_CHECK=1 ;;
|
|
-h|--help) sed -n '2,17p' "$0"; exit 0 ;;
|
|
*) echo "Unknown arg: $arg (use --help)" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [ "$SKIP_CHECK" -eq 0 ] && [ -x "$SCRIPT_DIR/check" ]; then
|
|
echo "=== Running script/check ==="
|
|
"$SCRIPT_DIR/check"
|
|
fi
|
|
|
|
echo "=== Building release binary ==="
|
|
cd "$PROJECT_ROOT"
|
|
cargo build --release --bin huskies
|
|
|
|
mkdir -p "$BIN_DIR"
|
|
|
|
# Snapshot current binary so we can roll back if the relaunch fails.
|
|
PREV_VERSION=""
|
|
if [ -x "$UNDERLYING" ]; then
|
|
PREV_VERSION="$("$UNDERLYING" --version 2>/dev/null || echo unknown)"
|
|
cp "$UNDERLYING" "$PREV_BIN"
|
|
fi
|
|
|
|
cp "$NEW_BIN" "$UNDERLYING"
|
|
chmod +x "$UNDERLYING"
|
|
codesign -s - -f "$UNDERLYING" 2>/dev/null
|
|
NEW_VERSION="$("$UNDERLYING" --version 2>/dev/null || echo unknown)"
|
|
echo "==> Installed binary: ${UNDERLYING}"
|
|
if [ -n "$PREV_VERSION" ]; then
|
|
echo " version: $PREV_VERSION → $NEW_VERSION"
|
|
else
|
|
echo " version: $NEW_VERSION (no prior install)"
|
|
fi
|
|
|
|
cat > "${WRAPPER}" << 'WRAPPER_EOF'
|
|
#!/usr/bin/env bash
|
|
# Codesign-heal wrapper — re-signs ~/bin/huskies-bin if the signature is
|
|
# missing or invalid, then execs the binary. Logs only when it re-signs.
|
|
BIN="${HOME}/bin/huskies-bin"
|
|
if ! codesign --verify --quiet "${BIN}" 2>/dev/null; then
|
|
codesign -s - "${BIN}"
|
|
echo "[codesign-heal] re-signed ~/bin/huskies-bin" >&2
|
|
fi
|
|
exec "${BIN}" "$@"
|
|
WRAPPER_EOF
|
|
chmod +x "${WRAPPER}"
|
|
echo "==> Installed wrapper: ${WRAPPER}"
|
|
|
|
# ── Hot-restart gateway if one is running ─────────────────────────────
|
|
collect_descendants() {
|
|
local pid="$1" kid
|
|
for kid in $(pgrep -P "$pid" 2>/dev/null); do
|
|
collect_descendants "$kid"
|
|
printf '%s\n' "$kid"
|
|
done
|
|
}
|
|
|
|
GATEWAY_PIDS="$(pgrep -f "$GATEWAY_PATTERN" || true)"
|
|
if [ -z "$GATEWAY_PIDS" ]; then
|
|
echo "==> No running gateway found; install complete."
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$(echo "$GATEWAY_PIDS" | wc -l)" -gt 1 ]; then
|
|
echo "Error: multiple gateway processes match '${GATEWAY_PATTERN}':" >&2
|
|
ps -p $GATEWAY_PIDS -o pid,args >&2 || true
|
|
echo "Refusing to guess which to restart." >&2
|
|
exit 3
|
|
fi
|
|
|
|
GATEWAY_PID="$GATEWAY_PIDS"
|
|
GATEWAY_ARGS="$(ps -p "$GATEWAY_PID" -o args= | sed -E 's@^[^ ]*huskies[^ ]* @@')"
|
|
GATEWAY_CWD="$(lsof -p "$GATEWAY_PID" 2>/dev/null | awk '$4=="cwd"{print $9; exit}')"
|
|
if [ -z "$GATEWAY_CWD" ]; then GATEWAY_CWD="$PWD"; fi
|
|
|
|
LOG_FILE="$LOG_DIR/gateway-$(date +%Y%m%d-%H%M%S).log"
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
DESCENDANTS="$(collect_descendants "$GATEWAY_PID" | tr '\n' ' ')"
|
|
echo "==> Stopping gateway tree (pids: $GATEWAY_PID $DESCENDANTS)"
|
|
# Kill descendants depth-first so PTY children die before the gateway, then the gateway.
|
|
for pid in $DESCENDANTS $GATEWAY_PID; do
|
|
kill "$pid" 2>/dev/null || true
|
|
done
|
|
sleep 2
|
|
|
|
echo "==> Restarting gateway"
|
|
echo " log: $LOG_FILE"
|
|
(
|
|
cd "$GATEWAY_CWD"
|
|
nohup "$WRAPPER" $GATEWAY_ARGS >> "$LOG_FILE" 2>&1 < /dev/null &
|
|
disown
|
|
)
|
|
|
|
# Wait up to 10s for the new gateway to appear AND be a different PID.
|
|
NEW_PID=""
|
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
sleep 1
|
|
candidate="$(pgrep -f "$GATEWAY_PATTERN" 2>/dev/null || true)"
|
|
if [ -n "$candidate" ] && [ "$candidate" != "$GATEWAY_PID" ]; then
|
|
NEW_PID="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "$NEW_PID" ]; then
|
|
echo "==> Gateway restarted as pid $NEW_PID"
|
|
exit 0
|
|
fi
|
|
|
|
# ── Rollback ──────────────────────────────────────────────────────────
|
|
echo "Error: new gateway failed to come up within 10s; rolling back" >&2
|
|
if [ -x "$PREV_BIN" ]; then
|
|
cp "$PREV_BIN" "$UNDERLYING"
|
|
chmod +x "$UNDERLYING"
|
|
codesign -s - -f "$UNDERLYING" 2>/dev/null
|
|
echo "==> Restored previous binary"
|
|
(
|
|
cd "$GATEWAY_CWD"
|
|
nohup "$WRAPPER" $GATEWAY_ARGS >> "$LOG_FILE" 2>&1 < /dev/null &
|
|
disown
|
|
)
|
|
sleep 2
|
|
if pgrep -f "$GATEWAY_PATTERN" >/dev/null 2>&1; then
|
|
echo "==> Gateway restored to previous version"
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo "Error: rollback failed; gateway is DOWN. Inspect $LOG_FILE." >&2
|
|
exit 1
|