#!/usr/bin/env bash # Deploy Submatter / Echodo production stack on a dedicated host (CT 116). # # Run on the CT after git sync, or via SSH from CI: # ssh root@192.168.68.116 'cd /opt/submatter && bash scripts/deploy-prod.sh' # # Modes: # build — serialized image build + compose up (default; needs ~8 GB RAM) # up — compose up only (images already built) # pull — docker compose pull + up (when using a registry; future) set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$repo_root" mode="${1:-build}" compose_files=(-f docker/docker-compose.prod.yml -f docker/docker-compose.prod.standalone.yml) # Optional Phase B: export COMPOSE_PROFILES=agent-server before deploy if [[ -n "${COMPOSE_PROFILES:-}" ]]; then echo "[deploy-prod] profiles=${COMPOSE_PROFILES}" fi if [[ ! -f .env ]]; then echo "[deploy-prod] ERROR: missing ${repo_root}/.env — copy from .env.example and fill production values." >&2 exit 1 fi export DOCKER_BUILDKIT=1 compose() { docker compose --project-directory "$repo_root" "${compose_files[@]}" "$@" } # Preserve separately managed services (such as opencode-v2); orphan cleanup # requires an explicit operator decision, not every application deployment. case "$mode" in build) bash scripts/build-prod.sh compose up -d ;; up) compose up -d ;; pull) compose pull compose up -d ;; *) echo "Usage: $0 [build|up|pull]" >&2 exit 1 ;; esac echo "[deploy-prod] waiting for web health..." deadline=$((SECONDS + 120)) while (( SECONDS < deadline )); do if compose ps web 2>/dev/null | grep -q "(healthy)"; then echo "[deploy-prod] web is healthy" compose ps exit 0 fi sleep 5 done echo "[deploy-prod] WARN: web not healthy within 120s — check logs:" >&2 compose logs web --tail 40 exit 1