diff --git a/deploy.sh b/deploy.sh index b98ba6ba..3703d31d 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,95 +1,146 @@ #!/bin/bash -set -e # Exit on any error +set -e -# Configuration — source of truth for all paths +# ───────────────────────────────────────────────────────────────────────────── +# Semblance — deploy script +# Builds frontend locally, rsyncs dist/ + backend to server, manages services. +# +# Server: optical-dev.oliver.solution (Ubuntu 24.04, Apache2, Docker) +# SSH alias: optical-dev (see ~/.ssh/config) +# +# First-time setup on server (run once manually): +# ssh optical-dev +# sudo mkdir -p /opt/semblance && sudo chown vadym.samoilenko:vadym.samoilenko /opt/semblance +# git clone git@bitbucket.org:zlalani/semblance-dev.git /opt/semblance +# cp /opt/semblance/backend/.env.example /opt/semblance/backend/.env && nano /opt/semblance/backend/.env +# sudo cp /opt/semblance/semblance.service /etc/systemd/system/ +# sudo systemctl daemon-reload && sudo systemctl enable semblance.service +# ───────────────────────────────────────────────────────────────────────────── + +SSH_HOST="optical-dev" DEPLOY_DIR="/opt/semblance" FRONTEND_DEST="/var/www/html/semblance" BACKEND_DIR="$DEPLOY_DIR/backend" -PYTHON_CMD="python3.13" +PYTHON_CMD="python3" echo "======================================" -echo "Starting deployment..." +echo "Semblance — starting deployment" echo "======================================" -# ── Pre-flight checks ───────────────────────────────────────────────────────── - -# Verify backend/.env exists (it is gitignored — must be provisioned manually) -if [ ! -f "$BACKEND_DIR/.env" ]; then +# ── Pre-flight: backend/.env must exist on server ──────────────────────────── +echo "" +echo "[pre-flight] Checking backend/.env on server..." +if ! ssh "$SSH_HOST" "test -f $BACKEND_DIR/.env"; then echo "" - echo "ERROR: $BACKEND_DIR/.env not found." - echo "This file is not tracked in git and must be created manually on the server." - echo "Copy backend/.env.example and fill in real values:" + echo "ERROR: $BACKEND_DIR/.env not found on server." + echo "Run on server:" echo " cp $BACKEND_DIR/.env.example $BACKEND_DIR/.env" echo " nano $BACKEND_DIR/.env" exit 1 fi -# Verify required env vars are set in backend/.env for VAR in SECRET_KEY JWT_SECRET_KEY OPENAI_API_KEY GEMINI_API_KEY; do - if ! grep -q "^${VAR}=.\+" "$BACKEND_DIR/.env" 2>/dev/null; then - echo "" - echo "ERROR: $VAR is not set in $BACKEND_DIR/.env" + if ! ssh "$SSH_HOST" "grep -q '^${VAR}=.\+' $BACKEND_DIR/.env 2>/dev/null"; then + echo "ERROR: $VAR is not set in $BACKEND_DIR/.env on server" exit 1 fi done echo "✓ backend/.env present and required vars set" -# ── Step 1: Pull latest changes ─────────────────────────────────────────────── +# ── Step 1: Pull latest code on server ─────────────────────────────────────── echo "" -echo "[1/7] Pulling latest changes from git..." -cd "$DEPLOY_DIR" -git pull +echo "[1/7] Pulling latest code on server..." +ssh "$SSH_HOST" "cd $DEPLOY_DIR && git pull" -# ── Step 2: Set up frontend environment ────────────────────────────────────── +# ── Step 2: Build frontend locally ─────────────────────────────────────────── echo "" -echo "[2/7] Setting up frontend environment..." +echo "[2/7] Building frontend locally..." cp .env.production .env - -# ── Step 3: Set up Python virtual environment ───────────────────────────────── -echo "" -echo "[3/7] Setting up Python virtual environment..." -cd "$BACKEND_DIR" -if [ ! -d "venv" ]; then - echo "Creating new virtual environment with $PYTHON_CMD..." - $PYTHON_CMD -m venv venv -else - echo "Virtual environment already exists." -fi - -# ── Step 4: Install Python dependencies ────────────────────────────────────── -echo "" -echo "[4/7] Installing Python dependencies..." -source venv/bin/activate -pip install -r requirements.txt --quiet - -# ── Step 5: Build and deploy frontend ──────────────────────────────────────── -echo "" -echo "[5/7] Building and deploying frontend..." -cd "$DEPLOY_DIR" npm install --silent npm run build +echo "✓ Frontend built (dist/)" -echo "Cleaning deployment directory..." -rm -rf "$FRONTEND_DEST"/* -echo "Copying new build..." -cp -r dist/* "$FRONTEND_DEST/" - -# ── Step 6: Ensure backend directories exist with correct ownership ─────────── +# ── Step 3: Deploy frontend to server ──────────────────────────────────────── echo "" -echo "[6/7] Creating backend directories..." -mkdir -p "$BACKEND_DIR/uploads" -mkdir -p "$BACKEND_DIR/temp" -sudo chown -R www-data:www-data "$BACKEND_DIR/uploads" "$BACKEND_DIR/temp" +echo "[3/7] Deploying frontend to server..." +ssh "$SSH_HOST" "sudo mkdir -p $FRONTEND_DEST && sudo chown vadym.samoilenko:www-data $FRONTEND_DEST && sudo chmod 755 $FRONTEND_DEST" +rsync -az --delete dist/ "$SSH_HOST:$FRONTEND_DEST/" +ssh "$SSH_HOST" "sudo chown -R www-data:www-data $FRONTEND_DEST" +echo "✓ Frontend deployed to $FRONTEND_DEST" -# ── Step 7: Restart backend service ────────────────────────────────────────── +# ── Step 4: Set up Python virtual environment on server ────────────────────── +echo "" +echo "[4/7] Setting up Python virtual environment..." +ssh "$SSH_HOST" " + cd $BACKEND_DIR + if [ ! -d venv ]; then + echo 'Creating new virtual environment...' + $PYTHON_CMD -m venv venv + fi + source venv/bin/activate + pip install -r requirements.txt --quiet + echo '✓ Python dependencies installed' +" + +# ── Step 5: Ensure MongoDB is running in Docker ─────────────────────────────── +echo "" +echo "[5/7] Ensuring MongoDB is running..." +ssh "$SSH_HOST" " + if ! docker ps --format '{{.Names}}' | grep -q '^semblance-mongo\$'; then + echo 'Starting MongoDB container...' + docker run -d \ + --name semblance-mongo \ + --restart unless-stopped \ + -p 127.0.0.1:27017:27017 \ + -v semblance-mongo-data:/data/db \ + mongo:7 + sleep 3 + echo '✓ MongoDB started' + else + echo '✓ MongoDB already running' + fi +" + +# ── Step 6: Ensure Apache proxy config for semblance ───────────────────────── +echo "" +echo "[6/7] Ensuring Apache proxy config..." +ssh "$SSH_HOST" " + CONF='/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf' + + if grep -q 'semblance_back' \"\$CONF\" 2>/dev/null; then + echo '✓ Apache semblance config already present' + else + echo 'Adding semblance blocks to Apache config...' + + # Insert before the closing tag + sudo sed -i 's|| # ----------------------------------------------------------------\n # Semblance — Quart/Hypercorn backend at :5137\n # ----------------------------------------------------------------\n\n # WebSocket (Socket.IO)\n RewriteCond %{HTTP:Upgrade} websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule ^/semblance_back/socket.io/(.*) ws://127.0.0.1:5137/socket.io/\$1 [P,L]\n\n # REST API\n ProxyPass /semblance_back/api/ http://127.0.0.1:5137/api/\n ProxyPassReverse /semblance_back/api/ http://127.0.0.1:5137/api/\n\n # Socket.IO HTTP polling fallback\n ProxyPass /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n ProxyPassReverse /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n\n # Semblance SPA — served at /semblance/\n Alias /semblance $FRONTEND_DEST\n \n Options -Indexes +FollowSymLinks\n AllowOverride None\n Require all granted\n RewriteEngine On\n RewriteBase /semblance/\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule ^ index.html [L]\n \n\n|' \"\$CONF\" + + sudo apache2ctl configtest && sudo systemctl reload apache2 + echo '✓ Apache config updated and reloaded' + fi +" + +# ── Step 7: Install / restart backend service ──────────────────────────────── echo "" echo "[7/7] Restarting backend service..." -sudo systemctl restart semblance.service +ssh "$SSH_HOST" " + # Install or update service file + sudo cp $DEPLOY_DIR/semblance.service /etc/systemd/system/semblance.service + sudo systemctl daemon-reload + sudo systemctl enable semblance.service + + # Ensure upload/temp dirs exist with correct ownership + sudo mkdir -p $BACKEND_DIR/uploads $BACKEND_DIR/temp + sudo chown -R www-data:www-data $BACKEND_DIR/uploads $BACKEND_DIR/temp + + sudo systemctl restart semblance.service + sleep 2 + systemctl status semblance.service --no-pager || true +" echo "" echo "======================================" echo "Deployment complete!" +echo " Frontend: https://optical-dev.oliver.solution/semblance/" +echo " API: https://optical-dev.oliver.solution/semblance_back/api/" echo "======================================" -echo "" -# Use || true so a non-active status doesn't cause set -e to abort with a confusing error -systemctl status semblance.service --no-pager || true