Files
AdrianoDev 9e7b98579b chore(V2): branch V2.0.0 come default deploy (no merge in main)
deploy-vps.sh: BRANCH default V2.0.0 invece di main.
README: clone con -b V2.0.0, nota che il branch in produzione è V2.0.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 10:31:58 +02:00

149 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# deploy-vps.sh — deploy Cerbero MCP V2 sul VPS senza passare per registry.
#
# Workflow:
# 1. git fetch + reset al ramo target
# 2. docker compose build (rebuild immagine se SHA è cambiata)
# 3. docker compose down (graceful, max 15s)
# 4. docker compose up -d
# 5. attesa healthcheck su /health
# 6. rollback automatico al SHA precedente se health fallisce
#
# Eseguito ON THE VPS, dentro la directory del repo (es. /opt/cerbero-mcp).
#
# Uso (sul VPS):
# cd /opt/cerbero-mcp
# bash scripts/deploy-vps.sh
#
# Uso (da macchina dev, via SSH):
# ssh user@vps 'cd /opt/cerbero-mcp && bash scripts/deploy-vps.sh'
#
# Variabili env (opzionali):
# BRANCH ramo git da deployare (default: V2.0.0)
# SERVICE nome servizio docker compose (default: cerbero-mcp)
# PORT porta /health da pingare (default: dal .env, fallback 9000)
# HEALTH_TIMEOUT_SECONDS attesa max health (default: 30)
# HEALTH_INTERVAL secondi tra retry health (default: 2)
# FORCE se "1", rebuild + restart anche se SHA invariata
# SKIP_ROLLBACK se "1", non fare rollback su health fail (per debug)
set -euo pipefail
# ─── Config ──────────────────────────────────────────────────────────────
BRANCH="${BRANCH:-V2.0.0}"
SERVICE="${SERVICE:-cerbero-mcp}"
HEALTH_TIMEOUT_SECONDS="${HEALTH_TIMEOUT_SECONDS:-30}"
HEALTH_INTERVAL="${HEALTH_INTERVAL:-2}"
# Risolvi PORT da .env se non passata
if [[ -z "${PORT:-}" ]]; then
if [[ -f .env ]] && grep -q '^PORT=' .env; then
PORT="$(grep '^PORT=' .env | head -1 | cut -d= -f2 | tr -d '[:space:]"')"
fi
fi
PORT="${PORT:-9000}"
HEALTH_URL="http://localhost:${PORT}/health"
# ─── Pre-check ───────────────────────────────────────────────────────────
command -v git >/dev/null || { echo "FATAL: git non installato"; exit 1; }
command -v docker >/dev/null || { echo "FATAL: docker non installato"; exit 1; }
command -v curl >/dev/null || { echo "FATAL: curl non installato"; exit 1; }
docker compose version >/dev/null 2>&1 || { echo "FATAL: docker compose non disponibile"; exit 1; }
if [[ ! -f .env ]]; then
echo "FATAL: .env non trovato in $(pwd)."
echo " Copia .env.example → .env e compila i valori prima del primo deploy."
exit 1
fi
if [[ ! -f docker-compose.yml ]]; then
echo "FATAL: docker-compose.yml non trovato in $(pwd)."
exit 1
fi
# Verifica working tree pulito
if [[ -n "$(git status --porcelain)" ]]; then
echo "FATAL: working tree non pulito. Modifiche locali non gestite:"
git status --short
echo " Risolvi prima di deployare (es. git stash o git reset)."
exit 1
fi
# ─── Stato corrente ──────────────────────────────────────────────────────
CURRENT_SHA="$(git rev-parse --short HEAD)"
echo "==> SHA attuale (rollback target): $CURRENT_SHA"
echo "==> branch: $BRANCH"
echo "==> port: $PORT"
# ─── Fetch + reset ───────────────────────────────────────────────────────
echo "==> git fetch + reset --hard origin/${BRANCH}"
git fetch --prune origin
git reset --hard "origin/${BRANCH}"
NEW_SHA="$(git rev-parse --short HEAD)"
echo "==> SHA nuovo: $NEW_SHA"
if [[ "$CURRENT_SHA" == "$NEW_SHA" ]] && [[ "${FORCE:-0}" != "1" ]]; then
echo "==> Già aggiornato a $NEW_SHA. Nessun deploy necessario."
echo " (esporta FORCE=1 per riavviare comunque)"
exit 0
fi
if [[ "$CURRENT_SHA" == "$NEW_SHA" ]]; then
echo "==> FORCE=1 → rebuild e restart anche se SHA invariata"
fi
# ─── Funzione di rollback ────────────────────────────────────────────────
rollback() {
if [[ "${SKIP_ROLLBACK:-0}" == "1" ]]; then
echo "==> SKIP_ROLLBACK=1 → niente rollback automatico"
return
fi
if [[ "$CURRENT_SHA" == "$NEW_SHA" ]]; then
echo "==> SHA invariata, niente da rollbackare"
return
fi
echo "==> ROLLBACK a $CURRENT_SHA"
git reset --hard "$CURRENT_SHA"
docker compose build "$SERVICE"
docker compose up -d --force-recreate "$SERVICE"
echo "==> rollback eseguito. Verifica manualmente lo stato."
}
# ─── Build ───────────────────────────────────────────────────────────────
echo "==> docker compose build $SERVICE"
docker compose build "$SERVICE"
# ─── Down + up ───────────────────────────────────────────────────────────
echo "==> docker compose down --timeout 15"
docker compose down --timeout 15
echo "==> docker compose up -d"
docker compose up -d
# ─── Health check ────────────────────────────────────────────────────────
echo "==> attendo /health (timeout ${HEALTH_TIMEOUT_SECONDS}s, retry ogni ${HEALTH_INTERVAL}s)"
deadline=$(( $(date +%s) + HEALTH_TIMEOUT_SECONDS ))
while [[ $(date +%s) -lt $deadline ]]; do
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
echo
echo "==> health OK"
curl -s "$HEALTH_URL"
echo
echo
echo "==> deploy DONE (SHA $CURRENT_SHA$NEW_SHA, branch $BRANCH)"
exit 0
fi
printf "."
sleep "$HEALTH_INTERVAL"
done
echo
echo "==> FAIL: /health non risponde dopo ${HEALTH_TIMEOUT_SECONDS}s"
echo "==> log container (ultime 40 righe):"
docker compose logs --tail 40 "$SERVICE" || true
rollback
exit 1