#!/usr/bin/env bash # earth-now.co # Author: Simon-Pierre Boucher # Contact: contact@spboucher.ai # File: infra/deploy-m3u96b-docker.sh # Purpose: Deploy to production node m3u96b — rsync, docker compose up, then MANDATORY health checks against www.earth-now.co set -euo pipefail REMOTE="m3u96b" REMOTE_DIR="~/earth-now" BASE_URL="https://www.earth-now.co" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ok() { printf '✓ %s\n' "$1"; } fail() { printf '✗ %s\n' "$1"; } echo "== earth-now deploy → ${REMOTE} ==" # --- 1. Sync sources (build happens on the node inside Docker) --------------- echo "-- rsync ${REPO_ROOT}/ → ${REMOTE}:${REMOTE_DIR}/" rsync -az --delete \ --exclude "node_modules" \ --exclude ".next" \ --exclude "dist" \ --exclude ".turbo" \ --exclude ".git" \ --exclude "data/" \ "${REPO_ROOT}/" "${REMOTE}:${REMOTE_DIR}/" ok "rsync complete" # --- 2. Build & (re)start the stack ------------------------------------------- echo "-- docker compose up -d --build on ${REMOTE}" ssh "${REMOTE}" "cd ${REMOTE_DIR} && docker compose -f infra/docker-compose.prod.yml up -d --build" ok "docker compose up" # --- 3. Health checks (per CLAUDE.md the deploy is NOT done until both pass) -- FAILED=0 echo "-- health check: GET ${BASE_URL}/api/health" if curl -fsS --max-time 15 "${BASE_URL}/api/health" > /dev/null; then ok "api health returned 200" else fail "api health check failed (${BASE_URL}/api/health)" FAILED=1 fi echo "-- health check: hold SSE ${BASE_URL}/sse/health ≥ 10 s" SSE_START=$(date +%s) SSE_OUTPUT="$(curl -sN --max-time 12 "${BASE_URL}/sse/health" | head -c 100 || true)" SSE_ELAPSED=$(( $(date +%s) - SSE_START )) if [ -n "${SSE_OUTPUT}" ]; then ok "SSE stream produced output (held ~${SSE_ELAPSED}s, first bytes: $(printf '%s' "${SSE_OUTPUT}" | head -c 40 | tr -d '\n'))" else fail "SSE health check produced no output after ${SSE_ELAPSED}s (${BASE_URL}/sse/health)" FAILED=1 fi if [ "${FAILED}" -ne 0 ]; then fail "deploy NOT healthy — investigate before declaring the deploy done" exit 1 fi ok "deploy to ${REMOTE} complete and healthy → ${BASE_URL}"