|
@@ -5,10 +5,14 @@ pipeline {
|
|
|
NODEJS_HOME = "${tool 'NodeJS'}"
|
|
NODEJS_HOME = "${tool 'NodeJS'}"
|
|
|
PATH = "${env.NODEJS_HOME}/bin:${env.PATH}"
|
|
PATH = "${env.NODEJS_HOME}/bin:${env.PATH}"
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+ DOCKER_REGISTRY = "nexus.foodgame.fr:8123"
|
|
|
|
|
+ IMAGE_NAME = "fatboar_repo/workflow_jenkins_1"
|
|
|
|
|
+ IMAGE_TAG = "latest"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
stages {
|
|
stages {
|
|
|
|
|
+
|
|
|
|
|
+ // 1. Déterminer l'environnement en fonction de la branche
|
|
|
stage('Select Environment') {
|
|
stage('Select Environment') {
|
|
|
steps {
|
|
steps {
|
|
|
script {
|
|
script {
|
|
@@ -35,15 +39,62 @@ pipeline {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // stage('Install Dependencies & Test') {
|
|
|
|
|
- // steps {
|
|
|
|
|
- // withEnv(["PATH=${tool 'NodeJS'}/bin:${env.PATH}"]) {
|
|
|
|
|
- // sh 'npm install --legacy-peer-deps'
|
|
|
|
|
- // sh 'npm run test -- --no-watch --no-progress --browsers=ChromeHeadless'
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ // 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') {
|
|
stage('SonarQube Analysis') {
|
|
|
steps {
|
|
steps {
|
|
|
script {
|
|
script {
|
|
@@ -61,31 +112,154 @@ pipeline {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 4. Arrêt et nettoyage des anciens conteneurs
|
|
|
// stage('Stop & Clean Containers') {
|
|
// stage('Stop & Clean Containers') {
|
|
|
// steps {
|
|
// steps {
|
|
|
// sh """
|
|
// sh """
|
|
|
- // docker ps | grep "workflow_" -v | awk '{if(NR>1) print \$1}' | xargs docker kill || true
|
|
|
|
|
// docker-compose -f ${env.COMPOSE_FILE} down || true
|
|
// docker-compose -f ${env.COMPOSE_FILE} down || true
|
|
|
// docker system prune -f
|
|
// docker system prune -f
|
|
|
// """
|
|
// """
|
|
|
// }
|
|
// }
|
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
|
|
+ //5. Build et déploiement des services
|
|
|
stage('Build & Deploy') {
|
|
stage('Build & Deploy') {
|
|
|
steps {
|
|
steps {
|
|
|
sh """
|
|
sh """
|
|
|
docker-compose -f ${env.COMPOSE_FILE} build --no-cache
|
|
docker-compose -f ${env.COMPOSE_FILE} build --no-cache
|
|
|
docker-compose -f ${env.COMPOSE_FILE} up -d --force-recreate --remove-orphans
|
|
docker-compose -f ${env.COMPOSE_FILE} up -d --force-recreate --remove-orphans
|
|
|
"""
|
|
"""
|
|
|
- script {
|
|
|
|
|
- echo "Lancement du backup sur le container ${env.BACKUP_CONTAINER}"
|
|
|
|
|
- def backupCmd = "docker exec ${env.BACKUP_CONTAINER} /backup.sh || echo '⚠️ Backup ${env.ENV.toUpperCase()} échoué'"
|
|
|
|
|
- sh backupCmd
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // stage('Push to Private Registry (Optional)') {
|
|
|
|
|
|
|
+ // 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 {
|
|
// when {
|
|
|
// anyOf {
|
|
// anyOf {
|
|
|
// branch 'dev'
|
|
// branch 'dev'
|
|
@@ -94,36 +268,17 @@ pipeline {
|
|
|
// }
|
|
// }
|
|
|
// steps {
|
|
// steps {
|
|
|
// script {
|
|
// script {
|
|
|
- // def registryUrl = 'nexus.foodgame.fr:8123'
|
|
|
|
|
- // def imageName = 'quay.io/prometheus/node-exporter'
|
|
|
|
|
- // 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}"
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // 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 {
|
|
post {
|
|
|
success {
|
|
success {
|
|
@@ -136,8 +291,6 @@ pipeline {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
// pipeline {
|
|
// pipeline {
|
|
|
// agent any
|
|
// agent any
|
|
|
// environment {
|
|
// environment {
|