pipeline { agent any options { disableConcurrentBuilds() timestamps() timeout(time: 30, unit: 'MINUTES') buildDiscarder(logRotator(numToKeepStr: '10')) } environment { // --- Deployment Target --- SERVER_IP = '160.187.166.95' SERVER_USER = 'ubuntu' PROJECT_DIR = '/home/ubuntu/ltts/Qassure-frontend' // --- Git Config --- // Using the verified repository URL GIT_REPO = 'https://git.tech4biz.wiki/yashwin/Qassure-frontend.git' GIT_BRANCH = 'main' GIT_CREDENTIALS_ID = 'git-cred' // --- SSH Config --- SSH_KEY_CREDENTIALS_ID = 'ltts' // --- Application Config --- NODE_HOME = '/home/ubuntu/.nvm/versions/node/v22.17.0' // --- Build Config --- VITE_API_BASE_URL = 'https://backendqaasure.tech4bizsolutions.com/api/v1' // --- System Config --- BACKUP_DIR = '/home/ubuntu/backups/qassure-frontend' EMAIL_RECIPIENTS = 'mohammed.yaseen@tech4biz.org' DEPLOY_START_TIME = "${System.currentTimeMillis()}" } stages { stage('Initialize') { steps { script { echo "🚀 Starting QAssur Frontend Deployment Pipeline" echo "📍 Target: ${SERVER_IP}" echo "🌿 Branch: ${GIT_BRANCH}" echo "📂 Directory: ${PROJECT_DIR}" } } } stage('Setup Project Directory') { steps { sshagent(credentials: [SSH_KEY_CREDENTIALS_ID]) { script { withCredentials([usernamePassword( credentialsId: GIT_CREDENTIALS_ID, usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD' )]) { sh """ ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${SERVER_USER}@${SERVER_IP} ' set -e echo "📁 Setting up project directory..." mkdir -p "\$(dirname "${PROJECT_DIR}")" mkdir -p ${BACKUP_DIR} if [ ! -d "${PROJECT_DIR}/.git" ]; then echo "📥 Repository not found. Cloning from scratch..." cd "\$(dirname "${PROJECT_DIR}")" git config --global credential.helper store echo "https://${GIT_USERNAME}:${GIT_PASSWORD}@git.tech4biz.wiki" > ~/.git-credentials git clone -b ${GIT_BRANCH} ${GIT_REPO} rm -f ~/.git-credentials git config --global --unset credential.helper echo "✅ Repository cloned successfully" else echo "✅ Repository already exists" fi ' """ } } } } } stage('Fetch Latest Changes') { steps { sshagent(credentials: [SSH_KEY_CREDENTIALS_ID]) { script { withCredentials([usernamePassword( credentialsId: GIT_CREDENTIALS_ID, usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD' )]) { def gitInfo = sh( script: """ ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} ' set -e cd ${PROJECT_DIR} echo "📥 Fetching latest changes..." git config credential.helper store echo "https://${GIT_USERNAME}:${GIT_PASSWORD}@git.tech4biz.wiki" > ~/.git-credentials # Force fetch and reset to ensure we match remote exactly git fetch origin ${GIT_BRANCH} git reset --hard origin/${GIT_BRANCH} rm -f ~/.git-credentials git config --unset credential.helper echo "COMMIT_SHORT=\$(git rev-parse --short HEAD)" echo "COMMIT_MSG=\$(git log -1 --pretty=%B)" echo "COMMIT_AUTHOR=\$(git log -1 --pretty=%an)" ' """, returnStdout: true ).trim() gitInfo.split('\n').each { line -> if (line.startsWith('COMMIT_SHORT=')) env.GIT_COMMIT_SHORT = line.split('=', 2)[1] else if (line.startsWith('COMMIT_MSG=')) env.GIT_COMMIT_MSG = line.split('=', 2)[1] else if (line.startsWith('COMMIT_AUTHOR=')) env.GIT_AUTHOR = line.split('=', 2)[1] } echo "✅ Updated to: ${env.GIT_COMMIT_SHORT}" } } } } } stage('Pre-Flight Checks') { steps { sshagent(credentials: [SSH_KEY_CREDENTIALS_ID]) { script { sh """ ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} ' set -e echo "🔍 Running pre-flight checks..." # Check Node.js export PATH="${NODE_HOME}/bin:\$PATH" if ! node --version >/dev/null 2>&1; then echo "❌ Node.js not found at ${NODE_HOME}" exit 1 fi # Disk Space (minimum 1GB) available=\$(df ${PROJECT_DIR} | tail -1 | awk "{print \\\$4}") if [ \$available -lt 1048576 ]; then echo "❌ Insufficient disk space" exit 1 fi echo "✅ Pre-flight checks passed" ' """ } } } } stage('Install & Build') { steps { sshagent(credentials: [SSH_KEY_CREDENTIALS_ID]) { script { sh """ ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} ' set -e cd ${PROJECT_DIR} export PATH="${NODE_HOME}/bin:\$PATH" export VITE_API_BASE_URL="${VITE_API_BASE_URL}" echo "📦 Installing dependencies..." # Use clean install for reliability npm ci --prefer-offline --no-audit echo "🏗️ Building application..." npm run build # Verify the build output if [ -f "dist/index.html" ]; then echo "✅ Build successful - dist/index.html optimized and ready" else echo "❌ Build failed - dist/index.html missing" exit 1 fi ' """ } } } } } post { failure { script { echo "❌ Deployment failed" def durationMillis = System.currentTimeMillis() - env.DEPLOY_START_TIME.toLong() def minutes = (durationMillis / 60000) as Integer def seconds = ((durationMillis % 60000) / 1000) as Integer def failureMessage = """❌ QAssur Frontend DEPLOYMENT FAILED ═══════════════════════════════════════ DETAILS ═══════════════════════════════════════ Branch: ${GIT_BRANCH} Commit: ${env.GIT_COMMIT_SHORT ?: 'Unknown'} Author: ${env.GIT_AUTHOR ?: 'Unknown'} Duration: ${minutes}m ${seconds}s Stage: ${env.STAGE_NAME ?: 'Unknown'} Server: ${SERVER_IP} Action Required: 1. Check Jenkins console logs for detailed error 2. Verify local build works: npm run build """ try { mail to: "${EMAIL_RECIPIENTS}", subject: "❌ QAssur Frontend FAILED - Build #${BUILD_NUMBER}", body: failureMessage } catch (e) { echo "⚠️ Email failed: ${e.message}" } } } success { script { def durationMillis = System.currentTimeMillis() - env.DEPLOY_START_TIME.toLong() def minutes = (durationMillis / 60000) as Integer def seconds = ((durationMillis % 60000) / 1000) as Integer def successMessage = """✅ QAssur Frontend DEPLOYMENT SUCCESS ═══════════════════════════════════════ DETAILS ═══════════════════════════════════════ Branch: ${GIT_BRANCH} Commit: ${env.GIT_COMMIT_SHORT} Author: ${env.GIT_AUTHOR} Duration: ${minutes}m ${seconds}s Live URL: https://qasure.tech4bizsolutions.com """ try { mail to: "${EMAIL_RECIPIENTS}", subject: "✅ QAssur Frontend SUCCESS - Build #${BUILD_NUMBER}", body: successMessage } catch (e) { echo "⚠️ Email failed: ${e.message}" } } } } }