Files
ATLAS/deploy/scripts/update.sh
2026-01-21 08:26:01 +00:00

486 lines
13 KiB
Bash

#!/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 "$@"