#!/bin/bash # ============================================================================ # POUNCE ZERO-DOWNTIME DEPLOY SCRIPT # - Builds locally first (optional) # - Syncs only changed files # - Hot-reloads backend without full restart # - Rebuilds frontend in background # ============================================================================ set -e # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Server config SERVER_USER="user" SERVER_HOST="10.42.0.73" SERVER_PATH="/home/user/pounce" SERVER_PASS="user" # Parse flags QUICK_MODE=false BACKEND_ONLY=false FRONTEND_ONLY=false while [[ "$#" -gt 0 ]]; do case $1 in -q|--quick) QUICK_MODE=true ;; -b|--backend) BACKEND_ONLY=true ;; -f|--frontend) FRONTEND_ONLY=true ;; *) COMMIT_MSG="$1" ;; esac shift done echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}" echo -e "${BLUE} POUNCE ZERO-DOWNTIME DEPLOY ${NC}" echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}" if $QUICK_MODE; then echo -e "${CYAN}⚡ Quick mode: Skipping git, only syncing changes${NC}" fi if $BACKEND_ONLY; then echo -e "${CYAN}🔧 Backend only mode${NC}" fi if $FRONTEND_ONLY; then echo -e "${CYAN}🎨 Frontend only mode${NC}" fi # Step 1: Git (unless quick mode) if ! $QUICK_MODE; then echo -e "\n${YELLOW}[1/4] Git operations...${NC}" # Check for changes if git diff --quiet && git diff --staged --quiet; then echo " No changes to commit" else git add -A if [ -z "$COMMIT_MSG" ]; then COMMIT_MSG="Deploy: $(date '+%Y-%m-%d %H:%M')" fi git commit -m "$COMMIT_MSG" || true echo " ✓ Committed: $COMMIT_MSG" fi git push origin main 2>/dev/null && echo " ✓ Pushed to git.6bit.ch" || echo " ⚠ Push failed or nothing to push" else echo -e "\n${YELLOW}[1/4] Skipping git (quick mode)${NC}" fi # Step 2: Sync files (only changed) echo -e "\n${YELLOW}[2/4] Syncing changed files...${NC}" RSYNC_OPTS="-az --info=name1 --delete" if ! $BACKEND_ONLY; then echo " Frontend:" sshpass -p "$SERVER_PASS" rsync $RSYNC_OPTS \ --exclude 'node_modules' \ --exclude '.next' \ --exclude '.git' \ frontend/ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/frontend/ 2>&1 | sed 's/^/ /' fi if ! $FRONTEND_ONLY; then echo " Backend:" sshpass -p "$SERVER_PASS" rsync $RSYNC_OPTS \ --exclude '__pycache__' \ --exclude '.pytest_cache' \ --exclude 'venv' \ --exclude '.git' \ --exclude '*.pyc' \ --exclude '.env' \ --exclude '*.db' \ backend/ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/backend/ 2>&1 | sed 's/^/ /' fi # Step 3: Reload backend (graceful, no restart) if ! $FRONTEND_ONLY; then echo -e "\n${YELLOW}[3/4] Reloading backend (graceful)...${NC}" sshpass -p "$SERVER_PASS" ssh $SERVER_USER@$SERVER_HOST << 'BACKEND_EOF' # Signal uvicorn to reload (if running with --reload) # Otherwise, just check it's running BACKEND_PID=$(pgrep -f 'uvicorn app.main:app' | head -1) if [ -n "$BACKEND_PID" ]; then # Touch a file to trigger auto-reload if uvicorn has --reload touch ~/pounce/backend/app/main.py echo " ✓ Backend reload triggered (PID: $BACKEND_PID)" else echo " ⚠ Backend not running, starting..." cd ~/pounce/backend source ../venv/bin/activate nohup uvicorn app.main:app --host 0.0.0.0 --port 8000 > backend.log 2>&1 & sleep 2 echo " ✓ Backend started" fi BACKEND_EOF else echo -e "\n${YELLOW}[3/4] Skipping backend (frontend only)${NC}" fi # Step 4: Rebuild frontend (in background to minimize downtime) if ! $BACKEND_ONLY; then echo -e "\n${YELLOW}[4/4] Rebuilding frontend...${NC}" sshpass -p "$SERVER_PASS" ssh $SERVER_USER@$SERVER_HOST << 'FRONTEND_EOF' cd ~/pounce/frontend # Build new version echo " Building..." npm run build 2>&1 | grep -E '(✓|○|λ|Error|error)' | head -10 | sed 's/^/ /' BUILD_EXIT=$? if [ $BUILD_EXIT -eq 0 ]; then # Gracefully restart Next.js NEXT_PID=$(pgrep -f 'next start' | head -1) if [ -n "$NEXT_PID" ]; then echo " Restarting Next.js (PID: $NEXT_PID)..." kill $NEXT_PID 2>/dev/null sleep 1 fi # Start new instance nohup npm run start > frontend.log 2>&1 & sleep 2 # Verify NEW_PID=$(pgrep -f 'next start' | head -1) if [ -n "$NEW_PID" ]; then echo " ✓ Frontend running (PID: $NEW_PID)" else echo " ⚠ Frontend may not have started correctly" fi else echo " ✗ Build failed, keeping old version" fi FRONTEND_EOF else echo -e "\n${YELLOW}[4/4] Skipping frontend (backend only)${NC}" fi # Summary echo -e "\n${GREEN}═══════════════════════════════════════════════════════════════${NC}" echo -e "${GREEN} ✅ DEPLOY COMPLETE! ${NC}" echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}" echo -e "" echo -e " Frontend: ${BLUE}https://pounce.ch${NC} / http://$SERVER_HOST:3000" echo -e " Backend: ${BLUE}https://pounce.ch/api${NC} / http://$SERVER_HOST:8000" echo -e "" echo -e "${CYAN}Quick commands:${NC}" echo -e " ./deploy.sh -q # Quick sync, no git" echo -e " ./deploy.sh -b # Backend only" echo -e " ./deploy.sh -f # Frontend only" echo -e " ./deploy.sh \"message\" # Full deploy with commit message"