fix(install): actualización desatendida en Windows, con reversión
install.sh recibió la maquinaria de actualización segura y install.ps1 nunca recibió el equivalente. La asimetría se notaba en producción: actualizar desde el PANEL dejaba el servidor sin agente. - -UpdateInPlace: actualiza conservando la tarea y la configuración, sin correr el bootstrap (una segunda instancia purga el Temp\ de la que está viva). - No se interrumpe una restauración en curso: sale con 75 (EX_TEMPFAIL), que el PANEL traduce a "reintenta luego". Windows no tenía esta guarda y una reinstalación a destiempo dejaba el respaldo vetado y la base en SINGLE_USER. - Respaldo del binario anterior y reversión automática si el nuevo no arranca. - Rearranque garantizado en TODOS los modos: la detención corría siempre, pero solo -Service volvía a arrancar algo. - Espera de liberación del .exe de 5s a 30s con reintentos de la copia. - 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. Y --headless deja de ser un no-op en Windows: _ensure_qt_platform() salía de inmediato en win32, así que la tarea ONSTART arrancaba como SYSTEM en la sesión 0 con el plugin Qt 'windows' intentando crear una ventana real. En Linux el unit fija QT_QPA_PLATFORM=offscreen por fuera, y esa asimetría escondió el defecto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
45
runner.py
45
runner.py
@@ -77,19 +77,33 @@ def _print_version() -> int:
|
||||
|
||||
def _ensure_qt_platform(headless: bool = False) -> str:
|
||||
"""
|
||||
Selecciona el plugin de plataforma Qt en Linux. En un servidor headless (sin
|
||||
DISPLAY/WAYLAND_DISPLAY) o con --headless, usa 'offscreen' para que la app
|
||||
arranque y el motor trabaje sin X; con display usa el default ('xcb'). No toca
|
||||
nada si el usuario ya fijó QT_QPA_PLATFORM, ni en Windows/macOS.
|
||||
Selecciona el plugin de plataforma Qt.
|
||||
|
||||
Devuelve la plataforma forzada ("offscreen") o "" si se deja el default.
|
||||
`--headless` fuerza 'offscreen' en TODAS las plataformas. Antes se salía de inmediato en
|
||||
Windows, así que la bandera no hacía nada ahí: la tarea programada ONSTART —que corre como
|
||||
SYSTEM, en la sesión 0, sin escritorio interactivo— arrancaba con el plugin 'windows' e
|
||||
intentaba crear una ventana real. Ese es el motivo de que el agente no levantara tras
|
||||
instalarse en Windows mientras en Linux, donde el unit fija QT_QPA_PLATFORM=offscreen, sí.
|
||||
El plugin va embebido en el binario de las dos plataformas (qoffscreen.dll / libqoffscreen.so).
|
||||
|
||||
La autodetección por DISPLAY/WAYLAND_DISPLAY sigue siendo solo de Linux: es donde su ausencia
|
||||
significa "no hay servidor gráfico". En Windows y macOS no existen esas variables y tomarlas
|
||||
como señal mandaría a offscreen a cualquiera que abra la app con doble clic.
|
||||
|
||||
No toca nada si el usuario ya fijó QT_QPA_PLATFORM: una elección explícita manda sobre todo
|
||||
lo demás.
|
||||
|
||||
Devuelve la plataforma en uso ("offscreen") o "" si se deja el default.
|
||||
"""
|
||||
if sys.platform in ("win32", "darwin"):
|
||||
return ""
|
||||
if os.environ.get("QT_QPA_PLATFORM"):
|
||||
return os.environ["QT_QPA_PLATFORM"]
|
||||
if headless:
|
||||
os.environ["QT_QPA_PLATFORM"] = "offscreen"
|
||||
return "offscreen"
|
||||
if sys.platform in ("win32", "darwin"):
|
||||
return ""
|
||||
has_display = bool(os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
|
||||
if headless or not has_display:
|
||||
if not has_display:
|
||||
os.environ["QT_QPA_PLATFORM"] = "offscreen"
|
||||
return "offscreen"
|
||||
return ""
|
||||
@@ -206,11 +220,16 @@ def _run() -> int:
|
||||
start_engine=start_engine,
|
||||
panel_configured=panel_ok,
|
||||
)
|
||||
# La ventana SIEMPRE se muestra al iniciar; cerrar (X) la manda a la bandeja.
|
||||
window.show()
|
||||
window.activateWindow()
|
||||
window.raise_()
|
||||
app_logger.info("Ventana principal mostrada")
|
||||
# Con display, la ventana SIEMPRE se muestra al iniciar; cerrar (X) la manda a la bandeja.
|
||||
# En offscreen no hay a quién mostrarla, y pedirlo igual solo da trabajo al plugin y ruido en
|
||||
# el log. El motor no depende de esto: arranca por su propio temporizador en MainWindow.
|
||||
if qt_platform == "offscreen":
|
||||
app_logger.info("Sin interfaz: la ventana no se muestra (motor headless)")
|
||||
else:
|
||||
window.show()
|
||||
window.activateWindow()
|
||||
window.raise_()
|
||||
app_logger.info("Ventana principal mostrada")
|
||||
|
||||
exit_code = app.exec()
|
||||
app_logger.info(f"Aplicación finalizada con código: {exit_code}")
|
||||
|
||||
Reference in New Issue
Block a user