Adapt deploy.sh for optical-dev server infrastructure

- Build frontend locally (Node not installed on server)
- rsync dist/ to server instead of building remotely
- Change PYTHON_CMD to python3 (server has 3.12, not 3.13)
- Add step to start MongoDB in Docker (mongo:7, not installed natively)
- Add step to inject Apache proxy config for /semblance_back/ and /semblance/
- SSH_HOST=optical-dev (uses ~/.ssh/config alias)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-03-20 12:59:32 +00:00
parent bb4dca0fe8
commit 42a8f25510

167
deploy.sh
View file

@ -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 </VirtualHost> tag
sudo sed -i 's|</VirtualHost>| # ----------------------------------------------------------------\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 <Directory $FRONTEND_DEST>\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 </Directory>\n\n</VirtualHost>|' \"\$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