#!/usr/bin/env bash set -euo pipefail # deploy-docker.sh — Deploy Stonks Oracle to a Docker host via SSH # # Usage: bash deploy-docker.sh [OPTIONS] # # Options: # --host USER@HOST SSH target (default: celes@192.168.42.254) # --ollama-url URL Ollama API URL (default: auto-detect or install) # --ollama-model MODEL Ollama model name (default: qwen3.5:9b-fast) # --dir PATH Remote install directory (default: ~/stonks-oracle) # # Examples: # bash deploy-docker.sh # bash deploy-docker.sh --ollama-url http://10.1.1.12:2701 --ollama-model qwen3.6 # bash deploy-docker.sh --host user@myserver --dir /opt/stonks # ------------------------------------------------------- # Configuration (override via flags or environment) # ------------------------------------------------------- REMOTE_HOST="${DEPLOY_HOST:-celes@192.168.42.254}" REMOTE_DIR="${DEPLOY_DIR:-/home/celes/stonks-oracle}" OLLAMA_URL="${DEPLOY_OLLAMA_URL:-}" OLLAMA_MODEL="${DEPLOY_OLLAMA_MODEL:-qwen3.5:9b-fast}" REPO_URL="http://admin:St0nks0racl3!@10.1.1.12:30300/admin/stonks-oracle.git" # Parse command-line flags while [[ $# -gt 0 ]]; do case $1 in --host) REMOTE_HOST="$2"; shift 2 ;; --ollama-url) OLLAMA_URL="$2"; shift 2 ;; --ollama-model) OLLAMA_MODEL="$2"; shift 2 ;; --dir) REMOTE_DIR="$2"; shift 2 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done echo "=== Stonks Oracle Docker Deployment ===" echo " Target: ${REMOTE_HOST}:${REMOTE_DIR}" echo " Model: ${OLLAMA_MODEL}" echo " Ollama: ${OLLAMA_URL:-auto-detect}" echo "" # ------------------------------------------------------- # Step 1: Clone or update the repo on the remote host # ------------------------------------------------------- echo "--- Step 1: Syncing repository ---" ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" "$REPO_URL" <<'REMOTE_SCRIPT' set -euo pipefail REMOTE_DIR="$1" REPO_URL="$2" if [ -d "$REMOTE_DIR/.git" ]; then echo " Updating existing repo..." cd "$REMOTE_DIR" git fetch origin git reset --hard origin/main else echo " Cloning fresh..." git clone "$REPO_URL" "$REMOTE_DIR" cd "$REMOTE_DIR" fi echo " ✓ Repo synced at $(git log --oneline -1)" REMOTE_SCRIPT echo "" # ------------------------------------------------------- # Step 2: Detect or configure Ollama # ------------------------------------------------------- echo "--- Step 2: Configuring Ollama ---" if [ -z "$OLLAMA_URL" ]; then # Auto-detect: check if Ollama is running on the remote host OLLAMA_URL=$(ssh "$REMOTE_HOST" bash -s <<'DETECT_SCRIPT' # Check common Ollama ports for port in 11434 2701; do if curl -sf --connect-timeout 2 "http://localhost:$port/api/tags" > /dev/null 2>&1; then echo "http://localhost:$port" exit 0 fi done echo "" DETECT_SCRIPT ) if [ -n "$OLLAMA_URL" ]; then echo " ✓ Found existing Ollama at: $OLLAMA_URL" else echo " No Ollama detected — will use Docker container" OLLAMA_URL="http://ollama:11434" fi else echo " Using provided Ollama URL: $OLLAMA_URL" fi # Determine if we need the Docker Ollama container USE_DOCKER_OLLAMA=false if [ "$OLLAMA_URL" = "http://ollama:11434" ]; then USE_DOCKER_OLLAMA=true fi echo "" # ------------------------------------------------------- # Step 3: Create .env and compose override # ------------------------------------------------------- echo "--- Step 3: Configuring environment ---" ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" "$OLLAMA_URL" "$OLLAMA_MODEL" "$USE_DOCKER_OLLAMA" <<'REMOTE_SCRIPT' set -euo pipefail REMOTE_DIR="$1" OLLAMA_URL="$2" OLLAMA_MODEL="$3" USE_DOCKER_OLLAMA="$4" cd "$REMOTE_DIR" # Read API keys from local files if they exist POLYGON_KEY="" ALPACA_KEY="" ALPACA_SECRET="" ALPACA_URL="https://paper-api.alpaca.markets" [ -f polygon.io.key ] && POLYGON_KEY=$(cat polygon.io.key) [ -f alpaca.key ] && ALPACA_KEY=$(cat alpaca.key) [ -f alpaca.secret ] && ALPACA_SECRET=$(cat alpaca.secret) [ -f alpaca.url ] && ALPACA_URL=$(cat alpaca.url) cat > .env < docker-compose.override.yml < docker-compose.override.yml </dev/null || true # Build all images echo " Building images (this may take a few minutes)..." docker compose build --quiet 2>&1 | tail -5 # Start infrastructure echo " Starting infrastructure..." if [ "$USE_DOCKER_OLLAMA" = "true" ]; then docker compose up -d postgres redis minio minio-init ollama else docker compose up -d postgres redis minio minio-init fi # Wait for infrastructure to be healthy echo " Waiting for infrastructure health checks..." for svc in postgres redis minio; do for i in $(seq 1 30); do if docker compose ps "$svc" 2>/dev/null | grep -q healthy; then break fi sleep 2 done done echo " ✓ Infrastructure healthy" # Start all application services echo " Starting application services..." docker compose up -d echo " Waiting for services to stabilize..." sleep 20 # Show status echo "" echo " Service Status:" docker compose ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | head -25 || docker compose ps REMOTE_SCRIPT echo "" # ------------------------------------------------------- # Step 5: Seed the database # ------------------------------------------------------- echo "--- Step 5: Seeding database ---" ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" <<'REMOTE_SCRIPT' set -euo pipefail cd "$1" # Wait for query-api to be healthy for i in $(seq 1 30); do if docker compose ps query-api 2>/dev/null | grep -q healthy; then break fi sleep 3 done # Run the symbol registry seed echo " Seeding symbol registry..." docker compose exec -T scheduler python -m services.symbol_registry.seed 2>/dev/null && echo " ✓ Database seeded" || echo " ⚠ Seed skipped (may already be seeded or service not ready)" REMOTE_SCRIPT echo "" # ------------------------------------------------------- # Step 6: Ensure Ollama model is available # ------------------------------------------------------- echo "--- Step 6: Checking Ollama model ---" ssh "$REMOTE_HOST" bash -s -- "$OLLAMA_URL" "$OLLAMA_MODEL" "$USE_DOCKER_OLLAMA" "$REMOTE_DIR" <<'REMOTE_SCRIPT' set -euo pipefail OLLAMA_URL="$1" OLLAMA_MODEL="$2" USE_DOCKER_OLLAMA="$3" REMOTE_DIR="$4" if [ "$USE_DOCKER_OLLAMA" = "true" ]; then # Pull via Docker container cd "$REMOTE_DIR" if docker compose exec -T ollama ollama list 2>/dev/null | grep -q "$OLLAMA_MODEL"; then echo " ✓ Model $OLLAMA_MODEL already available" else echo " Pulling $OLLAMA_MODEL via Docker Ollama..." docker compose exec -T ollama ollama pull "$OLLAMA_MODEL" echo " ✓ Model pulled" fi else # Check via API if curl -sf "$OLLAMA_URL/api/tags" 2>/dev/null | grep -q "$OLLAMA_MODEL"; then echo " ✓ Model $OLLAMA_MODEL already available at $OLLAMA_URL" else echo " Pulling $OLLAMA_MODEL via $OLLAMA_URL..." curl -sf "$OLLAMA_URL/api/pull" -d "{\"name\":\"$OLLAMA_MODEL\"}" | tail -1 echo " ✓ Model pulled" fi fi REMOTE_SCRIPT echo "" # ------------------------------------------------------- # Done # ------------------------------------------------------- REMOTE_IP=$(echo "$REMOTE_HOST" | cut -d@ -f2) echo "=== Deployment Complete ===" echo "" echo "Endpoints:" echo " Dashboard: http://${REMOTE_IP}:3000" echo " Query API: http://${REMOTE_IP}:8004" echo " Symbol Registry: http://${REMOTE_IP}:8001" echo " Trading Engine: http://${REMOTE_IP}:8002" echo " Risk Engine: http://${REMOTE_IP}:8003" echo " MinIO Console: http://${REMOTE_IP}:9001" echo " Superset: http://${REMOTE_IP}:8088" echo " Ollama: ${OLLAMA_URL}" echo "" echo "Commands:" echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose logs -f'" echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose ps'" echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose down'"