#!/bin/bash # ============================================ # Sistema de ADAN - Script de Actualizacion # ============================================ # Actualiza la aplicacion a la ultima version # # Uso: ./update.sh [--branch BRANCH] [--force] [--no-backup] # # Opciones: # --branch Branch a usar (default: main) # --force Forzar actualizacion (descartar cambios locales) # --no-backup No crear backup antes de actualizar # --backend Solo actualizar backend # --frontend Solo actualizar frontend # ============================================ set -e set -o pipefail # --------------------------------------------- # Colores # --------------------------------------------- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # --------------------------------------------- # Variables # --------------------------------------------- INSTALL_DIR="${INSTALL_DIR:-/opt/adan}" REPO_BRANCH="${REPO_BRANCH:-main}" BACKUP_BEFORE_UPDATE=true FORCE_UPDATE=false UPDATE_BACKEND=true UPDATE_FRONTEND=true # Cargar variables de entorno if [[ -f "$INSTALL_DIR/.env" ]]; then export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs) fi # --------------------------------------------- # Funciones # --------------------------------------------- log_info() { echo -e "${BLUE}[$(date '+%H:%M:%S')] [INFO]${NC} $1" } log_success() { echo -e "${GREEN}[$(date '+%H:%M:%S')] [OK]${NC} $1" } log_warn() { echo -e "${YELLOW}[$(date '+%H:%M:%S')] [WARN]${NC} $1" } log_error() { echo -e "${RED}[$(date '+%H:%M:%S')] [ERROR]${NC} $1" } # --------------------------------------------- # Parsear argumentos # --------------------------------------------- parse_args() { while [[ $# -gt 0 ]]; do case $1 in --branch) REPO_BRANCH="$2" shift 2 ;; --force) FORCE_UPDATE=true shift ;; --no-backup) BACKUP_BEFORE_UPDATE=false shift ;; --backend) UPDATE_FRONTEND=false shift ;; --frontend) UPDATE_BACKEND=false shift ;; --help|-h) echo "Uso: $0 [--branch BRANCH] [--force] [--no-backup]" echo "" echo "Opciones:" echo " --branch Branch a usar (default: main)" echo " --force Forzar (descartar cambios locales)" echo " --no-backup No crear backup antes de actualizar" echo " --backend Solo actualizar backend" echo " --frontend Solo actualizar frontend" exit 0 ;; *) log_error "Opcion desconocida: $1" exit 1 ;; esac done } # --------------------------------------------- # Verificar requisitos # --------------------------------------------- check_requirements() { log_info "Verificando requisitos..." # Verificar directorio if [[ ! -d "$INSTALL_DIR" ]]; then log_error "Directorio de instalacion no encontrado: $INSTALL_DIR" exit 1 fi # Verificar git if [[ ! -d "$INSTALL_DIR/.git" ]]; then log_error "No es un repositorio git" exit 1 fi # Verificar conexion if ! ping -c 1 github.com &> /dev/null; then log_error "Sin conexion a internet" exit 1 fi log_success "Requisitos OK" } # --------------------------------------------- # Crear backup # --------------------------------------------- create_backup() { if [[ "$BACKUP_BEFORE_UPDATE" != "true" ]]; then log_warn "Saltando backup (--no-backup)" return fi log_info "Creando backup antes de actualizar..." if [[ -f "$INSTALL_DIR/deploy/scripts/backup.sh" ]]; then bash "$INSTALL_DIR/deploy/scripts/backup.sh" || log_warn "Backup fallo, continuando..." else log_warn "Script de backup no encontrado" fi } # --------------------------------------------- # Obtener version actual # --------------------------------------------- get_current_version() { cd "$INSTALL_DIR" # Intentar obtener version de git tag local version=$(git describe --tags --always 2>/dev/null || echo "unknown") # O del commit local commit=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") echo "${version} (${commit})" } # --------------------------------------------- # Verificar cambios locales # --------------------------------------------- check_local_changes() { cd "$INSTALL_DIR" if [[ -n $(git status --porcelain) ]]; then if [[ "$FORCE_UPDATE" == "true" ]]; then log_warn "Descartando cambios locales..." git reset --hard HEAD git clean -fd else log_error "Hay cambios locales sin commitear" log_error "Usa --force para descartarlos" git status --short exit 1 fi fi } # --------------------------------------------- # Actualizar codigo # --------------------------------------------- update_code() { log_info "Actualizando codigo desde repositorio..." cd "$INSTALL_DIR" # Guardar version actual local old_version=$(get_current_version) # Fetch log_info "Obteniendo cambios..." git fetch origin # Verificar si hay actualizaciones local local_hash=$(git rev-parse HEAD) local remote_hash=$(git rev-parse origin/${REPO_BRANCH}) if [[ "$local_hash" == "$remote_hash" ]]; then log_success "Ya estas en la version mas reciente" return 0 fi # Mostrar cambios pendientes log_info "Cambios disponibles:" git log --oneline HEAD..origin/${REPO_BRANCH} | head -10 # Actualizar log_info "Aplicando cambios..." git reset --hard origin/${REPO_BRANCH} local new_version=$(get_current_version) log_success "Codigo actualizado: $old_version -> $new_version" return 1 # Indica que hubo cambios } # --------------------------------------------- # Actualizar backend # --------------------------------------------- update_backend() { if [[ "$UPDATE_BACKEND" != "true" ]]; then log_info "Saltando actualizacion de backend" return fi log_info "Actualizando backend..." cd "$INSTALL_DIR/backend" # Activar entorno virtual source venv/bin/activate # Actualizar dependencias log_info "Instalando dependencias Python..." if [[ -f "requirements.txt" ]]; then pip install -r requirements.txt -q --upgrade elif [[ -f "pyproject.toml" ]]; then pip install -e . -q --upgrade fi # Ejecutar migraciones log_info "Ejecutando migraciones..." if [[ -d "alembic" ]]; then alembic upgrade head 2>/dev/null || log_warn "Sin migraciones pendientes" fi deactivate log_success "Backend actualizado" } # --------------------------------------------- # Actualizar frontend # --------------------------------------------- update_frontend() { if [[ "$UPDATE_FRONTEND" != "true" ]]; then log_info "Saltando actualizacion de frontend" return fi log_info "Actualizando frontend..." cd "$INSTALL_DIR/frontend" # Verificar package.json if [[ ! -f "package.json" ]]; then log_warn "No hay package.json en frontend" return fi # Instalar dependencias log_info "Instalando dependencias Node.js..." if command -v pnpm &> /dev/null; then pnpm install --frozen-lockfile 2>/dev/null || pnpm install else npm ci 2>/dev/null || npm install fi # Build log_info "Compilando frontend..." if command -v pnpm &> /dev/null; then pnpm build else npm run build fi log_success "Frontend actualizado" } # --------------------------------------------- # Reiniciar servicios # --------------------------------------------- restart_services() { log_info "Reiniciando servicios..." if [[ "$UPDATE_BACKEND" == "true" ]]; then systemctl restart adan-api 2>/dev/null && log_success "adan-api reiniciado" || log_warn "adan-api no existe" fi if [[ "$UPDATE_FRONTEND" == "true" ]]; then systemctl restart adan-web 2>/dev/null && log_success "adan-web reiniciado" || log_warn "adan-web no existe" fi } # --------------------------------------------- # Verificar servicios # --------------------------------------------- verify_services() { log_info "Verificando servicios..." local all_ok=true # Esperar a que inicien sleep 3 if [[ "$UPDATE_BACKEND" == "true" ]]; then if systemctl is-active --quiet adan-api; then log_success "adan-api: activo" else log_error "adan-api: inactivo" all_ok=false fi fi if [[ "$UPDATE_FRONTEND" == "true" ]]; then if systemctl is-active --quiet adan-web; then log_success "adan-web: activo" else log_error "adan-web: inactivo" all_ok=false fi fi # Verificar API health if [[ "$UPDATE_BACKEND" == "true" ]]; then local api_port="${API_PORT:-8000}" if curl -s "http://localhost:${api_port}/health" > /dev/null 2>&1; then log_success "API health check: OK" else log_warn "API health check: no responde (puede estar iniciando)" fi fi if [[ "$all_ok" == "false" ]]; then log_error "Algunos servicios fallaron. Revisa los logs:" echo " journalctl -u adan-api -n 50" echo " journalctl -u adan-web -n 50" return 1 fi return 0 } # --------------------------------------------- # Limpiar cache # --------------------------------------------- clean_cache() { log_info "Limpiando cache..." # Python cache find "$INSTALL_DIR/backend" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true find "$INSTALL_DIR/backend" -type f -name "*.pyc" -delete 2>/dev/null || true # Node cache rm -rf "$INSTALL_DIR/frontend/.next/cache" 2>/dev/null || true log_success "Cache limpiado" } # --------------------------------------------- # Rollback # --------------------------------------------- rollback() { local previous_commit="$1" log_warn "Ejecutando rollback a: $previous_commit" cd "$INSTALL_DIR" git reset --hard "$previous_commit" update_backend update_frontend restart_services log_success "Rollback completado" } # --------------------------------------------- # Mostrar resumen # --------------------------------------------- show_summary() { echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} ACTUALIZACION COMPLETADA${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo "Version actual: $(get_current_version)" echo "Branch: $REPO_BRANCH" echo "" echo "Servicios:" systemctl is-active adan-api 2>/dev/null && echo " - adan-api: activo" || echo " - adan-api: inactivo" systemctl is-active adan-web 2>/dev/null && echo " - adan-web: activo" || echo " - adan-web: inactivo" echo "" } # --------------------------------------------- # Main # --------------------------------------------- main() { parse_args "$@" echo "" echo -e "${BLUE}========================================${NC}" echo -e "${BLUE} ACTUALIZANDO SISTEMA DE ADAN${NC}" echo -e "${BLUE}========================================${NC}" echo "" echo "Branch: $REPO_BRANCH" echo "Force: $FORCE_UPDATE" echo "Backup: $BACKUP_BEFORE_UPDATE" echo "" # Guardar commit actual para posible rollback cd "$INSTALL_DIR" PREVIOUS_COMMIT=$(git rev-parse HEAD) check_requirements create_backup check_local_changes # Intentar actualizar if update_code; then log_info "No hay actualizaciones disponibles" exit 0 fi # Actualizar componentes update_backend || { log_error "Fallo en backend, haciendo rollback..." rollback "$PREVIOUS_COMMIT" exit 1 } update_frontend || { log_error "Fallo en frontend, haciendo rollback..." rollback "$PREVIOUS_COMMIT" exit 1 } clean_cache restart_services # Verificar if ! verify_services; then echo "" read -p "Hacer rollback? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rollback "$PREVIOUS_COMMIT" exit 1 fi fi show_summary } # Manejo de errores trap 'log_error "Error en linea $LINENO"; exit 1' ERR # Ejecutar main "$@"