script/local-release: restore build + hot-restart workflow
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>
This commit is contained in:
Regular → Executable
+138
-16
@@ -1,43 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install huskies locally on macOS: the underlying binary + a codesign-heal wrapper.
|
||||
# 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. This script installs the binary as
|
||||
# ~/bin/huskies-bin and installs a thin wrapper at ~/bin/huskies that
|
||||
# re-signs the underlying binary whenever codesign validation fails, then
|
||||
# execs it. Normal launches (already signed) are silent and zero-overhead.
|
||||
# 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)"
|
||||
BINARY_PATH="${SCRIPT_DIR}/target/release/huskies"
|
||||
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"
|
||||
|
||||
if [ ! -f "${BINARY_PATH}" ]; then
|
||||
echo "Error: binary not found at ${BINARY_PATH}"
|
||||
echo "Run: cargo build --release"
|
||||
exit 1
|
||||
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
|
||||
|
||||
mkdir -p "${BIN_DIR}"
|
||||
echo "=== Building release binary ==="
|
||||
cd "$PROJECT_ROOT"
|
||||
cargo build --release --bin huskies
|
||||
|
||||
cp "${BINARY_PATH}" "${UNDERLYING}"
|
||||
chmod +x "${UNDERLYING}"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user