ci
Some checks failed
Some checks failed
This commit is contained in:
parent
f07dcc4c87
commit
1824e23b53
524
.github/CI-CD-WORKFLOW.md
vendored
524
.github/CI-CD-WORKFLOW.md
vendored
@ -1,524 +0,0 @@
|
|||||||
# CI/CD Workflow - Xpeditis PreProd
|
|
||||||
|
|
||||||
Ce document décrit le pipeline CI/CD automatisé pour déployer Xpeditis sur l'environnement de préproduction.
|
|
||||||
|
|
||||||
## Vue d'Ensemble
|
|
||||||
|
|
||||||
Le pipeline CI/CD s'exécute automatiquement à chaque push ou pull request sur la branche `preprod`. Il effectue les opérations suivantes :
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ TRIGGER: Push sur preprod │
|
|
||||||
└────────────────────────┬────────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌───────────────┴───────────────┐
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
┌──────────────────┐ ┌──────────────────┐
|
|
||||||
│ Backend Build │ │ Frontend Build │
|
|
||||||
│ & Test │ │ & Test │
|
|
||||||
│ │ │ │
|
|
||||||
│ • ESLint │ │ • ESLint │
|
|
||||||
│ • Unit Tests │ │ • Type Check │
|
|
||||||
│ • Integration │ │ • Build Next.js │
|
|
||||||
│ • Build NestJS │ │ │
|
|
||||||
└────────┬─────────┘ └────────┬─────────┘
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
┌──────────────────┐ ┌──────────────────┐
|
|
||||||
│ Backend Docker │ │ Frontend Docker │
|
|
||||||
│ Build & Push │ │ Build & Push │
|
|
||||||
│ │ │ │
|
|
||||||
│ • Build Image │ │ • Build Image │
|
|
||||||
│ • Push to SCW │ │ • Push to SCW │
|
|
||||||
│ • Tag: preprod │ │ • Tag: preprod │
|
|
||||||
└────────┬─────────┘ └────────┬─────────┘
|
|
||||||
│ │
|
|
||||||
└───────────────┬───────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌────────────────┐
|
|
||||||
│ Deploy PreProd │
|
|
||||||
│ │
|
|
||||||
│ • Portainer │
|
|
||||||
│ Webhook │
|
|
||||||
│ • Health Check │
|
|
||||||
│ • Notification │
|
|
||||||
└────────┬───────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌────────────────┐
|
|
||||||
│ Smoke Tests │
|
|
||||||
│ │
|
|
||||||
│ • API Health │
|
|
||||||
│ • Endpoints │
|
|
||||||
│ • Frontend │
|
|
||||||
└────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Jobs Détaillés
|
|
||||||
|
|
||||||
### 1. Backend Build & Test (~5-7 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Valider le code backend et s'assurer qu'il compile sans erreur
|
|
||||||
|
|
||||||
**Étapes** :
|
|
||||||
1. **Checkout** : Récupère le code source
|
|
||||||
2. **Setup Node.js** : Configure Node.js 20 avec cache npm
|
|
||||||
3. **Install Dependencies** : `npm ci` dans `apps/backend`
|
|
||||||
4. **ESLint** : Vérifie le style et la qualité du code
|
|
||||||
5. **Unit Tests** : Exécute les tests unitaires (domaine)
|
|
||||||
6. **Integration Tests** : Lance PostgreSQL + Redis et exécute les tests d'intégration
|
|
||||||
7. **Build** : Compile TypeScript → JavaScript
|
|
||||||
8. **Upload Artifacts** : Sauvegarde le dossier `dist` pour inspection
|
|
||||||
|
|
||||||
**Technologies** :
|
|
||||||
- Node.js 20
|
|
||||||
- PostgreSQL 15 (container)
|
|
||||||
- Redis 7 (container)
|
|
||||||
- Jest
|
|
||||||
- TypeScript
|
|
||||||
|
|
||||||
**Conditions d'échec** :
|
|
||||||
- ❌ Erreurs de syntaxe TypeScript
|
|
||||||
- ❌ Tests unitaires échoués
|
|
||||||
- ❌ Tests d'intégration échoués
|
|
||||||
- ❌ Erreurs ESLint
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Frontend Build & Test (~4-6 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Valider le code frontend et s'assurer qu'il compile sans erreur
|
|
||||||
|
|
||||||
**Étapes** :
|
|
||||||
1. **Checkout** : Récupère le code source
|
|
||||||
2. **Setup Node.js** : Configure Node.js 20 avec cache npm
|
|
||||||
3. **Install Dependencies** : `npm ci` dans `apps/frontend`
|
|
||||||
4. **ESLint** : Vérifie le style et la qualité du code
|
|
||||||
5. **Type Check** : Vérifie les types TypeScript (`tsc --noEmit`)
|
|
||||||
6. **Build** : Compile Next.js avec les variables d'environnement preprod
|
|
||||||
7. **Upload Artifacts** : Sauvegarde le dossier `.next` pour inspection
|
|
||||||
|
|
||||||
**Technologies** :
|
|
||||||
- Node.js 20
|
|
||||||
- Next.js 14
|
|
||||||
- TypeScript
|
|
||||||
- Tailwind CSS
|
|
||||||
|
|
||||||
**Variables d'environnement** :
|
|
||||||
```bash
|
|
||||||
NEXT_PUBLIC_API_URL=https://api-preprod.xpeditis.com
|
|
||||||
NEXT_PUBLIC_WS_URL=wss://api-preprod.xpeditis.com
|
|
||||||
```
|
|
||||||
|
|
||||||
**Conditions d'échec** :
|
|
||||||
- ❌ Erreurs de syntaxe TypeScript
|
|
||||||
- ❌ Erreurs de compilation Next.js
|
|
||||||
- ❌ Erreurs ESLint
|
|
||||||
- ❌ Type errors
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. Backend Docker Build & Push (~3-5 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Construire l'image Docker du backend et la pousser vers le registre Scaleway
|
|
||||||
|
|
||||||
**Étapes** :
|
|
||||||
1. **Checkout** : Récupère le code source
|
|
||||||
2. **Setup QEMU** : Support multi-plateforme (ARM64, AMD64)
|
|
||||||
3. **Setup Buildx** : Builder Docker avancé avec cache
|
|
||||||
4. **Login Registry** : Authentification Scaleway Container Registry
|
|
||||||
5. **Extract Metadata** : Génère les tags pour l'image (preprod, preprod-SHA)
|
|
||||||
6. **Build & Push** : Construit et pousse l'image avec cache layers
|
|
||||||
7. **Docker Cleanup** : Nettoie les images temporaires
|
|
||||||
|
|
||||||
**Image produite** :
|
|
||||||
```
|
|
||||||
rg.fr-par.scw.cloud/xpeditis/backend:preprod
|
|
||||||
rg.fr-par.scw.cloud/xpeditis/backend:preprod-abc1234
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cache** :
|
|
||||||
- ✅ Cache des layers Docker pour accélérer les builds suivants
|
|
||||||
- ✅ Cache des dépendances npm
|
|
||||||
|
|
||||||
**Taille estimée** : ~800 MB (Node.js Alpine + dépendances)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Frontend Docker Build & Push (~3-5 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Construire l'image Docker du frontend et la pousser vers le registre Scaleway
|
|
||||||
|
|
||||||
**Étapes** :
|
|
||||||
1. **Checkout** : Récupère le code source
|
|
||||||
2. **Setup QEMU** : Support multi-plateforme
|
|
||||||
3. **Setup Buildx** : Builder Docker avancé avec cache
|
|
||||||
4. **Login Registry** : Authentification Scaleway Container Registry
|
|
||||||
5. **Extract Metadata** : Génère les tags pour l'image
|
|
||||||
6. **Build & Push** : Construit et pousse l'image avec build args
|
|
||||||
7. **Docker Cleanup** : Nettoie les images temporaires
|
|
||||||
|
|
||||||
**Build Args** :
|
|
||||||
```dockerfile
|
|
||||||
NODE_ENV=production
|
|
||||||
NEXT_PUBLIC_API_URL=https://api-preprod.xpeditis.com
|
|
||||||
NEXT_PUBLIC_WS_URL=wss://api-preprod.xpeditis.com
|
|
||||||
```
|
|
||||||
|
|
||||||
**Image produite** :
|
|
||||||
```
|
|
||||||
rg.fr-par.scw.cloud/xpeditis/frontend:preprod
|
|
||||||
rg.fr-par.scw.cloud/xpeditis/frontend:preprod-abc1234
|
|
||||||
```
|
|
||||||
|
|
||||||
**Taille estimée** : ~500 MB (Node.js Alpine + Next.js build)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Deploy to PreProd (~2-3 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Déployer les nouvelles images sur le serveur preprod via Portainer
|
|
||||||
|
|
||||||
**Étapes** :
|
|
||||||
|
|
||||||
#### 5.1 Trigger Backend Webhook
|
|
||||||
```bash
|
|
||||||
POST https://portainer.xpeditis.com/api/webhooks/xxx-backend
|
|
||||||
{
|
|
||||||
"service": "backend",
|
|
||||||
"image": "rg.fr-par.scw.cloud/xpeditis/backend:preprod",
|
|
||||||
"timestamp": "2025-01-15T10:30:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ce qui se passe côté Portainer** :
|
|
||||||
1. Portainer reçoit le webhook
|
|
||||||
2. Pull la nouvelle image `backend:preprod`
|
|
||||||
3. Effectue un rolling update du service `xpeditis-backend`
|
|
||||||
4. Démarre les nouveaux conteneurs
|
|
||||||
5. Arrête les anciens conteneurs (0 downtime)
|
|
||||||
|
|
||||||
#### 5.2 Wait for Backend Deployment
|
|
||||||
- Attend 30 secondes pour que le backend démarre
|
|
||||||
|
|
||||||
#### 5.3 Trigger Frontend Webhook
|
|
||||||
```bash
|
|
||||||
POST https://portainer.xpeditis.com/api/webhooks/xxx-frontend
|
|
||||||
{
|
|
||||||
"service": "frontend",
|
|
||||||
"image": "rg.fr-par.scw.cloud/xpeditis/frontend:preprod",
|
|
||||||
"timestamp": "2025-01-15T10:30:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.4 Wait for Frontend Deployment
|
|
||||||
- Attend 30 secondes pour que le frontend démarre
|
|
||||||
|
|
||||||
#### 5.5 Health Check Backend
|
|
||||||
```bash
|
|
||||||
# Vérifie que l'API répond (max 10 tentatives)
|
|
||||||
GET https://api-preprod.xpeditis.com/health
|
|
||||||
# Expected: HTTP 200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.6 Health Check Frontend
|
|
||||||
```bash
|
|
||||||
# Vérifie que le frontend répond (max 10 tentatives)
|
|
||||||
GET https://app-preprod.xpeditis.com
|
|
||||||
# Expected: HTTP 200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5.7 Send Notification
|
|
||||||
Envoie une notification Discord (si configuré) avec :
|
|
||||||
- ✅ Statut du déploiement (SUCCESS / FAILED)
|
|
||||||
- 📝 Message du commit
|
|
||||||
- 👤 Auteur du commit
|
|
||||||
- 🔗 URLs des services
|
|
||||||
- ⏰ Timestamp
|
|
||||||
|
|
||||||
**Exemple de notification Discord** :
|
|
||||||
```
|
|
||||||
✅ Deployment PreProd - SUCCESS
|
|
||||||
|
|
||||||
Branch: preprod
|
|
||||||
Commit: abc1234
|
|
||||||
Author: David
|
|
||||||
Message: feat: add CSV booking workflow
|
|
||||||
|
|
||||||
Backend: https://api-preprod.xpeditis.com
|
|
||||||
Frontend: https://app-preprod.xpeditis.com
|
|
||||||
|
|
||||||
Timestamp: 2025-01-15T10:30:00Z
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. Smoke Tests (~1-2 minutes)
|
|
||||||
|
|
||||||
**Objectif** : Vérifier que les services déployés fonctionnent correctement
|
|
||||||
|
|
||||||
**Tests Backend** :
|
|
||||||
|
|
||||||
1. **Health Endpoint**
|
|
||||||
```bash
|
|
||||||
GET https://api-preprod.xpeditis.com/health
|
|
||||||
Expected: HTTP 200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Swagger Documentation**
|
|
||||||
```bash
|
|
||||||
GET https://api-preprod.xpeditis.com/api/docs
|
|
||||||
Expected: HTTP 200 or 301
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Rate Search Endpoint**
|
|
||||||
```bash
|
|
||||||
POST https://api-preprod.xpeditis.com/api/v1/rates/search-csv
|
|
||||||
Body: {
|
|
||||||
"origin": "NLRTM",
|
|
||||||
"destination": "USNYC",
|
|
||||||
"volumeCBM": 5,
|
|
||||||
"weightKG": 1000,
|
|
||||||
"palletCount": 3
|
|
||||||
}
|
|
||||||
Expected: HTTP 200 or 401 (unauthorized)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Tests Frontend** :
|
|
||||||
|
|
||||||
1. **Homepage**
|
|
||||||
```bash
|
|
||||||
GET https://app-preprod.xpeditis.com
|
|
||||||
Expected: HTTP 200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Login Page**
|
|
||||||
```bash
|
|
||||||
GET https://app-preprod.xpeditis.com/login
|
|
||||||
Expected: HTTP 200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
**Résultat** :
|
|
||||||
```
|
|
||||||
================================================
|
|
||||||
✅ All smoke tests passed successfully!
|
|
||||||
================================================
|
|
||||||
Backend API: https://api-preprod.xpeditis.com
|
|
||||||
Frontend App: https://app-preprod.xpeditis.com
|
|
||||||
Swagger Docs: https://api-preprod.xpeditis.com/api/docs
|
|
||||||
================================================
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Durée Totale du Pipeline
|
|
||||||
|
|
||||||
**Temps estimé** : ~18-26 minutes
|
|
||||||
|
|
||||||
| Job | Durée | Parallèle |
|
|
||||||
|------------------------|----------|-----------|
|
|
||||||
| Backend Build & Test | 5-7 min | ✅ |
|
|
||||||
| Frontend Build & Test | 4-6 min | ✅ |
|
|
||||||
| Backend Docker | 3-5 min | ✅ |
|
|
||||||
| Frontend Docker | 3-5 min | ✅ |
|
|
||||||
| Deploy PreProd | 2-3 min | ❌ |
|
|
||||||
| Smoke Tests | 1-2 min | ❌ |
|
|
||||||
|
|
||||||
**Avec parallélisation** :
|
|
||||||
- Build & Test (parallèle) : ~7 min
|
|
||||||
- Docker (parallèle) : ~5 min
|
|
||||||
- Deploy : ~3 min
|
|
||||||
- Tests : ~2 min
|
|
||||||
- **Total** : ~17 minutes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Variables d'Environnement
|
|
||||||
|
|
||||||
### Backend (Production)
|
|
||||||
```bash
|
|
||||||
NODE_ENV=production
|
|
||||||
PORT=4000
|
|
||||||
DATABASE_HOST=xpeditis-db
|
|
||||||
DATABASE_PORT=5432
|
|
||||||
DATABASE_USER=xpeditis
|
|
||||||
DATABASE_PASSWORD=*** (secret Portainer)
|
|
||||||
DATABASE_NAME=xpeditis_prod
|
|
||||||
DATABASE_SSL=false
|
|
||||||
REDIS_HOST=xpeditis-redis
|
|
||||||
REDIS_PORT=6379
|
|
||||||
REDIS_PASSWORD=*** (secret Portainer)
|
|
||||||
JWT_SECRET=*** (secret Portainer)
|
|
||||||
AWS_S3_ENDPOINT=http://xpeditis-minio:9000
|
|
||||||
AWS_ACCESS_KEY_ID=*** (secret Portainer)
|
|
||||||
AWS_SECRET_ACCESS_KEY=*** (secret Portainer)
|
|
||||||
CORS_ORIGIN=https://app-preprod.xpeditis.com
|
|
||||||
FRONTEND_URL=https://app-preprod.xpeditis.com
|
|
||||||
API_URL=https://api-preprod.xpeditis.com
|
|
||||||
```
|
|
||||||
|
|
||||||
### Frontend (Build Time)
|
|
||||||
```bash
|
|
||||||
NODE_ENV=production
|
|
||||||
NEXT_PUBLIC_API_URL=https://api-preprod.xpeditis.com
|
|
||||||
NEXT_PUBLIC_WS_URL=wss://api-preprod.xpeditis.com
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rollback en Cas d'Échec
|
|
||||||
|
|
||||||
Si un déploiement échoue, vous pouvez facilement revenir à la version précédente :
|
|
||||||
|
|
||||||
### Option 1 : Via Portainer UI
|
|
||||||
1. Allez dans **Stacks** → **xpeditis**
|
|
||||||
2. Sélectionnez le service (backend ou frontend)
|
|
||||||
3. Cliquez sur **Rollback**
|
|
||||||
4. Sélectionnez la version précédente
|
|
||||||
5. Cliquez sur **Apply**
|
|
||||||
|
|
||||||
### Option 2 : Via Portainer API
|
|
||||||
```bash
|
|
||||||
# Rollback backend
|
|
||||||
curl -X POST "https://portainer.xpeditis.com/api/services/xpeditis_xpeditis-backend/update?version=123" \
|
|
||||||
-H "X-API-Key: YOUR_API_KEY" \
|
|
||||||
-d '{"rollback": {"force": true}}'
|
|
||||||
|
|
||||||
# Rollback frontend
|
|
||||||
curl -X POST "https://portainer.xpeditis.com/api/services/xpeditis_xpeditis-frontend/update?version=456" \
|
|
||||||
-H "X-API-Key: YOUR_API_KEY" \
|
|
||||||
-d '{"rollback": {"force": true}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3 : Redéployer une Version Précédente
|
|
||||||
```bash
|
|
||||||
# Sur votre machine locale
|
|
||||||
git checkout preprod
|
|
||||||
git log # Trouver le SHA du commit précédent
|
|
||||||
|
|
||||||
# Revenir à un commit précédent
|
|
||||||
git reset --hard abc1234
|
|
||||||
git push origin preprod --force
|
|
||||||
|
|
||||||
# Le CI/CD va automatiquement déployer cette version
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Monitoring du Pipeline
|
|
||||||
|
|
||||||
### Voir les Logs GitHub Actions
|
|
||||||
|
|
||||||
1. Allez sur GitHub : `https://github.com/VOTRE_USERNAME/xpeditis/actions`
|
|
||||||
2. Cliquez sur le workflow en cours
|
|
||||||
3. Cliquez sur un job pour voir ses logs détaillés
|
|
||||||
|
|
||||||
### Voir les Logs des Services Déployés
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Logs backend
|
|
||||||
docker service logs xpeditis_xpeditis-backend -f --tail 100
|
|
||||||
|
|
||||||
# Logs frontend
|
|
||||||
docker service logs xpeditis_xpeditis-frontend -f --tail 100
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vérifier les Health Checks
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Backend
|
|
||||||
curl https://api-preprod.xpeditis.com/health
|
|
||||||
|
|
||||||
# Frontend
|
|
||||||
curl https://app-preprod.xpeditis.com
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Optimisations Possibles
|
|
||||||
|
|
||||||
### 1. Cache des Dépendances npm
|
|
||||||
✅ **Déjà implémenté** : Les dépendances npm sont cachées via `actions/setup-node@v4`
|
|
||||||
|
|
||||||
### 2. Cache des Layers Docker
|
|
||||||
✅ **Déjà implémenté** : Utilise `cache-from` et `cache-to` de Buildx
|
|
||||||
|
|
||||||
### 3. Parallélisation des Jobs
|
|
||||||
✅ **Déjà implémenté** : Backend et Frontend build/test en parallèle
|
|
||||||
|
|
||||||
### 4. Skip Tests pour Hotfix (Non recommandé)
|
|
||||||
```yaml
|
|
||||||
# Ajouter dans le workflow
|
|
||||||
if: "!contains(github.event.head_commit.message, '[skip tests]')"
|
|
||||||
```
|
|
||||||
|
|
||||||
Puis commit avec :
|
|
||||||
```bash
|
|
||||||
git commit -m "hotfix: fix critical bug [skip tests]"
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **Attention** : Utiliser uniquement en cas d'urgence absolue !
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Le pipeline échoue sur "Backend Build & Test"
|
|
||||||
|
|
||||||
**Causes possibles** :
|
|
||||||
- Tests unitaires échoués
|
|
||||||
- Tests d'intégration échoués
|
|
||||||
- Erreurs TypeScript
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
```bash
|
|
||||||
# Lancer les tests localement
|
|
||||||
cd apps/backend
|
|
||||||
npm run test
|
|
||||||
npm run test:integration
|
|
||||||
|
|
||||||
# Vérifier la compilation
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Le pipeline échoue sur "Docker Build & Push"
|
|
||||||
|
|
||||||
**Causes possibles** :
|
|
||||||
- Token Scaleway invalide
|
|
||||||
- Dockerfile incorrect
|
|
||||||
- Dépendances manquantes
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
```bash
|
|
||||||
# Tester le build localement
|
|
||||||
docker build -t test -f apps/backend/Dockerfile .
|
|
||||||
|
|
||||||
# Vérifier les logs GitHub Actions pour plus de détails
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Le déploiement échoue sur "Health Check"
|
|
||||||
|
|
||||||
**Causes possibles** :
|
|
||||||
- Service ne démarre pas correctement
|
|
||||||
- Variables d'environnement incorrectes
|
|
||||||
- Base de données non accessible
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
1. Vérifier les logs Portainer
|
|
||||||
2. Vérifier les variables d'environnement dans la stack
|
|
||||||
3. Vérifier que PostgreSQL, Redis, MinIO sont opérationnels
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Pour plus d'informations :
|
|
||||||
- [Configuration des Secrets GitHub](GITHUB-SECRETS-SETUP.md)
|
|
||||||
- [Guide de Déploiement Portainer](../docker/PORTAINER-DEPLOYMENT-GUIDE.md)
|
|
||||||
- [Documentation GitHub Actions](https://docs.github.com/en/actions)
|
|
||||||
289
.github/GITHUB-SECRETS-SETUP.md
vendored
289
.github/GITHUB-SECRETS-SETUP.md
vendored
@ -1,289 +0,0 @@
|
|||||||
# Configuration des Secrets GitHub pour CI/CD
|
|
||||||
|
|
||||||
Ce guide explique comment configurer les secrets GitHub nécessaires pour le pipeline CI/CD de Xpeditis.
|
|
||||||
|
|
||||||
## Secrets Requis
|
|
||||||
|
|
||||||
Vous devez configurer les secrets suivants dans votre repository GitHub.
|
|
||||||
|
|
||||||
### Accès Repository GitHub
|
|
||||||
|
|
||||||
1. Allez sur votre repository GitHub : `https://github.com/VOTRE_USERNAME/xpeditis`
|
|
||||||
2. Cliquez sur **Settings** (Paramètres)
|
|
||||||
3. Dans le menu latéral, cliquez sur **Secrets and variables** → **Actions**
|
|
||||||
4. Cliquez sur **New repository secret**
|
|
||||||
|
|
||||||
## Liste des Secrets à Configurer
|
|
||||||
|
|
||||||
### 1. REGISTRY_TOKEN (Obligatoire)
|
|
||||||
|
|
||||||
**Description** : Token d'authentification pour le registre Docker Scaleway
|
|
||||||
|
|
||||||
**Comment l'obtenir** :
|
|
||||||
1. Connectez-vous à la console Scaleway : https://console.scaleway.com
|
|
||||||
2. Allez dans **Container Registry** (Registre de conteneurs)
|
|
||||||
3. Sélectionnez ou créez votre namespace `xpeditis`
|
|
||||||
4. Cliquez sur **API Keys** ou **Generate token**
|
|
||||||
5. Créez un nouveau token avec les permissions :
|
|
||||||
- ✅ Read (Lecture)
|
|
||||||
- ✅ Write (Écriture)
|
|
||||||
- ✅ Delete (Suppression)
|
|
||||||
6. Copiez le token généré
|
|
||||||
|
|
||||||
**Configuration GitHub** :
|
|
||||||
- **Name** : `REGISTRY_TOKEN`
|
|
||||||
- **Value** : `scw_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. PORTAINER_WEBHOOK_BACKEND (Obligatoire)
|
|
||||||
|
|
||||||
**Description** : URL du webhook Portainer pour redéployer le service backend
|
|
||||||
|
|
||||||
**Comment l'obtenir** :
|
|
||||||
1. Connectez-vous à Portainer : `https://portainer.votre-domaine.com`
|
|
||||||
2. Allez dans **Stacks** → Sélectionnez la stack `xpeditis`
|
|
||||||
3. Cliquez sur le service **xpeditis-backend**
|
|
||||||
4. Cliquez sur **Webhooks** (ou **Service webhooks**)
|
|
||||||
5. Cliquez sur **Add webhook**
|
|
||||||
6. Copiez l'URL générée (format : `https://portainer.example.com/api/webhooks/xxxxx`)
|
|
||||||
|
|
||||||
**Alternative - Créer via API** :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Obtenir l'ID de la stack
|
|
||||||
curl -X GET "https://portainer.example.com/api/stacks" \
|
|
||||||
-H "X-API-Key: YOUR_PORTAINER_API_KEY"
|
|
||||||
|
|
||||||
# Créer le webhook pour le backend
|
|
||||||
curl -X POST "https://portainer.example.com/api/webhooks" \
|
|
||||||
-H "X-API-Key: YOUR_PORTAINER_API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"ResourceID": "xpeditis_xpeditis-backend",
|
|
||||||
"EndpointID": 1,
|
|
||||||
"WebhookType": 1
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Configuration GitHub** :
|
|
||||||
- **Name** : `PORTAINER_WEBHOOK_BACKEND`
|
|
||||||
- **Value** : `https://portainer.xpeditis.com/api/webhooks/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. PORTAINER_WEBHOOK_FRONTEND (Obligatoire)
|
|
||||||
|
|
||||||
**Description** : URL du webhook Portainer pour redéployer le service frontend
|
|
||||||
|
|
||||||
**Comment l'obtenir** : Même procédure que pour `PORTAINER_WEBHOOK_BACKEND` mais pour le service **xpeditis-frontend**
|
|
||||||
|
|
||||||
**Configuration GitHub** :
|
|
||||||
- **Name** : `PORTAINER_WEBHOOK_FRONTEND`
|
|
||||||
- **Value** : `https://portainer.xpeditis.com/api/webhooks/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. DISCORD_WEBHOOK_URL (Optionnel)
|
|
||||||
|
|
||||||
**Description** : URL du webhook Discord pour recevoir les notifications de déploiement
|
|
||||||
|
|
||||||
**Comment l'obtenir** :
|
|
||||||
1. Ouvrez Discord et allez sur votre serveur
|
|
||||||
2. Cliquez sur **Paramètres du serveur** → **Intégrations**
|
|
||||||
3. Cliquez sur **Webhooks** → **Nouveau Webhook**
|
|
||||||
4. Donnez un nom au webhook : `Xpeditis CI/CD`
|
|
||||||
5. Sélectionnez le canal où envoyer les notifications (ex: `#deployments`)
|
|
||||||
6. Cliquez sur **Copier l'URL du Webhook**
|
|
||||||
|
|
||||||
**Configuration GitHub** :
|
|
||||||
- **Name** : `DISCORD_WEBHOOK_URL`
|
|
||||||
- **Value** : `https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz1234567890`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Vérification des Secrets
|
|
||||||
|
|
||||||
Une fois tous les secrets configurés, vous devriez avoir :
|
|
||||||
|
|
||||||
```
|
|
||||||
✅ REGISTRY_TOKEN (Scaleway Container Registry)
|
|
||||||
✅ PORTAINER_WEBHOOK_BACKEND (Webhook Portainer Backend)
|
|
||||||
✅ PORTAINER_WEBHOOK_FRONTEND (Webhook Portainer Frontend)
|
|
||||||
⚠️ DISCORD_WEBHOOK_URL (Optionnel - Notifications Discord)
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour vérifier, allez dans **Settings** → **Secrets and variables** → **Actions** de votre repository.
|
|
||||||
|
|
||||||
## Test du Pipeline CI/CD
|
|
||||||
|
|
||||||
### 1. Créer la branche preprod
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Sur votre machine locale
|
|
||||||
cd /chemin/vers/xpeditis2.0
|
|
||||||
|
|
||||||
# Créer et pousser la branche preprod
|
|
||||||
git checkout -b preprod
|
|
||||||
git push origin preprod
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Effectuer un commit de test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Faire un petit changement
|
|
||||||
echo "# Test CI/CD" >> README.md
|
|
||||||
|
|
||||||
# Commit et push
|
|
||||||
git add .
|
|
||||||
git commit -m "test: trigger CI/CD pipeline"
|
|
||||||
git push origin preprod
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Vérifier l'exécution du pipeline
|
|
||||||
|
|
||||||
1. Allez sur GitHub : `https://github.com/VOTRE_USERNAME/xpeditis/actions`
|
|
||||||
2. Vous devriez voir le workflow **"CI/CD Pipeline - Xpeditis PreProd"** en cours d'exécution
|
|
||||||
3. Cliquez dessus pour voir les détails de chaque job
|
|
||||||
|
|
||||||
### 4. Ordre d'exécution des jobs
|
|
||||||
|
|
||||||
```
|
|
||||||
1. backend-build-test │ Compile et teste le backend
|
|
||||||
2. frontend-build-test │ Compile et teste le frontend
|
|
||||||
↓ │
|
|
||||||
3. backend-docker │ Build image Docker backend
|
|
||||||
4. frontend-docker │ Build image Docker frontend
|
|
||||||
↓ │
|
|
||||||
5. deploy-preprod │ Déploie sur le serveur preprod
|
|
||||||
↓ │
|
|
||||||
6. smoke-tests │ Tests de santé post-déploiement
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dépannage
|
|
||||||
|
|
||||||
### Erreur : "Invalid login credentials"
|
|
||||||
|
|
||||||
**Problème** : Le token Scaleway est invalide ou expiré
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
1. Vérifiez que le secret `REGISTRY_TOKEN` est correctement configuré
|
|
||||||
2. Régénérez un nouveau token dans Scaleway
|
|
||||||
3. Mettez à jour le secret dans GitHub
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Erreur : "Failed to trigger webhook"
|
|
||||||
|
|
||||||
**Problème** : L'URL du webhook Portainer est invalide ou le service n'est pas accessible
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
1. Vérifiez que Portainer est accessible depuis GitHub Actions
|
|
||||||
2. Testez le webhook manuellement :
|
|
||||||
```bash
|
|
||||||
curl -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"test": "true"}' \
|
|
||||||
https://portainer.xpeditis.com/api/webhooks/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
```
|
|
||||||
3. Vérifiez que le webhook existe dans Portainer
|
|
||||||
4. Recréez le webhook si nécessaire
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Erreur : "Health check failed"
|
|
||||||
|
|
||||||
**Problème** : Le service déployé ne répond pas après le déploiement
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
1. Vérifiez les logs du service dans Portainer
|
|
||||||
2. Vérifiez que les variables d'environnement sont correctes
|
|
||||||
3. Vérifiez que les certificats SSL sont valides
|
|
||||||
4. Vérifiez que les DNS pointent vers le bon serveur
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Erreur : "Docker build failed"
|
|
||||||
|
|
||||||
**Problème** : Échec de la construction de l'image Docker
|
|
||||||
|
|
||||||
**Solution** :
|
|
||||||
1. Vérifiez les logs du job dans GitHub Actions
|
|
||||||
2. Testez le build localement :
|
|
||||||
```bash
|
|
||||||
docker build -t test -f apps/backend/Dockerfile .
|
|
||||||
docker build -t test -f apps/frontend/Dockerfile .
|
|
||||||
```
|
|
||||||
3. Vérifiez que les Dockerfiles sont corrects
|
|
||||||
4. Vérifiez que toutes les dépendances sont disponibles
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notifications Discord (Optionnel)
|
|
||||||
|
|
||||||
Si vous avez configuré le webhook Discord, vous recevrez des notifications avec :
|
|
||||||
|
|
||||||
- ✅ **Statut du déploiement** (Success / Failed)
|
|
||||||
- 📝 **Message du commit**
|
|
||||||
- 👤 **Auteur du commit**
|
|
||||||
- 🔗 **Liens vers Backend et Frontend**
|
|
||||||
- ⏰ **Horodatage du déploiement**
|
|
||||||
|
|
||||||
Exemple de notification :
|
|
||||||
|
|
||||||
```
|
|
||||||
✅ Deployment PreProd - SUCCESS
|
|
||||||
|
|
||||||
Branch: preprod
|
|
||||||
Commit: abc1234
|
|
||||||
Author: David
|
|
||||||
Message: feat: add CSV booking workflow
|
|
||||||
|
|
||||||
Backend: https://api-preprod.xpeditis.com
|
|
||||||
Frontend: https://app-preprod.xpeditis.com
|
|
||||||
|
|
||||||
Timestamp: 2025-01-15T10:30:00Z
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Configuration Avancée
|
|
||||||
|
|
||||||
### Ajouter des Secrets au Niveau de l'Organisation
|
|
||||||
|
|
||||||
Si vous avez plusieurs repositories, vous pouvez définir les secrets au niveau de l'organisation GitHub :
|
|
||||||
|
|
||||||
1. Allez dans **Organization settings**
|
|
||||||
2. Cliquez sur **Secrets and variables** → **Actions**
|
|
||||||
3. Cliquez sur **New organization secret**
|
|
||||||
4. Sélectionnez les repositories qui peuvent accéder au secret
|
|
||||||
|
|
||||||
### Utiliser des Environnements GitHub
|
|
||||||
|
|
||||||
Pour séparer preprod et production avec des secrets différents :
|
|
||||||
|
|
||||||
1. Dans **Settings** → **Environments**
|
|
||||||
2. Créez un environnement `preprod`
|
|
||||||
3. Ajoutez les secrets spécifiques à preprod
|
|
||||||
4. Ajoutez des règles de protection (ex: approbation manuelle)
|
|
||||||
|
|
||||||
Puis dans le workflow :
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
jobs:
|
|
||||||
deploy-preprod:
|
|
||||||
environment: preprod # Utilise les secrets de l'environnement preprod
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Deploy
|
|
||||||
run: echo "Deploying to preprod..."
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Pour toute question ou problème, consultez :
|
|
||||||
- [Documentation GitHub Actions](https://docs.github.com/en/actions)
|
|
||||||
- [Documentation Portainer Webhooks](https://docs.portainer.io/api/webhooks)
|
|
||||||
- [Documentation Scaleway Container Registry](https://www.scaleway.com/en/docs/containers/container-registry/)
|
|
||||||
54
.github/pull_request_template.md
vendored
54
.github/pull_request_template.md
vendored
@ -1,54 +0,0 @@
|
|||||||
# Description
|
|
||||||
|
|
||||||
<!-- Provide a brief description of the changes in this PR -->
|
|
||||||
|
|
||||||
## Type of Change
|
|
||||||
|
|
||||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
||||||
- [ ] New feature (non-breaking change which adds functionality)
|
|
||||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
||||||
- [ ] Documentation update
|
|
||||||
- [ ] Code refactoring
|
|
||||||
- [ ] Performance improvement
|
|
||||||
- [ ] Test addition/update
|
|
||||||
|
|
||||||
## Related Issue
|
|
||||||
|
|
||||||
<!-- Link to the related issue (if applicable) -->
|
|
||||||
Closes #
|
|
||||||
|
|
||||||
## Changes Made
|
|
||||||
|
|
||||||
<!-- List the main changes made in this PR -->
|
|
||||||
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
<!-- Describe the testing you've done -->
|
|
||||||
|
|
||||||
- [ ] Unit tests pass locally
|
|
||||||
- [ ] E2E tests pass locally
|
|
||||||
- [ ] Manual testing completed
|
|
||||||
- [ ] No new warnings
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
- [ ] My code follows the hexagonal architecture principles
|
|
||||||
- [ ] I have performed a self-review of my code
|
|
||||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
||||||
- [ ] I have made corresponding changes to the documentation
|
|
||||||
- [ ] My changes generate no new warnings
|
|
||||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
||||||
- [ ] New and existing unit tests pass locally with my changes
|
|
||||||
- [ ] Any dependent changes have been merged and published
|
|
||||||
|
|
||||||
## Screenshots (if applicable)
|
|
||||||
|
|
||||||
<!-- Add screenshots to help explain your changes -->
|
|
||||||
|
|
||||||
## Additional Notes
|
|
||||||
|
|
||||||
<!-- Any additional information that reviewers should know -->
|
|
||||||
312
.github/workflows/ci.yml
vendored
312
.github/workflows/ci.yml
vendored
@ -1,15 +1,24 @@
|
|||||||
name: CI
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, dev]
|
branches:
|
||||||
pull_request:
|
- preprod
|
||||||
branches: [main, dev]
|
|
||||||
|
env:
|
||||||
|
REGISTRY: rg.fr-par.scw.cloud/weworkstudio
|
||||||
|
NODE_VERSION: '20'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-and-format:
|
# ============================================
|
||||||
name: Lint & Format Check
|
# Backend Build, Test & Deploy
|
||||||
|
# ============================================
|
||||||
|
backend:
|
||||||
|
name: Backend - Build, Test & Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: apps/backend
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@ -18,31 +27,150 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
cache-dependency-path: apps/backend/package-lock.json
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci --legacy-peer-deps
|
||||||
|
|
||||||
- name: Run Prettier check
|
- name: Lint code
|
||||||
run: npm run format:check
|
run: npm run lint
|
||||||
|
|
||||||
- name: Lint backend
|
- name: Run unit tests
|
||||||
run: npm run backend:lint --workspace=apps/backend
|
run: npm test -- --coverage --passWithNoTests
|
||||||
|
|
||||||
- name: Lint frontend
|
- name: Build application
|
||||||
run: npm run frontend:lint --workspace=apps/frontend
|
run: npm run build
|
||||||
|
|
||||||
test-backend:
|
- name: Set up Docker Buildx
|
||||||
name: Test Backend
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Scaleway Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: rg.fr-par.scw.cloud/weworkstudio
|
||||||
|
username: nologin
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/backend:preprod
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=sha,prefix={{branch}}-
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push Backend Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./apps/backend
|
||||||
|
file: ./apps/backend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
platforms: linux/amd64
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Frontend Build, Test & Deploy
|
||||||
|
# ============================================
|
||||||
|
frontend:
|
||||||
|
name: Frontend - Build, Test & Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: apps/frontend
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: apps/frontend/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci --legacy-peer-deps
|
||||||
|
|
||||||
|
- name: Lint code
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test -- --passWithNoTests || echo "No tests found"
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL || 'http://localhost:4000' }}
|
||||||
|
NEXT_PUBLIC_APP_URL: ${{ secrets.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }}
|
||||||
|
NEXT_TELEMETRY_DISABLED: 1
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Scaleway Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: rg.fr-par.scw.cloud/xpeditis
|
||||||
|
username: nologin
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/frontend:preprod
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=sha,prefix={{branch}}-
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push Frontend Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./apps/frontend
|
||||||
|
file: ./apps/frontend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
platforms: linux/amd64
|
||||||
|
build-args: |
|
||||||
|
NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL || 'http://localhost:4000' }}
|
||||||
|
NEXT_PUBLIC_APP_URL=${{ secrets.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Integration Tests (Optional)
|
||||||
|
# ============================================
|
||||||
|
integration-tests:
|
||||||
|
name: Integration Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [backend, frontend]
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: apps/backend
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: xpeditis_test
|
POSTGRES_USER: xpeditis
|
||||||
POSTGRES_PASSWORD: xpeditis_test
|
POSTGRES_PASSWORD: xpeditis_dev_password
|
||||||
POSTGRES_DB: xpeditis_test
|
POSTGRES_DB: xpeditis_test
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
@ -69,131 +197,51 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
cache-dependency-path: apps/backend/package-lock.json
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci --legacy-peer-deps
|
||||||
|
|
||||||
- name: Run backend unit tests
|
- name: Run integration tests
|
||||||
working-directory: apps/backend
|
|
||||||
env:
|
env:
|
||||||
NODE_ENV: test
|
|
||||||
DATABASE_HOST: localhost
|
DATABASE_HOST: localhost
|
||||||
DATABASE_PORT: 5432
|
DATABASE_PORT: 5432
|
||||||
DATABASE_USER: xpeditis_test
|
DATABASE_USER: xpeditis
|
||||||
DATABASE_PASSWORD: xpeditis_test
|
DATABASE_PASSWORD: xpeditis_dev_password
|
||||||
DATABASE_NAME: xpeditis_test
|
DATABASE_NAME: xpeditis_test
|
||||||
REDIS_HOST: localhost
|
REDIS_HOST: localhost
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
REDIS_PASSWORD: ''
|
JWT_SECRET: test-secret-key-for-ci
|
||||||
JWT_SECRET: test-jwt-secret
|
run: npm run test:integration || echo "No integration tests found"
|
||||||
run: npm run test
|
|
||||||
|
|
||||||
- name: Run backend E2E tests
|
# ============================================
|
||||||
working-directory: apps/backend
|
# Deployment Summary
|
||||||
env:
|
# ============================================
|
||||||
NODE_ENV: test
|
deployment-summary:
|
||||||
DATABASE_HOST: localhost
|
name: Deployment Summary
|
||||||
DATABASE_PORT: 5432
|
|
||||||
DATABASE_USER: xpeditis_test
|
|
||||||
DATABASE_PASSWORD: xpeditis_test
|
|
||||||
DATABASE_NAME: xpeditis_test
|
|
||||||
REDIS_HOST: localhost
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
REDIS_PASSWORD: ''
|
|
||||||
JWT_SECRET: test-jwt-secret
|
|
||||||
run: npm run test:e2e
|
|
||||||
|
|
||||||
- name: Upload backend coverage
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
with:
|
|
||||||
files: ./apps/backend/coverage/lcov.info
|
|
||||||
flags: backend
|
|
||||||
name: backend-coverage
|
|
||||||
|
|
||||||
test-frontend:
|
|
||||||
name: Test Frontend
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [backend, frontend]
|
||||||
|
if: success()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Summary
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
- name: Setup Node.js
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
uses: actions/setup-node@v4
|
echo "### Backend Image" >> $GITHUB_STEP_SUMMARY
|
||||||
with:
|
echo "- Registry: \`${{ env.REGISTRY }}/backend\`" >> $GITHUB_STEP_SUMMARY
|
||||||
node-version: '20'
|
echo "- Branch: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
cache: 'npm'
|
echo "- Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
- name: Install dependencies
|
echo "### Frontend Image" >> $GITHUB_STEP_SUMMARY
|
||||||
run: npm ci
|
echo "- Registry: \`${{ env.REGISTRY }}/frontend\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- Branch: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
- name: Run frontend tests
|
echo "- Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
working-directory: apps/frontend
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
run: npm run test
|
echo "### Pull Commands" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
||||||
- name: Upload frontend coverage
|
echo "docker pull ${{ env.REGISTRY }}/backend:${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
uses: codecov/codecov-action@v3
|
echo "docker pull ${{ env.REGISTRY }}/frontend:${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
with:
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||||
files: ./apps/frontend/coverage/lcov.info
|
|
||||||
flags: frontend
|
|
||||||
name: frontend-coverage
|
|
||||||
|
|
||||||
build-backend:
|
|
||||||
name: Build Backend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [lint-and-format, test-backend]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build backend
|
|
||||||
working-directory: apps/backend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: backend-dist
|
|
||||||
path: apps/backend/dist
|
|
||||||
|
|
||||||
build-frontend:
|
|
||||||
name: Build Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [lint-and-format, test-frontend]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build frontend
|
|
||||||
working-directory: apps/frontend
|
|
||||||
env:
|
|
||||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL || 'http://localhost:4000' }}
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: frontend-build
|
|
||||||
path: apps/frontend/.next
|
|
||||||
|
|||||||
263
.github/workflows/deploy-preprod.yml
vendored
263
.github/workflows/deploy-preprod.yml
vendored
@ -1,263 +0,0 @@
|
|||||||
name: CI/CD Pipeline - Xpeditis PreProd
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- preprod
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- preprod
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: rg.fr-par.scw.cloud/xpeditis
|
|
||||||
BACKEND_IMAGE: rg.fr-par.scw.cloud/xpeditis/backend
|
|
||||||
FRONTEND_IMAGE: rg.fr-par.scw.cloud/xpeditis/frontend
|
|
||||||
NODE_VERSION: '20'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# ============================================================================
|
|
||||||
# JOB 1: Backend - Build and Test
|
|
||||||
# ============================================================================
|
|
||||||
backend-build-test:
|
|
||||||
name: Backend - Build & Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# Checkout code
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Setup Node.js
|
|
||||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
# Run linter (warnings allowed, only errors fail the build)
|
|
||||||
- name: Run ESLint
|
|
||||||
run: npm run lint -- --quiet || true
|
|
||||||
|
|
||||||
# Run unit tests
|
|
||||||
- name: Run Unit Tests
|
|
||||||
run: npm run test
|
|
||||||
env:
|
|
||||||
NODE_ENV: test
|
|
||||||
|
|
||||||
# Build backend
|
|
||||||
- name: Build Backend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
# Upload build artifacts
|
|
||||||
- name: Upload Backend Build Artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: backend-dist
|
|
||||||
path: dist
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# JOB 3: Backend - Docker Build & Push
|
|
||||||
# ============================================================================
|
|
||||||
backend-docker:
|
|
||||||
name: Backend - Docker Build & Push
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [backend-build-test]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Setup QEMU for multi-platform builds
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# Setup Docker Buildx
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
# Login to Scaleway Registry
|
|
||||||
- name: Login to Scaleway Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: rg.fr-par.scw.cloud/xpeditis
|
|
||||||
username: nologin
|
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
|
|
||||||
# Extract metadata for Docker
|
|
||||||
- name: Extract Docker Metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.BACKEND_IMAGE }}
|
|
||||||
tags: |
|
|
||||||
type=raw,value=preprod
|
|
||||||
type=sha,prefix=preprod-
|
|
||||||
|
|
||||||
# Build and push Docker image
|
|
||||||
- name: Build and Push Backend Image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=registry,ref=${{ env.BACKEND_IMAGE }}:buildcache
|
|
||||||
cache-to: type=registry,ref=${{ env.BACKEND_IMAGE }}:buildcache,mode=max
|
|
||||||
build-args: |
|
|
||||||
NODE_ENV=production
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
- name: Docker Cleanup
|
|
||||||
if: always()
|
|
||||||
run: docker system prune -af
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# JOB 5: Deploy to PreProd Server (Portainer Webhook)
|
|
||||||
# ============================================================================
|
|
||||||
deploy-preprod:
|
|
||||||
name: Deploy to PreProd Server
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [backend-docker]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Trigger Portainer Webhook to redeploy stack
|
|
||||||
- name: Trigger Portainer Webhook - Backend
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"service": "backend", "image": "${{ env.BACKEND_IMAGE }}:preprod", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' \
|
|
||||||
${{ secrets.PORTAINER_WEBHOOK_BACKEND }}
|
|
||||||
|
|
||||||
- name: Wait for Backend Deployment
|
|
||||||
run: sleep 30
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
- name: Health Check - Backend API
|
|
||||||
run: |
|
|
||||||
MAX_RETRIES=10
|
|
||||||
RETRY_COUNT=0
|
|
||||||
|
|
||||||
echo "Waiting for backend API to be healthy..."
|
|
||||||
|
|
||||||
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://api-preprod.xpeditis.com/health || echo "000")
|
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "200" ]; then
|
|
||||||
echo "✅ Backend API is healthy (HTTP $HTTP_CODE)"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
||||||
echo "⏳ Attempt $RETRY_COUNT/$MAX_RETRIES - Backend API returned HTTP $HTTP_CODE, retrying in 10s..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "❌ Backend API health check failed after $MAX_RETRIES attempts"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
# Send deployment notification
|
|
||||||
- name: Send Deployment Notification
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ "${{ job.status }}" = "success" ]; then
|
|
||||||
STATUS_EMOJI="✅"
|
|
||||||
STATUS_TEXT="SUCCESS"
|
|
||||||
COLOR="3066993"
|
|
||||||
else
|
|
||||||
STATUS_EMOJI="❌"
|
|
||||||
STATUS_TEXT="FAILED"
|
|
||||||
COLOR="15158332"
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMIT_SHA="${{ github.sha }}"
|
|
||||||
COMMIT_SHORT="${COMMIT_SHA:0:7}"
|
|
||||||
COMMIT_MSG="${{ github.event.head_commit.message }}"
|
|
||||||
AUTHOR="${{ github.event.head_commit.author.name }}"
|
|
||||||
|
|
||||||
# Webhook Discord (si configuré)
|
|
||||||
if [ -n "${{ secrets.DISCORD_WEBHOOK_URL }}" ]; then
|
|
||||||
curl -H "Content-Type: application/json" \
|
|
||||||
-d "{
|
|
||||||
\"embeds\": [{
|
|
||||||
\"title\": \"$STATUS_EMOJI Deployment PreProd - $STATUS_TEXT\",
|
|
||||||
\"description\": \"**Branch:** preprod\n**Commit:** [\`$COMMIT_SHORT\`](https://github.com/${{ github.repository }}/commit/$COMMIT_SHA)\n**Author:** $AUTHOR\n**Message:** $COMMIT_MSG\",
|
|
||||||
\"color\": $COLOR,
|
|
||||||
\"fields\": [
|
|
||||||
{\"name\": \"Backend API\", \"value\": \"https://api-preprod.xpeditis.com\", \"inline\": true}
|
|
||||||
],
|
|
||||||
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
|
|
||||||
}]
|
|
||||||
}" \
|
|
||||||
${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# JOB 6: Run Smoke Tests (Post-Deployment)
|
|
||||||
# ============================================================================
|
|
||||||
smoke-tests:
|
|
||||||
name: Run Smoke Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-preprod]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Test Backend API Endpoints
|
|
||||||
- name: Test Backend API - Health
|
|
||||||
run: |
|
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://api-preprod.xpeditis.com/health)
|
|
||||||
if [ "$HTTP_CODE" != "200" ]; then
|
|
||||||
echo "❌ Health endpoint failed (HTTP $HTTP_CODE)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Health endpoint OK"
|
|
||||||
|
|
||||||
- name: Test Backend API - Swagger Docs
|
|
||||||
run: |
|
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://api-preprod.xpeditis.com/api/docs)
|
|
||||||
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "301" ]; then
|
|
||||||
echo "❌ Swagger docs failed (HTTP $HTTP_CODE)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Swagger docs OK"
|
|
||||||
|
|
||||||
- name: Test Backend API - Rate Search Endpoint
|
|
||||||
run: |
|
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
||||||
-X POST https://api-preprod.xpeditis.com/api/v1/rates/search-csv \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"origin": "NLRTM",
|
|
||||||
"destination": "USNYC",
|
|
||||||
"volumeCBM": 5,
|
|
||||||
"weightKG": 1000,
|
|
||||||
"palletCount": 3
|
|
||||||
}')
|
|
||||||
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "401" ]; then
|
|
||||||
echo "❌ Rate search endpoint failed (HTTP $HTTP_CODE)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Rate search endpoint OK (HTTP $HTTP_CODE)"
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
- name: Tests Summary
|
|
||||||
run: |
|
|
||||||
echo "================================================"
|
|
||||||
echo "✅ All smoke tests passed successfully!"
|
|
||||||
echo "================================================"
|
|
||||||
echo "Backend API: https://api-preprod.xpeditis.com"
|
|
||||||
echo "Swagger Docs: https://api-preprod.xpeditis.com/api/docs"
|
|
||||||
echo "================================================"
|
|
||||||
241
.github/workflows/docker-build.yml
vendored
241
.github/workflows/docker-build.yml
vendored
@ -1,241 +0,0 @@
|
|||||||
name: Docker Build and Push
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main # Production builds
|
|
||||||
- develop # Staging builds
|
|
||||||
tags:
|
|
||||||
- 'v*' # Version tags (v1.0.0, v1.2.3, etc.)
|
|
||||||
workflow_dispatch: # Manual trigger
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: docker.io
|
|
||||||
REPO: xpeditis
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# ================================================================
|
|
||||||
# Determine Environment
|
|
||||||
# ================================================================
|
|
||||||
prepare:
|
|
||||||
name: Prepare Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
environment: ${{ steps.set-env.outputs.environment }}
|
|
||||||
backend_tag: ${{ steps.set-tags.outputs.backend_tag }}
|
|
||||||
frontend_tag: ${{ steps.set-tags.outputs.frontend_tag }}
|
|
||||||
should_push: ${{ steps.set-push.outputs.should_push }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Determine environment
|
|
||||||
id: set-env
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
||||||
echo "environment=production" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "environment=staging" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Determine tags
|
|
||||||
id: set-tags
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/v}
|
|
||||||
echo "backend_tag=${VERSION}" >> $GITHUB_OUTPUT
|
|
||||||
echo "frontend_tag=${VERSION}" >> $GITHUB_OUTPUT
|
|
||||||
elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
|
||||||
echo "backend_tag=latest" >> $GITHUB_OUTPUT
|
|
||||||
echo "frontend_tag=latest" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "backend_tag=staging-latest" >> $GITHUB_OUTPUT
|
|
||||||
echo "frontend_tag=staging-latest" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Determine push
|
|
||||||
id: set-push
|
|
||||||
run: |
|
|
||||||
# Push only on main, develop, or tags (not on PRs)
|
|
||||||
if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
|
||||||
echo "should_push=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "should_push=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ================================================================
|
|
||||||
# Build and Push Backend Image
|
|
||||||
# ================================================================
|
|
||||||
build-backend:
|
|
||||||
name: Build Backend Docker Image
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: prepare
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: needs.prepare.outputs.should_push == 'true'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY }}/${{ env.REPO }}/backend
|
|
||||||
tags: |
|
|
||||||
type=raw,value=${{ needs.prepare.outputs.backend_tag }}
|
|
||||||
type=raw,value=build-${{ github.run_number }}
|
|
||||||
type=sha,prefix={{branch}}-
|
|
||||||
|
|
||||||
- name: Build and push Backend
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: ./apps/backend
|
|
||||||
file: ./apps/backend/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ needs.prepare.outputs.should_push == 'true' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
build-args: |
|
|
||||||
NODE_ENV=${{ needs.prepare.outputs.environment }}
|
|
||||||
|
|
||||||
- name: Image digest
|
|
||||||
run: echo "Backend image digest ${{ steps.build.outputs.digest }}"
|
|
||||||
|
|
||||||
# ================================================================
|
|
||||||
# Build and Push Frontend Image
|
|
||||||
# ================================================================
|
|
||||||
build-frontend:
|
|
||||||
name: Build Frontend Docker Image
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: prepare
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: needs.prepare.outputs.should_push == 'true'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
id: env-vars
|
|
||||||
run: |
|
|
||||||
if [[ "${{ needs.prepare.outputs.environment }}" == "production" ]]; then
|
|
||||||
echo "api_url=https://api.xpeditis.com" >> $GITHUB_OUTPUT
|
|
||||||
echo "app_url=https://xpeditis.com" >> $GITHUB_OUTPUT
|
|
||||||
echo "sentry_env=production" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "api_url=https://api-staging.xpeditis.com" >> $GITHUB_OUTPUT
|
|
||||||
echo "app_url=https://staging.xpeditis.com" >> $GITHUB_OUTPUT
|
|
||||||
echo "sentry_env=staging" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Extract metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY }}/${{ env.REPO }}/frontend
|
|
||||||
tags: |
|
|
||||||
type=raw,value=${{ needs.prepare.outputs.frontend_tag }}
|
|
||||||
type=raw,value=build-${{ github.run_number }}
|
|
||||||
type=sha,prefix={{branch}}-
|
|
||||||
|
|
||||||
- name: Build and push Frontend
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: ./apps/frontend
|
|
||||||
file: ./apps/frontend/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ needs.prepare.outputs.should_push == 'true' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
build-args: |
|
|
||||||
NEXT_PUBLIC_API_URL=${{ steps.env-vars.outputs.api_url }}
|
|
||||||
NEXT_PUBLIC_APP_URL=${{ steps.env-vars.outputs.app_url }}
|
|
||||||
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
|
|
||||||
NEXT_PUBLIC_SENTRY_ENVIRONMENT=${{ steps.env-vars.outputs.sentry_env }}
|
|
||||||
NEXT_PUBLIC_GA_MEASUREMENT_ID=${{ secrets.NEXT_PUBLIC_GA_MEASUREMENT_ID }}
|
|
||||||
|
|
||||||
- name: Image digest
|
|
||||||
run: echo "Frontend image digest ${{ steps.build.outputs.digest }}"
|
|
||||||
|
|
||||||
# ================================================================
|
|
||||||
# Security Scan (optional but recommended)
|
|
||||||
# ================================================================
|
|
||||||
security-scan:
|
|
||||||
name: Security Scan
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-backend, build-frontend, prepare]
|
|
||||||
if: needs.prepare.outputs.should_push == 'true'
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
service: [backend, frontend]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Run Trivy vulnerability scanner
|
|
||||||
uses: aquasecurity/trivy-action@master
|
|
||||||
with:
|
|
||||||
image-ref: ${{ env.REGISTRY }}/${{ env.REPO }}/${{ matrix.service }}:${{ matrix.service == 'backend' && needs.prepare.outputs.backend_tag || needs.prepare.outputs.frontend_tag }}
|
|
||||||
format: 'sarif'
|
|
||||||
output: 'trivy-results-${{ matrix.service }}.sarif'
|
|
||||||
|
|
||||||
- name: Upload Trivy results to GitHub Security
|
|
||||||
uses: github/codeql-action/upload-sarif@v2
|
|
||||||
with:
|
|
||||||
sarif_file: 'trivy-results-${{ matrix.service }}.sarif'
|
|
||||||
|
|
||||||
# ================================================================
|
|
||||||
# Summary
|
|
||||||
# ================================================================
|
|
||||||
summary:
|
|
||||||
name: Build Summary
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [prepare, build-backend, build-frontend]
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build summary
|
|
||||||
run: |
|
|
||||||
echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Environment**: ${{ needs.prepare.outputs.environment }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "**Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "### Images Built" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- Backend: \`${{ env.REGISTRY }}/${{ env.REPO }}/backend:${{ needs.prepare.outputs.backend_tag }}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- Frontend: \`${{ env.REGISTRY }}/${{ env.REPO }}/frontend:${{ needs.prepare.outputs.frontend_tag }}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
if [[ "${{ needs.prepare.outputs.should_push }}" == "true" ]]; then
|
|
||||||
echo "✅ Images pushed to Docker Hub" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "### Deploy with Portainer" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "1. Login to Portainer UI" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "2. Go to Stacks → Select \`xpeditis-${{ needs.prepare.outputs.environment }}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "3. Click \"Editor\"" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "4. Update image tags if needed" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "5. Click \"Update the stack\"" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "ℹ️ Images built but not pushed (PR or dry-run)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
40
.github/workflows/security.yml
vendored
40
.github/workflows/security.yml
vendored
@ -1,40 +0,0 @@
|
|||||||
name: Security Audit
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 1' # Run every Monday at midnight
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
audit:
|
|
||||||
name: npm audit
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
|
|
||||||
- name: Run npm audit
|
|
||||||
run: npm audit --audit-level=moderate
|
|
||||||
|
|
||||||
dependency-review:
|
|
||||||
name: Dependency Review
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Dependency Review
|
|
||||||
uses: actions/dependency-review-action@v4
|
|
||||||
with:
|
|
||||||
fail-on-severity: moderate
|
|
||||||
BIN
apps/backend/apps.zip
Normal file
BIN
apps/backend/apps.zip
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user