Sustituye a 1.1.3, cuyo artefacto publicado se genero antes de los arreglos: su install.ps1 no traia -UpdateInPlace y su binario llevaba el runner.py que ignora --headless en Windows. Actualizar con el 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 sesion 0 sin offscreen. Se quema el numero en lugar de reemplazar 1.1.3 con --force: los paquetes genericos de Gitea son inmutables, y dos contenidos distintos con la misma version fue exactamente lo que hizo caro el diagnostico. Hay que reconstruir los binarios aunque el codigo ya estuviera arreglado, porque __version__ va compilado dentro del ejecutable y el PANEL compara el sello config/.version contra la version que creia estar instalando. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 KiB
Changelog
[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.ps1alinea la tarea programada con el binario instalado.Start-ScheduledTaskejecuta 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-EnvFilecon unconfig\.envde una sola línea. Al asignar la salida de unif, PowerShell desenrolla un array de un elemento a escalar, así que$lines.Countreventaba bajoSet-StrictModey 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 elTemp\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 enSINGLE_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
-Servicevolvía a arrancar algo: actualizar condesktopononemataba el agente y se iba sin dejar señal. - La espera a que el SO libere el
.exepasa 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.mdafirmaba que la app no puede correr como servicio de Windows y sugería NSSM, lo que contradecía ainstall.ps1desde que existe. Corregido.packaging/LEEME.txtsolo 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ásSHA256SUMSyrelease.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,--forcey--notify-panel.build-all.sh --publishencadena 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-configahora reporta tambiénplatformyarch, para que el PANEL sepa qué artefacto le corresponde a cada servidor.processed_folderya 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ó ascripts/dev-setup.ps1. Se estaba empaquetando por error en el zip del ejecutable autocontenido, que no necesita nada de eso. install.sheinstall.ps1aceptan--panel-env-file/-PanelEnvFile: fusionan las clavesCLOUDRESTORE_PANEL_*enconfig/.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 enpsni en el historial del destino.install.shdetiene 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 daETXTBSY— era incorrecta: eso le pasa acp, no ainstall, que desvincula el destino antes de crearlo. Ver BUILD.md.)
Versionado
app/__init__.pyes la fuente única de la versión; el diálogo Acerca de ya no la trae hardcodeada.- Flag
--versionen el binario, y selloconfig/.versionque escribe el bootstrap (el instalador remoto lo lee por SFTP, porque el.exese compila conconsole=False). - El
.exeya lleva metadatos de versión de Windows (VSVersionInfo).
Correcciones
config/7zipyconfig/odbcse re-despliegan cuando el build trae otras versiones embebidas, comparando un sello con el sha256 debundled-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 principalinstall.ps1: Script de instalación automáticabuild.ps1: Script para generar ejecutable con PyInstallertest_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.