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:
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,5 +1,25 @@
|
|||||||
# Changelog
|
# 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
|
## [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ó
|
Recoge todo lo que sigue de esta sección y **sustituye a 1.1.3**, cuyo artefacto publicado se generó
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
# package-release.sh (nombres de artefacto y release.json) y el reporte al PANEL.
|
# package-release.sh (nombres de artefacto y release.json) y el reporte al PANEL.
|
||||||
# Formato obligatorio: puntos y números, monotónico creciente — el PANEL compara
|
# Formato obligatorio: puntos y números, monotónico creciente — el PANEL compara
|
||||||
# versiones como tuplas de enteros para detectar si hay una más nueva.
|
# versiones como tuplas de enteros para detectar si hay una más nueva.
|
||||||
__version__ = "1.1.4"
|
__version__ = "1.1.5"
|
||||||
__author__ = "Aduanasoft"
|
__author__ = "Aduanasoft"
|
||||||
|
|||||||
@@ -175,16 +175,30 @@ def ensure_runtime_layout() -> None:
|
|||||||
):
|
):
|
||||||
directory.mkdir(parents=True, exist_ok=True)
|
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)
|
# 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.
|
# para que respeten el monkeypatch de las pruebas y no escriban en el config/ real.
|
||||||
deps_stamp_path = CONFIG_DIR / BUNDLED_STAMP_PATH.name
|
deps_stamp_path = CONFIG_DIR / BUNDLED_STAMP_PATH.name
|
||||||
version_stamp_path = CONFIG_DIR / VERSION_PATH.name
|
version_stamp_path = CONFIG_DIR / VERSION_PATH.name
|
||||||
|
|
||||||
# Las deps embebidas se re-despliegan cuando el build trae otras versiones. El sello
|
# Sello de versión: lo lee el instalador remoto del PANEL por SFTP para verificar el
|
||||||
# se escribe DESPUÉS de copiar: si la copia falla a medias, el próximo arranque lo
|
# despliegue (en Windows el .exe es console=False y no tiene stdout confiable).
|
||||||
# reintenta en lugar de quedar marcado como al día.
|
#
|
||||||
|
# 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()
|
deps_stamp = _bundled_deps_stamp()
|
||||||
refresh_deps = _read_stamp(deps_stamp_path) != deps_stamp
|
refresh_deps = _read_stamp(deps_stamp_path) != deps_stamp
|
||||||
_copy_bundled_tree(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR, refresh_deps)
|
_copy_bundled_tree(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR, refresh_deps)
|
||||||
@@ -193,10 +207,6 @@ def ensure_runtime_layout() -> None:
|
|||||||
if refresh_deps:
|
if refresh_deps:
|
||||||
_write_stamp(deps_stamp_path, deps_stamp)
|
_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
|
from ..constants import DB_PATH
|
||||||
|
|
||||||
if not DB_PATH.exists():
|
if not DB_PATH.exists():
|
||||||
|
|||||||
@@ -193,8 +193,14 @@ def _run() -> int:
|
|||||||
panel_ok = is_panel_configured()
|
panel_ok = is_panel_configured()
|
||||||
start_engine = args.start_engine or launch.start_engine
|
start_engine = args.start_engine or launch.start_engine
|
||||||
|
|
||||||
|
from app import __version__
|
||||||
|
|
||||||
app_logger.info("=" * 80)
|
app_logger.info("=" * 80)
|
||||||
app_logger.info("CloudRestoreAS - Iniciando aplicación")
|
# La versión va en la PRIMERA línea del arranque a propósito: es la forma de comprobar qué
|
||||||
|
# binario está corriendo de verdad sin depender del sello ni del reporte al panel, mirando
|
||||||
|
# solo config/logs. Sin esto, verificar una actualización obligaba a creerse lo que dijera
|
||||||
|
# otro sistema.
|
||||||
|
app_logger.info(f"CloudRestoreAS {__version__} - Iniciando aplicación")
|
||||||
app_logger.info(f"Directorio app: {ROOT_DIR}")
|
app_logger.info(f"Directorio app: {ROOT_DIR}")
|
||||||
if qt_platform == "offscreen":
|
if qt_platform == "offscreen":
|
||||||
app_logger.info("Modo: sin display → plataforma Qt 'offscreen' (motor headless)")
|
app_logger.info("Modo: sin display → plataforma Qt 'offscreen' (motor headless)")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from app.config import bootstrap
|
||||||
from app.config.bootstrap import ensure_runtime_layout
|
from app.config.bootstrap import ensure_runtime_layout
|
||||||
from app.config.env_loader import (
|
from app.config.env_loader import (
|
||||||
apply_env_overrides,
|
apply_env_overrides,
|
||||||
@@ -112,6 +113,41 @@ def test_bootstrap_escribe_sello_de_version(tmp_path: Path, monkeypatch: pytest.
|
|||||||
assert (config_dir / ".version").read_text(encoding="utf-8").strip() == __version__
|
assert (config_dir / ".version").read_text(encoding="utf-8").strip() == __version__
|
||||||
|
|
||||||
|
|
||||||
|
def test_sello_de_version_se_escribe_antes_de_copiar_las_deps(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
El sello tiene que existir ANTES de re-desplegar 7-Zip y ODBC.
|
||||||
|
|
||||||
|
Iba al final, y eso lo volvía inservible justo cuando más importa: al cambiar de versión las
|
||||||
|
deps embebidas 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. El PANEL se rendía
|
||||||
|
esperándolo y reportaba como fallida una actualización que en realidad iba bien.
|
||||||
|
"""
|
||||||
|
from app import __version__
|
||||||
|
|
||||||
|
config_dir = _redirect_bootstrap(tmp_path, monkeypatch)
|
||||||
|
|
||||||
|
# Se observa el estado del sello EN EL MOMENTO en que empieza la copia de deps.
|
||||||
|
visto: dict[str, str | None] = {}
|
||||||
|
real = bootstrap._copy_bundled_tree
|
||||||
|
|
||||||
|
def espia(*args, **kwargs):
|
||||||
|
sello = config_dir / ".version"
|
||||||
|
visto.setdefault(
|
||||||
|
"al_copiar",
|
||||||
|
sello.read_text(encoding="utf-8").strip() if sello.exists() else None,
|
||||||
|
)
|
||||||
|
return real(*args, **kwargs)
|
||||||
|
|
||||||
|
monkeypatch.setattr("app.config.bootstrap._copy_bundled_tree", espia)
|
||||||
|
ensure_runtime_layout()
|
||||||
|
|
||||||
|
assert visto["al_copiar"] == __version__, (
|
||||||
|
"el sello de versión debe existir antes de empezar a copiar las deps embebidas"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_bootstrap_redespliega_deps_cuando_cambia_el_manifiesto(
|
def test_bootstrap_redespliega_deps_cuando_cambia_el_manifiesto(
|
||||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
):
|
):
|
||||||
|
|||||||
Reference in New Issue
Block a user