Déploiement & CI/CD
Ce document décrit comment le monorepo est déployé en production sur un serveur Linux, et comment le pipeline GitLab CI/CD l’automatise. Toutes les commandes de ce guide sont faites à la main sur le serveur — ce document est un runbook, pas un script à lancer tel quel.
1. Vue d’ensemble
- Test : tourne sur les runners partagés GitLab.com, sur chaque branche/MR — aucune pipeline de branche ne touche jamais le serveur de production.
- Deploy : tourne uniquement sur le runner auto-hébergé installé sur le serveur, uniquement quand la branche est
main.
[!IMPORTANT] Aucun secret applicatif ne transite par GitLab CI/CD.
DATABASE_URL,JWT_SECRET, les clés R2/CinetPay restent exclusivement dans des fichiers.envsur le serveur, configurés une fois à la main et jamais écrasés par un déploiement (rsync --exclude='.env*'dans .gitlab-ci.yml). Le job de déploiement ne fait que synchroniser le code et redémarrer les process.
[!WARNING]
npm run lintn’est volontairement pas dans le jobtest. À l’introduction de ce pipeline, le dépôt comptait environ 300 avertissements/erreurs ESLint pré-existants (jamais appliqués faute de CI avant ça), répartis sur des fichiers sans rapport avec les changements en cours à ce moment-là — les bloquer tous aurait empêché tout déploiement.check-types+buildsont la vraie porte de qualité du pipeline. Le nettoyage lint reste un chantier à mener séparément ; une fois fait, ajouternpm run lintauscriptdu jobtestdans .gitlab-ci.yml.
2. Prérequis sur le serveur
À installer une seule fois (Ubuntu/Debian — adapter selon la distribution) :
# Node.js 20+ (via nvm ou le dépôt NodeSource, pas l'apt par défaut souvent trop ancien)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# PM2 (gestionnaire de process Node)
sudo npm install -g pm2
# Docker (pour Postgres)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER # puis se reconnecter
# nginx + certbot (HTTPS)
sudo apt-get install -y nginx certbot python3-certbot-nginx
# rsync, git (généralement déjà présents)
sudo apt-get install -y rsync git3. Premier déploiement (bootstrap manuel, une seule fois)
Le chemin /opt/marketplace est fixe : c’est celui que PM2 utilise (chemins relatifs dans ecosystem.config.js) et celui que le pipeline CI/CD synchronise ensuite — ne pas en changer sans mettre à jour les deux.
[!NOTE] Convention de ce guide : chaque bloc de commandes indique explicitement son dossier de départ (
📂 ...). Ne jamais supposer qu’on est resté dans le dossier du bloc précédent.
📂 N’importe où (ex: ~)
sudo mkdir -p /opt/marketplace
sudo chown $USER:$USER /opt/marketplace
git clone https://gitlab.com/ernestmekontso.codevibe-group/marketplace.git /opt/marketplace3.1 Base de données (Postgres via Docker)
📂 /opt/marketplace
cd /opt/marketplace
cp .env.docker.example .env.docker
nano .env.docker
# Éditer .env.docker : POSTGRES_PASSWORD doit être une vraie valeur forte,
# jamais "conTrol123" (celle du docker-compose.yml de dev local).
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml ps # vérifier "healthy"Voir docker-compose.prod.yml — Postgres n’écoute que sur
127.0.0.1:5432, jamais exposé publiquement.
3.2 Fichiers d’environnement applicatifs
Chaque app a besoin de son .env (ou .env.local pour les deux frontends Next.js — chargé au build, pas au runtime, donc doit exister avant npm run build).
📂 /opt/marketplace/apps/api
cd /opt/marketplace/apps/api
# Variables : voir onboarding.md, payments.md (CINETPAY_*), vendor-validation.md (R2_*)
cat > .env <<'EOF'
DATABASE_URL="postgresql://marketplace_owner:<le-mot-de-passe-de-.env.docker>@localhost:5432/marketplace_db"
JWT_SECRET="<générer une vraie valeur aléatoire, ex: openssl rand -hex 32>"
PAYMENT_GATEWAY_MODE="fake"
CINETPAY_API_KEY="..."
CINETPAY_SITE_ID="..."
CINETPAY_BASE_URL="https://api-checkout.cinetpay.com/v2"
CINETPAY_NOTIFY_URL="https://am-api.ernest-nde.dev/payments/webhook/cinetpay"
CINETPAY_RETURN_URL="https://am-store.ernest-nde.dev/payment-return"
R2_ACCOUNT_ID="..."
R2_ACCESS_KEY_ID="..."
R2_SECRET_ACCESS_KEY="..."
R2_BUCKET_NAME="marketplace-media"
R2_PUBLIC_URL="https://pub-xxxxxxxx.r2.dev"
EOF📂 /opt/marketplace/apps/dashboard
cd /opt/marketplace/apps/dashboard
echo 'NEXT_PUBLIC_API_URL="https://am-api.ernest-nde.dev"' > .env.local📂 /opt/marketplace/apps/store
cd /opt/marketplace/apps/store
echo 'NEXT_PUBLIC_API_URL="https://am-api.ernest-nde.dev"' > .env.local[!WARNING] Ces fichiers existent uniquement sur le serveur, jamais dans git. S’ils sont perdus (changement de serveur, etc.), il faut les recréer à la main — ils ne sont pas sauvegardés par le pipeline.
3.3 Installation, migrations, build, démarrage
Même ordre que le pipeline CI/CD (.gitlab-ci.yml) et deploy.sh : install → migrate → build → pm2.
📂 /opt/marketplace
cd /opt/marketplace
npm install📂 /opt/marketplace/apps/api
cd /opt/marketplace/apps/api
npx prisma migrate deploy📂 /opt/marketplace
cd /opt/marketplace
npm run build
pm2 start ecosystem.config.js
pm2 save
pm2 startup # affiche une commande à exécuter une fois pour survivre à un reboot du serveur4. nginx + HTTPS
Les fichiers de référence sont versionnés dans infra/nginx/ (am-api.conf, am-dashboard.conf, am-store.conf, am-docs.conf).
📂 /opt/marketplace
cd /opt/marketplace
for f in am-api am-dashboard am-store am-docs; do
sudo cp infra/nginx/$f.conf /etc/nginx/sites-available/$f.ernest-nde.dev
sudo ln -s /etc/nginx/sites-available/$f.ernest-nde.dev /etc/nginx/sites-enabled/
done
sudo nginx -t && sudo systemctl reload nginx
# HTTPS — certbot réécrit chaque fichier pour ajouter les directives SSL
# et la redirection 80 -> 443 automatiquement.
sudo certbot --nginx -d am-api.ernest-nde.dev -d am-dashboard.ernest-nde.dev -d am-store.ernest-nde.dev -d am-docs.ernest-nde.dev[!NOTE] Les sous-domaines (
am-api,am-dashboard,am-store,am-docssurernest-nde.dev) doivent déjà pointer (DNS, enregistrements A) vers l’IP du serveur avant de lancer certbot, sinon la validation ACME échoue.
5. Runner GitLab CI/CD auto-hébergé
📂 N’importe où (ex: ~) — aucun lien avec le dépôt
curl -L "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb" -o gitlab-runner.deb
sudo dpkg -i gitlab-runner.deb
sudo gitlab-runner register \
--url https://gitlab.com/ \
--token <TOKEN_DU_PROJET> \
--executor shell \
--tag-list prod-deploy \
--description "serveur-prod-marketplace"Le token s’obtient dans GitLab : Settings > CI/CD > Runners > New project runner (choisir le tag prod-deploy, cocher Protected — c’est la vraie barrière de sécurité : seules les pipelines sur une branche protégée pourront l’utiliser, même si quelqu’un modifiait .gitlab-ci.yml dans une merge request).
[!IMPORTANT] Dans Settings > Repository > Protected branches, vérifier que
mainest bien protégée (Maintainers only pour push/merge). Sans ça, le flag “Protected” du runner ne protège rien.
Le runner tourne sous l’utilisateur système qui l’a enregistré — vérifier qu’il a les droits sur /opt/marketplace, docker, et pm2 (le pm2 du job CI doit voir les mêmes process que celui lancé en 3.3 — même utilisateur système, ou pm2 partagé via PM2_HOME).
6. Fonctionnement du pipeline (.gitlab-ci.yml)
| Variable | Où | Rôle |
|---|---|---|
DEPLOY_PATH | .gitlab-ci.yml | Chemin fixe /opt/marketplace — à garder cohérent avec pm2. |
.env, .env.local, .env.docker | Serveur uniquement | Jamais touchés par le rsync --delete (exclus explicitement). |
7. Déploiement manuel de secours
Si le pipeline est indisponible, deploy.sh reproduit exactement les mêmes étapes (git pull au lieu de rsync, car lancé directement dans /opt/marketplace) :
cd /opt/marketplace
./deploy.sh8. Rollback
En cas de déploiement cassé :
cd /opt/marketplace
git log --oneline -5 # repérer le dernier commit sain
git checkout <sha-sain>
npm install
npm run build
pm2 reload ecosystem.config.js[!WARNING] Un rollback de code ne défait pas une migration Prisma déjà appliquée. Si le déploiement cassé incluait une migration de schéma incompatible avec l’ancien code, il faut aussi évaluer si la migration doit être défaite manuellement (
prisma migrate resolve/ rollback SQL écrit à la main) —prisma migrate deployn’a pas de commande “down” automatique.