#!/bin/bash # ============================================================ # LLM Gateway — One-command deploy (run locally on Mac) # # Usage: # bash deploy/deploy.sh # bash deploy/deploy.sh --skip-build # skip local build # bash deploy/deploy.sh --health-only # just check remote health # ============================================================ set -euo pipefail ERIK_HOST="217.154.82.179" ERIK_USER="root" REMOTE_DIR="/opt/llm-gateway" GITEA_BRANCH="main" HEALTH_URL="http://${ERIK_HOST}:3100/health" HEALTH_URL_CF="https://llm-gateway.context-x.org/health" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; } section() { echo -e "\n${BLUE}>> $*${NC}"; } # Parse args SKIP_BUILD=false HEALTH_ONLY=false for arg in "$@"; do case $arg in --skip-build) SKIP_BUILD=true ;; --health-only) HEALTH_ONLY=true ;; *) warn "Unknown argument: $arg" ;; esac done # ------------------------------------------------------- # Health-only mode # ------------------------------------------------------- if [[ "$HEALTH_ONLY" == "true" ]]; then section "Remote health check" STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null || echo "000") BODY=$(curl -s "$HEALTH_URL" 2>/dev/null || echo "{}") echo " HTTP status: $STATUS" echo " Response: $BODY" [[ "$STATUS" == "200" ]] && info "Gateway is healthy." || warn "Gateway may be unhealthy." exit 0 fi # ------------------------------------------------------- # 0. Pre-deploy checks # ------------------------------------------------------- section "0. Pre-deploy checks" command -v npm >/dev/null || error "npm not found." command -v git >/dev/null || error "git not found." command -v ssh >/dev/null || error "ssh not found." command -v curl >/dev/null || error "curl not found." # Check we're on main (warn only — don't block) CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") if [[ "$CURRENT_BRANCH" != "$GITEA_BRANCH" ]]; then warn "Current branch is '$CURRENT_BRANCH', not '$GITEA_BRANCH'. Proceeding anyway." fi # Check for uncommitted changes if ! git diff --quiet 2>/dev/null; then warn "You have uncommitted changes. They will NOT be deployed." fi info "Pre-deploy checks passed." # ------------------------------------------------------- # 1. Local build # ------------------------------------------------------- section "1. Local build" if [[ "$SKIP_BUILD" == "true" ]]; then warn "Skipping local build (--skip-build)" else info "Running: npm run build" npm run build info "Build successful." fi # ------------------------------------------------------- # 2. Push to Gitea # ------------------------------------------------------- section "2. Pushing to Gitea" COMMIT_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") info "Pushing commit $COMMIT_SHA to Gitea ($GITEA_BRANCH)..." git push origin "$GITEA_BRANCH" info "Push complete." # ------------------------------------------------------- # 3. Deploy on Erik # ------------------------------------------------------- section "3. Deploying on Erik (${ERIK_HOST})" info "Connecting via SSH..." # shellcheck disable=SC2087 ssh -o ConnectTimeout=15 "${ERIK_USER}@${ERIK_HOST}" bash << 'REMOTE_SCRIPT' set -euo pipefail REMOTE_DIR="/opt/llm-gateway" LOG_DIR="/var/log/llm-gateway" echo "[remote] Pulling latest code..." cd "$REMOTE_DIR" git fetch origin git reset --hard origin/main echo "[remote] Installing dependencies..." npm install --prefer-offline 2>/dev/null || npm install echo "[remote] Building..." npm run build echo "[remote] Ensuring log directory..." mkdir -p "$LOG_DIR" echo "[remote] Restarting PM2 processes..." if pm2 list | grep -q "llm-gateway"; then pm2 reload llm-gateway llm-learning --update-env else echo "[remote] PM2 processes not found — starting from ecosystem config..." pm2 start deploy/ecosystem.config.cjs fi pm2 save echo "[remote] Deploy complete." REMOTE_SCRIPT info "Remote deploy finished." # ------------------------------------------------------- # 4. Post-deploy health check # ------------------------------------------------------- section "4. Post-deploy health check" MAX_RETRIES=8 RETRY_DELAY=5 info "Waiting ${RETRY_DELAY}s for restart to complete..." sleep $RETRY_DELAY for i in $(seq 1 $MAX_RETRIES); do STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$HEALTH_URL" 2>/dev/null || echo "000") if [[ "$STATUS" == "200" ]]; then BODY=$(curl -s --max-time 10 "$HEALTH_URL" 2>/dev/null || echo "{}") info "Health check PASSED (HTTP 200)" echo " $BODY" break fi if [[ $i -eq $MAX_RETRIES ]]; then warn "Health check did not return 200 after ${MAX_RETRIES} attempts." warn "Check logs on Erik: ssh root@${ERIK_HOST} 'pm2 logs llm-gateway --lines 50'" exit 1 fi info " Attempt $i/$MAX_RETRIES — HTTP $STATUS. Retrying in ${RETRY_DELAY}s..." sleep $RETRY_DELAY done # ------------------------------------------------------- # 5. Summary # ------------------------------------------------------- echo "" echo -e "${GREEN}Deploy successful!${NC}" echo "" echo " Commit: $COMMIT_SHA" echo " Direct: $HEALTH_URL" echo " Cloudflare: $HEALTH_URL_CF" echo " PM2 status: ssh root@${ERIK_HOST} 'pm2 status'" echo " Logs: ssh root@${ERIK_HOST} 'pm2 logs llm-gateway'" echo ""