# 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--{linux,win}-.{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/`) 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.