diff --git a/Jenkinsfile b/Jenkinsfile index 03a36a5..b7edae6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,9 +1,7 @@ pipeline { agent any - // Déclenchement automatique sur push Git triggers { - // Vérifie les changements toutes les minutes (polling SCM) pollSCM('* * * * *') } @@ -18,6 +16,7 @@ pipeline { environment { REGISTRY_URL = "registry.wk-archi-o24a-15m-g3.fr" IMAGE_NAME = "the-tip-top-frontend" + NPM_CACHE = "/var/jenkins_home/npm-cache" } stages { @@ -53,82 +52,65 @@ pipeline { } } - stage('Tests & Qualité') { + stage('Install Dependencies') { agent { docker { image 'node:20' - args '-u root' + args "-u root -v ${NPM_CACHE}:/root/.npm" } } steps { - echo "🧪 Lancement des tests et analyse du code..." - script { - def lintStatus = sh(script: 'npm ci && npm run lint', returnStatus: true) - def testStatus = sh(script: 'npm test', returnStatus: true) - - if (lintStatus != 0) { - error "❌ ESLint a échoué - Le déploiement est bloqué" - } - - if (testStatus != 0) { - error "❌ Les tests ont échoué - Le déploiement est bloqué" - } - - echo "✅ Tests et lint passés avec succès" - } + echo "📦 Installation des dépendances..." + sh 'npm ci --prefer-offline' + stash includes: 'node_modules/**', name: 'node_modules' } } - stage('SonarQube Analysis') { - agent { - docker { - image 'sonarsource/sonar-scanner-cli:latest' - args '-u root' - } - } - steps { - echo "🔍 Analyse SonarQube..." - withSonarQubeEnv('SonarQube') { - sh """ - sonar-scanner \ - -Dsonar.projectKey=Th-Tip-Top-Frontend \ - -Dsonar.projectName='Thé Tip Top Frontend' \ - -Dsonar.sources=. \ - -Dsonar.exclusions=node_modules/**,.next/**,coverage/**,out/** \ - -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info - """ - } - } - } - - stage('Build Frontend') { - agent { - docker { - image 'node:20' - args '-u root' - } - } - steps { - echo "⚙️ Build de l'application Next.js..." - script { - // Nettoyer le cache Next.js pour forcer une reconstruction complète - sh 'rm -rf .next node_modules/.cache' - - // Next.js peut échouer avec des erreurs SSR mais générer quand même un build fonctionnel - def buildStatus = sh(script: 'npm ci && npm run build', returnStatus: true) - - // Vérifier que le build a été généré même si des erreurs SSR sont survenues - def nextExists = sh(script: '[ -d .next ] && echo "true" || echo "false"', returnStdout: true).trim() - - if (nextExists == "false") { - error "❌ Le build Next.js a complètement échoué - pas de dossier .next généré" + stage('Quality Checks') { + parallel { + stage('Lint & Tests') { + agent { + docker { + image 'node:20' + args "-u root -v ${NPM_CACHE}:/root/.npm" + } } + steps { + unstash 'node_modules' + echo "🧪 Lancement des tests et lint..." + script { + def lintStatus = sh(script: 'npm run lint', returnStatus: true) + def testStatus = sh(script: 'npm test', returnStatus: true) - if (buildStatus != 0) { - echo "⚠️ Avertissement: Des erreurs SSR sont survenues mais le build .next a été généré" - echo " Ces erreurs n'affectent pas le fonctionnement en production (pages client-side)" - } else { - echo "✅ Build Next.js réussi sans erreurs" + if (lintStatus != 0) { + error "❌ ESLint a échoué" + } + if (testStatus != 0) { + error "❌ Les tests ont échoué" + } + echo "✅ Tests et lint passés" + } + } + } + + stage('SonarQube') { + agent { + docker { + image 'sonarsource/sonar-scanner-cli:latest' + args '-u root' + } + } + steps { + echo "🔍 Analyse SonarQube..." + withSonarQubeEnv('SonarQube') { + sh """ + sonar-scanner \ + -Dsonar.projectKey=Th-Tip-Top-Frontend \ + -Dsonar.projectName='Thé Tip Top Frontend' \ + -Dsonar.sources=. \ + -Dsonar.exclusions=node_modules/**,.next/**,coverage/**,out/** + """ + } } } } @@ -138,11 +120,9 @@ pipeline { steps { echo "🐳 Construction de l'image Docker frontend..." script { - // Charger les variables d'environnement selon l'environnement def envFile = ".env.${env.ENV}" def envVars = sh(script: "cat ${envFile} | grep -E '^NEXT_PUBLIC_' | xargs", returnStdout: true).trim() - // Construire les arguments de build def buildArgs = "" envVars.split().each { envVar -> def parts = envVar.split('=', 2) @@ -152,7 +132,7 @@ pipeline { } sh """ - docker build --no-cache ${buildArgs} -t ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} . + docker build ${buildArgs} -t ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} . docker tag ${REGISTRY_URL}/${IMAGE_NAME}:${env.TAG} ${REGISTRY_URL}/${IMAGE_NAME}:latest """ } @@ -161,7 +141,7 @@ pipeline { stage('Push to Registry') { steps { - echo "📤 Envoi de l’image vers le registre Docker..." + echo "📤 Envoi de l'image vers le registre Docker..." withCredentials([usernamePassword(credentialsId: 'registry-credentials', usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) { sh """ echo "$REG_PASS" | docker login ${REGISTRY_URL} -u "$REG_USER" --password-stdin @@ -172,22 +152,6 @@ pipeline { } } - stage('Backup before deploy') { - steps { - echo "💾 Sauvegarde avant déploiement (${env.ENV})..." - sh ''' - BACKUP_DIR="/srv/backups/the-tip-top/${ENV}/$(date +%F_%H-%M-%S)" - mkdir -p $BACKUP_DIR - echo "📦 Sauvegarde du docker-compose et logs..." - cp -r ${DEPLOY_PATH}/docker-compose.yml $BACKUP_DIR/ 2>/dev/null || true - cp -r ${DEPLOY_PATH}/logs $BACKUP_DIR/ 2>/dev/null || true - tar -czf ${BACKUP_DIR}.tar.gz -C $(dirname $BACKUP_DIR) $(basename $BACKUP_DIR) - rm -rf $BACKUP_DIR - echo "✅ Sauvegarde compressée : ${BACKUP_DIR}.tar.gz" - ''' - } - } - stage('Deploy') { steps { echo "🚀 Déploiement du frontend sur ${env.ENV}..."