← back to Rentv

deploy.sh

95 lines

#!/bin/zsh
# ─────────────────────────────────────────────────────────────────────────────
# deploy.sh — one-command RENTV deploy to the LIVE Kamatera box.
#
# Does the three things a bare file-copy can't:
#   1. rsync CODE local→prod   (excludes prod-canonical data/ + .env + junk)
#   2. apply any PENDING DB migrations as the postgres SUPERUSER (pr_app can't DDL)
#   3. pm2 reload rentv + rentv-pr-worker so new routes/services load
# then a health check. Idempotent + forward-only; safe to re-run.
#
# Usage:  ./deploy.sh              # full: code + migrations + reload
#         ./deploy.sh --no-migrate # skip the migration step
#         ./deploy.sh --code-only  # rsync only (no migrate, no reload)
# Cost: $0 (rsync/ssh/psql). Called automatically by .git/hooks/post-commit on
# code changes; can also be run by hand.
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

HOST="root@45.61.58.125"
RDIR="/root/public-projects/rentv"
KEY="$HOME/.ssh/id_ed25519_wallco_20260530"
SSHOPTS=(-i "$KEY" -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new -o BatchMode=yes)
SRC="$HOME/Projects/rentv"
PORT=9704
LOG="$SRC/logs/deploy.log"
mkdir -p "$SRC/logs"
stamp() { date -u +%FT%TZ }
say() { echo "$(stamp) $*" | tee -a "$LOG"; }

DO_MIGRATE=1; DO_RELOAD=1
for a in "$@"; do
  case "$a" in
    --no-migrate) DO_MIGRATE=0 ;;
    --code-only)  DO_MIGRATE=0; DO_RELOAD=0 ;;
  esac
done

say "DEPLOY start → $HOST:$RDIR"

# ── 1. CODE rsync ────────────────────────────────────────────────────────────
# Exclude prod-canonical data/ (deals.json etc. are grown BY prod crons), prod's
# own .env, and all junk. --delete is intentionally OFF so we never remove a
# prod-only file. Code only.
rsync -az --checksum -e "ssh ${SSHOPTS[*]}" \
  --exclude '.git/' --exclude 'node_modules/' --exclude 'data/' \
  --exclude '.env' --exclude '.env.*' --exclude 'logs/' --exclude 'tmp/' \
  --exclude '*.log' --exclude '*.jar' --exclude '.DS_Store' \
  "$SRC/" "$HOST:$RDIR/" >> "$LOG" 2>&1
say "  ✓ code synced"

# ── 2. Pending migrations (superuser) ────────────────────────────────────────
# Apply, IN FILENAME ORDER, any *.sql in src/pr/migrations/ not yet in
# pr_migrations — as the postgres superuser (root reads the file via stdin, the
# postgres role executes the DDL). Records each on success. Forward-only.
if [ "$DO_MIGRATE" = 1 ]; then
  ssh "${SSHOPTS[@]}" "$HOST" bash -s <<'REMOTE' >> "$LOG" 2>&1
set -euo pipefail
cd /root/public-projects/rentv/src/pr/migrations
for f in $(ls *.sql | sort); do
  applied=$(sudo -u postgres psql -d rentv_pr -tAc "SELECT 1 FROM pr_migrations WHERE name='$f'" 2>/dev/null || echo)
  if [ "$applied" != "1" ]; then
    echo "  applying migration $f"
    sudo -u postgres psql -d rentv_pr -v ON_ERROR_STOP=1 < "$f"
    sudo -u postgres psql -d rentv_pr -c "INSERT INTO pr_migrations(name) VALUES ('$f') ON CONFLICT DO NOTHING" >/dev/null
  fi
done
echo "  ✓ migrations up to date"
REMOTE
  say "  ✓ migrations applied (if any)"
fi

# ── 3. pm2 reload ────────────────────────────────────────────────────────────
# Reload each proc SEPARATELY — a combined `pm2 reload a b` was observed to skip
# the second app. rentv is fork-mode, so reload = a brief restart (handled by the
# retrying health check below).
if [ "$DO_RELOAD" = 1 ]; then
  ssh "${SSHOPTS[@]}" "$HOST" 'pm2 reload rentv --update-env >/dev/null 2>&1; pm2 reload rentv-pr-worker --update-env >/dev/null 2>&1; echo reloaded' >> "$LOG" 2>&1
  say "  ✓ pm2 reloaded (rentv + rentv-pr-worker)"
fi

# ── 4. Health check (retry through the fork-mode reload window) ──────────────
# Anon hit is expected to 401 (the auth gate) — that means the app is up + gated.
CODE=000
for i in 1 2 3 4 5 6; do
  sleep 2
  CODE=$(ssh "${SSHOPTS[@]}" "$HOST" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:$PORT/api/pr/health" 2>/dev/null || echo 000)
  { [ "$CODE" = 401 ] || [ "$CODE" = 200 ]; } && break
done
if [ "$CODE" = 401 ] || [ "$CODE" = 200 ]; then
  say "DEPLOY ok — app healthy (health=$CODE)"
else
  say "DEPLOY WARN — health=$CODE (expected 401/200); check pm2 logs rentv"
  exit 1
fi