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>
This commit is contained in:
2026-07-31 15:11:36 -06:00
parent 896528734b
commit 9214a1feab
5 changed files with 83 additions and 11 deletions

View File

@@ -175,16 +175,30 @@ def ensure_runtime_layout() -> None:
):
directory.mkdir(parents=True, exist_ok=True)
_write_env_if_missing()
# Los sellos se ubican bajo el CONFIG_DIR vigente (no la ruta absoluta precalculada)
# para que respeten el monkeypatch de las pruebas y no escriban en el config/ real.
deps_stamp_path = CONFIG_DIR / BUNDLED_STAMP_PATH.name
version_stamp_path = CONFIG_DIR / VERSION_PATH.name
# Las deps embebidas se re-despliegan cuando el build trae otras versiones. El sello
# se escribe DESPUÉS de copiar: si la copia falla a medias, el próximo arranque lo
# reintenta en lugar de quedar marcado como al día.
# Sello de versión: lo lee el instalador remoto del PANEL por SFTP para verificar el
# despliegue (en Windows el .exe es console=False y no tiene stdout confiable).
#
# Se escribe LO PRIMERO, en cuanto existen las carpetas. Antes iba al final, detrás del
# re-despliegue de las deps embebidas, y eso lo hacía inservible justo cuando más importa: al
# cambiar de versión las deps se re-copian enteras, así que el sello quedaba por detrás de esa
# copia y del desempaquetado del onefile. El PANEL se rendía esperándolo y daba por fallida una
# actualización que en realidad iba bien.
#
# Escribirlo aquí es además más honesto sobre lo que el sello significa: "qué binario está
# corriendo", que es cierto desde que el proceso arranca. Si el despliegue de deps fallara
# después, el sello de DEPS —que sí va al final— lo detecta y el próximo arranque reintenta.
_write_stamp(version_stamp_path, __version__)
_write_env_if_missing()
# Las deps embebidas se re-despliegan cuando el build trae otras versiones. Su sello se
# escribe DESPUÉS de copiar: si la copia falla a medias, el próximo arranque lo reintenta
# en lugar de quedar marcado como al día.
deps_stamp = _bundled_deps_stamp()
refresh_deps = _read_stamp(deps_stamp_path) != deps_stamp
_copy_bundled_tree(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR, refresh_deps)
@@ -193,10 +207,6 @@ def ensure_runtime_layout() -> None:
if refresh_deps:
_write_stamp(deps_stamp_path, deps_stamp)
# Sello de versión: lo lee el instalador remoto del PANEL por SFTP para verificar el
# despliegue (en Windows el .exe es console=False y no tiene stdout confiable).
_write_stamp(version_stamp_path, __version__)
from ..constants import DB_PATH
if not DB_PATH.exists():