2026-05-21 21:42:43 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# RemoteRig — Pi-side deploy script
|
|
|
|
|
# Deploys a new binary with backup, health-check, and automatic rollback.
|
|
|
|
|
#
|
|
|
|
|
# Usage:
|
|
|
|
|
# sudo ./deploy.sh [BINARY_PATH] [DEPLOY_PATH] [SERVICE_NAME]
|
|
|
|
|
#
|
|
|
|
|
# Defaults:
|
|
|
|
|
# BINARY_PATH = ./remoterig (new binary to deploy)
|
|
|
|
|
# DEPLOY_PATH = /opt/remoterig/remoterig
|
|
|
|
|
# SERVICE_NAME = remoterig
|
|
|
|
|
#
|
|
|
|
|
# Examples:
|
|
|
|
|
# # Deploy locally-built binary with defaults
|
|
|
|
|
# sudo ./deploy.sh ./remoterig
|
|
|
|
|
#
|
|
|
|
|
# # Custom paths
|
|
|
|
|
# sudo ./deploy.sh /tmp/remoterig-arm64 /opt/remoterig/remoterig remoterig
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Args
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
BINARY="${1:-remoterig}"
|
|
|
|
|
DEPLOY_PATH="${2:-/opt/remoterig/remoterig}"
|
|
|
|
|
SERVICE="${3:-remoterig}"
|
|
|
|
|
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
|
|
|
|
|
BACKUP="${DEPLOY_PATH}.${TIMESTAMP}.bak"
|
|
|
|
|
MAX_BACKUPS=3
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Helpers
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info() { echo "[INFO] $*"; }
|
|
|
|
|
ok() { echo "[OK] $*"; }
|
|
|
|
|
fail() { echo "[FAIL] $*" >&2; }
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Pre-flight checks
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
|
|
|
echo "ERROR: must run as root (sudo ./deploy.sh ...)" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ ! -f "${BINARY}" ]; then
|
|
|
|
|
fail "Binary not found: ${BINARY}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo "=============================================="
|
|
|
|
|
echo " RemoteRig Deploy"
|
|
|
|
|
echo " Binary: ${BINARY}"
|
|
|
|
|
echo " Deploy path: ${DEPLOY_PATH}"
|
|
|
|
|
echo " Service: ${SERVICE}"
|
|
|
|
|
echo " Timestamp: ${TIMESTAMP}"
|
|
|
|
|
echo "=============================================="
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 1. Backup existing binary
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info "Backing up current binary..."
|
|
|
|
|
if [ -f "${DEPLOY_PATH}" ]; then
|
|
|
|
|
cp "${DEPLOY_PATH}" "${BACKUP}"
|
|
|
|
|
ok "Backed up to ${BACKUP}"
|
|
|
|
|
else
|
|
|
|
|
info "No existing binary at ${DEPLOY_PATH} — fresh install"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 2. Deploy new binary
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info "Deploying new binary..."
|
2026-06-05 11:57:15 -04:00
|
|
|
# Atomic replace: copy alongside then rename over the target. A plain
|
|
|
|
|
# cp over a running executable fails with "Text file busy"; rename swaps
|
|
|
|
|
# the directory entry and works while the old binary is still running.
|
|
|
|
|
cp "${BINARY}" "${DEPLOY_PATH}.new"
|
|
|
|
|
chmod +x "${DEPLOY_PATH}.new"
|
|
|
|
|
mv -f "${DEPLOY_PATH}.new" "${DEPLOY_PATH}"
|
2026-05-21 21:42:43 +00:00
|
|
|
ok "Binary installed at ${DEPLOY_PATH}"
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 3. Reload systemd and restart service
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info "Reloading systemd and restarting ${SERVICE}..."
|
|
|
|
|
systemctl daemon-reload
|
|
|
|
|
|
|
|
|
|
# Restart (or start if not running)
|
|
|
|
|
if systemctl is-active --quiet "${SERVICE}" 2>/dev/null; then
|
|
|
|
|
systemctl restart "${SERVICE}"
|
|
|
|
|
else
|
|
|
|
|
systemctl start "${SERVICE}"
|
|
|
|
|
fi
|
|
|
|
|
ok "Service restart issued"
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 4. Health check
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info "Waiting 3s for service to stabilize..."
|
|
|
|
|
sleep 3
|
|
|
|
|
|
|
|
|
|
if systemctl is-active --quiet "${SERVICE}"; then
|
|
|
|
|
ok "${SERVICE} is active — deploy successful"
|
|
|
|
|
|
|
|
|
|
# Optional: curl health endpoint
|
|
|
|
|
if command -v curl >/dev/null 2>&1; then
|
|
|
|
|
HEALTH_URL="http://localhost:8080/health"
|
|
|
|
|
if curl -sf --max-time 3 "${HEALTH_URL}" >/dev/null 2>&1; then
|
|
|
|
|
ok "Health check passed: ${HEALTH_URL}"
|
|
|
|
|
else
|
|
|
|
|
info "Health endpoint not reachable (may need more startup time)"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
fail "${SERVICE} is NOT active — rolling back"
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
# 5. Rollback on failure
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
if [ -f "${BACKUP}" ]; then
|
|
|
|
|
info "Restoring backup: ${BACKUP}"
|
2026-06-05 11:57:15 -04:00
|
|
|
cp "${BACKUP}" "${DEPLOY_PATH}.new"
|
|
|
|
|
chmod +x "${DEPLOY_PATH}.new"
|
|
|
|
|
mv -f "${DEPLOY_PATH}.new" "${DEPLOY_PATH}"
|
2026-05-21 21:42:43 +00:00
|
|
|
|
|
|
|
|
systemctl restart "${SERVICE}" 2>/dev/null || true
|
|
|
|
|
|
|
|
|
|
sleep 2
|
|
|
|
|
if systemctl is-active --quiet "${SERVICE}"; then
|
|
|
|
|
ok "Rollback successful — previous binary restored and service is active"
|
|
|
|
|
else
|
|
|
|
|
fail "Rollback failed — service still not active"
|
|
|
|
|
echo "Check logs: journalctl -u ${SERVICE} -n 50" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
fail "No backup available — cannot roll back"
|
|
|
|
|
echo "Check logs: journalctl -u ${SERVICE} -n 50" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 6. Cleanup old backups (keep last N)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
info "Cleaning up old backups (keeping last ${MAX_BACKUPS})..."
|
|
|
|
|
DEPLOY_DIR="$(dirname "${DEPLOY_PATH}")"
|
|
|
|
|
BASE_NAME="$(basename "${DEPLOY_PATH}")"
|
|
|
|
|
|
|
|
|
|
# List backups, skip current, keep last MAX_BACKUPS, delete the rest
|
|
|
|
|
ls -1t "${DEPLOY_DIR}/${BASE_NAME}."*.bak 2>/dev/null | \
|
|
|
|
|
tail -n +$((MAX_BACKUPS + 1)) | \
|
|
|
|
|
while IFS= read -r old_backup; do
|
|
|
|
|
rm -f "${old_backup}"
|
|
|
|
|
info "Removed old backup: $(basename "${old_backup}")"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
ok "Deploy complete — ${MAX_BACKUPS} backups retained"
|
|
|
|
|
echo ""
|