#!/usr/bin/env bash # # Production deploy script for Prism v2. # # Runs on the production server, AFTER the working tree has been updated # (typically by a git post-receive hook that does `git checkout -f` into # /var/www/prism-v2). This script does NOT pull — it only builds + restarts. # # Usage: # sudo ./scripts/deploy.sh # build + restart + smoke-check # sudo ./scripts/deploy.sh --install # also install systemd units + nginx site # sudo ./scripts/deploy.sh --help set -euo pipefail APP_DIR="${APP_DIR:-/var/www/prism-v2}" APP_USER="${APP_USER:-www-data}" APP_GROUP="${APP_GROUP:-www-data}" DOMAIN="${DOMAIN:-prism.tylerhoang.xyz}" BACKEND_SVC="prismv2-backend.service" FRONTEND_SVC="prismv2-frontend.service" DO_INSTALL=0 usage() { sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//' exit 0 } for arg in "$@"; do case "$arg" in --install) DO_INSTALL=1 ;; -h|--help) usage ;; *) echo "Unknown arg: $arg" >&2; exit 2 ;; esac done log() { printf '\n=== %s ===\n' "$*"; } if [[ $EUID -ne 0 ]]; then echo "deploy.sh must be run as root (use sudo)" >&2 exit 1 fi if [[ ! -d "$APP_DIR/backend" || ! -d "$APP_DIR/frontend" ]]; then echo "Expected working tree at $APP_DIR (backend/ + frontend/ not found)" >&2 exit 1 fi cd "$APP_DIR" log "Ensuring ownership of $APP_DIR" chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" install -d -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR/frontend/.npm" install -d -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR/backend/data" run_as_app() { sudo -u "$APP_USER" \ env HOME="$APP_DIR/frontend" \ NPM_CONFIG_CACHE="$APP_DIR/frontend/.npm" \ "$@" } log "Backend: venv + dependencies" if [[ ! -x "$APP_DIR/backend/.venv/bin/pip" ]]; then sudo -u "$APP_USER" python3 -m venv "$APP_DIR/backend/.venv" fi sudo -u "$APP_USER" "$APP_DIR/backend/.venv/bin/pip" install --quiet --upgrade pip sudo -u "$APP_USER" "$APP_DIR/backend/.venv/bin/pip" install --quiet -r "$APP_DIR/backend/requirements.txt" log "Frontend: npm ci + build" run_as_app npm --prefix "$APP_DIR/frontend" ci run_as_app npm --prefix "$APP_DIR/frontend" run build if [[ $DO_INSTALL -eq 1 ]]; then log "Installing systemd units" cp "$APP_DIR/systemd/$BACKEND_SVC" "/etc/systemd/system/$BACKEND_SVC" cp "$APP_DIR/systemd/$FRONTEND_SVC" "/etc/systemd/system/$FRONTEND_SVC" systemctl daemon-reload systemctl enable "$BACKEND_SVC" "$FRONTEND_SVC" log "Installing nginx site" cp "$APP_DIR/nginx/$DOMAIN.conf" "/etc/nginx/sites-available/$DOMAIN" ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" nginx -t systemctl reload nginx fi log "Restarting services" systemctl restart "$BACKEND_SVC" "$FRONTEND_SVC" log "Smoke checks" sleep 2 if curl -fsS http://127.0.0.1:8001/health >/dev/null; then echo " backend /health OK" else echo " backend /health FAILED" >&2 journalctl -u "$BACKEND_SVC" -n 50 --no-pager >&2 || true exit 1 fi if curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3001/ | grep -qE '^(200|301|302|307|308)$'; then echo " frontend / OK" else echo " frontend / FAILED" >&2 journalctl -u "$FRONTEND_SVC" -n 50 --no-pager >&2 || true exit 1 fi log "Deploy complete" systemctl status "$BACKEND_SVC" "$FRONTEND_SVC" --no-pager --lines=0 || true