diff --git a/BUILD.md b/BUILD.md index 3e32108..274f6b3 100644 --- a/BUILD.md +++ b/BUILD.md @@ -194,9 +194,10 @@ sudo systemctl restart cloudrestoreas # tras editar config/.env Expand-Archive CloudRestoreAS--win-x86_64.zip -DestinationPath . cd CloudRestoreAS -.\install.ps1 -Service # tarea programada ONSTART como SYSTEM (24/7 headless) -.\install.ps1 -Desktop # arranque al iniciar sesión (tarea ONLOGON de la app) -.\install.ps1 # solo instala + bootstrap +.\install.ps1 -Service # tarea programada ONSTART como SYSTEM (24/7 headless) +.\install.ps1 -Desktop # arranque al iniciar sesión (tarea ONLOGON de la app) +.\install.ps1 -UpdateInPlace # actualiza una instalación existente, conservando su tarea +.\install.ps1 # solo instala + bootstrap Get-Help .\install.ps1 -Detailed ``` Parámetros: `-Prefix` (default `C:\Aduanasoft\CloudRestoreAS`), `-PanelEnvFile`. @@ -205,6 +206,30 @@ Parámetros: `-Prefix` (default `C:\Aduanasoft\CloudRestoreAS`), `-PanelEnvFile` arranque 24/7 se resuelve con una tarea programada, no con NSSM: descargarlo violaría la regla de que en el servidor destino no se instala ni se baja nada. +Si la cuenta pertenece a Administradores y aun así se rechaza, el mensaje lo dice explícitamente: +es el **token filtrado por UAC**, que es lo que recibe una sesión de OpenSSH. No se arregla +cambiando de cuenta sino con `LocalAccountTokenFilterPolicy=1` (DWORD) en +`HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System`. + +### Garantías al reemplazar el binario (las mismas en los dos instaladores) + +Reemplazar el binario de un servidor en producción no puede dejarlo sin restaurador: + +1. **No se actúa si hay una restauración en curso** (`Temp\` no vacío): se sale con **75** + (`EX_TEMPFAIL`), que el PANEL traduce a "reintenta luego" y no a "falló la instalación". + Interrumpirla dejaría ese respaldo vetado en cada escaneo posterior y la base en `SINGLE_USER`. +2. **Se respalda el binario anterior** antes de pisarlo (`.CloudRestoreAS.exe.prev`). +3. **Se confirma que el agente volvió a arrancar** y, si no, se **revierte** al binario anterior. + El respaldo solo se descarta tras esa confirmación. + +`-UpdateInPlace` además no vuelve a registrar la tarea (así no pisa ajustes hechos sobre ella) y +se salta el bootstrap: una segunda instancia purgaría el `Temp\` de la que está viva. Es el modo +que usa el PANEL para actualizar. + +El arranque headless no depende del entorno de la tarea: `--headless` hace que el binario elija +el plugin Qt `offscreen` en cualquier plataforma, que es lo que le permite correr como SYSTEM en +la sesión 0, donde no hay escritorio interactivo. + > `scripts/dev-setup.ps1` es otra cosa: prepara el entorno de **desarrollo** (Python, venv, > `requirements.txt`) para correr `python runner.py`. No sirve para desplegar el binario. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5057b7c..6f374d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## [Sin publicar] + +### 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 diff --git a/README.md b/README.md index b3d1ca9..b7fcf05 100644 --- a/README.md +++ b/README.md @@ -472,7 +472,7 @@ R: Sí, especifica el nombre o IP del servidor remoto en Configuración → SQL R: Sí, la aplicación usa RESTORE FILELISTONLY para detectar todos los archivos lógicos (MDF, LDF, y archivos adicionales). Solo mantiene el archivo principal de datos (.mdf) y de log (.ldf). **P: ¿Puedo ejecutar esto como Windows Service?** -R: No, la aplicación está diseñada como "daemon de usuario" con interfaz gráfica. Para ejecutar como servicio, considera usar NSSM (Non-Sucking Service Manager) para envolver el ejecutable, pero perderás la UI. +R: Sí, con `install.ps1 -Service`: registra una tarea programada ONSTART que corre como SYSTEM, sin sesión iniciada y sin UI. **No** se usa NSSM ni ningún envoltorio descargado — el servidor destino no instala ni baja nada, y una tarea programada ya viene en el SO. Requiere PowerShell como Administrador. Ver [BUILD.md](BUILD.md) §6. ## 📄 Licencia diff --git a/packaging/LEEME.txt b/packaging/LEEME.txt index 3241545..1b7d103 100644 --- a/packaging/LEEME.txt +++ b/packaging/LEEME.txt @@ -18,6 +18,29 @@ Cerrar la ventana (X) minimiza a la bandeja; la app sigue en ejecucion. Para salir por completo: clic derecho en el icono de bandeja -> Salir, o menu Archivo -> Salir. +------------------------------------- +Windows en SERVIDOR (24/7, sin sesion iniciada) +------------------------------------- +Lo normal es que el PANEL instale y actualice solo, desde Versiones CRAS. Esto es +el camino manual, y es lo mismo que el PANEL ejecuta por dentro. + +Opcion A - servicio 24/7 (recomendado en servidor SQL): + .\install.ps1 -Service + (registra una tarea programada ONSTART que corre como SYSTEM, sin sesion + iniciada y sin ventana; ver estado: Get-ScheduledTask -TaskName CloudRestoreAS) + Requiere PowerShell como Administrador. + +Opcion B - actualizar una instalacion que ya existe: + .\install.ps1 -UpdateInPlace + (reemplaza el binario conservando la tarea y la configuracion; no actua si hay + una restauracion en curso, y revierte solo si la version nueva no arranca) + +Opcion C - escritorio Windows (con sesion): + .\install.ps1 -Desktop + +No se usa NSSM ni ningun envoltorio de servicio: la tarea programada ya viene en el +SO, y en el servidor destino no se instala ni se descarga nada. + ------------------------------------- Linux en SERVIDOR SIN ESCRITORIO (headless) -------------------------------------