#!/usr/bin/env node /** * Script de déploiement production pour Veza V5 Ultra * Objectif: Déploiement zéro downtime avec rollback automatique */ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); console.log('🚀 VEZA V5 ULTRA - Production Deployment'); console.log('========================================\n'); // Configuration de déploiement const deployConfig = { staging: { url: 'https://staging.veza.app', branch: 'staging', healthCheck: '/api/health', }, production: { url: 'https://veza.app', branch: 'main', healthCheck: '/api/health', }, backup: { enabled: true, maxBackups: 5, retentionDays: 30, }, monitoring: { sentry: process.env.SENTRY_DSN, analytics: process.env.GA_ID, }, }; // Fonction pour exécuter une commande avec logging function execCommand(command, description) { console.log(`\n🔧 ${description}...`); try { const result = execSync(command, { encoding: 'utf8', stdio: 'pipe' }); console.log(`✅ ${description} completed`); return { success: true, result }; } catch (error) { console.log(`❌ ${description} failed:`, error.message); return { success: false, error: error.message }; } } // Fonction pour vérifier la santé de l'application function healthCheck(url, maxRetries = 5) { console.log(`\n🏥 Health check: ${url}`); for (let i = 0; i < maxRetries; i++) { try { const result = execSync(`curl -f -s ${url}${deployConfig.staging.healthCheck}`, { encoding: 'utf8', timeout: 10000 }); if (result.includes('ok') || result.includes('healthy')) { console.log(`✅ Health check passed (attempt ${i + 1})`); return true; } } catch (error) { console.log(`⚠️ Health check failed (attempt ${i + 1}/${maxRetries})`); if (i < maxRetries - 1) { console.log('⏳ Waiting 10s before retry...'); execSync('sleep 10'); } } } console.log('❌ Health check failed after all retries'); return false; } // Fonction pour créer un backup function createBackup() { if (!deployConfig.backup.enabled) return true; console.log('\n💾 Creating backup...'); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupDir = path.join(__dirname, '..', 'backups', timestamp); try { execSync(`mkdir -p ${backupDir}`, { stdio: 'pipe' }); execSync(`cp -r dist ${backupDir}/`, { stdio: 'pipe' }); execSync(`cp package.json ${backupDir}/`, { stdio: 'pipe' }); execSync(`cp package-lock.json ${backupDir}/`, { stdio: 'pipe' }); console.log(`✅ Backup created: ${backupDir}`); return true; } catch (error) { console.log(`❌ Backup failed:`, error.message); return false; } } // Fonction pour nettoyer les anciens backups function cleanupBackups() { if (!deployConfig.backup.enabled) return; console.log('\n🧹 Cleaning up old backups...'); const backupsDir = path.join(__dirname, '..', 'backups'); if (!fs.existsSync(backupsDir)) return; try { const backups = fs.readdirSync(backupsDir) .map(name => ({ name, path: path.join(backupsDir, name), mtime: fs.statSync(path.join(backupsDir, name)).mtime })) .sort((a, b) => b.mtime - a.mtime); // Garder seulement les 5 derniers backups const toDelete = backups.slice(deployConfig.backup.maxBackups); toDelete.forEach(backup => { execSync(`rm -rf ${backup.path}`, { stdio: 'pipe' }); console.log(`🗑️ Deleted old backup: ${backup.name}`); }); console.log(`✅ Cleanup completed`); } catch (error) { console.log(`⚠️ Cleanup failed:`, error.message); } } // Fonction pour déployer sur staging function deployStaging() { console.log('\n🚀 DEPLOYING TO STAGING'); console.log('======================='); // 1. Préparation console.log('\n📋 Step 1: Preparation'); execCommand('git fetch origin', 'Fetching latest changes'); execCommand('git checkout staging', 'Switching to staging branch'); execCommand('git pull origin staging', 'Pulling latest staging changes'); // 2. Build console.log('\n🔨 Step 2: Build'); execCommand('npm ci', 'Installing dependencies'); execCommand('npm run build:optimized', 'Building optimized bundle'); // 3. Tests console.log('\n🧪 Step 3: Tests'); execCommand('npm run prod:test', 'Running production tests'); // 4. Backup console.log('\n💾 Step 4: Backup'); createBackup(); // 5. Deploy console.log('\n🚀 Step 5: Deploy'); execCommand('npm run deploy:staging', 'Deploying to staging'); // 6. Health Check console.log('\n🏥 Step 6: Health Check'); if (healthCheck(deployConfig.staging.url)) { console.log('✅ Staging deployment successful!'); return true; } else { console.log('❌ Staging deployment failed!'); return false; } } // Fonction pour déployer sur production function deployProduction() { console.log('\n🚀 DEPLOYING TO PRODUCTION'); console.log('=========================='); // 1. Préparation console.log('\n📋 Step 1: Preparation'); execCommand('git checkout main', 'Switching to main branch'); execCommand('git pull origin main', 'Pulling latest main changes'); // 2. Build console.log('\n🔨 Step 2: Build'); execCommand('npm ci', 'Installing dependencies'); execCommand('npm run build:optimized', 'Building optimized bundle'); // 3. Tests console.log('\n🧪 Step 3: Tests'); execCommand('npm run prod:test', 'Running production tests'); // 4. Backup console.log('\n💾 Step 4: Backup'); createBackup(); // 5. Deploy console.log('\n🚀 Step 5: Deploy'); execCommand('npm run deploy:prod', 'Deploying to production'); // 6. Health Check console.log('\n🏥 Step 6: Health Check'); if (healthCheck(deployConfig.production.url)) { console.log('✅ Production deployment successful!'); return true; } else { console.log('❌ Production deployment failed!'); return false; } } // Fonction pour rollback function rollback() { console.log('\n🔄 ROLLBACK'); console.log('==========='); const backupsDir = path.join(__dirname, '..', 'backups'); if (!fs.existsSync(backupsDir)) { console.log('❌ No backups found for rollback'); return false; } const backups = fs.readdirSync(backupsDir) .map(name => ({ name, path: path.join(backupsDir, name), mtime: fs.statSync(path.join(backupsDir, name)).mtime })) .sort((a, b) => b.mtime - a.mtime); if (backups.length === 0) { console.log('❌ No backups available for rollback'); return false; } const latestBackup = backups[0]; console.log(`🔄 Rolling back to: ${latestBackup.name}`); try { execSync(`rm -rf dist`, { stdio: 'pipe' }); execSync(`cp -r ${latestBackup.path}/dist .`, { stdio: 'pipe' }); execSync(`npm run deploy:prod`, { stdio: 'pipe' }); console.log('✅ Rollback completed'); return true; } catch (error) { console.log('❌ Rollback failed:', error.message); return false; } } // Fonction pour monitoring post-deploy function setupMonitoring() { console.log('\n📊 Setting up monitoring...'); // Sentry if (deployConfig.monitoring.sentry) { console.log('✅ Sentry monitoring enabled'); } // Analytics if (deployConfig.monitoring.analytics) { console.log('✅ Google Analytics enabled'); } // Logs console.log('✅ Application logs configured'); console.log('📊 Monitoring dashboard: https://dashboard.veza.app'); } // Fonction principale async function main() { const args = process.argv.slice(2); const command = args[0] || 'staging'; console.log(`🎯 Deployment target: ${command}`); let success = false; switch (command) { case 'staging': success = deployStaging(); break; case 'production': success = deployProduction(); break; case 'rollback': success = rollback(); break; default: console.log('❌ Unknown command. Use: staging, production, or rollback'); process.exit(1); } if (success) { setupMonitoring(); cleanupBackups(); console.log('\n🎉 DEPLOYMENT SUCCESSFUL!'); console.log('========================'); console.log('✅ Application deployed'); console.log('✅ Health checks passed'); console.log('✅ Monitoring active'); console.log('✅ Backup created'); console.log('\n📱 Next steps:'); console.log('- Monitor application metrics'); console.log('- Check error logs'); console.log('- Verify user feedback'); console.log('- Update documentation'); } else { console.log('\n❌ DEPLOYMENT FAILED!'); console.log('===================='); console.log('❌ Check logs for errors'); console.log('❌ Consider rollback if needed'); console.log('❌ Fix issues before retry'); process.exit(1); } } // Exécuter le script main().catch(error => { console.error('❌ Deployment script failed:', error); process.exit(1); });