Skip to content

merge: accueil citations (une ligne + état tout-complété + texte proc… #158

merge: accueil citations (une ligne + état tout-complété + texte proc…

merge: accueil citations (une ligne + état tout-complété + texte proc… #158

Workflow file for this run

name: Deploy to VPS
on:
push:
branches: [main]
jobs:
# ── Job 0 : Pré-validation rapide (syntaxe — ne nécessite pas de SSH) ──────
pre-check:
name: Pré-validation deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Validation workflows GitHub Actions (actionlint)
# actionlint : binaire Go natif, validation sémantique GitHub Actions
# (script officiel rhysd/actionlint). Remplace l'ancien python3+pyyaml.
run: |
bash <(curl -sSf https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -color
- name: Validation syntaxe Bash (scripts/*.sh)
run: |
errors=0
for f in scripts/*.sh; do
[[ -f "$f" ]] || continue
if bash -n "$f" 2>&1; then echo " ✅ $f"
else echo " ❌ $f"; errors=$((errors + 1)); fi
done
exit $errors
- name: Pas de répertoires fantômes à la racine
run: |
for f in db_profiles.json app_settings.json; do
if [[ -d "$f" ]]; then
echo " ❌ $f est un répertoire — volume Docker corrompu"
exit 1
fi
done
echo " ✅ Aucun répertoire fantôme à la racine"
# ── Job 1 : Déploiement VPS ────────────────────────────────────────────────
deploy:
name: Deploy lvelup.info
runs-on: ubuntu-latest
needs: pre-check
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: deploy
key: ${{ secrets.VPS_SSH_KEY }}
script: |
git config --global --add safe.directory /opt/levelup
cd /opt/levelup
bash /opt/levelup/scripts/deploy.sh
# ── Job 2 : Regen données démo ────────────────────────────────────────────
deploy-demo:
name: Regen demo.lvelup.info
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Regen données démo via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: deploy
key: ${{ secrets.VPS_SSH_KEY }}
script: |
set -euo pipefail
cd /opt/levelup
# Regen données démo — NON DESTRUCTIF (incident 2026-06-05).
# On NE supprime PAS data/demo/warehouse|players AVANT le seed : seed-demo
# est idempotent (os.Remove par fichier puis recrée). Supprimer d'abord =
# si le seed échoue (ex: binaire levelup absent), la démo reste vidée.
# On nettoie uniquement les répertoires fantômes que Docker crée à la place
# d'un JSON absent au bind-mount ; les fichiers valides sont écrasés par seed-demo.
for f in data/demo/db_profiles.json data/demo/app_settings.json; do
if [[ -d "$f" ]]; then
rm -rf "$f"
echo "[regen] Répertoire fantôme supprimé: $f"
fi
done
# Permissions fixées automatiquement par docker-entrypoint.sh (gosu pattern)
mkdir -p data/demo
# Garde anti-crash-loop : si une tache de fond hote (cmd/[b]ackfill_*) tient
# le lock DuckDB, stopper la prod l'empecherait de rouvrir (cf. memoire
# deploy_hazards). On SKIP le regen sans faire echouer le deploiement (la
# demo garde ses donnees sur disque). Le motif [b]ackfill evite que pgrep
# matche le mot dans sa propre ligne de commande (faux positif permanent).
if pgrep -f '[b]ackfill' >/dev/null 2>&1; then
echo "[regen] Tache de fond hote detectee — regen demo SKIP (prod preservee)"
exit 0
fi
# seed-demo lit les DB source du joueur JGtm que la PROD tient verrouillées
# (lock DuckDB mono-writer) → on stoppe la prod le temps du seed (~5-10 s),
# puis on la REDÉMARRE TOUJOURS (même si le seed échoue, sinon prod down).
# Pas de `levelup seed` ici : seed-demo COPIE la metadata prod (référentiels
# + rank_translations déjà présents) ; l'ancien `levelup seed` verrouillait
# la metadata prod = cause de l'échec historique de ce job.
echo "[regen] Stop prod (libère les locks des DB source)..."
docker compose stop levelup
# Indexation des captures Halo 5 (mp4 web-natifs servis en direct) : associe
# chaque clip à son match h5 par timestamp et écrit media_files/associations
# dans shared_social h5 — lues ensuite par seed-demo pour extraire des clips
# démo (extractDemoMediaH5). Best-effort + IDEMPOTENT (re-association). Requiert
# ffmpeg (miniatures) — présent sur le VPS. Prod stoppée → DB source libres.
# buffer-min 10 : les captures Game Bar sont prises en FIN de match (delta jusqu'à
# ~8 min vs start_time). captures-dir = {media base}/JGtm (résolu par la CLI).
echo "[regen] Index media Halo 5 (captures → associations)..."
docker compose run --rm levelup \
levelup index-media --gamertag JGtm --title halo_5 --buffer-min 10 \
|| echo "[regen] ⚠️ index-media halo_5 a échoué (pas de clip H5 ce regen)"
# seed-demo seede désormais TOUS les titres où JGtm a des données (dérivés de
# db_profiles) : halo_infinite (flux HLS) + halo_5 (clips mp4 indexés ci-dessus).
docker compose run --rm levelup \
levelup seed-demo --gamertag JGtm --service-tag SPTA --out data/demo --max-media 12 \
|| echo "[regen] ⚠️ seed-demo a échoué — démo inchangée (prod va être redémarrée)"
echo "[regen] Restart prod..."
docker compose up -d levelup
# [Guard] Vérification post-regen — les configs doivent être des fichiers.
ok=true
for f in data/demo/db_profiles.json data/demo/app_settings.json; do
if [[ -d "/opt/levelup/$f" ]]; then
echo "[regen] ❌ $f est un répertoire (regen incomplet)"; ok=false
else
echo "[regen] ✅ $f OK"
fi
done
$ok || { echo "[regen] ❌ Démo non recréée (configs invalides) — prod déjà redémarrée"; exit 1; }
# Recréer le conteneur démo (nouvelle data + image + mounts à jour).
docker compose up -d --force-recreate levelup-demo
echo "[regen] ✅ levelup-demo recréé avec les données fraîches"