/** * ============================================================================ * PIPELINE CI/CD - THÉ TIP TOP FRONTEND * ============================================================================ * * Ce pipeline automatise le processus de déploiement continu du frontend. * * ÉTAPES DU PIPELINE : * 1. Init - Détection de l'environnement (dev/preprod/prod) * 2. Checkout - Récupération du code source depuis Git * 3. Install - Installation des dépendances Node.js * 4. Lint & Tests - ESLint + Jest avec couverture de code * 5. SonarQube - Analyse statique avec rapport de couverture * 6. Build - Construction de l'image Docker (avec variables env) * 7. Push - Envoi de l'image vers le registre Docker privé * 8. Deploy - Déploiement sur le serveur cible * * ENVIRONNEMENTS : * - dev : Développement (branche dev) * - preprod : Pré-production (branche preprod) * - prod : Production (branche main) * * TECHNOLOGIE : Next.js 14 avec React 18 * * ============================================================================ */ pipeline { agent any // ========================================================================= // DÉCLENCHEUR : Vérifie les changements Git chaque minute // ========================================================================= triggers { pollSCM('* * * * *') } // ========================================================================= // PARAMÈTRES : Permet de choisir manuellement l'environnement // ========================================================================= parameters { choice( name: 'ENV', choices: ['dev', 'preprod', 'prod'], description: 'Choisir l environnement de deploiement' ) } // ========================================================================= // VARIABLES D'ENVIRONNEMENT GLOBALES // ========================================================================= environment { REGISTRY_URL = "registry.wk-archi-o24a-15m-g3.fr" IMAGE_NAME = "the-tip-top-frontend" NPM_CACHE = "/var/jenkins_home/npm-cache" } stages { // ===================================================================== // ÉTAPE 1 : INITIALISATION // --------------------------------------------------------------------- // But : Détecter automatiquement l'environnement selon la branche Git // - Branche 'dev' → Environnement dev // - Branche 'preprod' → Environnement preprod // - Branche 'main' → Environnement prod // ===================================================================== stage('🧭 Init - Détection environnement') { steps { script { def currentBranch = sh(script: "git rev-parse --abbrev-ref HEAD", returnStdout: true).trim() echo "🧭 Branche détectée : ${currentBranch}" if (["dev", "preprod", "main"].contains(currentBranch)) { env.ENV = (currentBranch == "main") ? "prod" : currentBranch } else { env.ENV = params.ENV ?: "dev" } env.TAG = "${env.ENV}-latest" env.DEPLOY_PATH = "/srv/devops/the-tip-top/${env.ENV}" echo """ ╔══════════════════════════════════════════╗ ║ CONFIGURATION PIPELINE ║ ╠══════════════════════════════════════════╣ ║ 🌍 Environnement : ${env.ENV.padRight(18)} ║ ║ 🏷️ Tag Docker : ${env.TAG.padRight(18)} ║ ║ 📂 Chemin déploie. : ${env.DEPLOY_PATH.take(18).padRight(18)} ║ ╚══════════════════════════════════════════╝ """ } } } // ===================================================================== // ÉTAPE 2 : CHECKOUT // --------------------------------------------------------------------- // But : Récupérer le code source depuis le dépôt Gitea // Action : Clone ou pull du code selon l'état du workspace // ===================================================================== stage('📦 Checkout - Récupération code source') { steps { echo "📦 Récupération du code source depuis Gitea..." checkout scm echo "✅ Code source récupéré avec succès" } } // ===================================================================== // ÉTAPE 3 : INSTALLATION DES DÉPENDANCES // --------------------------------------------------------------------- // But : Installer les packages Node.js nécessaires // Container : node:20 (requis pour Next.js 14) // Commande : npm ci (installation propre depuis package-lock.json) // Cache : Utilise un cache NPM partagé pour accélérer les builds // ===================================================================== stage('📦 Install - Dépendances Node.js') { agent { docker { image 'node:20' args "-u root -v ${NPM_CACHE}:/root/.npm" } } steps { echo "📦 Installation des dépendances Node.js..." sh 'npm ci --prefer-offline' stash includes: 'node_modules/**', name: 'node_modules' echo "✅ Dépendances installées avec succès" } } // ===================================================================== // ÉTAPE 4 : SONARQUBE - Qualité de code // --------------------------------------------------------------------- // Analyse statique du code pour détecter : // - Bugs potentiels // - Vulnérabilités de sécurité // - Code smells (mauvaises pratiques) // - Duplications de code // ===================================================================== stage('📊 SonarQube Analysis') { agent { docker { image 'sonarsource/sonar-scanner-cli:latest' args '-u root' } } steps { echo "📊 Analyse SonarQube - Qualité de code..." withSonarQubeEnv('SonarQube') { sh """ sonar-scanner """ } echo "✅ Analyse SonarQube terminée" } } // ===================================================================== // ÉTAPE 5 : LINT & TESTS // --------------------------------------------------------------------- // ESLint : Vérifie le style et les erreurs de code // Jest : Exécute les tests unitaires React/Next.js // Couverture : Génère un rapport de couverture (lcov) // ===================================================================== stage('🧪 Lint & Tests') { agent { docker { image 'node:20' args "-u root -v ${NPM_CACHE}:/root/.npm" } } steps { unstash 'node_modules' echo "🧪 Lancement ESLint et Jest..." script { def lintStatus = sh(script: 'npm run lint', returnStatus: true) def testStatus = sh(script: 'npm test -- --coverage', returnStatus: true) if (lintStatus != 0) { error "❌ ESLint a échoué - Corrigez les erreurs de style" } if (testStatus != 0) { error "❌ Les tests ont échoué - Vérifiez les tests unitaires" } echo "✅ Lint et tests passés avec succès" } } } // ===================================================================== // ÉTAPE 6 : BUILD IMAGE DOCKER // --------------------------------------------------------------------- // But : Construire l'image Docker du frontend Next.js // Spécificité : Injection des variables NEXT_PUBLIC_* au build time // Variables lues depuis : .env.{dev|preprod|prod} // Tags créés : // - {env}-latest (ex: dev-latest, prod-latest) // - latest // ===================================================================== stage('🐳 Build - Image Docker') { steps { echo "🐳 Construction de l'image Docker frontend Next.js..." script { // Lecture des variables d'environnement NEXT_PUBLIC_* def envFile = ".env.${env.ENV}" def envVars = sh(script: "cat ${envFile} | grep -E '^NEXT_PUBLIC_' | xargs", returnStdout: true).trim() // Construction des arguments de build Docker def buildArgs = "" envVars.split().each { envVar -> def parts = envVar.split('=', 2) if (parts.size() == 2) { buildArgs += "--build-arg ${parts[0]}='${parts[1]}' " } } sh """ docker build ${buildArgs} -t ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} . docker tag ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} ${REGISTRY_URL}/${IMAGE_NAME}:latest """ } echo "✅ Image Docker construite : ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG}" } } // ===================================================================== // ÉTAPE 7 : PUSH VERS LE REGISTRE // --------------------------------------------------------------------- // But : Envoyer l'image Docker vers le registre privé // Registre : registry.wk-archi-o24a-15m-g3.fr // Authentification : Credentials Jenkins sécurisés // ===================================================================== stage('📤 Push - Registre Docker') { steps { echo "📤 Envoi de l'image vers le registre Docker privé..." withCredentials([usernamePassword(credentialsId: 'registry-credentials', usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) { sh """ echo "$REG_PASS" | docker login ${REGISTRY_URL} -u "$REG_USER" --password-stdin docker push ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} docker push ${REGISTRY_URL}/${IMAGE_NAME}:latest """ } echo "✅ Image envoyée avec succès vers ${REGISTRY_URL}" } } // ===================================================================== // ÉTAPE 8 : DÉPLOIEMENT // --------------------------------------------------------------------- // But : Déployer le frontend sur le serveur cible // Actions : // 1. Pull de la nouvelle image depuis le registre // 2. Recréation du container avec la nouvelle image // Option : --no-deps pour ne pas redémarrer le backend // Chemins : /srv/devops/the-tip-top/{dev|preprod|prod} // ===================================================================== stage('🚀 Deploy - Mise en production') { steps { echo "🚀 Déploiement du frontend sur l'environnement ${env.ENV}..." sh """ cd "${env.DEPLOY_PATH}" docker compose pull frontend docker compose up -d --no-deps --force-recreate frontend """ echo "✅ Frontend déployé avec succès sur ${env.ENV}" } } } // ========================================================================= // ACTIONS POST-PIPELINE // ========================================================================= post { success { echo """ ╔══════════════════════════════════════════╗ ║ ✅ PIPELINE TERMINÉ AVEC SUCCÈS ║ ╠══════════════════════════════════════════╣ ║ Environnement : ${env.ENV.padRight(23)} ║ ║ Image : ${IMAGE_NAME.padRight(23)} ║ ║ Tag : ${env.TAG.padRight(23)} ║ ╚══════════════════════════════════════════╝ """ emailext( to: 'soufiane.baali99@gmail.com', subject: "✅ Pipeline Frontend SUCCÈS - ${env.ENV}", body: """

✅ Pipeline Frontend terminé avec succès

Projet${env.JOB_NAME}
Build#${env.BUILD_NUMBER}
Environnement${env.ENV}
Image${IMAGE_NAME}:${env.TAG}
Durée${currentBuild.durationString}

🔗 Voir les détails du build

""", mimeType: 'text/html' ) } failure { echo """ ╔══════════════════════════════════════════╗ ║ ❌ ÉCHEC DU PIPELINE ║ ╠══════════════════════════════════════════╣ ║ Environnement : ${env.ENV.padRight(23)} ║ ║ Vérifiez les logs pour plus de détails ║ ╚══════════════════════════════════════════╝ """ emailext( to: 'soufiane.baali99@gmail.com', subject: "❌ Pipeline Frontend ÉCHEC - ${env.ENV}", body: """

❌ Pipeline Frontend a échoué

Projet${env.JOB_NAME}
Build#${env.BUILD_NUMBER}
Environnement${env.ENV}
Durée${currentBuild.durationString}

🔗 Voir les logs du build

🔗 Console Output

""", mimeType: 'text/html' ) } } }