Aller au contenu

Pipeline CI/CD

Fonctionnement général

Le pipeline se déclenche automatiquement à chaque push sur la branche main. Il est identique pour tous les projets (Alert-Parent, Pedago Watch, etc.).

Push main → Build image → Push GHCR → Deploy VPS → Health check → Notify

Les 3 jobs

Job 1 : Build & Push

  1. Checkout du code source
  2. Configuration Docker Buildx avec cache GitHub Actions
  3. Login sur ghcr.io avec GITHUB_TOKEN
  4. Build de l'image Docker depuis le Dockerfile
  5. Push avec deux tags :
  6. sha-XXXXXXX (SHA du commit, pour traçabilité)
  7. latest (pour faciliter les pulls manuels)

Job 2 : Deploy

  1. Checkout du code (pour accéder au docker-compose.yml)
  2. SCP du fichier docker-compose.yml vers le VPS (évite le git pull sur le VPS)
  3. SSH vers le VPS, puis :
  4. Écriture du fichier .env depuis le secret DOTENV_FILE
  5. Login sur GHCR via GITHUB_TOKEN passé en variable d'environnement
  6. docker compose pull (télécharge la nouvelle image)
  7. docker compose up -d (redémarre le service)
  8. Boucle de health check : 12 tentatives x 5s = 60s max
  9. Nettoyage des images Docker > 7 jours

Job 3 : Notify

Envoie un webhook avec :

  • Statut : success ou failure
  • SHA du commit
  • Acteur (qui a poussé)
  • Lien vers le run GitHub Actions

Flux CI/CD

sequenceDiagram
    participant Dev as Développeur
    participant GH as GitHub Actions
    participant GHCR as GHCR Registry
    participant VPS as VPS Hostinger

    Dev->>GH: git push main
    GH->>GH: Build Docker image
    GH->>GHCR: Push image sha-XXXXXX + latest
    GH->>VPS: SCP docker-compose.yml
    GH->>VPS: SSH: write .env from DOTENV_FILE
    GH->>VPS: SSH: docker login GHCR
    GH->>VPS: SSH: docker compose pull && up -d
    VPS->>VPS: Health check loop (/health)
    GH->>GH: Notify success/failure (webhook)

Secrets GitHub Actions requis

Secret Description Obligatoire
SERVER_HOST IP ou hostname du VPS Oui
SERVER_USERNAME Utilisateur SSH (ex: root) Oui
SERVER_SSH_KEY Clé privée SSH complète (contenu de ~/.ssh/id_ed25519) Oui
DOTENV_FILE Contenu complet du fichier .env de production Oui
WEBHOOK_URL URL webhook pour notifications de déploiement Non

Configurer les secrets

gh secret set SERVER_HOST --repo EpitechAfrik/alert-parent-backend --body "IP_DU_VPS"
gh secret set SERVER_USERNAME --repo EpitechAfrik/alert-parent-backend --body "root"
gh secret set SERVER_SSH_KEY --repo EpitechAfrik/alert-parent-backend < ~/.ssh/id_ed25519
gh secret set DOTENV_FILE --repo EpitechAfrik/alert-parent-backend < .env.prod

Leçons apprises et corrections

Utiliser SCP au lieu de git pull

Problème : git pull sur le VPS nécessite une clé SSH GitHub configurée sur le VPS pour les repos privés. Cela ajoute de la complexité et des risques de sécurité.

Solution : Le pipeline copie uniquement le docker-compose.yml via SCP. Le code applicatif est dans l'image Docker, pas besoin du repo complet sur le VPS.

Pattern DOTENV_FILE

Problème : Gérer des dizaines de secrets individuels dans GitHub Actions est fastidieux et source d'erreurs.

Solution : Stocker l'intégralité du fichier .env comme un seul secret DOTENV_FILE. Le pipeline le réécrit à chaque déploiement sur le VPS.

# Dans le workflow
echo "${{ secrets.DOTENV_FILE }}" > /root/projects/alert-parent/alert-parent-backend/.env

Login GHCR sur le VPS

Problème : Le VPS doit s'authentifier auprès de GHCR pour tirer les images des repos privés.

Solution : Le GITHUB_TOKEN est passé en variable d'environnement lors de la session SSH, puis utilisé pour le login.

Chemins absolus

Problème : Utiliser ~ ou $HOME dans les scripts SSH peut résoudre vers le mauvais répertoire home (par exemple si l'utilisateur SSH n'est pas root).

Solution : Toujours utiliser des chemins absolus : /root/projects/alert-parent/alert-parent-backend, pas ~/projects/....

Permissions utilisateur

Problème : L'utilisateur de déploiement n'avait pas les droits Docker ou les permissions sur les répertoires.

Solutions :

  • usermod -aG docker UTILISATEUR : ajouter l'utilisateur au groupe Docker
  • chown UTILISATEUR:UTILISATEUR /root/projects/... : donner la propriété des fichiers
  • chmod 711 /root et chmod 711 /root/projects : permettre la traversée des répertoires

Rollout coordonné frontend/backend pour un gate de droits (breaking change)

Problème : Un gate de permission (nouveau droit requis pour voir/faire quelque chose) implique deux repos distincts (frontend + backend). Si le frontend qui vérifie le droit est déployé avant que le backend ne renvoie/n'applique réellement ce droit, des admins qui avaient légitimement accès à une fonctionnalité la voient disparaître de l'UI — une régression visible, le temps que le backend rattrape.

Solution : Construire et tester le gate frontend en avance, mais retenir volontairement son déploiement jusqu'à confirmation explicite que le backend correspondant est live en production. Concrètement (cas vécu : frontend + backend epidocs, coordination entre deux sessions Claude Code travaillant chacune sur son repo) :

  1. Développer et tester le gate côté frontend sur une branche, sans merger/déployer.
  2. Attendre la confirmation explicite (message de l'autre session/équipe) que le PR backend correspondant est live en prod — pas seulement mergé.
  3. Une fois confirmé : commit, push, PR, merge sur main côté frontend.
  4. Vérifier le déploiement de bout en bout (voir ci-dessous), pas seulement que la PR a été mergée.

PR mergée ≠ déploiement réussi

Une pull request verte/mergée ne garantit pas que le déploiement en production a réussi — ce sont deux étapes distinctes du pipeline. Un run précédent (sur une autre PR) avait vu le build et les tests passer, puis échouer précisément à l'étape Deploy to Production. Toujours vérifier le run GitHub Actions étape par étape, pas juste son statut global :

gh run watch <run_id> --exit-status --repo <owner>/<repo>

En suivant la progression Build & Test → Build Docker Image → Deploy to Production → health-check, et en confirmant que chaque étape est verte, pas uniquement la dernière connue.

Workflow de contribution (branche → PR → CI → merge → déploiement)

Procédure suivie systématiquement pour livrer un changement de code sur epidocs-frontend (et applicable tel quel à epidocs-api) — que ce soit une session humaine ou un agent IA qui pousse le code. Aucune de ces étapes n'est spécifique à un outil IA : c'est simplement la discipline attendue avant de merger sur main, qui déclenche un déploiement automatique.

# 1. Toujours vérifier l'état du dépôt avant de commencer (des fichiers
#    non liés peuvent déjà être modifiés localement — ne jamais les inclure
#    dans le commit par un `git add -A` aveugle)
git status

# 2. Une branche par changement logique, jamais de commit direct sur main
git checkout -b feat/nom-descriptif   # ou fix/, selon la nature

# 3. Stager UNIQUEMENT les fichiers du changement en cours
git add chemin/fichier1.js chemin/fichier2.js

# 4. Commit avec un message qui explique le POURQUOI, pas juste le quoi
git commit -m "..."

# 5. Pousser et ouvrir la PR
git push -u origin feat/nom-descriptif
gh pr create --title "..." --body "..."

# 6. Attendre la CI de la PR (pas juste lancer et espérer)
gh pr checks <NUMERO_PR> --watch

# 7. Merger seulement une fois vert
gh pr merge <NUMERO_PR> --merge --delete-branch

# 8. Revenir sur main à jour, retrouver le run de déploiement déclenché par le merge
git checkout main && git pull --ff-only
gh run list --branch main --limit 2

# 9. Vérifier le déploiement de bout en bout, pas juste que la PR est verte
#    (voir l'avertissement "PR mergée ≠ déploiement réussi" plus bas)
gh run watch <RUN_ID> --exit-status

gh pr checks --watch peut échouer pour une raison qui n'a rien à voir avec la CI

gh pr checks <N> --watch peut se terminer en erreur (exited with code 1, message error connecting to api.github.com) alors que la CI elle-même est passée — un simple aléa réseau côté gh, pas un échec de check. Toujours relancer gh pr checks <N> (sans --watch) pour lire l'état réel avant de conclure à un échec ou de bloquer un merge.

Pour un changement qui touche un gate de droits partagé frontend/backend

Ne jamais merger/déployer le côté frontend avant confirmation explicite (pas juste "mergé", mais "vérifié live en prod") que le backend correspondant est déployé — voir la section Rollout coordonné frontend/backend ci-dessous. Un moyen rapide de vérifier soi-même qu'une route existe déjà en prod sans avoir de token : curl dessus et vérifier qu'elle répond 401 (authentification requise) plutôt que 404 (route inexistante).

Déploiement manuel

Si le pipeline échoue, déployer manuellement :

ssh root@IP_DU_VPS

cd /root/projects/alert-parent/alert-parent-backend

# Mettre à jour le fichier .env si nécessaire
nano .env

# Tirer la dernière image
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker compose pull
docker compose up -d

# Vérifier
curl http://127.0.0.1:5011/health