feat(V2): deploy-vps.sh per deploy via clone (no registry)
Il deploy ora avviene clonando il repo direttamente sul VPS, costruendo l'immagine in loco e riavviando il container. Sostituisce il workflow build & push verso registry + Watchtower. Lo script automatizza: - git fetch + reset --hard origin/<branch> - docker compose build - restart graceful (down 15s + up -d) - attesa healthcheck con timeout configurabile - rollback automatico al SHA precedente se /health fallisce Variabili: BRANCH, PORT, HEALTH_TIMEOUT_SECONDS, FORCE, SKIP_ROLLBACK. Rimosso scripts/build-push.sh (workflow registry abbandonato). README aggiornato con la nuova procedura. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+148
@@ -0,0 +1,148 @@
|
||||
#!/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: main)
|
||||
# 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:-main}"
|
||||
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
|
||||
Reference in New Issue
Block a user