Explorar el Código

mis à jour de la base de donnée en production et jenkinsfile

DESKTOP-SMCIPAV\falko hace 4 meses
padre
commit
ca982bb106

+ 199 - 46
Jenkinsfile

@@ -5,10 +5,14 @@ pipeline {
         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 {
@@ -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') {
             steps {
                 script {
@@ -61,31 +112,154 @@ pipeline {
             }
         }
 
+        // 4. Arrêt et nettoyage des anciens conteneurs
         // stage('Stop & Clean Containers') {
         //     steps {
         //         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 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
                 """
-                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 {
         //         anyOf {
         //             branch 'dev'
@@ -94,36 +268,17 @@ pipeline {
         //     }
         //     steps {
         //         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 {
         success {
@@ -136,8 +291,6 @@ pipeline {
 }
 
 
-
-
 // pipeline {
 //     agent any
 //     environment {

+ 19 - 1
angular-client/angular.json

@@ -114,7 +114,25 @@
               "/contact"
             ]
           }
-        }
+        },
+      "test": {
+        "builder": "@angular-devkit/build-angular:karma",
+        "options": {
+        "main": "src/test.ts",
+        "polyfills": "src/polyfills.ts",
+        "tsConfig": "tsconfig.spec.json",
+        "karmaConfig": "karma.conf.js",
+        "assets": [
+           "src/favicon.ico",
+           "src/assets"
+            ],
+        "styles": [
+           "src/styles.scss"
+          ],
+       "scripts": []
+  }
+}
+
       }
     }
   },

+ 69 - 8
angular-client/karma.conf.js

@@ -17,16 +17,77 @@ module.exports = function (config) {
     },
     coverageIstanbulReporter: {
       dir: require('path').join(__dirname, './coverage/fatboar-burger'),
-      reports: ['html', 'lcovonly', 'text-summary'],
-      fixWebpackSourcePaths: true
+      reports: ['html', 'lcovonly', 'text-summary', 'cobertura'], // Ajout du format cobertura pour Jenkins
+      fixWebpackSourcePaths: true,
+      // Seuils de couverture (optionnel - décommentez si vous voulez des seuils minimum)
+      // thresholds: {
+      //   global: {
+      //     statements: 80,
+      //     branches: 75,
+      //     functions: 80,
+      //     lines: 80
+      //   }
+      // }
     },
-    reporters: ['progress', 'kjhtml'],
+    reporters: ['progress', 'kjhtml', 'coverage-istanbul'], // Ajout du reporter coverage-istanbul
     port: 9876,
     colors: true,
     logLevel: config.LOG_INFO,
-    autoWatch: true,
-    browsers: ['Chrome'],
-    singleRun: false,
-    restartOnFileChange: true
+    autoWatch: false,
+    browsers: ['ChromeHeadless'],
+    singleRun: true,
+    restartOnFileChange: false,
+
+    // Ajouts pour éviter les déconnexions ChromeHeadless
+    captureTimeout: 60000,
+    browserDisconnectTimeout: 60000,
+    browserDisconnectTolerance: 3,
+    browserNoActivityTimeout: 60000,
+
+    // Custom launcher configurations ajoutées pour Jenkins CI
+    customLaunchers: {
+      ChromeHeadlessCustom: {
+        base: 'ChromeHeadless',
+        flags: [
+          '--no-sandbox',
+          '--disable-web-security',
+          '--disable-features=VizDisplayCompositor',
+          '--disable-gpu',
+          '--disable-canvas-aa',
+          '--disable-3d-apis',
+          '--disable-extensions',
+          '--disable-dev-shm-usage',
+          '--disable-translate',
+          '--disable-logging',
+          '--disable-background-timer-throttling',
+          '--disable-backgrounding-occluded-windows',
+          '--disable-renderer-backgrounding',
+          '--remote-debugging-port=9222'
+        ]
+      },
+      ChromeHeadlessCI: {
+        base: 'ChromeHeadless',
+        flags: [
+          '--no-sandbox',                    // CRUCIAL pour Docker/Jenkins
+          '--disable-web-security',
+          '--disable-features=VizDisplayCompositor',
+          '--disable-gpu',
+          '--disable-canvas-aa',
+          '--disable-3d-apis',
+          '--disable-extensions',
+          '--disable-dev-shm-usage',        // Important pour les environnements Docker
+          '--disable-translate',
+          '--disable-logging',
+          '--disable-background-timer-throttling',
+          '--disable-backgrounding-occluded-windows',
+          '--disable-renderer-backgrounding',
+          '--headless',
+          '--disable-ipc-flooding-protection',
+          '--disable-hang-monitor',
+          '--disable-prompt-on-repost',
+          '--disable-default-apps'
+        ]
+      }
+    }
   });
-};
+};

+ 3 - 1
angular-client/package.json

@@ -6,6 +6,7 @@
     "start": "ng serve --host 0.0.0.0",
     "build": "ng build --prod",
     "test": "ng test",
+    "test-headless": "ng test --no-watch --no-progress --browsers=ChromeHeadless",
     "lint": "ng lint",
     "e2e": "ng e2e",
     "test-ci": "ng test --karma-config=karma-ci.conf.js --no-progress",
@@ -40,6 +41,7 @@
     "karma-phantomjs-launcher": "^1.0.4",
     "leaflet": "^1.6.0",
     "ng2-charts": "^2.3.0",
+    "preboot": "^8.0.0",
     "rxjs": "~6.5.4",
     "saturn-datepicker": "^8.0.5",
     "tslib": "^1.10.0",
@@ -78,4 +80,4 @@
     "tslint": "~6.1.0",
     "typescript": "3.8.3"
   }
-}
+}

+ 1 - 10
angular-client/src/app/app.component.spec.ts

@@ -13,13 +13,4 @@ describe('AppComponent', () => {
     const app = fixture.componentInstance;
     expect(app).toBeTruthy();
   });
-
-  it(`should have as title 'fatboar'`, () => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.componentInstance;
-    expect(app.title).toEqual('fatboar');
-  });
-
-  
-  
-});
+});

+ 45 - 0
angular-client/src/test-material.module.ts

@@ -0,0 +1,45 @@
+import { NgModule } from '@angular/core';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+// Import des modules Angular Material nécessaires
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatCardModule } from '@angular/material/card';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatTableModule } from '@angular/material/table';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSortModule } from '@angular/material/sort';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatListModule } from '@angular/material/list';
+
+@NgModule({
+  exports: [
+    BrowserAnimationsModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatButtonModule,
+    MatIconModule,
+    MatToolbarModule,
+    MatCardModule,
+    MatSnackBarModule,
+    MatDialogModule,
+    MatMenuModule,
+    MatTableModule,
+    MatPaginatorModule,
+    MatSortModule,
+    MatProgressSpinnerModule,
+    MatCheckboxModule,
+    MatSelectModule,
+    MatSidenavModule,
+    MatListModule
+  ]
+})
+export class TestMaterialModule {}

+ 29 - 0
angular-client/src/test-setup.ts

@@ -0,0 +1,29 @@
+import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { TestMaterialModule } from './test-material.module';
+
+const originalConfigureTestingModule = TestBed.configureTestingModule;
+
+TestBed.configureTestingModule = function (moduleDef: any) {
+  moduleDef = moduleDef || {};
+  moduleDef.imports = moduleDef.imports || [];
+  moduleDef.schemas = moduleDef.schemas || [];
+
+  // Ajout global de RouterTestingModule
+  if (!moduleDef.imports.includes(RouterTestingModule)) {
+    moduleDef.imports.push(RouterTestingModule);
+  }
+
+  // Ajout global de TestMaterialModule
+  if (!moduleDef.imports.includes(TestMaterialModule)) {
+    moduleDef.imports.push(TestMaterialModule);
+  }
+
+  // Ajout global de CUSTOM_ELEMENTS_SCHEMA
+  if (!moduleDef.schemas.includes(CUSTOM_ELEMENTS_SCHEMA)) {
+    moduleDef.schemas.push(CUSTOM_ELEMENTS_SCHEMA);
+  }
+
+  return originalConfigureTestingModule.call(this, moduleDef);
+} as any;

+ 5 - 6
angular-client/src/test.ts

@@ -1,6 +1,5 @@
-// This file is required by karma.conf.js and loads recursively all the .spec and framework files
-
 import 'zone.js/dist/zone-testing';
+import './test-setup'; // Patch global
 import { getTestBed } from '@angular/core/testing';
 import {
   BrowserDynamicTestingModule,
@@ -14,12 +13,12 @@ declare const require: {
   };
 };
 
-// First, initialize the Angular testing environment.
+// Initialisation Angular
 getTestBed().initTestEnvironment(
   BrowserDynamicTestingModule,
   platformBrowserDynamicTesting()
 );
-// Then we find all the tests.
+
+// Charger tous les tests
 const context = require.context('./', true, /\.spec\.ts$/);
-// And load the modules.
-context.keys().map(context);
+context.keys().map(context);

+ 2 - 0
docker-compose.prod.yml

@@ -39,6 +39,8 @@ services:
 
   database_prod:
     image: mongo:4.4.6
+    ports:
+      - "27017:27017"
     volumes:
       - ./backup:/backup
       - mongo_data_prod:/data/db