Volver a la wiki

Bootstrap 2.14.0 roto — PyInstaller descartó app_main.py

Resumen

El bootstrap 2.14.0 de app_main.py fue descartado por PyInstaller durante el build cuando se pasaba como --add-data .py suelto en agent_modules/. Causa: PyInstaller analiza módulos y descarta los que colisionan con su análisis interno → con app_main.py suelto, el fallback embebido se rompía.

Síntoma: 2 builds fallidos (07-07) con error "app_main.py no encontrado en agent_modules" tras un swap limpio.

Duración: ~3 horas (desde build hasta root-cause).

Timeline

HoraEvento
07-07 13:00Build 2.14.0 falló
07-07 14:00Segundo intento falló con mismo error
07-07 15:00Root-cause: PyInstaller descartó app_main.py
07-07 16:30Fix: app_main.py embebido como ZIP, no suelto
07-07 17:002.14.1 build OK, paquete validado
07-07 18:00E2E en máquina de Edu: update automático exitoso

Causa raíz

Antes (2.14.0, bug)

Ahora (2.14.1, fix)

Impacto

Fix e implementación

build_agent.bat (orquestación)

REM 1. Generar ZIP embebido
python scripts/agent/build_app_package.py --output app-%VERSION%.zip

REM 2. Compilar bootstrap con ZIP embebido
pyinstaller --add-data "app-%VERSION%.zip:root" ...

scripts/agent/build_app_package.py (generador ZIP)

bootstrap._embedded_source() (extractor)

def _embedded_source() -> bytes:
    """Extrae y valida ZIP embebido en fallback."""
    if not _EMBEDDED_ZIP:
        raise RuntimeError("Código embebido no disponible")
    
    # Validar SHA-256 del ZIP
    actual_hash = hashlib.sha256(_EMBEDDED_ZIP).hexdigest()
    expected_hash = _EMBEDDED_HASH  # Generado en build
    
    if actual_hash != expected_hash:
        raise RuntimeError(f"Hash mismatch: {actual_hash} != {expected_hash}")
    
    # Descomprimir
    return zlib.decompress(_EMBEDDED_ZIP)

Testing

Lecciones aprendidas

  1. PyInstaller + análisis de módulos: Ten cuidado con archivos .py que PyInstaller pudiera confundir. Prueba con --analyze si dudas.
  2. Embeber binarios vs. código: ZIP/datos binarios son más seguros que .py sueltos dentro del análisis de PyInstaller.
  3. Self-test en CI: El antiguo self-test pasaba porque usaba current.txt (versión anterior intacta) → enmascara el bug. Ahora fuerza embebido con env var.

Resoluciones

AspectoResolución
Código embebidoZIP con hash validado
Self-testFuerza embebido con CREARACK_FORCE_EMBEDDED=1
Orquestación buildbuild_agent.bat + build_app_package.py
Validación en runtime_embedded_source() con SHA-256 + descomprensión

Véase también

Subir