← back to Petitionyour

deploy-kamatera.sh

98 lines

#!/bin/bash
# petitionyour.org — Kamatera deploy
#
# GATED — this script is NOT run automatically by any agent. Steve runs it
# himself (or approves the exact `!` commands in the paste-safe runbook).
#
# Pre-reqs this script does NOT do for you (see README.md "Deploy runbook"):
#   1. nginx vhost installed at /etc/nginx/sites-available/petitionyour.org
#      (copy deploy/nginx-petitionyour.org.conf) + `nginx -t` verified clean
#      + `systemctl reload nginx` — BEFORE requesting the cert (port 80 must
#      already answer petitionyour.org for certbot's HTTP-01 challenge).
#   2. Let's Encrypt cert via certbot for petitionyour.org + www.petitionyour.org
#   3. Node + pm2 already installed on Kamatera (they are, fleet-wide)
#
# What this script DOES (idempotent, safe to re-run):
#   1. rsync project to Kamatera at /root/public-projects/petitionyour-org
#   2. npm install --omit=dev
#   3. pm2 startOrReload ecosystem.config.js + pm2 save
#   4. 4-layer health check (origin direct / CF proxied / SSL valid / www)
#
# Usage:
#   ./deploy-kamatera.sh                # full deploy
#   ./deploy-kamatera.sh --dry-run      # show what would run, no changes
#   ./deploy-kamatera.sh --health-only  # only run the post-deploy health check

set -euo pipefail

KAMATERA="root@45.61.58.125"
LOCAL_DIR="$(cd "$(dirname "$0")" && pwd)"
REMOTE_DIR="/root/public-projects/petitionyour-org"
PM2_NAME="petitionyour-org"
DOMAIN="petitionyour.org"
PORT="9730"

DRY_RUN=false
HEALTH_ONLY=false
case "${1:-}" in
  --dry-run)     DRY_RUN=true ;;
  --health-only) HEALTH_ONLY=true ;;
  --help|-h)     sed -n '2,26p' "$0"; exit 0 ;;
esac

run() {
  if $DRY_RUN; then
    echo "[DRY-RUN] $*"
  else
    eval "$@"
  fi
}

health_check() {
  echo "=== health check (4 layers) ==="
  echo -n "  1. origin direct (HTTP, by Host header):  "
  curl -sI -o /dev/null -w "%{http_code}\n" -m 8 -H "Host: $DOMAIN" "http://45.61.58.125:$PORT/health" || echo FAIL
  echo -n "  2. CF proxied (HTTPS):                    "
  curl -sI -o /dev/null -w "%{http_code}\n" -m 8 "https://$DOMAIN/" || echo FAIL
  echo -n "  3. SSL cert valid (openssl):               "
  echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null || echo FAIL
  echo -n "  4. www subdomain:                          "
  curl -sI -o /dev/null -w "%{http_code}\n" -m 8 "https://www.$DOMAIN/" || echo FAIL
  echo -n "  pm2 status:                                "
  ssh "$KAMATERA" "pm2 jlist 2>/dev/null | python3 -c 'import sys,json; ps=[p for p in json.load(sys.stdin) if p[\"name\"]==\"'$PM2_NAME'\"]; print(ps[0][\"pm2_env\"][\"status\"] if ps else \"not registered\")'" 2>/dev/null
}

if $HEALTH_ONLY; then
  health_check
  exit 0
fi

echo "=== checking SSH to Kamatera ==="
run "ssh -o ConnectTimeout=8 $KAMATERA 'hostname && uptime' || (echo 'SSH failed'; exit 1)"

echo ""
echo "=== rsync to $REMOTE_DIR ==="
run "ssh $KAMATERA 'mkdir -p $REMOTE_DIR'"
run "rsync -az --delete --exclude=node_modules --exclude=.git --exclude='*.log' --exclude='.env' --exclude='logs/' '$LOCAL_DIR/' '$KAMATERA:$REMOTE_DIR/'"

echo ""
echo "=== npm install --omit=dev ==="
run "ssh $KAMATERA 'cd $REMOTE_DIR && npm install --omit=dev --no-fund --no-audit'"

echo ""
echo "=== pm2 ==="
if $DRY_RUN; then
  echo "[DRY-RUN] ssh $KAMATERA 'cd $REMOTE_DIR && pm2 startOrReload ecosystem.config.js && pm2 save'"
else
  ssh "$KAMATERA" "cd $REMOTE_DIR && pm2 startOrReload ecosystem.config.js && pm2 save"
fi

echo ""
sleep 3
health_check

echo ""
echo "=== deploy complete ==="
echo "Site: https://$DOMAIN/"
echo "pm2:  ssh $KAMATERA 'pm2 logs $PM2_NAME --lines 20'"