Files
benevoles/deploy
AdamThomasandClaude Sonnet 5 d5f67b2701 Déploiement : CI/CD GitLab + image frontend de prod + Caddy
Remplace la chaîne GitHub Actions (jamais réellement branchée : aucun
remote git n'existait) par une chaîne GitLab CI/CD, et comble un manque
qu'elle avait déjà : le frontend n'était jusqu'ici jamais construit ni
déployé en production.

- `.gitlab-ci.yml` : tests (backend + frontend) à chaque push ; sur `main`,
  build des images backend et frontend (registre de conteneurs GitLab) puis
  déploiement SSH sur le VPS (pull, migration Alembic, up -d).
- `frontend/Dockerfile.prod` + `frontend/nginx.conf` : build Vite statique
  servi par nginx (repli SPA sur index.html). `VITE_API_MODE`/
  `VITE_EDITION_ID` sont des variables de build, passées en `--build-arg`
  depuis la variable CI/CD `VITE_EDITION_ID`.
- `deploy/docker-compose.prod.yml` : ajout du service `frontend` ; `backend`
  et `frontend` ne publient plus que sur `127.0.0.1` (Caddy, déjà installé
  sur le serveur, devient l'unique porte d'entrée publique).
- `deploy/Caddyfile.benevoles` : bloc de vhost à ajouter au Caddy existant
  (`/api/*` vers le backend avec préfixe retiré, le reste vers le frontend).
- `deploy/README.md` : runbook complet (remote GitLab, variables CI/CD,
  préparation du VPS, création de l'édition de production avant le premier
  build frontend, vhost Caddy).
- CLAUDE.md et README.md mis à jour en conséquence.

Vérifié : `docker compose -f deploy/docker-compose.prod.yml config`,
build réel de l'image frontend de prod, / et une route SPA (/grille)
répondent 200 via nginx.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-14 19:04:28 +02:00
..

Déploiement

Le pipeline .gitlab-ci.yml se déclenche sur chaque push :

  1. tests (backend : ruff + pytest + alembic check ; frontend : typecheck + lint + build + smoke) ;
  2. sur main seulement : build des images backend et frontend, poussées sur le registre de conteneurs du projet GitLab ;
  3. copie de deploy/docker-compose.prod.yml sur le VPS par SSH ;
  4. docker compose pull, migration Alembic, up -d.

Le VPS n'expose rien directement : un Caddy déjà installé sur le serveur (service système, hors de ce dépôt) reçoit le trafic public sur 443 et reverse-proxy vers les deux conteneurs, qui n'écoutent que sur 127.0.0.1.

Étape 0 — pousser le dépôt sur GitLab

# Créer d'abord un projet vide sur GitLab (sans README ni .gitignore), puis :
git remote add origin git@gitlab.com:<namespace>/<projet>.git
git push -u origin main

Variables CI/CD à définir (Paramètres → CI/CD → Variables)

Variable Description
SSH_HOST Hôte du VPS
SSH_USER Utilisateur SSH
SSH_KEY Clé privée SSH (format OpenSSH) — masquée, sur main seulement
DEPLOY_PATH Dossier du projet sur le VPS (ex. /opt/benevoles)
VITE_EDITION_ID UUID de l'édition de production — à ajouter après l'étape 3

CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD et CI_REGISTRY_IMAGE sont fournies automatiquement par GitLab, rien à configurer.

La clé publique correspondant à SSH_KEY doit être dans le ~/.ssh/authorized_keys de SSH_USER sur le VPS.

Étape 1 — préparation du VPS (une fois)

mkdir -p /opt/benevoles
cd /opt/benevoles
cat > .env <<'EOF'
POSTGRES_USER=benevoles
POSTGRES_PASSWORD=<mot de passe fort>
POSTGRES_DB=benevoles
JWT_SECRET=<secret fort ; openssl rand -hex 32>
PUBLIC_BASE_URL=https://benevoles.mon-domaine.fr

# Emails (récapitulatifs de planning + liens de connexion). Laisser
# EMAIL_ENABLED=false tant que le SMTP n'est pas prêt : les emails sont alors
# seulement journalisés.
EMAIL_ENABLED=true
SMTP_HOST=smtp.mon-domaine.fr
SMTP_PORT=587
SMTP_USER=no-reply@mon-domaine.fr
SMTP_PASSWORD=<mot de passe SMTP>
SMTP_STARTTLS=true
SMTP_SSL=false
SMTP_FROM=Festival <no-reply@mon-domaine.fr>
EOF

Le pipeline dépose docker-compose.prod.yml dans ce dossier et l'utilise tel quel — il n'y a rien d'autre à y copier.

Étape 2 — premier déploiement (backend + base seuls)

Le frontend a besoin de l'UUID d'une édition pour être construit (voir plus bas) : le tout premier déploiement se fait donc en deux temps.

  1. Sur GitLab, lancer le pipeline sur main normalement (Push → CI/CD → Pipelines, ou un simple commit). Le job build-frontend échouera ou produira une image sans édition tant que VITE_EDITION_ID n'existe pas : c'est attendu, on l'ignore pour l'instant.
  2. Une fois build-backend et deploy passés, db et backend tournent sur le VPS.

Étape 3 — créer l'édition de production

ssh SSH_USER@SSH_HOST
cd /opt/benevoles
docker compose -f docker-compose.prod.yml run --rm backend \
  uv run python -m app.bootstrap \
  --name "Festival 2026" --start 2026-07-01 --end 2026-07-04 \
  --email coordination@mon-domaine.fr --password <mot-de-passe-solide>

La commande affiche l'UUID de l'édition créée. Récupérer aussi celui-ci à tout moment avec :

docker compose -f docker-compose.prod.yml exec -T db \
  psql -U benevoles -tAc "select id from edition order by created_at desc limit 1"

Étape 4 — brancher le frontend sur cette édition

Dans GitLab, ajouter la variable CI/CD VITE_EDITION_ID avec cet UUID, puis relancer le pipeline sur main (bouton Retry sur le dernier pipeline, ou un nouveau commit). Cette fois build-frontend construit une image qui pointe sur la bonne édition, et deploy démarre le service frontend.

VITE_EDITION_ID ne change plus ensuite : elle ne se touche à nouveau que pour une nouvelle édition (festival de l'année suivante) qu'on voudrait publier sur ce même domaine.

Étape 5 — le vhost Caddy

Caddy tourne déjà sur ce serveur (service système) : ajouter le bloc deploy/Caddyfile.benevoles à son Caddyfile (ou au fichier qu'il importe pour les sites), avec le vrai nom de domaine, puis :

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Le DNS du domaine doit déjà pointer (enregistrement A/AAAA) vers ce VPS — Caddy obtient et renouvelle seul le certificat HTTPS (Let's Encrypt) au premier accès.

Vérifier ensuite que PUBLIC_BASE_URL dans le .env du VPS (étape 1) correspond bien à https://<ce domaine> — c'est cette valeur qui est utilisée dans les liens des emails envoyés aux bénévoles.

Déploiements suivants

Un simple push sur main suffit : tests → build → déploiement → migration → redémarrage, sans aucune intervention sur le VPS ni sur Caddy.