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:
AdrianoDev
2026-05-01 09:05:26 +02:00
parent 8ecc1a24a9
commit 51081f4e18
3 changed files with 191 additions and 57 deletions
-50
View File
@@ -1,50 +0,0 @@
#!/usr/bin/env bash
# Cerbero MCP — build & push immagine unica V2.0.0 al registry Gitea.
#
# Pre-requisiti:
# - docker
# - PAT Gitea con scope `write:package` in env $GITEA_PAT
# - $GITEA_USER (default: adriano)
#
# Uso:
# ./scripts/build-push.sh
# VERSION=2.0.1 ./scripts/build-push.sh
set -euo pipefail
REGISTRY="${REGISTRY:-git.tielogic.xyz}"
IMAGE_PREFIX="${IMAGE_PREFIX:-$REGISTRY/adriano/cerbero-mcp}"
GITEA_USER="${GITEA_USER:-adriano}"
VERSION="${VERSION:-2.0.0}"
SHA="$(git rev-parse --short HEAD)"
command -v docker >/dev/null || { echo "FATAL: docker non installato"; exit 1; }
# Login solo se non già autenticato sul registry.
if grep -q "\"$REGISTRY\"" ~/.docker/config.json 2>/dev/null; then
echo "=== docker già loggato su $REGISTRY (skip login) ==="
elif [ -n "${GITEA_PAT:-}" ]; then
echo "=== docker login $REGISTRY ==="
echo "$GITEA_PAT" | docker login "$REGISTRY" -u "$GITEA_USER" --password-stdin
else
echo "FATAL: non autenticato su $REGISTRY e GITEA_PAT non settata."
echo " Esegui una volta: docker login $REGISTRY -u $GITEA_USER"
exit 1
fi
TAG_VERSION="$IMAGE_PREFIX:$VERSION"
TAG_LATEST="$IMAGE_PREFIX:latest"
TAG_SHA="$IMAGE_PREFIX:sha-$SHA"
echo "=== build cerbero-mcp:$VERSION ==="
docker build -t "$TAG_VERSION" -t "$TAG_LATEST" -t "$TAG_SHA" .
echo "=== push ==="
for tag in "$TAG_VERSION" "$TAG_LATEST" "$TAG_SHA"; do
docker push "$tag"
echo " pushed: $tag"
done
echo
echo "=== Done (commit $SHA, version $VERSION) ==="
echo "VPS Watchtower farà pull entro WATCHTOWER_POLL_INTERVAL (default 5min)."
+148
View File
@@ -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