Files
CloudRecoveryAS/CHANGELOG.md
hreyes 9214a1feab fix(bootstrap): escribir el sello de version lo primero, y hacerla visible en el log
Actualizar a 1.1.4 fallaba con "no escribio config\.version tras la
actualizacion" aunque el binario nuevo estuviera instalado y corriendo desde la
ruta correcta. La causa era el ORDEN dentro de ensure_runtime_layout(): el sello
iba al final, detras del re-despliegue de las deps embebidas (7-Zip y ODBC). Al
cambiar de version esas deps se re-copian ENTERAS, asi que el sello quedaba por
detras de esa copia y del desempaquetado del onefile de ~254 MB con el antivirus
escaneando cada archivo. El PANEL se rendia esperandolo y daba por fallida una
actualizacion que iba bien.

- El sello se escribe lo primero, en cuanto existen las carpetas. Es tambien mas
  honesto sobre lo que significa —"que binario esta corriendo"—, que es cierto
  desde que el proceso arranca. El sello de DEPS sigue yendo al final, donde su
  comentario explica por que: si la copia falla a medias, el proximo arranque
  reintenta en vez de quedar marcado como al dia.
- La version va en la PRIMERA linea del log de arranque. Permite comprobar que
  binario corre de verdad mirando solo config/logs, sin depender del sello ni del
  reporte al panel: verificar una actualizacion deja de obligar a creerse lo que
  diga otro sistema.

La prueba nueva observa el estado del sello EN EL MOMENTO en que empieza la copia
de deps, no al final, que es la unica forma de fijar el orden. Comprobado que
muerde: devolviendo el sello al final falla con `assert None == '1.1.5'`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 15:11:36 -06:00

13 KiB

Changelog

[1.1.5] - 2026-07-31

La versión ahora es comprobable, y el sello deja de llegar tarde

Actualizar a 1.1.4 fallaba con "no escribió config\.version tras la actualización" aunque el binario nuevo sí estuviera instalado y corriendo desde la ruta correcta. La causa era el orden dentro de ensure_runtime_layout(): el sello de versión se escribía al final, detrás del re-despliegue de las dependencias embebidas (7-Zip y ODBC). Y al cambiar de versión esas deps se re-copian enteras, así que el sello quedaba por detrás de esa copia y del desempaquetado del onefile de ~254 MB, con el antivirus escaneando cada archivo. El PANEL se rendía esperándolo y daba por fallida una actualización que iba bien.

  • El sello se escribe lo primero, en cuanto existen las carpetas. Es además más honesto sobre lo que significa —"qué binario está corriendo"—, que es cierto desde que el proceso arranca. Si el despliegue de deps fallara después, su propio sello (que sigue yendo al final) lo detecta y el siguiente arranque reintenta.
  • La versión va en la primera línea del log de arranque. Permite comprobar qué binario corre de verdad mirando solo config/logs, sin depender del sello ni del reporte al panel. Verificar una actualización ya no obliga a creerse lo que diga otro sistema.

[1.1.4] - 2026-07-31

Recoge todo lo que sigue de esta sección y sustituye a 1.1.3, cuyo artefacto publicado se generó antes de los arreglos: su install.ps1 no traía -UpdateInPlace y su binario llevaba el runner.py que ignoraba --headless en Windows. Actualizar con él mataba el agente, cambiaba la tarea programada a SYSTEM y la dejaba sin arrancar, porque Qt no puede crear su plataforma como SYSTEM en la sesión 0 sin offscreen.

Los paquetes genéricos de Gitea son inmutables, así que se quema el número en lugar de reemplazar 1.1.3: dos contenidos distintos con la misma versión fue exactamente lo que hizo caro el diagnóstico. Hay que reconstruir los binarios aunque el código ya estuviera arreglado, porque __version__ va compilado dentro del ejecutable y el PANEL compara el sello config/.version contra la versión que creía estar instalando.

[Sin publicar]

La actualización de Windows decía que funcionó y no cambiaba nada

Actualizar sobre un servidor con el agente en una carpeta no estándar terminaba en verde y dejaba el servidor con la versión anterior. La causa: todo el camino de Windows identificaba al agente por nombre, mientras que lo único que se actualiza se identifica por ruta.

  • install.ps1 alinea la tarea programada con el binario instalado. Start-ScheduledTask ejecuta la ruta registrada en la acción de la tarea, no el -Prefix: si difieren, se copiaba el binario nuevo en un sitio y se arrancaba el viejo del otro. Ahora se reapunta la tarea conservando disparador, principal, ajustes y argumentos; si no se puede corregir, falla.
  • La confirmación de arranque mira la ruta del proceso, no solo su nombre. Un agente viejo que nunca se detuvo satisfacía igual de bien un Get-Process -Name CloudRestoreAS. Si la ruta no es legible —un proceso de SYSTEM no la expone a una cuenta sin elevación— se acepta por nombre y se avisa, en vez de revertir una actualización correcta por falta de información.
  • Nuevo scripts/emular-actualizacion-windows.ps1: reproduce el escenario completo con un agente falso, sin elevación y sin tocar la instalación real de la máquina. Es lo que destapó este defecto y el siguiente.
  • Corregido Merge-EnvFile con un config\.env de una sola línea. Al asignar la salida de un if, PowerShell desenrolla un array de un elemento a escalar, así que $lines.Count reventaba bajo Set-StrictMode y la siembra de credenciales abortaba la instalación.

Instalación y actualización desatendidas en Windows

El instalador de Windows nunca recibió la maquinaria de actualización segura que sí tiene install.sh, y la asimetría se notaba en producción: actualizar desde el PANEL dejaba el servidor sin agente, o fallaba con un error que no correspondía.

  • install.ps1 -UpdateInPlace: actualiza una instalación existente conservando su tarea programada y su configuración, y sin correr el bootstrap (una segunda instancia purgaría el Temp\ de la que está viva). Es el modo que usa el PANEL para actualizar.
  • No se interrumpe una restauración en curso (Temp\ no vacío): sale con 75 (EX_TEMPFAIL), que el PANEL traduce a "reintenta luego". Windows no tenía esta guarda y una reinstalación a destiempo se llevaba por delante el respaldo que se estuviera restaurando, dejándolo vetado y la base en SINGLE_USER.
  • Respaldo y reversión automática: si el binario nuevo no arranca, se vuelve al anterior. El respaldo solo se descarta tras confirmar que la versión nueva corre.
  • Rearranque garantizado en todos los modos. La detención corría siempre, pero solo -Service volvía a arrancar algo: actualizar con desktop o none mataba el agente y se iba sin dejar señal.
  • La espera a que el SO libere el .exe pasa de 5 s a 30 s con reintentos de la copia: con un antivirus escaneando un onefile de ~270 MB, 5 s se quedaban cortos y la copia abortaba.
  • Se distingue "no es administrador" de "es administrador con el token filtrado por UAC", que es lo que recibe una sesión de OpenSSH. Se veían idénticos y el remedio es el opuesto: el segundo no se arregla cambiando de cuenta sino con LocalAccountTokenFilterPolicy.

--headless ahora funciona en Windows

_ensure_qt_platform() salía de inmediato en win32, así que la bandera no hacía nada ahí. La tarea ONSTART corre como SYSTEM en la sesión 0, sin escritorio interactivo, y arrancaba con el plugin Qt windows intentando crear una ventana real. En Linux el mismo modo funcionaba porque el unit de systemd fija QT_QPA_PLATFORM=offscreen por fuera, y esa asimetría escondió el defecto. Ahora --headless fuerza offscreen en todas las plataformas y no se muestra ventana.

Documentación

  • README.md afirmaba que la app no puede correr como servicio de Windows y sugería NSSM, lo que contradecía a install.ps1 desde que existe. Corregido.
  • packaging/LEEME.txt solo documentaba el camino manual para Windows; ahora incluye el instalador, igual que ya hacía para Linux.

[1.1.0] - 2026-07-29

Distribución e instalación automatizada vía Gitea + PANEL

Publicación de versiones

  • Artefactos con versión en el nombre: CloudRestoreAS-<version>-{linux,win}-<arch>.{tar.gz,zip}, más SHA256SUMS y release.json (manifiesto con sha256, tamaño y deps embebidas).
  • packaging/scripts/publish-release.sh: publica en los paquetes genéricos de Gitea (ADUANASOFT/generic/cloudrestoreas/<version>) y verifica el sha256 contra la propia API de Gitea antes de dar la publicación por buena. Soporta --dry-run, --force y --notify-panel.
  • build-all.sh --publish encadena build → empaquetado → publicación. Se niega a publicar si falta una plataforma: los paquetes genéricos son inmutables y corregirlo quemaría el número de versión.

Contrato con el PANEL

  • POST /api/restore/instance-config ahora reporta también platform y arch, para que el PANEL sepa qué artefacto le corresponde a cada servidor.
  • processed_folder ya se envía en ese mismo reporte (antes se calculaba, no se mandaba).

Instaladores

  • Nuevo install.ps1: instalador de despliegue Windows, autocontenido. Registra una tarea programada ONSTART como SYSTEM para el 24/7 headless — sin NSSM ni descargas en el servidor destino. Detiene la instancia en ejecución antes de reemplazar el .exe.
  • El antiguo install.ps1 (preparación del entorno de desarrollo: Python, venv, pip) se movió a scripts/dev-setup.ps1. Se estaba empaquetando por error en el zip del ejecutable autocontenido, que no necesita nada de eso.
  • install.sh e install.ps1 aceptan --panel-env-file / -PanelEnvFile: fusionan las claves CLOUDRESTORE_PANEL_* en config/.env (replace-or-append, idempotente, con lista blanca) y borran el archivo. El token viaja por archivo 0600, nunca por argumentos, para que no quede visible en ps ni en el historial del destino.
  • install.sh detiene el servicio antes de reemplazar el binario y lo vuelve a levantar si estaba activo. (La razón que se dio aquí —que un ELF en ejecución da ETXTBSY— era incorrecta: eso le pasa a cp, no a install, que desvincula el destino antes de crearlo. Ver BUILD.md.)

Versionado

  • app/__init__.py es la fuente única de la versión; el diálogo Acerca de ya no la trae hardcodeada.
  • Flag --version en el binario, y sello config/.version que escribe el bootstrap (el instalador remoto lo lee por SFTP, porque el .exe se compila con console=False).
  • El .exe ya lleva metadatos de versión de Windows (VSVersionInfo).

Correcciones

  • config/7zip y config/odbc se re-despliegan cuando el build trae otras versiones embebidas, comparando un sello con el sha256 de bundled-versions.json. Antes solo se copiaban si la carpeta estaba vacía, así que una actualización con driver ODBC nuevo conservaba el viejo indefinidamente.

[1.0.0] - 2026-01-25

Lanzamiento Inicial

Características Principales

  • Interfaz gráfica completa con PySide6/Qt
  • Motor de procesamiento concurrente con workers configurables
  • Vigilancia automática de carpeta de entrada
  • Soporte para archivos ZIP multipart (.zip.001, .zip.002, etc.)
  • Extracción automática con 7-Zip
  • Restauración automática de bases de datos SQL Server
  • Sistema de mapeo de nodos (archivo → base de datos)
  • Minimización a bandeja del sistema (system tray)
  • Base de datos SQLite para métricas y auditoría
  • Logging rotativo con múltiples niveles
  • Cifrado de contraseñas con DPAPI
  • Soporte Windows Auth y SQL Auth
  • Modo Dry Run para pruebas
  • Detección de estabilidad de archivos
  • Soporte para marcador .ready
  • Timeouts configurables
  • Tolerancia a fallos y manejo de errores

Tabs de la Interfaz

  • Dashboard: Estadísticas en tiempo real
  • Jobs: Gestión y visualización de jobs con detalles
  • Nodos: CRUD de mapeos nodo → base de datos
  • Configuración: Configuración completa de la aplicación
  • Logs: Visualización de eventos y logs

Base de Datos

  • Tabla jobs: Registro completo de jobs
  • Tabla job_steps: Pasos detallados de cada job
  • Tabla nodes: Mapeos de nodos
  • Tabla events: Log de eventos
  • Tabla config: Configuración persistente

Documentación

  • README.md: Documentación completa
  • QUICKSTART.md: Guía rápida de inicio
  • ADVANCED.md: Configuración avanzada
  • SCRIPTS.md: Ejemplos de scripts de automatización

Scripts Incluidos

  • runner.py: Punto de entrada principal
  • install.ps1: Script de instalación automática
  • build.ps1: Script para generar ejecutable con PyInstaller
  • test_installation.py: Script de verificación de instalación

Requisitos del Sistema

  • Windows 10/11 o Windows Server 2019/2022+
  • Python 3.11+
  • 7-Zip
  • ODBC Driver 17 for SQL Server
  • SQL Server 2016+ (destino de restauraciones)

Dependencias

  • PySide6 >= 6.6.0
  • pyodbc >= 5.0.0
  • pywin32 >= 306

Características Futuras Planificadas

v1.1.0 (Próximo Release)

  • Reintentos automáticos configurables desde UI
  • Notificaciones por email
  • Verificación de espacio en disco antes de procesar
  • Soporte para múltiples instancias SQL Server
  • Importación/Exportación de nodos desde CSV
  • Dashboard mejorado con gráficos
  • Filtros avanzados en tabla de jobs

v1.2.0 (Futuro)

  • API REST para integración externa
  • Webhooks para notificaciones
  • Programación de restauraciones (scheduling)
  • Soporte para otros formatos de compresión (RAR, TAR)
  • Modo cluster (múltiples instancias coordinadas)
  • Soporte para Azure SQL Database
  • Backup/Restore de configuración desde UI

v2.0.0 (Largo Plazo)

  • Soporte multiplataforma (Linux)
  • Interfaz web opcional
  • Modo servicio de Windows
  • Soporte para PostgreSQL y MySQL
  • Machine learning para predicción de tiempos
  • Dashboard en tiempo real con WebSockets

Problemas Conocidos

Limitaciones Actuales

  • Solo soporta una configuración SQL Server (no múltiples instancias)
  • Reintentos automáticos no expuestos en UI (requiere edición manual)
  • No hay verificación automática de espacio en disco
  • System tray no muestra notificaciones toast
  • No hay soporte nativo para archivos RAR

Workarounds Documentados

  • Múltiples instancias SQL: Ver ADVANCED.md sección 7
  • Notificaciones: Ver SCRIPTS.md para monitoreo externo
  • Limpieza automática: Ver SCRIPTS.md para scripts de limpieza

Notas de Seguridad

  • Las contraseñas se cifran con DPAPI (vinculadas a usuario/máquina)
  • Los logs pueden contener información sensible (rutas, nombres de DB)
  • Se recomienda ejecutar con cuenta de servicio dedicada
  • Asegurar permisos apropiados en carpetas de trabajo

Agradecimientos

Desarrollado por el equipo de Aduanasoft para automatización de restauraciones de bases de datos SQL Server.


Nota de Versión: Esta es la versión inicial de CloudRestoreAS. Se agradecen comentarios y sugerencias para futuras versiones.