#!/bin/bash # ============================================ # Script de Deploy - App Canchas de Pádel # ============================================ # Uso: ./deploy.sh [environment] # Ejemplo: ./deploy.sh production # ============================================ set -e # ============================================ # CONFIGURACIÓN # ============================================ # Colores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Variables por defecto ENVIRONMENT="${1:-production}" APP_NAME="app-padel-api" APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PM2_CONFIG="$APP_DIR/ecosystem.config.js" HEALTH_CHECK_URL="http://localhost:3000/api/v1/health" MAX_RETRIES=5 RETRY_DELAY=5 # ============================================ # FUNCIONES # ============================================ log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[OK]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } print_banner() { echo "" echo "========================================" echo " 🚀 Deploy - App Canchas de Pádel" echo " Environment: $ENVIRONMENT" echo " Date: $(date)" echo "========================================" echo "" } check_prerequisites() { log_info "Verificando prerrequisitos..." # Verificar Node.js if ! command -v node &> /dev/null; then log_error "Node.js no está instalado" exit 1 fi # Verificar npm if ! command -v npm &> /dev/null; then log_error "npm no está instalado" exit 1 fi # Verificar PM2 if ! command -v pm2 &> /dev/null; then log_error "PM2 no está instalado. Instalar con: npm install -g pm2" exit 1 fi # Verificar git if ! command -v git &> /dev/null; then log_error "Git no está instalado" exit 1 fi # Verificar directorio de la aplicación if [ ! -d "$APP_DIR" ]; then log_error "Directorio de la aplicación no encontrado: $APP_DIR" exit 1 fi # Verificar archivo de configuración PM2 if [ ! -f "$PM2_CONFIG" ]; then log_error "Archivo de configuración PM2 no encontrado: $PM2_CONFIG" exit 1 fi log_success "Prerrequisitos verificados" } backup_current() { log_info "Creando backup..." BACKUP_DIR="$APP_DIR/backups" BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S).tar.gz" mkdir -p "$BACKUP_DIR" # Crear backup de dist y .env if [ -d "$APP_DIR/dist" ]; then tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$APP_DIR" dist .env 2>/dev/null || true log_success "Backup creado: $BACKUP_DIR/$BACKUP_NAME" else log_warning "No hay build anterior para respaldar" fi } update_code() { log_info "Actualizando código desde repositorio..." cd "$APP_DIR" # Guardar cambios locales si existen if [ -n "$(git status --porcelain)" ]; then log_warning "Hay cambios locales sin commitear" git stash fi # Pull de cambios git fetch origin # Checkout a la rama correcta if [ "$ENVIRONMENT" = "production" ]; then git checkout main || git checkout master else git checkout develop || git checkout development fi git pull origin $(git branch --show-current) log_success "Código actualizado" } install_dependencies() { log_info "Instalando dependencias..." cd "$APP_DIR" # Limpiar node_modules para evitar conflictos if [ "$ENVIRONMENT" = "production" ]; then npm ci --only=production else npm ci fi log_success "Dependencias instaladas" } build_app() { log_info "Compilando aplicación..." cd "$APP_DIR" # Limpiar build anterior rm -rf dist # Compilar TypeScript npm run build if [ ! -d "$APP_DIR/dist" ]; then log_error "La compilación falló - no se encontró directorio dist" exit 1 fi log_success "Aplicación compilada" } run_migrations() { log_info "Ejecutando migraciones de base de datos..." cd "$APP_DIR" # Generar cliente Prisma npx prisma generate # Ejecutar migraciones npx prisma migrate deploy log_success "Migraciones completadas" } restart_app() { log_info "Reiniciando aplicación con PM2..." cd "$APP_DIR" # Verificar si la aplicación ya está corriendo if pm2 list | grep -q "$APP_NAME"; then log_info "Recargando aplicación existente..." pm2 reload "$PM2_CONFIG" --env "$ENVIRONMENT" else log_info "Iniciando aplicación..." pm2 start "$PM2_CONFIG" --env "$ENVIRONMENT" fi # Guardar configuración PM2 pm2 save log_success "Aplicación reiniciada" } health_check() { log_info "Verificando salud de la aplicación..." local retries=0 local is_healthy=false while [ $retries -lt $MAX_RETRIES ]; do if curl -sf "$HEALTH_CHECK_URL" | grep -q '"success":true'; then is_healthy=true break fi retries=$((retries + 1)) log_warning "Intento $retries/$MAX_RETRIES fallido. Reintentando en ${RETRY_DELAY}s..." sleep $RETRY_DELAY done if [ "$is_healthy" = true ]; then log_success "Health check pasó - API funcionando correctamente" return 0 else log_error "Health check falló después de $MAX_RETRIES intentos" return 1 fi } rollback() { log_warning "Ejecutando rollback..." BACKUP_DIR="$APP_DIR/backups" # Encontrar backup más reciente LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | head -n 1) if [ -n "$LATEST_BACKUP" ]; then log_info "Restaurando desde: $LATEST_BACKUP" cd "$APP_DIR" tar -xzf "$LATEST_BACKUP" pm2 reload "$PM2_CONFIG" log_success "Rollback completado" else log_error "No se encontró backup para restaurar" fi } cleanup() { log_info "Limpiando archivos temporales..." # Limpiar backups antiguos (mantener últimos 10) BACKUP_DIR="$APP_DIR/backups" if [ -d "$BACKUP_DIR" ]; then ls -t "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null || true fi # Limpiar logs antiguos (mantener últimos 7 días) find "$APP_DIR/logs" -name "*.log" -mtime +7 -delete 2>/dev/null || true log_success "Limpieza completada" } # ============================================ # EJECUCIÓN PRINCIPAL # ============================================ main() { print_banner # Validar environment if [ "$ENVIRONMENT" != "production" ] && [ "$ENVIRONMENT" != "development" ]; then log_error "Environment inválido. Usar: production o development" exit 1 fi # Ejecutar pasos check_prerequisites backup_current update_code install_dependencies build_app run_migrations restart_app # Health check if health_check; then log_success "🎉 Deploy completado exitosamente!" cleanup echo "" echo "========================================" echo " 📊 Estado de la Aplicación:" echo "========================================" pm2 status "$APP_NAME" echo "" echo " 🔗 URL: $HEALTH_CHECK_URL" echo " 📜 Logs: pm2 logs $APP_NAME" echo "========================================" else log_error "❌ Deploy falló - ejecutando rollback" rollback exit 1 fi } # Manejar errores trap 'log_error "Error en línea $LINENO"' ERR # Ejecutar main "$@"