pipeline { agent any environment { NODEJS_HOME = "${tool 'NodeJS'}" PATH = "${env.NODEJS_HOME}/bin:${env.PATH}" DOCKER_REGISTRY = "nexus.foodgame.fr:8123" IMAGE_NAME = "fatboar_repo/workflow_jenkins_1" IMAGE_TAG = "latest" } stages { // 1. Déterminer l'environnement en fonction de la branche stage('Select Environment') { steps { script { if (env.BRANCH_NAME == 'feature') { env.ENV = 'dev' env.COMPOSE_FILE = 'docker-compose.dev.yml' env.URL = "dev.foodgame.fr" env.BACKUP_CONTAINER = "mongodb-backup-dev" } else if (env.BRANCH_NAME == 'dev') { env.ENV = 'preprod' env.COMPOSE_FILE = 'docker-compose.preprod.yml' env.URL = "preprod.foodgame.fr" env.BACKUP_CONTAINER = "mongodb-backup-preprod" } else if (env.BRANCH_NAME == 'master') { env.ENV = 'prod' env.COMPOSE_FILE = 'docker-compose.prod.yml' env.URL = "fatboar.foodgame.fr" env.BACKUP_CONTAINER = "mongodb-backup-prod" } else { error "Branche non gérée : ${env.BRANCH_NAME}" } echo "Déploiement sur l'environnement ${env.ENV} (${env.URL})" } } } // 2. Installation des dépendances et tests unitaires Angular stage('Unit Tests') { agent { docker { image 'cypress/browsers:node14.17.0-chrome91-ff89' args '-u root:root --shm-size=2g' } } environment { CHROME_BIN = '/usr/bin/google-chrome' NODE_OPTIONS = '--max-old-space-size=4096' DISPLAY = ':99' } steps { dir('angular-client') { sh ''' echo "🔧 Configuration de l'environnement" export DISPLAY=:99 Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & echo "✅ Installation des dépendances" npm install --legacy-peer-deps echo "🧪 Vérification de Chrome" google-chrome --version echo "🚀 Lancement des tests unitaires avec couverture" npx ng test --no-watch --no-progress --browsers=ChromeHeadlessCI --code-coverage ''' } } post { always { // Publier le rapport de couverture HTML publishHTML([ allowMissing: false, alwaysLinkToLastBuild: false, keepAll: true, reportDir: 'angular-client/coverage/fatboar-burger', reportFiles: 'index.html', reportName: 'Code Coverage Report', reportTitles: 'Coverage Report' ]) // Archiver les artefacts de couverture archiveArtifacts artifacts: 'angular-client/coverage/**/*', fingerprint: true } failure { echo '❌ Tests échoués - Vérifiez les logs ci-dessus' } success { echo '✅ Tests réussis avec couverture générée' } } } // 3. Analyse SonarQube pour la qualité du code stage('SonarQube Analysis') { steps { script { def scannerHome = tool name: 'SonarQube Scanner', type: 'hudson.plugins.sonar.SonarRunnerInstallation' withSonarQubeEnv('SonarQube') { sh """ ${scannerHome}/bin/sonar-scanner \ -Dsonar.projectKey=FatboarProject-${env.BRANCH_NAME} \ -Dsonar.sources=. \ -Dsonar.host.url=https://sonarqube.foodgame.fr \ -Dsonar.login=sqa_9ec3588a80a0b8458d9273dbb6eb7f6ae91b446a """ } } } } // 4. Arrêt et nettoyage des anciens conteneurs // stage('Stop & Clean Containers') { // steps { // sh """ // docker-compose -f ${env.COMPOSE_FILE} down || true // docker system prune -f // """ // } // } //5. Build et déploiement des services stage('Build & Deploy') { steps { sh """ docker-compose -f ${env.COMPOSE_FILE} build --no-cache docker-compose -f ${env.COMPOSE_FILE} up -d --force-recreate --remove-orphans """ } } // 6. Lancement des backups après déploiement stage('Backup') { steps { script { echo "🚀 Lancement du backup sur le container ${env.BACKUP_CONTAINER}" try { def containerExists = sh( script: "docker ps -a --filter name=${env.BACKUP_CONTAINER} --format '{{.Names}}'", returnStdout: true ).trim() if (!containerExists) { echo "⚠️ Container ${env.BACKUP_CONTAINER} n'existe pas, lancement..." sh "docker-compose -f ${env.COMPOSE_FILE} up -d mongodb-backup-${env.ENV}" sleep(30) } def containerStatus = sh( script: "docker ps --filter name=${env.BACKUP_CONTAINER} --format '{{.Status}}'", returnStdout: true ).trim() if (containerStatus.contains('Up')) { echo "✅ Container ${env.BACKUP_CONTAINER} est actif" sleep(10) // Vérifier les logs du container pour s'assurer qu'il fonctionne bien echo "📋 Vérification des logs récents du container backup..." sh "docker logs --tail 10 ${env.BACKUP_CONTAINER} 2>&1 || true" // Déclencher un backup manuel en utilisant mongodump avec les bonnes variables echo "💾 Déclenchement du backup manuel..." def backupExitCode = sh( script: """ docker exec ${env.BACKUP_CONTAINER} bash -c ' echo "=== Démarrage du backup manuel ===" && BACKUP_DIR="/backup/manual-\$(date +%Y-%m-%d_%H-%M-%S)" && mkdir -p "\$BACKUP_DIR" && mongodump --host=\$MONGODB_HOST --port=\$MONGODB_PORT \\ --username=\$MONGODB_USER --password=\$MONGODB_PASS \\ --authenticationDatabase=\$MONGODB_AUTHENTICATION_DATABASE \\ --archive="\$BACKUP_DIR/dump.archive.gz" --gzip && echo "Backup terminé dans \$BACKUP_DIR" ' """, returnStatus: true ) if (backupExitCode == 0) { echo "✅ Commande de backup lancée avec succès" // Attendre un peu pour que le backup se termine sleep(30) // Vérifier les fichiers de backup créés def backupFiles = sh( script: "docker exec ${env.BACKUP_CONTAINER} find /backup -name '*.gz' -type f -mtime -1 | head -5", returnStdout: true ).trim() if (backupFiles) { echo "✅ Backup ${env.ENV.toUpperCase()} réussi !" echo "📁 Fichiers créés :" backupFiles.split('\n').each { file -> if (file.trim()) { def fileInfo = sh( script: "docker exec ${env.BACKUP_CONTAINER} ls -lh '${file.trim()}'", returnStdout: true ).trim() echo fileInfo } } } else { echo "⚠️ Aucun fichier de backup récent trouvé, vérification des logs..." sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true" } // Afficher le statut final echo "📊 Statut final du backup :" sh """ docker exec ${env.BACKUP_CONTAINER} bash -c ' echo "=== Espace disque utilisé ===" && df -h /backup && echo "=== Derniers backups ===" && ls -la /backup/ | tail -10 ' || true """ } else { echo "❌ Backup échoué avec code ${backupExitCode}" echo "📋 Logs détaillés :" sh "docker logs --tail 30 ${env.BACKUP_CONTAINER} 2>&1 || true" currentBuild.result = 'UNSTABLE' } } else { echo "❌ Container ${env.BACKUP_CONTAINER} inactif — tentative de redémarrage" sh "docker-compose -f ${env.COMPOSE_FILE} restart mongodb-backup-${env.ENV} || true" sleep(30) // Vérifier le statut après redémarrage def newStatus = sh( script: "docker ps --filter name=${env.BACKUP_CONTAINER} --format '{{.Status}}'", returnStdout: true ).trim() if (newStatus.contains('Up')) { echo "✅ Container redémarré avec succès" } else { echo "❌ Échec du redémarrage, vérification des logs..." sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true" currentBuild.result = 'UNSTABLE' } } } catch (Exception e) { echo "❌ Erreur lors du backup: ${e.getMessage()}" echo "📋 Logs d'urgence :" sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true" currentBuild.result = 'UNSTABLE' } } } } // 7. Push optionnel de l'image dans le registre Nexus // stage('Push to Private Registry') { // when { // anyOf { // branch 'dev' // branch 'master' // } // } // steps { // script { // docker.withRegistry("https://${DOCKER_REGISTRY}", 'nexus') { // sh """ // docker pull ${IMAGE_NAME}:${IMAGE_TAG} || true // docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} // docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} // """ // } // } // } // } } post { success { echo "✅ Déploiement réussi sur ${env.ENV} !" } failure { echo "❌ Échec du pipeline sur ${env.ENV}." } } } // pipeline { // agent any // environment { // NODEJS_HOME = "${tool 'NodeJS'}" // PATH = "${env.NODEJS_HOME}/bin:${env.PATH}" // DEV_URL = "dev.foodgame.fr" // dev_URL = "dev.foodgame.fr" // PROD_URL = "prod.foodgame.fr" // } // stages { // stage('Setup Environment') { // steps { // script { // echo "Environnement détecté : ${env.BRANCH_NAME}" // if (env.BRANCH_NAME == 'test') { // echo "Déploiement sur DEV (${DEV_URL})" // } else if (env.BRANCH_NAME == 'dev') { // echo "Déploiement sur dev (${dev_URL})" // } else if (env.BRANCH_NAME == 'master') { // echo "Déploiement sur PROD (${PROD_URL})" // } else { // error "Branche non prise en charge : ${env.BRANCH_NAME}" // } // } // sh 'npm --version' // } // } // stage('Checkout Code') { // steps { // deleteDir() // checkout scm // } // } // stage('Stop Containers') { // steps { // sh ''' // docker ps | grep "workflow_" -v | awk -F " " '{ if(NR>1) print $1}' | xargs docker kill || true // docker system prune -f // ''' // } // } // stage('Build & Deploy') { // steps { // sh ''' // docker-compose stop // docker-compose build // docker-compose up -d // ''' // } // } // // stage('Push Docker Image (Nexus)') { // // when { // // branch 'master' // // } // // steps { // // withDockerRegistry([credentialsId: 'nexus', url: 'https://nexus.foodgame.fr']) { // // script { // // def dockerImageName = 'workflow_jenkins_1' // // def dockerImageTag = 'latest' // // def nexusRepository = 'fatboar_repo' // // sh """ // // docker tag ${dockerImageName}:${dockerImageTag} ${nexusRepository}/${dockerImageName}:${dockerImageTag} // // docker push ${nexusRepository}/${dockerImageName}:${dockerImageTag} // // """ // // } // // } // // } // // } // stage('Docker Registry Login, Pull, and Push') { // when { // branch 'dev' // } // steps { // script { // def registryUrl = 'nexus.foodgame.fr:8123' // def imageName = 'grafana/tns-db' // def imageVersion = 'latest' // docker.withRegistry("https://${registryUrl}", 'nexus') { // try { // Try pulling the image from the registry // echo "Trying to pull image: ${registryUrl}/${imageName}:${imageVersion}" // docker.image("${registryUrl}/${imageName}:${imageVersion}").pull() // } catch (Exception e) { // echo "Image pull failed. Attempting to build and push." // Pull base image from Docker Hub // echo "Pulling base image: ${imageName}:${imageVersion}" // sh "docker pull ${imageName}:${imageVersion}" // Tag the image for the private registry // echo "Tagging image for private registry" // sh "docker tag ${imageName}:${imageVersion} ${registryUrl}/${imageName}:${imageVersion}" // Push the tagged image to the private registry // echo "Pushing image to private registry" // sh "docker push ${registryUrl}/${imageName}:${imageVersion}" // } // } // } // } // } // stage('Cleanup') { // steps { // echo "Nettoyage terminé pour la branche ${env.BRANCH_NAME}" // } // } // stage('Fin du Pipeline') { // steps { // sh 'echo "Félicitations, le pipeline s\'est terminé avec succès !"' // } // } // } // post { // success { // echo "Pipeline exécuté avec succès pour la branche ${env.BRANCH_NAME}." // } // failure { // echo "Échec du pipeline pour la branche ${env.BRANCH_NAME}." // } // } // } // // node{ // // env.NODEJS_HOME = "${tool 'NodeJS'}" // // // on linux / mac // // env.PATH="${env.NODEJS_HOME}/bin:${env.PATH}" // // // on windows // // //env.PATH="${env.NODEJS_HOME};${env.PATH}" // // sh 'npm --version' // // stage('checkout') // // { // // deleteDir() // // checkout scm // // } // // stage('Stop Containers') // // { // // sh 'docker ps | grep "workflow_" -v | awk -F " " \'{ if(NR>1) print $1}\' |xargs docker kill |xargs docker rm || true' // // sh 'docker system prune -f' // // } // // // stage("Push dev images to nexus") // // // { // // // docker.withRegistry('http://localhost:8083','885ef60c-9352-489a-bd1c-e4b695747c21') // // // { // // // imageApache.push('latest') // // // imageExpress.push('latest') // // // }*/ // // // } // // // stage('SonarQube analysis') // // // { // // // def scannerHome = tool name: 'SonarQube Scanner', type: 'hudson.plugins.sonar.SonarRunnerInstallation'; // // // withSonarQubeEnv('SonarQube') // // // { // // // // If you have configured more than one global server connection, you can specify its name // // // sh "${scannerHome}/bin/sonar-scanner \ // // // -Dsonar.projectKey=FatboarProject \ // // // -Dsonar.sources=. \ // // // -Dsonar.host.url=https://sonarqube.foodgame.fr \ // // // -Dsonar.login=sqp_09ee9072c917af8212864baf0f75c950afc14c64" // // // } // // // } // // stage('Build Docker MEAN Stack(Test Deployment)') // // { // // sh 'docker-compose -v' // // sh 'docker-compose build' // // sh 'docker-compose up -d' // // } // // stage('Fin du Pipeline') // // { // // sh 'echo "Félicitation tout c\'est bien déroulé!"' // // } // // }