CreaRack-SL

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)

  • scripts/agent/build_app_package.py generaba app_main.py suelto en agent_modules/.
  • PyInstaller --add-data agent_modules/app_main.py:agent_modules → ingresaba como instrucción “añade este .py”.
  • Durante el análisis de modelos, PyInstaller detectaba que app_main colisionaba con su propio análisis y descartaba silenciosamente el archivo.
  • Bootstrap en runtime: _import_app_main() → ModuleNotFoundError.

Ahora (2.14.1, fix)

  • build_agent.bat genera app-VER.zip ANTES de PyInstaller, conteniendo todo el paquete.
  • PyInstaller --add-data app-VER.zip:root → incrusta el ZIP como dato binario (no código Python).
  • PyInstaller no lo analiza como módulo → no lo descarta.
  • Bootstrap: _embedded_source() descomprime el ZIP, valida hash, ejecuta.

Impacto

  • Desarrollo: El Lanzador Estable F1 (bootstrap + paquete) no fue entregable hasta resolver esto.
  • Producción: Ninguno (2.14.0 nunca se distribuyó, cancelado tras 2 fallos).
  • Flota legacy (2.13.6): Sin impacto, seguía con auto-update por .exe legacy (que aún está).

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)

  • Genera ZIP con código completo + hash SHA-256.
  • __init__.py carga como agent_bootstrap (módulo).

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

  • 3 tests nuevos en tests/agent/test_agent_launcher.py:

    • test_embedded_source_integrity: ZIP válido y válida el hash.
    • test_embedded_source_corrupted: ZIP corrupto → error.
    • test_selftest_with_embedded: self-test fuerza CREARACK_FORCE_EMBEDDED=1 → prueba el camino embebido real (no current.txt).
  • Suite: 26/26 tests passing.

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

  • [[feature—agent-lanzador—bootstrap-zip]]
  • [[feature—agent-lanzador—paquete-firmado-ed25519]]
  • [[concept—agent-lanzador—cadena-fallback]]
  • [[concept—agent-lanzador—plan-f3]]