diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 0d85b1b..480739d 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -2,7 +2,7 @@ name: ci on: push: - branches: [main, dev] + branches: [dev] pull_request: branches: [main] diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..d07ca77 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,166 @@ +# ═══════════════════════════════════════════════════════════════════════════ +# Vision Studio — Deploiement automatique via Forgejo Actions +# +# Declencheur : push sur `main`. +# Runner : forgejo-runner self-hosted (label omhk-deploy, sur .22). +# Cible : 192.168.1.117 -> conteneur Docker `vision-bot` +# (/home/discord/Bots/VisionApp, bind-mount .:/app). +# +# Pipeline : syntaxe -> rsync -> deploy -> health check -> rollback auto. +# Calque du deploy.yml de Kuby. +# ═══════════════════════════════════════════════════════════════════════════ +name: deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: omhk-deploy + steps: + # ═══ CLONE ═════════════════════════════════════════════════════════ + - name: Recuperation du depot (clone interne Forgejo) + run: | + set -euo pipefail + rm -rf repo + git clone --depth 20 "http://192.168.1.22:3000/Omega_Kube/VisionApp.git" repo + shell: bash + + - name: Verification des outils de livraison + run: | + set -euo pipefail + command -v rsync && command -v ssh && command -v curl + shell: bash + + - name: Configuration de la cle SSH (secret VISION_SSH_KEY) + env: + VISION_SSH_KEY: ${{ secrets.VISION_SSH_KEY }} + run: | + set -euo pipefail + mkdir -p ~/.ssh + chmod 700 ~/.ssh + printf '%s\n' "$VISION_SSH_KEY" > ~/.ssh/id_vision + chmod 600 ~/.ssh/id_vision + ssh-keyscan -H 192.168.1.117 >> ~/.ssh/known_hosts 2>/dev/null + shell: bash + + # ═══ TEST SYNTAXE (sur .22, rapide) ══════════════════════════════ + - name: Test syntaxe Python + run: | + set -euo pipefail + cd repo + NPROC=$(nproc 2>/dev/null || echo 2) + ERR_FILE=$(mktemp) + find . -name "*.py" \ + -not -path "*/backups/*" \ + -not -path "*/__pycache__/*" \ + -not -path "*/.git/*" \ + -not -path "*/venv/*" \ + -not -path "*/.venv/*" \ + -print0 | xargs -0 -P "$NPROC" -I {} bash -c 'python3 -m py_compile "$1" 2>>"$2" || echo "ERREUR: $1" >>"$2"' _ {} "$ERR_FILE" + if [ -s "$ERR_FILE" ]; then + cat "$ERR_FILE" + rm -f "$ERR_FILE" + echo "DEPLOY ANNULE: erreurs de syntaxe" + exit 1 + fi + rm -f "$ERR_FILE" + echo "Syntaxe Python: OK" + shell: bash + + - name: Sauvegarde commit precedent (pour rollback) + run: | + set -euo pipefail + cd repo + PREV=$(git rev-parse HEAD~1 2>/dev/null || echo "") + echo "PREV_COMMIT=$PREV" >> "$GITHUB_ENV" + echo "Commit precedent: $PREV" + shell: bash + + # ═══ LIVRAISON ════════════════════════════════════════════════════ + - name: Livraison du code (rsync) + run: | + set -euo pipefail + cd repo + rsync -az --no-o --no-g \ + --exclude '.git/' --exclude '.env' --exclude '.env.*' \ + --exclude 'data/' --exclude 'logs/' --exclude '*.log' --exclude 'nohup.out' \ + --exclude '__pycache__/' --exclude 'venv/' --exclude '.venv/' \ + --exclude '*.db' --exclude '*.db-journal' --exclude '*.db-wal' --exclude '*.db-shm' \ + --exclude '.vscode/' --exclude '.idea/' \ + -e "ssh -i ~/.ssh/id_vision -o StrictHostKeyChecking=no -o UserKnownHostsFile=~/.ssh/known_hosts" \ + . \ + discord@192.168.1.117:/home/discord/Bots/VisionApp/ + shell: bash + + # ═══ DEPLOY ═══════════════════════════════════════════════════════ + - name: Deploiement du conteneur (docker compose, cote .117) + run: | + set -euo pipefail + cd repo + COMMIT="$(git rev-parse --short HEAD)" + CHANGED="$(git diff --name-only HEAD~20 HEAD 2>/dev/null || true)" + REBUILD="false" + if echo "$CHANGED" | grep -qE '(^|/)(requirements\.txt|Dockerfile)$'; then + REBUILD="true" + fi + ssh -i ~/.ssh/id_vision -o StrictHostKeyChecking=no -o UserKnownHostsFile=~/.ssh/known_hosts \ + discord@192.168.1.117 \ + "bash /home/discord/Bots/VisionApp/scripts/deploy_runner.sh main '${COMMIT}' '${REBUILD}'" + shell: bash + + # ═══ HEALTH CHECK ═════════════════════════════════════════════════ + - name: Health check post-deploy + id: health_check + run: | + set -euo pipefail + echo "Attente du demarrage du bot..." + for i in $(seq 1 10); do + sleep 3 + STATUS=$(curl -s -m 5 http://192.168.1.117:1890/health 2>/dev/null || echo "") + if echo "$STATUS" | grep -q '"status":"ok"'; then + echo "Health check OK (tentative $i)" + exit 0 + fi + echo "Tentative $i/10..." + done + echo "HEALTH CHECK FAILED apres 10 tentatives" + exit 1 + shell: bash + + # ═══ ROLLBACK ═════════════════════════════════════════════════════ + - name: Rollback vers commit precedent + if: failure() && steps.health_check.outcome == 'failure' + run: | + set -euo pipefail + if [ -z "${PREV_COMMIT:-}" ]; then + echo "Pas de commit precedent — rollback impossible" + exit 1 + fi + echo "═══ ROLLBACK vers ${PREV_COMMIT} ═══" + cd repo + git checkout "$PREV_COMMIT" + rsync -az --no-o --no-g \ + --exclude '.git/' --exclude '.env' --exclude '.env.*' \ + --exclude 'data/' --exclude 'logs/' --exclude '*.log' --exclude 'nohup.out' \ + --exclude '__pycache__/' --exclude 'venv/' --exclude '.venv/' \ + --exclude '*.db' --exclude '*.db-journal' --exclude '*.db-wal' --exclude '*.db-shm' \ + --exclude '.vscode/' --exclude '.idea/' \ + -e "ssh -i ~/.ssh/id_vision -o StrictHostKeyChecking=no -o UserKnownHostsFile=~/.ssh/known_hosts" \ + . \ + discord@192.168.1.117:/home/discord/Bots/VisionApp/ + ssh -i ~/.ssh/id_vision -o StrictHostKeyChecking=no -o UserKnownHostsFile=~/.ssh/known_hosts \ + discord@192.168.1.117 \ + "bash /home/discord/Bots/VisionApp/scripts/deploy_runner.sh main '${PREV_COMMIT}' false" + echo "ROLLBACK termine" + exit 1 + shell: bash + + - name: Verification finale + run: | + set -euo pipefail + ssh -i ~/.ssh/id_vision -o StrictHostKeyChecking=no -o UserKnownHostsFile=~/.ssh/known_hosts \ + discord@192.168.1.117 \ + "docker ps --filter name=vision-bot --format '{{.Names}} {{.Status}}' && curl -s -m 5 http://localhost:1890/health && echo" + shell: bash diff --git a/scripts/deploy_prod.sh b/scripts/deploy_prod.sh deleted file mode 100755 index e867b12..0000000 --- a/scripts/deploy_prod.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# ══════════════════════════════════════════════════════════════════════════════ -# Vision Studio — Deploy manuel vers pavillon80.changeip.org:444 -# -# Usage: ./scripts/deploy_prod.sh [branch] -# Défaut: main -# ══════════════════════════════════════════════════════════════════════════════ -set -euo pipefail - -BRANCH="${1:-main}" -HOST="discord@176.182.176.84" -PORT=444 -REMOTE_DIR="/home/discord/Bots/VisionApp" -SSH_OPTS="-o StrictHostKeyChecking=no -p $PORT" - -log() { echo "[deploy] $(date '+%F %T') $*"; } - -log "Deploy de la branche $BRANCH vers $HOST:$PORT..." - -# 1. Rsync du code -log "Rsync du code..." -rsync -az --no-o --no-g \ - --exclude '.git/' --exclude '.env' --exclude '.env.*' \ - --exclude 'data/' --exclude 'logs/' --exclude '*.log' --exclude 'nohup.out' \ - --exclude '__pycache__/' --exclude 'venv/' --exclude '.venv/' \ - --exclude '*.db' --exclude '*.db-journal' --exclude '*.db-wal' --exclude '*.db-shm' \ - --exclude '.vscode/' --exclude '.idea/' \ - -e "ssh $SSH_OPTS" \ - . "$HOST:$REMOTE_DIR/" - -# 2. Vérification syntaxe sur le serveur -log "Vérification syntaxe..." -ssh $SSH_OPTS "$HOST" "cd $REMOTE_DIR && find . -name '*.py' -not -path '*/__pycache__/*' -not -path '*/.git/*' -not -path '*/venv/*' | head -20 | xargs -I{} python3 -m py_compile {} 2>&1 | head -5" - -# 3. Rebuild + restart Docker -log "Rebuild Docker..." -ssh $SSH_OPTS "$HOST" "cd $REMOTE_DIR && docker compose build --no-cache vision 2>&1 | tail -3" - -log "Restart du conteneur..." -ssh $SSH_OPTS "$HOST" "cd $REMOTE_DIR && docker compose up -d --force-recreate vision 2>&1 | tail -3" - -# 4. Health check -log "Health check..." -sleep 8 -STATUS=$(ssh $SSH_OPTS "$HOST" "curl -s -m 5 http://localhost:1890/health 2>/dev/null || echo ''") -if echo "$STATUS" | grep -q '"status":"ok"'; then - log "✅ Deploy réussi — Health OK" - ssh $SSH_OPTS "$HOST" "docker logs --tail 5 vision-bot 2>&1" -else - log "❌ Health check échoué" - ssh $SSH_OPTS "$HOST" "docker logs --tail 20 vision-bot 2>&1" - exit 1 -fi