diff --git a/.gitignore b/.gitignore index a11e06a..72c0961 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ MANIFEST # Virtual Environment venv/ +venv-windows/ +venv-linux/ ENV/ env/ @@ -35,16 +37,25 @@ env/ *.swo *~ -# Logs +# Logs y datos locales logs/*.log logs/*.log.* - -# Database data/*.db data/*.db-journal +config/.env +config/data/ +config/logs/ +config/odbc/ +config/7zip/ +config/.bootstrap_ok +config/.autostart_registered -# PyInstaller -*.spec +# PyInstaller / empaquetado +packaging/bundled/ +dist/installers/ + +# Variables de entorno locales +.env # OS .DS_Store diff --git a/QUICKSTART.md b/QUICKSTART.md index 0f5d187..84cfd47 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -126,10 +126,10 @@ C:\CloudRestore\ ## Para Ejecutar 24/7 -1. Inicia la aplicación -2. Menú Motor → Iniciar -3. Minimiza (se va a la bandeja del sistema) -4. La app continuará monitoreando en segundo plano +1. Inicia la aplicación (el motor arranca solo si `CLOUDRESTORE_AUTO_START=true`) +2. Cierra la ventana con **X** — se minimiza a la bandeja del sistema +3. La app continuará monitoreando en segundo plano +4. Para salir por completo: bandeja → **Salir** o menú **Archivo → Salir** --- diff --git a/README.md b/README.md index 0bbb864..9c9bf1b 100644 --- a/README.md +++ b/README.md @@ -73,19 +73,28 @@ CloudRestoreAS es una aplicación Windows de escritorio desarrollada en Python 3 python runner.py ``` -### Opción 2: Ejecutable Empaquetado +### Opción 2: Ejecutable portable (recomendado para producción) -1. **Generar el ejecutable** - ```powershell - .\build.ps1 - ``` - - Esto creará `CloudRestoreAS.exe` en la carpeta `dist\` +**Windows** — generar: +```powershell +.\build.ps1 +``` +Salida: `dist\CloudRestoreAS.exe` -2. **Distribuir** - - Copiar `CloudRestoreAS.exe` a la ubicación deseada - - Crear carpetas `data` y `logs` en el mismo directorio - - El ejecutable es portable (no requiere instalación) +**Linux** — generar: +```bash +./build.sh +``` +Salida: `dist/CloudRestoreAS` + +**Desplegar en cada equipo** (sin Python, sin instalador): +1. Copiar **solo** el ejecutable a una carpeta. +2. Ejecutarlo (doble clic o `./CloudRestoreAS`). +3. Se crean automáticamente: `config/`, `Entrada/`, `Procesados/`, `Fallados/`, `Temp/`. +4. Editar `config/.env` (URL, token e instancia del panel). +5. Reiniciar la aplicación. + +Ver `packaging/LEEME.txt` para instrucciones resumidas. ## ⚙️ Configuración Inicial diff --git a/app/config/__init__.py b/app/config/__init__.py new file mode 100644 index 0000000..c1e8c59 --- /dev/null +++ b/app/config/__init__.py @@ -0,0 +1,36 @@ +"""Configuración de entorno, bootstrap y arranque.""" + +from .bootstrap import ensure_runtime_layout +from .env_loader import ( + apply_env_overrides, + get_launch_options, + is_panel_configured, + load_env_file, +) +from .odbc_setup import configure_odbc_environment +from .autostart import register_autostart_if_requested + + +def initialize_runtime() -> None: + """Prepara directorios, ODBC y auto-arranque (llamar antes de importar pyodbc).""" + ensure_runtime_layout() + configure_odbc_environment() + + +def initialize_env() -> None: + """Post-bootstrap: carga .env y registra auto-arranque si aplica.""" + load_env_file() + register_autostart_if_requested() + + +__all__ = [ + "initialize_runtime", + "initialize_env", + "ensure_runtime_layout", + "load_env_file", + "apply_env_overrides", + "get_launch_options", + "is_panel_configured", + "configure_odbc_environment", + "register_autostart_if_requested", +] diff --git a/app/config/autostart.py b/app/config/autostart.py new file mode 100644 index 0000000..b342068 --- /dev/null +++ b/app/config/autostart.py @@ -0,0 +1,88 @@ +"""Registro de arranque automático al login (Windows / Linux).""" + +import os +import subprocess +import sys +from pathlib import Path + +from ..constants import APP_DIR, CONFIG_DIR, IS_WINDOWS +from .env_loader import _env_bool, load_env_file + + +def _marker_path() -> Path: + return CONFIG_DIR / ".autostart_registered" + + +def _executable_command() -> tuple[str, list[str]]: + if getattr(sys, "frozen", False): + exe = Path(sys.executable).resolve() + return str(exe), ["--start-engine"] + runner = (APP_DIR / "runner.py").resolve() + return sys.executable, [str(runner), "--start-engine"] + + +def _register_windows(exe: str, args: list[str]) -> None: + task_name = "CloudRestoreAS" + arg_str = " ".join(f'"{a}"' if " " in a else a for a in args) + tr = f'"{exe}" {arg_str}' + check = subprocess.run( + ["schtasks", "/Query", "/TN", task_name], + capture_output=True, + creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0, + ) + if check.returncode == 0: + return + subprocess.run( + [ + "schtasks", + "/Create", + "/TN", + task_name, + "/TR", + tr, + "/SC", + "ONLOGON", + "/RL", + "LIMITED", + "/F", + ], + check=False, + creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0, + ) + + +def _register_linux(exe: str, args: list[str]) -> None: + autostart_dir = Path.home() / ".config" / "autostart" + autostart_dir.mkdir(parents=True, exist_ok=True) + desktop = autostart_dir / "cloudrestoreas.desktop" + if desktop.exists(): + return + exec_line = " ".join([exe, *args]) + content = f"""[Desktop Entry] +Type=Application +Name=CloudRestoreAS +Comment=Restauración automática SQL Server +Exec={exec_line} +Path={APP_DIR} +Terminal=false +X-GNOME-Autostart-enabled=true +""" + desktop.write_text(content, encoding="utf-8") + + +def register_autostart_if_requested() -> None: + load_env_file() + if not _env_bool("CLOUDRESTORE_REGISTER_AUTOSTART", True): + return + if _marker_path().exists(): + return + + exe, args = _executable_command() + try: + if IS_WINDOWS: + _register_windows(exe, args) + else: + _register_linux(exe, args) + _marker_path().write_text("ok", encoding="utf-8") + except OSError: + pass diff --git a/app/config/bootstrap.py b/app/config/bootstrap.py new file mode 100644 index 0000000..c2e9e1d --- /dev/null +++ b/app/config/bootstrap.py @@ -0,0 +1,83 @@ +"""Creación automática de config/ y carpetas de trabajo.""" + +import shutil +import sys +from pathlib import Path + +from ..constants import ( + APP_DIR, + BUNDLE_DIR, + BUNDLED_SOURCE_7ZIP, + BUNDLED_SOURCE_ODBC, + CONFIG_DIR, + DATA_DIR, + DIR_ENTRADA, + DIR_FALLADOS, + DIR_PROCESADOS, + DIR_TEMP, + ENV_PATH, + LOGS_DIR, + ODBC_DIR, + SEVEN_ZIP_DIR, +) +from ..db.database import DatabaseManager +from .env_loader import render_env_template + + +def _copy_tree_if_missing(src: Path, dest: Path) -> None: + if not src.is_dir() or dest.exists(): + return + dest.parent.mkdir(parents=True, exist_ok=True) + shutil.copytree(src, dest) + + +def _write_env_if_missing() -> None: + if ENV_PATH.exists(): + return + template_path = BUNDLE_DIR / "packaging" / "templates" / "env.default" + if not template_path.is_file(): + template_path = APP_DIR / "packaging" / "templates" / "env.default" + if template_path.is_file(): + content = template_path.read_text(encoding="utf-8") + content = content.replace("{APP_DIR}", str(APP_DIR)) + else: + content = render_env_template(APP_DIR) + ENV_PATH.write_text(content, encoding="utf-8") + try: + ENV_PATH.chmod(0o600) + except OSError: + pass + + +def ensure_runtime_layout() -> None: + """ + Idempotente: crea config/, carpetas de trabajo y .env si no existen. + No sobrescribe .env ni app.db existentes. + """ + for directory in ( + CONFIG_DIR, + DATA_DIR, + LOGS_DIR, + ODBC_DIR, + SEVEN_ZIP_DIR, + DIR_ENTRADA, + DIR_PROCESADOS, + DIR_FALLADOS, + DIR_TEMP, + ): + directory.mkdir(parents=True, exist_ok=True) + + _write_env_if_missing() + + _copy_tree_if_missing(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR) + _copy_tree_if_missing(BUNDLED_SOURCE_ODBC, ODBC_DIR) + + from ..constants import DB_PATH + + if not DB_PATH.exists(): + DatabaseManager(DB_PATH) + + if getattr(sys, "frozen", False): + marker = CONFIG_DIR / ".bootstrap_ok" + if not marker.exists(): + marker.write_text("ok", encoding="utf-8") diff --git a/app/config/env_loader.py b/app/config/env_loader.py new file mode 100644 index 0000000..e454feb --- /dev/null +++ b/app/config/env_loader.py @@ -0,0 +1,138 @@ +"""Carga de variables desde config/.env.""" + +import os +from dataclasses import dataclass +from pathlib import Path + +from dotenv import load_dotenv + +from ..constants import ( + APP_DIR, + DIR_ENTRADA, + DIR_FALLADOS, + DIR_PROCESADOS, + DIR_TEMP, + ENV_PATH, + default_seven_zip_path, +) + + +def _env_bool(name: str, default: bool = False) -> bool: + raw = os.getenv(name, "").strip().lower() + if not raw: + return default + return raw in ("1", "true", "yes", "on") + + +def load_env_file() -> bool: + """Carga config/.env si existe.""" + if ENV_PATH.is_file(): + load_dotenv(ENV_PATH, override=True) + return True + return False + + +def is_panel_configured() -> bool: + """True si URL, token e instancia del panel están definidos.""" + url = os.getenv("CLOUDRESTORE_PANEL_API_URL", "").strip() + token = os.getenv("CLOUDRESTORE_PANEL_API_TOKEN", "").strip() + instance = os.getenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", "").strip() + return bool(url and token and instance) + + +@dataclass +class LaunchOptions: + minimized: bool + start_engine: bool + register_autostart: bool + + +def get_launch_options() -> LaunchOptions: + load_env_file() + return LaunchOptions( + minimized=_env_bool("CLOUDRESTORE_START_MINIMIZED", False), + start_engine=_env_bool("CLOUDRESTORE_AUTO_START", True), + register_autostart=_env_bool("CLOUDRESTORE_REGISTER_AUTOSTART", True), + ) + + +def _path_from_env(name: str, default: Path) -> str: + value = os.getenv(name, "").strip() + return value if value else str(default) + + +def apply_env_overrides(config: dict) -> dict: + """Aplica variables de entorno sobre la configuración en memoria.""" + load_env_file() + + paths = config.setdefault("paths", {}) + paths["input_folder"] = _path_from_env("CLOUDRESTORE_INPUT_FOLDER", DIR_ENTRADA) + paths["processed_folder"] = _path_from_env( + "CLOUDRESTORE_PROCESSED_FOLDER", DIR_PROCESADOS + ) + paths["failed_folder"] = _path_from_env("CLOUDRESTORE_FAILED_FOLDER", DIR_FALLADOS) + paths["extract_folder"] = _path_from_env("CLOUDRESTORE_EXTRACT_FOLDER", DIR_TEMP) + data_sql = os.getenv("CLOUDRESTORE_DATA_SQL_FOLDER", "").strip() + if data_sql: + paths["data_sql_folder"] = data_sql + + seven_zip = os.getenv("CLOUDRESTORE_SEVEN_ZIP_EXE", "").strip() + paths["seven_zip_exe"] = seven_zip or default_seven_zip_path() + + panel = config.setdefault("panel", {}) + api_url = os.getenv("CLOUDRESTORE_PANEL_API_URL", "").strip() + if api_url: + panel["api_url"] = api_url + api_token = os.getenv("CLOUDRESTORE_PANEL_API_TOKEN", "").strip() + if api_token: + panel["api_token"] = api_token + instance = os.getenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", "").strip() + if instance: + panel["instance_key"] = instance + if os.getenv("CLOUDRESTORE_PANEL_VERIFY_SSL"): + panel["verify_ssl"] = _env_bool("CLOUDRESTORE_PANEL_VERIFY_SSL", False) + + sql = config.setdefault("sql", {}) + server = os.getenv("CLOUDRESTORE_SQL_SERVER", "").strip() + if server: + sql["server"] = server + if os.getenv("CLOUDRESTORE_SQL_USERNAME"): + sql["username"] = os.getenv("CLOUDRESTORE_SQL_USERNAME", "").strip() + if os.getenv("CLOUDRESTORE_SQL_USE_WINDOWS_AUTH"): + sql["use_windows_auth"] = _env_bool("CLOUDRESTORE_SQL_USE_WINDOWS_AUTH", False) + + return config + + +def render_env_template(app_dir: Path | None = None) -> str: + """Genera contenido inicial de config/.env.""" + base = (app_dir or APP_DIR).resolve() + sep = "\\" if os.name == "nt" else "/" + + def p(*parts: str) -> str: + return sep.join([str(base), *parts]) + + return f"""# CloudRestoreAS — configuración local (editar y reiniciar la app) + +# Arranca el motor al abrir (la ventana siempre se muestra salvo START_MINIMIZED=true) +CLOUDRESTORE_AUTO_START=true +CLOUDRESTORE_START_MINIMIZED=false +CLOUDRESTORE_REGISTER_AUTOSTART=true + +# Servicio PANEL_BASES_ANEXO24 (enrutamiento automático de nodos) +CLOUDRESTORE_PANEL_API_URL= +CLOUDRESTORE_PANEL_API_TOKEN= +CLOUDRESTORE_PANEL_INSTANCE_KEY= +CLOUDRESTORE_PANEL_VERIFY_SSL=false + +# Carpetas de trabajo +CLOUDRESTORE_INPUT_FOLDER={p("Entrada")} +CLOUDRESTORE_PROCESSED_FOLDER={p("Procesados")} +CLOUDRESTORE_FAILED_FOLDER={p("Fallados")} +CLOUDRESTORE_EXTRACT_FOLDER={p("Temp")} + +# SQL local (solo si no usa panel para credenciales) +# CLOUDRESTORE_SQL_SERVER=localhost +# CLOUDRESTORE_SQL_USERNAME= +# CLOUDRESTORE_DATA_SQL_FOLDER= +""" diff --git a/app/config/odbc_setup.py b/app/config/odbc_setup.py new file mode 100644 index 0000000..55b702e --- /dev/null +++ b/app/config/odbc_setup.py @@ -0,0 +1,71 @@ +"""Configura ODBC portable desde config/odbc/.""" + +import os +import sys +from pathlib import Path + +from ..constants import IS_WINDOWS, ODBC_DIR +from ..utils.logger import app_logger + + +def _write_portable_odbc_ini(odbc_dir: Path) -> None: + """Genera odbcinst.ini mínimo si no existe.""" + inst = odbc_dir / "odbcinst.ini" + if inst.exists(): + return + + if IS_WINDOWS: + driver_path = odbc_dir / "msodbcsql18.dll" + if not driver_path.exists(): + for candidate in odbc_dir.glob("msodbcsql*.dll"): + driver_path = candidate + break + driver_line = str(driver_path.resolve()) if driver_path.exists() else "msodbcsql18.dll" + content = f"""[ODBC Driver 18 for SQL Server] +Description=Microsoft ODBC Driver 18 for SQL Server +Driver={driver_line} +UsageCount=1 +""" + else: + so_path = None + for pattern in ("libmsodbcsql-18*.so*", "msodbcsql-18*.so*"): + matches = list(odbc_dir.glob(pattern)) + if matches: + so_path = matches[0] + break + driver_line = str(so_path.resolve()) if so_path else "libmsodbcsql-18.so" + content = f"""[ODBC Driver 18 for SQL Server] +Description=Microsoft ODBC Driver 18 for SQL Server +Driver={driver_line} +UsageCount=1 +""" + inst.write_text(content, encoding="utf-8") + + ini = odbc_dir / "odbc.ini" + if not ini.exists(): + ini.write_text("[ODBC Data Sources]\n", encoding="utf-8") + + +def configure_odbc_environment() -> None: + """Apunta pyodbc al driver embebido en config/odbc si está disponible.""" + if not ODBC_DIR.is_dir(): + return + + lib_dir = ODBC_DIR / "lib" + if lib_dir.is_dir(): + existing = os.environ.get("LD_LIBRARY_PATH", "") + lib_path = str(lib_dir.resolve()) + if lib_path not in existing.split(":"): + os.environ["LD_LIBRARY_PATH"] = ( + f"{lib_path}:{existing}" if existing else lib_path + ) + + _write_portable_odbc_ini(ODBC_DIR) + odbc_sys = str(ODBC_DIR.resolve()) + os.environ["ODBCSYSINI"] = odbc_sys + os.environ["ODBCINI"] = str((ODBC_DIR / "odbc.ini").resolve()) + + if IS_WINDOWS: + os.environ["PATH"] = odbc_sys + os.pathsep + os.environ.get("PATH", "") + + app_logger.debug(f"ODBC configurado: ODBCSYSINI={odbc_sys}") diff --git a/app/constants.py b/app/constants.py index ab05e33..5f87fa1 100644 --- a/app/constants.py +++ b/app/constants.py @@ -1,13 +1,51 @@ """Constantes globales de la aplicación.""" +import sys from pathlib import Path -# Rutas base -APP_DIR = Path(__file__).parent.parent -DATA_DIR = APP_DIR / "data" -LOGS_DIR = APP_DIR / "logs" -# Base de datos +def _resolve_app_dir() -> Path: + """Directorio donde vive el ejecutable (persistente).""" + if getattr(sys, "frozen", False): + return Path(sys.executable).resolve().parent + return Path(__file__).resolve().parent.parent + + +def _resolve_bundle_dir() -> Path: + """Recursos embebidos en el binario PyInstaller onefile.""" + if getattr(sys, "frozen", False): + return Path(sys._MEIPASS) + platform = "windows" if sys.platform == "win32" else "linux" + return _resolve_app_dir() / "packaging" / "bundled" / platform + + +APP_DIR = _resolve_app_dir() +CONFIG_DIR = APP_DIR / "config" +ENV_PATH = CONFIG_DIR / ".env" +DATA_DIR = CONFIG_DIR / "data" +LOGS_DIR = CONFIG_DIR / "logs" +ODBC_DIR = CONFIG_DIR / "odbc" +SEVEN_ZIP_DIR = CONFIG_DIR / "7zip" + +IS_WINDOWS = sys.platform == "win32" +BUNDLE_DIR = _resolve_bundle_dir() +BUNDLED_SOURCE_7ZIP = BUNDLE_DIR / "bundled" / "7zip" +BUNDLED_SOURCE_ODBC = BUNDLE_DIR / "bundled" / "odbc" +def _default_7zip_name() -> str: + if IS_WINDOWS: + return "7z.exe" + return "7zz" + + +BUNDLED_7ZIP_EXE = SEVEN_ZIP_DIR / _default_7zip_name() +BUNDLED_7ZIP_FALLBACK = SEVEN_ZIP_DIR / ("7za.exe" if IS_WINDOWS else "7zz") + +# Carpetas de trabajo junto al ejecutable +DIR_ENTRADA = APP_DIR / "Entrada" +DIR_PROCESADOS = APP_DIR / "Procesados" +DIR_FALLADOS = APP_DIR / "Fallados" +DIR_TEMP = APP_DIR / "Temp" + DB_PATH = DATA_DIR / "app.db" # Estados de jobs @@ -21,7 +59,7 @@ class JobStatus: FAILED_RESTART = "failed_restart" CANCELLED = "cancelled" -# Niveles de log + class LogLevel: DEBUG = "DEBUG" INFO = "INFO" @@ -29,7 +67,7 @@ class LogLevel: ERROR = "ERROR" CRITICAL = "CRITICAL" -# Tipos de paso de job + class StepType: STABILITY_CHECK = "stability_check" NODE_MAPPING = "node_mapping" @@ -39,57 +77,63 @@ class StepType: FILELIST = "filelist" RESTORE = "restore" CLEANUP = "cleanup" - SFTP_COPY = "sftp_copy" # Transferencia del .bak al servidor SQL externo (SFTP/SSH) - FORWARD_ZIP = "forward_zip" # Reenvío del ZIP al input_folder del servidor destino + SFTP_COPY = "sftp_copy" + FORWARD_ZIP = "forward_zip" -# Configuración por defecto -DEFAULT_CONFIG = { - "paths": { - "input_folder": "", - "processed_folder": "", - "failed_folder": "", - "extract_folder": "", + +def default_seven_zip_path() -> str: + for candidate in (BUNDLED_7ZIP_EXE, BUNDLED_7ZIP_FALLBACK): + if candidate.exists(): + return str(candidate) + return "" + + +def default_work_paths() -> dict[str, str]: + return { + "input_folder": str(DIR_ENTRADA), + "processed_folder": str(DIR_PROCESADOS), + "failed_folder": str(DIR_FALLADOS), + "extract_folder": str(DIR_TEMP), "data_sql_folder": "", - "seven_zip_exe": "" - }, + "seven_zip_exe": default_seven_zip_path(), + } + + +DEFAULT_CONFIG = { + "paths": default_work_paths(), "sql": { "server": "localhost", - "use_windows_auth": True, + "use_windows_auth": IS_WINDOWS, "username": "", - "password_encrypted": "" + "password_encrypted": "", }, "concurrency": { "extract_workers": 1, - "restore_workers": 1 + "restore_workers": 1, }, "stability": { "check_enabled": True, "check_interval_seconds": 5, "stable_duration_seconds": 10, - "use_ready_marker": False + "use_ready_marker": False, }, "timeouts": { "extract_minutes": 30, - "restore_minutes": 60 + "restore_minutes": 60, }, "retries": { "max_attempts": 3, - "retry_delay_seconds": 60 + "retry_delay_seconds": 60, }, "features": { "dry_run_mode": False, "auto_scan_enabled": True, - "scan_interval_seconds": 30 + "scan_interval_seconds": 30, }, - # Integración con PANEL_BASES_ANEXO24. Si api_url está vacío, CloudRestoreAS - # opera en modo local usando la sección "sql" (retrocompatibilidad). "panel": { "api_url": "", "api_token": "", - # Nombre del restore_target en el panel (identidad de este CRA). - # Con panel configurado: enrutamiento automático restore_local | forward. "instance_key": "", - # False en dev: el panel local usa HTTPS con certificado propio. "verify_ssl": False, - } + }, } diff --git a/app/engine/engine.py b/app/engine/engine.py index d3c7479..ba656e4 100644 --- a/app/engine/engine.py +++ b/app/engine/engine.py @@ -12,6 +12,7 @@ from .. import __version__ from ..db.job_repository import JobRepository from ..db.event_repository import EventRepository from ..db.config_repository import ConfigRepository +from ..config.env_loader import apply_env_overrides, load_env_file from ..constants import JobStatus, DEFAULT_CONFIG from ..panel import panel_client from ..utils.logger import app_logger @@ -50,14 +51,15 @@ class RestoreEngine(QObject): app_logger.info("RestoreEngine inicializado") def _load_config(self) -> dict: - """Carga la configuración desde la base de datos.""" + """Carga la configuración desde la base de datos y variables de entorno.""" + load_env_file() config = ConfigRepository.get("app_config", DEFAULT_CONFIG.copy()) - - # Asegurar que tiene todas las claves + for key, value in DEFAULT_CONFIG.items(): if key not in config: config[key] = value - + + config = apply_env_overrides(config) self.signals.config_loaded.emit(config) return config diff --git a/app/engine/restore_worker.py b/app/engine/restore_worker.py index b705805..71daeae 100644 --- a/app/engine/restore_worker.py +++ b/app/engine/restore_worker.py @@ -335,11 +335,17 @@ class RestoreWorker(QRunnable): stderr=stderr[:1000] if stderr else None ) - # Buscar archivo .bak + # Buscar archivo .bak. El backup puede venir con terminaciones extra + # (p.ej. ".KNOWNWORLD") tanto en el ZIP como en el propio .bak; se + # normaliza a ".bak". El nodo es siempre el primer segmento del + # nombre del archivo de origen. locate_step_id = JobStepRepository.create(self.job_id, StepType.LOCATE_BAK) - - self._bak_path = SevenZipExtractor.find_bak_file(self._extract_dir) - + + node_base = Path(job.source_name).name.split(".")[0].strip() or None + self._bak_path = SevenZipExtractor.find_bak_file( + self._extract_dir, node_name=node_base + ) + if not self._bak_path: raise FileNotFoundError("No se encontró archivo .bak en el archivo extraído") diff --git a/app/extract/seven_zip.py b/app/extract/seven_zip.py index 0d1db2c..29a6ead 100644 --- a/app/extract/seven_zip.py +++ b/app/extract/seven_zip.py @@ -1,9 +1,13 @@ """Gestión de extracción de archivos con 7-Zip.""" +import shutil import subprocess +import sys from pathlib import Path from typing import Optional, Tuple import time + +from ..constants import BUNDLED_7ZIP_EXE, BUNDLED_7ZIP_FALLBACK, IS_WINDOWS from ..utils.logger import app_logger @@ -25,19 +29,30 @@ class SevenZipExtractor: @staticmethod def _auto_detect_7zip() -> Optional[str]: - """Auto-detecta la ubicación de 7z.exe.""" - possible_paths = [ - r"C:\Program Files\7-Zip\7z.exe", - r"D:\Program Files\7-Zip\7z.exe", - r"C:\Program Files (x86)\7-Zip\7z.exe", - r"D:\Program Files (x86)\7-Zip\7z.exe" - ] - - for path in possible_paths: - if Path(path).exists(): - app_logger.info(f"7-Zip auto-detectado en: {path}") - return path - + """Prioriza 7-Zip embebido; en desarrollo busca en el sistema.""" + for candidate in (BUNDLED_7ZIP_EXE, BUNDLED_7ZIP_FALLBACK): + if candidate.exists(): + app_logger.info(f"7-Zip embebido: {candidate}") + return str(candidate) + + if not getattr(sys, "frozen", False): + if IS_WINDOWS: + possible_paths = [ + r"C:\Program Files\7-Zip\7z.exe", + r"C:\Program Files (x86)\7-Zip\7z.exe", + ] + else: + possible_paths = [] + for name in ("7z", "7zz", "7za"): + found = shutil.which(name) + if found: + possible_paths.append(found) + + for path in possible_paths: + if Path(path).exists(): + app_logger.info(f"7-Zip detectado en: {path}") + return path + return None def extract( @@ -134,35 +149,78 @@ class SevenZipExtractor: return result.returncode, result.stdout, result.stderr @staticmethod - def find_bak_file(extract_dir: str) -> Optional[str]: + def find_bak_file(extract_dir: str, node_name: Optional[str] = None) -> Optional[str]: """ - Busca el archivo .bak extraído en el directorio. - + Busca el backup extraído y, si se indica node_name, lo normaliza a + '.bak'. + + Tolera terminaciones extra en el nombre (el backup puede venir como + 'nodo.KNOWNWORLD.bak', 'nodo.bak.unknownworld' o incluso sin extensión + '.bak'). Estrategia de búsqueda en orden: + 1. archivos que terminan exactamente en '.bak' + 2. si no hay, archivos cuyo nombre contiene '.bak' en cualquier parte + 3. si no hay, el archivo más grande de la extracción (el backup suele serlo) + Args: extract_dir: Directorio donde se extrajo - + node_name: Nombre del nodo; si se indica, el backup se renombra a + '.bak' + Returns: - Ruta al archivo .bak o None si no se encuentra - - Raises: - ValueError: Si hay múltiples archivos .bak + Ruta al archivo .bak (ya renombrado si node_name) o None si no hay archivos. """ extract_path = Path(extract_dir) - bak_files = list(extract_path.rglob("*.bak")) - - if not bak_files: - app_logger.error(f"No se encontró archivo .bak en {extract_dir}") + all_files = [p for p in extract_path.rglob("*") if p.is_file()] + + if not all_files: + app_logger.error(f"No se encontró ningún archivo en {extract_dir}") return None - - if len(bak_files) > 1: - # Si hay múltiples, tomar el más reciente + + bak_files = [p for p in all_files if p.suffix.lower() == ".bak"] + if not bak_files: + bak_files = [p for p in all_files if ".bak" in p.name.lower()] + if not bak_files: + largest = max(all_files, key=lambda p: p.stat().st_size) app_logger.warning( - f"Múltiples archivos .bak encontrados ({len(bak_files)}), " - "seleccionando el más reciente" + "No se encontró archivo con extensión .bak; usando el archivo más " + f"grande de la extracción: {largest.name}" ) - bak_files.sort(key=lambda p: p.stat().st_mtime, reverse=True) - - bak_path = str(bak_files[0]) + bak_files = [largest] + + if len(bak_files) > 1: + # Preferir el que empiece con el nombre del nodo; si no, el más reciente. + if node_name: + prefixed = [ + p for p in bak_files + if p.name.lower().startswith(node_name.lower()) + ] + if prefixed: + bak_files = prefixed + if len(bak_files) > 1: + app_logger.warning( + f"Múltiples backups encontrados ({len(bak_files)}), " + "seleccionando el más reciente" + ) + bak_files.sort(key=lambda p: p.stat().st_mtime, reverse=True) + + bak_file = bak_files[0] + + # Normalizar el nombre a '.bak'. + if node_name: + target = bak_file.parent / f"{node_name}.bak" + if bak_file.name != target.name: + try: + if target.exists() and not bak_file.samefile(target): + target.unlink() + bak_file = bak_file.rename(target) + app_logger.info(f"Backup normalizado a: {bak_file.name}") + except OSError as e: + # No es fatal para el RESTORE: se continúa con el nombre original. + app_logger.error( + f"No se pudo renombrar el backup a '{target.name}': {e}" + ) + + bak_path = str(bak_file) app_logger.info(f"Archivo .bak encontrado: {bak_path}") return bak_path diff --git a/app/sql/sql_manager.py b/app/sql/sql_manager.py index baaf86f..5d4bb5d 100644 --- a/app/sql/sql_manager.py +++ b/app/sql/sql_manager.py @@ -4,8 +4,28 @@ import pyodbc from typing import Optional, Tuple, List import time from dataclasses import dataclass + from ..utils.logger import app_logger +_ODBC_DRIVER_CANDIDATES = ( + "ODBC Driver 18 for SQL Server", + "ODBC Driver 17 for SQL Server", + "ODBC Driver 13 for SQL Server", + "FreeTDS", +) + + +def resolve_odbc_driver() -> str: + """Devuelve el primer driver ODBC de SQL Server disponible.""" + installed = set(pyodbc.drivers()) + for name in _ODBC_DRIVER_CANDIDATES: + if name in installed: + return name + raise RuntimeError( + "No se encontró driver ODBC para SQL Server. " + f"Instalados: {', '.join(pyodbc.drivers()) or 'ninguno'}" + ) + @dataclass class LogicalFile: @@ -50,10 +70,13 @@ class SQLServerManager: Returns: Cadena de conexión ODBC """ + driver = resolve_odbc_driver() parts = [ - f"DRIVER={{ODBC Driver 17 for SQL Server}}", - f"SERVER={self.server}" + f"DRIVER={{{driver}}}", + f"SERVER={self.server}", ] + if "18" in driver: + parts.append("TrustServerCertificate=yes") if database: parts.append(f"DATABASE={database}") diff --git a/app/ui/config_tab.py b/app/ui/config_tab.py index ae39644..b44c8e8 100644 --- a/app/ui/config_tab.py +++ b/app/ui/config_tab.py @@ -1,5 +1,7 @@ """Tab de configuración.""" +import sys + from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout, QLineEdit, QPushButton, QSpinBox, @@ -104,6 +106,12 @@ class ConfigTab(QWidget): self.sql_windows_auth_checkbox.setChecked(True) self.sql_windows_auth_checkbox.toggled.connect(self._on_auth_changed) sql_layout.addRow("Usar Windows Auth:", self.sql_windows_auth_checkbox) + if sys.platform != "win32": + self.sql_windows_auth_checkbox.setChecked(False) + self.sql_windows_auth_checkbox.setEnabled(False) + self.sql_windows_auth_checkbox.setToolTip( + "Windows Auth no está disponible en Linux; use SQL Auth." + ) self.sql_username_input = QLineEdit() self.sql_username_input.setEnabled(False) diff --git a/app/ui/dashboard_tab.py b/app/ui/dashboard_tab.py index db48271..a7a326d 100644 --- a/app/ui/dashboard_tab.py +++ b/app/ui/dashboard_tab.py @@ -32,6 +32,14 @@ class DashboardTab(QWidget): title_font.setBold(True) title.setFont(title_font) layout.addWidget(title) + + self.panel_warning_label = QLabel("") + self.panel_warning_label.setWordWrap(True) + self.panel_warning_label.setStyleSheet( + "background-color: #fff3cd; color: #856404; padding: 8px; border-radius: 4px;" + ) + self.panel_warning_label.hide() + layout.addWidget(self.panel_warning_label) # Grid de estadísticas stats_group = QGroupBox("Estadísticas de Jobs") @@ -94,17 +102,29 @@ class DashboardTab(QWidget): times_group.setLayout(times_layout) layout.addWidget(times_group) + self.engine_status_badge = QLabel("Detenido") + badge_font = QFont() + badge_font.setPointSize(12) + badge_font.setBold(True) + self.engine_status_badge.setFont(badge_font) + self.engine_status_badge.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.engine_status_badge.setStyleSheet( + "background-color: #e9ecef; color: #495057; padding: 6px 14px; " + "border-radius: 4px;" + ) + layout.addWidget(self.engine_status_badge) + # Estado del motor motor_group = QGroupBox("Estado del Motor") motor_layout = QHBoxLayout() - + self.motor_status_label = QLabel("Detenido") motor_status_font = QFont() motor_status_font.setPointSize(14) motor_status_font.setBold(True) self.motor_status_label.setFont(motor_status_font) self.motor_status_label.setStyleSheet("color: red;") - + motor_layout.addWidget(QLabel("Estado:")) motor_layout.addWidget(self.motor_status_label) motor_layout.addStretch() @@ -136,6 +156,7 @@ class DashboardTab(QWidget): def _load_initial_stats(self): """Carga las estadísticas iniciales.""" + self.update_motor_status() stats = self.engine.get_stats() self.update_stats(stats) self._refresh_recent_jobs() @@ -193,3 +214,40 @@ class DashboardTab(QWidget): self.recent_table.setItem(i, 4, status_item) self.recent_table.resizeColumnsToContents() + + def update_motor_status(self) -> None: + """Actualiza badge y etiqueta según el estado actual del motor.""" + if not self.engine.is_running(): + text = "Detenido" + badge_style = ( + "background-color: #e9ecef; color: #495057; padding: 6px 14px; " + "border-radius: 4px;" + ) + label_style = "color: red;" + elif self.engine.is_paused(): + text = "Pausado" + badge_style = ( + "background-color: #fff3cd; color: #856404; padding: 6px 14px; " + "border-radius: 4px;" + ) + label_style = "color: orange;" + else: + text = "Activo" + badge_style = ( + "background-color: #d4edda; color: #155724; padding: 6px 14px; " + "border-radius: 4px;" + ) + label_style = "color: green;" + + self.engine_status_badge.setText(f"Motor: {text}") + self.engine_status_badge.setStyleSheet(badge_style) + self.motor_status_label.setText(text) + self.motor_status_label.setStyleSheet(label_style) + + def set_panel_warning(self, message: str) -> None: + """Muestra aviso cuando falta configuración del servicio de bases en config/.env.""" + if message: + self.panel_warning_label.setText(message) + self.panel_warning_label.show() + else: + self.panel_warning_label.hide() diff --git a/app/ui/main_window.py b/app/ui/main_window.py index 9dff81a..d5db679 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -2,181 +2,207 @@ from PySide6.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QTabWidget, - QSystemTrayIcon, QMenu + QSystemTrayIcon, QMenu, QMessageBox, QApplication, ) from PySide6.QtCore import Qt, QTimer, Signal -from PySide6.QtGui import QIcon, QCloseEvent, QAction +from PySide6.QtGui import QCloseEvent, QAction from .dashboard_tab import DashboardTab from .jobs_tab import JobsTab from .nodes_tab import NodesTab from .config_tab import ConfigTab from .logs_tab import LogsTab +from .tray_assets import load_tray_icon from ..engine.engine import RestoreEngine from ..utils.logger import app_logger class MainWindow(QMainWindow): """Ventana principal de la aplicación.""" - - # Señal para minimizar a tray + minimize_to_tray = Signal() - - def __init__(self): + + def __init__( + self, + minimized: bool = False, + start_engine: bool = False, + panel_configured: bool = True, + ): """Inicializa la ventana principal.""" super().__init__() - - # Motor de restauración + + self._start_on_load = start_engine + self._minimized_on_load = minimized + self._panel_configured = panel_configured + self._force_quit = False + self._tray_hint_shown = False + self.engine = RestoreEngine() - - # Configurar ventana + self.setWindowTitle("CloudRestoreAS - Restauración Automática SQL Server") self.setMinimumSize(1200, 800) - - # System tray - self.tray_icon: QSystemTrayIcon = None + + self.tray_icon: QSystemTrayIcon | None = None self._setup_tray() - - # UI + self._setup_ui() - - # Conectar señales del motor self._connect_engine_signals() - - # Timer para actualizar stats + self.stats_timer = QTimer() self.stats_timer.timeout.connect(self._update_stats) - self.stats_timer.start(5000) # Cada 5 segundos - + self.stats_timer.start(5000) + + if not self._panel_configured: + self.dashboard_tab.set_panel_warning( + "Configure la conexión al servicio de bases en config/.env " + "(CLOUDRESTORE_PANEL_API_URL, TOKEN e INSTANCE_KEY)." + ) + + if self._start_on_load: + QTimer.singleShot(500, self._start_engine) + + if self._minimized_on_load: + QTimer.singleShot(100, self.hide) + + self._update_tray_status() + self.dashboard_tab.update_motor_status() app_logger.info("Ventana principal inicializada") - + def _setup_ui(self): """Configura la interfaz de usuario.""" - # Widget central central_widget = QWidget() self.setCentralWidget(central_widget) - - # Layout principal + layout = QVBoxLayout(central_widget) layout.setContentsMargins(10, 10, 10, 10) - - # Tabs + self.tabs = QTabWidget() - - # Crear tabs + self.dashboard_tab = DashboardTab(self.engine) self.jobs_tab = JobsTab(self.engine) self.nodes_tab = NodesTab(self.engine) self.config_tab = ConfigTab(self.engine) self.logs_tab = LogsTab(self.engine) - - # Agregar tabs + self.tabs.addTab(self.dashboard_tab, "📊 Dashboard") self.tabs.addTab(self.jobs_tab, "📋 Jobs") self.tabs.addTab(self.nodes_tab, "🔗 Nodos") self.tabs.addTab(self.config_tab, "⚙️ Configuración") self.tabs.addTab(self.logs_tab, "📝 Logs") - + layout.addWidget(self.tabs) - - # Menu bar self._setup_menu() - + def _setup_menu(self): """Configura el menú.""" menubar = self.menuBar() - - # Menú Motor + + file_menu = menubar.addMenu("Archivo") + + quit_action = QAction("Salir", self) + quit_action.triggered.connect(self._quit_application) + file_menu.addAction(quit_action) + motor_menu = menubar.addMenu("Motor") - + self.start_action = QAction("▶️ Iniciar", self) self.start_action.triggered.connect(self._start_engine) motor_menu.addAction(self.start_action) - + self.pause_action = QAction("⏸️ Pausar", self) self.pause_action.triggered.connect(self._pause_engine) self.pause_action.setEnabled(False) motor_menu.addAction(self.pause_action) - + self.resume_action = QAction("▶️ Continuar", self) self.resume_action.triggered.connect(self._resume_engine) self.resume_action.setEnabled(False) motor_menu.addAction(self.resume_action) - + self.stop_action = QAction("⏹️ Detener", self) self.stop_action.triggered.connect(self._stop_engine) self.stop_action.setEnabled(False) motor_menu.addAction(self.stop_action) - + motor_menu.addSeparator() - + self.scan_action = QAction("🔍 Escanear Ahora", self) self.scan_action.triggered.connect(self._scan_now) motor_menu.addAction(self.scan_action) - - # Menú Ayuda + help_menu = menubar.addMenu("Ayuda") - + about_action = QAction("Acerca de", self) about_action.triggered.connect(self._show_about) help_menu.addAction(about_action) - + def _setup_tray(self): """Configura el icono de bandeja del sistema.""" - # Crear icono (usar un icono por defecto o crear uno simple) + if not QSystemTrayIcon.isSystemTrayAvailable(): + app_logger.warning("Bandeja del sistema no disponible en este entorno") + return + self.tray_icon = QSystemTrayIcon(self) + self.tray_icon.setIcon(load_tray_icon()) self.tray_icon.setToolTip("CloudRestoreAS") - - # Menú del tray + tray_menu = QMenu() - + show_action = QAction("Mostrar", self) show_action.triggered.connect(self._show_window) tray_menu.addAction(show_action) - + hide_action = QAction("Ocultar", self) hide_action.triggered.connect(self.hide) tray_menu.addAction(hide_action) - + tray_menu.addSeparator() - + pause_tray_action = QAction("Pausar", self) pause_tray_action.triggered.connect(self._pause_engine) tray_menu.addAction(pause_tray_action) - + resume_tray_action = QAction("Continuar", self) resume_tray_action.triggered.connect(self._resume_engine) tray_menu.addAction(resume_tray_action) - + tray_menu.addSeparator() - - quit_action = QAction("Salir", self) - quit_action.triggered.connect(self._quit_application) - tray_menu.addAction(quit_action) - + + quit_tray_action = QAction("Salir", self) + quit_tray_action.triggered.connect(self._quit_application) + tray_menu.addAction(quit_tray_action) + self.tray_icon.setContextMenu(tray_menu) - - # Doble clic en tray para mostrar/ocultar self.tray_icon.activated.connect(self._on_tray_activated) - - # Mostrar tray self.tray_icon.show() - + def _connect_engine_signals(self): """Conecta las señales del motor.""" self.engine.signals.job_created.connect(self._on_job_created) self.engine.signals.stats_updated.connect(self._on_stats_updated) self.engine.signals.config_loaded.connect(self._on_config_loaded) - + def closeEvent(self, event: QCloseEvent): - """Maneja el evento de cierre de ventana (minimizar a tray).""" - if self.tray_icon.isVisible(): + """Minimiza a bandeja al cerrar la ventana (no sale de la aplicación).""" + if self._force_quit: + event.accept() + return + + if self.tray_icon and self.tray_icon.isVisible(): self.hide() event.ignore() app_logger.info("Ventana minimizada a bandeja del sistema") + if not self._tray_hint_shown: + self.tray_icon.showMessage( + "CloudRestoreAS sigue en ejecución", + "La aplicación está en la bandeja. Use clic derecho → Salir para cerrar.", + QSystemTrayIcon.MessageIcon.Information, + 4000, + ) + self._tray_hint_shown = True else: event.accept() - + def _on_tray_activated(self, reason): """Maneja la activación del icono de tray.""" if reason == QSystemTrayIcon.ActivationReason.DoubleClick: @@ -184,35 +210,56 @@ class MainWindow(QMainWindow): self.hide() else: self._show_window() - + def _show_window(self): """Muestra la ventana.""" self.show() self.activateWindow() self.raise_() - + + def _motor_status_text(self) -> str: + if not self.engine.is_running(): + return "Detenido" + if self.engine.is_paused(): + return "Pausado" + return "Activo" + + def _update_tray_status(self): + """Actualiza tooltip del icono de bandeja según estado del motor.""" + if not self.tray_icon: + return + status = self._motor_status_text() + self.tray_icon.setToolTip(f"CloudRestoreAS — Motor: {status}") + def _start_engine(self): """Inicia el motor.""" self.engine.start() self.start_action.setEnabled(False) self.pause_action.setEnabled(True) self.stop_action.setEnabled(True) + self.resume_action.setEnabled(False) + self._update_tray_status() + self.dashboard_tab.update_motor_status() app_logger.info("Motor iniciado desde UI") - + def _pause_engine(self): """Pausa el motor.""" self.engine.pause() self.pause_action.setEnabled(False) self.resume_action.setEnabled(True) + self._update_tray_status() + self.dashboard_tab.update_motor_status() app_logger.info("Motor pausado desde UI") - + def _resume_engine(self): """Reanuda el motor.""" self.engine.resume() self.resume_action.setEnabled(False) self.pause_action.setEnabled(True) + self._update_tray_status() + self.dashboard_tab.update_motor_status() app_logger.info("Motor reanudado desde UI") - + def _stop_engine(self): """Detiene el motor.""" self.engine.stop() @@ -220,48 +267,53 @@ class MainWindow(QMainWindow): self.pause_action.setEnabled(False) self.resume_action.setEnabled(False) self.start_action.setEnabled(True) + self._update_tray_status() + self.dashboard_tab.update_motor_status() app_logger.info("Motor detenido desde UI") - + def _scan_now(self): """Fuerza un escaneo manual.""" self.engine.scan_now() app_logger.info("Escaneo manual solicitado desde UI") - + def _update_stats(self): """Actualiza las estadísticas.""" + self._update_tray_status() + self.dashboard_tab.update_motor_status() if self.engine.is_running(): stats = self.engine.get_stats() self.dashboard_tab.update_stats(stats) - + def _on_job_created(self, job_id: str): """Maneja la creación de un nuevo job.""" self.jobs_tab.refresh_jobs() self._update_stats() - + def _on_stats_updated(self, stats: dict): """Maneja la actualización de estadísticas.""" self.dashboard_tab.update_stats(stats) - + def _on_config_loaded(self, config: dict): """Maneja la carga de configuración.""" self.config_tab.load_config(config) - + def _show_about(self): """Muestra el diálogo Acerca de.""" - from PySide6.QtWidgets import QMessageBox QMessageBox.about( self, "Acerca de CloudRestoreAS", "CloudRestoreAS v1.0.0\n\n" "Aplicación de restauración automática de bases de datos SQL Server.\n\n" - "© 2026 Aduanasoft" + "Al cerrar la ventana, la aplicación permanece en la bandeja del sistema.\n" + "Use Archivo → Salir o la bandeja → Salir para cerrar por completo.\n\n" + "© 2026 Aduanasoft", ) - + def _quit_application(self): """Sale de la aplicación completamente.""" app_logger.info("Saliendo de la aplicación") + self._force_quit = True self.engine.stop() - self.tray_icon.hide() - self.close() - from PySide6.QtWidgets import QApplication + if self.tray_icon: + self.tray_icon.hide() QApplication.quit() diff --git a/app/ui/tray_assets.py b/app/ui/tray_assets.py new file mode 100644 index 0000000..21acf0f --- /dev/null +++ b/app/ui/tray_assets.py @@ -0,0 +1,55 @@ +"""Icono de bandeja del sistema.""" + +import sys +from pathlib import Path + +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor, QIcon, QPainter, QPixmap + +_ASSETS_DIR = Path(__file__).resolve().parent.parent.parent / "packaging" / "assets" + + +def _generated_tray_icon() -> QIcon: + """Icono simple en memoria si no hay archivo empaquetado.""" + size = 32 + pixmap = QPixmap(size, size) + pixmap.fill(Qt.GlobalColor.transparent) + painter = QPainter(pixmap) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + painter.setBrush(QColor(0, 120, 215)) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawRoundedRect(2, 2, size - 4, size - 4, 6, 6) + painter.setPen(QColor(255, 255, 255)) + font = painter.font() + font.setBold(True) + font.setPointSize(14) + painter.setFont(font) + painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "C") + painter.end() + return QIcon(pixmap) + + +def load_tray_icon() -> QIcon: + """Carga icono de bandeja desde assets empaquetados o genera uno por defecto.""" + candidates: list[Path] = [] + meipass = getattr(sys, "_MEIPASS", None) + if meipass: + base = Path(meipass) + candidates.extend( + [ + base / "packaging" / "assets" / "tray-icon.png", + base / "packaging" / "assets" / "tray-icon.ico", + ] + ) + candidates.extend( + [ + _ASSETS_DIR / "tray-icon.png", + _ASSETS_DIR / "tray-icon.ico", + ] + ) + for path in candidates: + if path.is_file(): + icon = QIcon(str(path)) + if not icon.isNull(): + return icon + return _generated_tray_icon() diff --git a/app/utils/crypto.py b/app/utils/crypto.py index 5493132..844adf7 100644 --- a/app/utils/crypto.py +++ b/app/utils/crypto.py @@ -1,59 +1,68 @@ -"""Utilidades de cifrado para passwords usando DPAPI en Windows.""" +"""Utilidades de cifrado para passwords (DPAPI en Windows, Fernet en Linux).""" import base64 -import win32crypt +import sys +from pathlib import Path from typing import Optional +from ..constants import DATA_DIR + +_IS_WINDOWS = sys.platform == "win32" +_SECRET_FILE = DATA_DIR / ".secret" + + +def _get_fernet(): + from cryptography.fernet import Fernet + + DATA_DIR.mkdir(parents=True, exist_ok=True) + if not _SECRET_FILE.exists(): + key = Fernet.generate_key() + _SECRET_FILE.write_bytes(key) + try: + _SECRET_FILE.chmod(0o600) + except OSError: + pass + else: + key = _SECRET_FILE.read_bytes() + return Fernet(key) + def encrypt_password(password: str) -> str: - """ - Cifra una contraseña usando DPAPI de Windows. - - Args: - password: Contraseña en texto plano - - Returns: - Contraseña cifrada en base64 - """ + """Cifra una contraseña.""" if not password: return "" - + try: - encrypted_bytes = win32crypt.CryptProtectData( - password.encode('utf-8'), - None, - None, - None, - None, - 0 - ) - return base64.b64encode(encrypted_bytes).decode('ascii') + if _IS_WINDOWS: + import win32crypt + + encrypted_bytes = win32crypt.CryptProtectData( + password.encode("utf-8"), None, None, None, None, 0 + ) + return base64.b64encode(encrypted_bytes).decode("ascii") + + fernet = _get_fernet() + return fernet.encrypt(password.encode("utf-8")).decode("ascii") except Exception as e: - raise RuntimeError(f"Error cifrando contraseña: {e}") + raise RuntimeError(f"Error cifrando contraseña: {e}") from e def decrypt_password(encrypted_password: str) -> str: - """ - Descifra una contraseña usando DPAPI de Windows. - - Args: - encrypted_password: Contraseña cifrada en base64 - - Returns: - Contraseña en texto plano - """ + """Descifra una contraseña.""" if not encrypted_password: return "" - + try: - encrypted_bytes = base64.b64decode(encrypted_password) - decrypted_bytes = win32crypt.CryptUnprotectData( - encrypted_bytes, - None, - None, - None, - 0 - )[1] - return decrypted_bytes.decode('utf-8') + if _IS_WINDOWS: + import win32crypt + + encrypted_bytes = base64.b64decode(encrypted_password) + decrypted_bytes = win32crypt.CryptUnprotectData( + encrypted_bytes, None, None, None, 0 + )[1] + return decrypted_bytes.decode("utf-8") + + fernet = _get_fernet() + return fernet.decrypt(encrypted_password.encode("ascii")).decode("utf-8") except Exception as e: - raise RuntimeError(f"Error descifrando contraseña: {e}") + raise RuntimeError(f"Error descifrando contraseña: {e}") from e diff --git a/build-linux.log b/build-linux.log new file mode 100644 index 0000000..733e06f --- /dev/null +++ b/build-linux.log @@ -0,0 +1,499 @@ +debconf: delaying package configuration, since apt-utils is not installed +=============================================== +CloudRestoreAS - Build Linux (onefile) +=============================================== +==> 7-Zip Linux (7zz) + 7zz ya existe +==> ODBC Driver 18 + unixODBC + ODBC ya existe +Listo: /app/packaging/bundled/linux +Ejecutando PyInstaller (onefile)... +74 INFO: PyInstaller: 6.20.0, contrib hooks: 2026.6 +74 INFO: Python: 3.10.12 +75 INFO: Platform: Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.35 +75 INFO: Python environment: /app/venv-linux +77 INFO: Removing temporary files and cleaning cache in /root/.cache/pyinstaller +89 WARNING: Failed to collect submodules for 'PySide6.scripts.deploy_lib' because importing 'PySide6.scripts.deploy_lib' raised: ModuleNotFoundError: No module named 'project_lib' +713 INFO: Module search paths (PYTHONPATH): +['/app', + '/usr/lib/python310.zip', + '/usr/lib/python3.10', + '/usr/lib/python3.10/lib-dynload', + '/app/venv-linux/lib/python3.10/site-packages', + '/app'] +793 INFO: Appending 'binaries' from .spec +797 INFO: Appending 'datas' from .spec +854 INFO: checking Analysis +854 INFO: Building Analysis because Analysis-00.toc is non existent +854 INFO: Looking for Python shared library... +861 INFO: Using Python shared library: /lib/x86_64-linux-gnu/libpython3.10.so.1.0 +861 INFO: Running Analysis Analysis-00.toc +861 INFO: Target bytecode optimization level: 0 +861 INFO: Initializing module dependency graph... +861 INFO: Initializing module graph hook caches... +867 INFO: Analyzing modules for base_library.zip ... +1077 INFO: Processing standard module hook 'hook-heapq.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +1111 INFO: Processing standard module hook 'hook-encodings.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +1770 INFO: Processing standard module hook 'hook-pickle.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +2325 INFO: Caching module dependency graph... +2349 INFO: Analyzing /app/runner.py +2355 INFO: Processing standard module hook 'hook-sqlite3.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +2595 INFO: Processing standard module hook 'hook-PySide6.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +2687 INFO: Processing standard module hook 'hook-shiboken6.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +2753 INFO: Processing standard module hook 'hook-PySide6.QtNetwork.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +3261 INFO: Processing standard module hook 'hook-PySide6.QtCore.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +3732 INFO: Processing standard module hook 'hook-PySide6.QtWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +4244 INFO: Processing standard module hook 'hook-PySide6.QtGui.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +5411 INFO: Processing standard module hook 'hook-platform.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +5434 INFO: Processing standard module hook 'hook-cryptography.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +6128 INFO: hook-cryptography: cryptography does not seem to be using dynamically linked OpenSSL. +6278 INFO: Processing standard module hook 'hook-pyodbc.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +6390 INFO: Processing standard module hook 'hook-urllib3.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +6500 INFO: Processing pre-safe-import-module hook 'hook-backports.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/pre_safe_import_module' +6503 INFO: SetuptoolsInfo: initializing cached setuptools info... +7023 INFO: Processing pre-safe-import-module hook 'hook-typing_extensions.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/pre_safe_import_module' +7167 INFO: Processing standard module hook 'hook-multiprocessing.util.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +7199 INFO: Processing standard module hook 'hook-xml.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +7334 INFO: Processing standard module hook 'hook-_ctypes.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +7979 INFO: Processing standard module hook 'hook-certifi.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +7998 INFO: Processing standard module hook 'hook-charset_normalizer.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +8193 INFO: Processing standard module hook 'hook-bcrypt.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +8489 INFO: Processing standard module hook 'hook-difflib.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +8655 INFO: Processing standard module hook 'hook-nacl.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/stdhooks' +8816 INFO: Analyzing hidden import 'PySide6.Qt3DAnimation' +8830 INFO: Processing standard module hook 'hook-PySide6.Qt3DAnimation.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +8938 INFO: Processing standard module hook 'hook-PySide6.Qt3DCore.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +9135 INFO: Processing standard module hook 'hook-PySide6.Qt3DRender.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10294 INFO: Processing standard module hook 'hook-PySide6.QtOpenGL.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10400 INFO: Analyzing hidden import 'PySide6.Qt3DExtras' +10458 INFO: Processing standard module hook 'hook-PySide6.Qt3DExtras.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10552 INFO: Analyzing hidden import 'PySide6.Qt3DInput' +10564 INFO: Processing standard module hook 'hook-PySide6.Qt3DInput.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10645 INFO: Analyzing hidden import 'PySide6.Qt3DLogic' +10646 INFO: Processing standard module hook 'hook-PySide6.Qt3DLogic.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10725 INFO: Analyzing hidden import 'PySide6.QtAsyncio' +10740 INFO: Analyzing hidden import 'PySide6.QtBluetooth' +10770 INFO: Processing standard module hook 'hook-PySide6.QtBluetooth.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +10850 INFO: Analyzing hidden import 'PySide6.QtCanvasPainter' +10915 INFO: Processing standard module hook 'hook-PySide6.QtQuick.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +11049 INFO: Processing standard module hook 'hook-PySide6.QtQml.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +--- Logging error --- +Traceback (most recent call last): + File "/usr/lib/python3.10/logging/__init__.py", line 1100, in emit + msg = self.format(record) + File "/usr/lib/python3.10/logging/__init__.py", line 943, in format + return fmt.format(record) + File "/usr/lib/python3.10/logging/__init__.py", line 678, in format + record.message = record.getMessage() + File "/usr/lib/python3.10/logging/__init__.py", line 368, in getMessage + msg = msg % self.args +TypeError: not enough arguments for format string +Call stack: + File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main + return _run_code(code, main_globals, None, + File "/usr/lib/python3.10/runpy.py", line 86, in _run_code + exec(code, run_globals) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/__main__.py", line 321, in + run() + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/__main__.py", line 215, in run + run_build(pyi_config, spec_file, **vars(args)) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/__main__.py", line 70, in run_build + PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 1275, in main + build(specfile, distpath, workpath, clean_build) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 1213, in build + exec(code, spec_namespace) + File "packaging/CloudRestoreAS.spec", line 59, in + a = Analysis( + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 584, in __init__ + self.__postinit__() + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/building/datastruct.py", line 184, in __postinit__ + self.assemble() + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 719, in assemble + self.graph.add_hiddenimports(self.hiddenimports) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 770, in add_hiddenimports + nodes = self.import_hook(modnm) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 477, in _safe_import_hook + ret_modules = super()._safe_import_hook( + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 2103, in _safe_import_hook + target_modules = self.import_hook( + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 477, in _safe_import_hook + ret_modules = super()._safe_import_hook( + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 2103, in _safe_import_hook + target_modules = self.import_hook( + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/lib/modulegraph/modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 385, in _safe_import_hook + excluded_imports = self._find_all_excluded_imports(source_module.identifier) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/analysis.py", line 373, in _find_all_excluded_imports + excluded_imports.update(module_hook.excludedimports) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/imphook.py", line 343, in __getattr__ + self._load_hook_module() + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/depend/imphook.py", line 422, in _load_hook_module + self._hook_module = importlib_load_source(self.hook_module_name, self.hook_filename) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/compat.py", line 566, in importlib_load_source + mod_loader.exec_module(mod) + File "", line 883, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/hook-PySide6.QtQml.py", line 15, in + qml_binaries, qml_datas = pyside6_library_info.collect_qtqml_files() + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/utils/hooks/qt/__init__.py", line 963, in collect_qtqml_files + plugin_binaries, plugin_datas = self._process_qml_plugin(qmldir_file) + File "/app/venv-linux/lib/python3.10/site-packages/PyInstaller/utils/hooks/qt/__init__.py", line 1014, in _process_qml_plugin + logger.warn("%s: QML plugin binary %r does not exist!", str(plugin_file)) +Message: '%s: QML plugin binary %r does not exist!' +Arguments: ('/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/Qt/labs/assetdownloader/libqmlassetdownloaderprivateplugin.so',) +12557 INFO: Analyzing hidden import 'PySide6.QtCharts' +12610 INFO: Processing standard module hook 'hook-PySide6.QtCharts.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +12689 INFO: Analyzing hidden import 'PySide6.QtConcurrent' +12692 INFO: Processing standard module hook 'hook-PySide6.QtConcurrent.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +12769 INFO: Analyzing hidden import 'PySide6.QtDBus' +12787 INFO: Processing standard module hook 'hook-PySide6.QtDBus.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +12863 INFO: Analyzing hidden import 'PySide6.QtDataVisualization' +12910 INFO: Processing standard module hook 'hook-PySide6.QtDataVisualization.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +12990 INFO: Analyzing hidden import 'PySide6.QtDesigner' +13019 INFO: Processing standard module hook 'hook-PySide6.QtDesigner.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13139 INFO: Analyzing hidden import 'PySide6.QtGraphs' +13211 INFO: Processing standard module hook 'hook-PySide6.QtGraphs.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13297 INFO: Analyzing hidden import 'PySide6.QtGraphsWidgets' +13307 INFO: Processing standard module hook 'hook-PySide6.QtGraphsWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13384 INFO: Processing standard module hook 'hook-PySide6.QtQuickWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13465 INFO: Analyzing hidden import 'PySide6.QtHelp' +13473 INFO: Processing standard module hook 'hook-PySide6.QtHelp.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13554 INFO: Analyzing hidden import 'PySide6.QtHttpServer' +13561 INFO: Processing standard module hook 'hook-PySide6.QtHttpServer.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13642 INFO: Analyzing hidden import 'PySide6.QtLocation' +13689 INFO: Processing standard module hook 'hook-PySide6.QtLocation.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13809 INFO: Processing standard module hook 'hook-PySide6.QtPositioning.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +13921 INFO: Analyzing hidden import 'PySide6.QtMultimedia' +13959 INFO: Processing standard module hook 'hook-PySide6.QtMultimedia.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14068 INFO: Analyzing hidden import 'PySide6.QtMultimediaWidgets' +14070 INFO: Processing standard module hook 'hook-PySide6.QtMultimediaWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14149 INFO: Analyzing hidden import 'PySide6.QtNetworkAuth' +14162 INFO: Processing standard module hook 'hook-PySide6.QtNetworkAuth.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14243 INFO: Analyzing hidden import 'PySide6.QtNfc' +14253 INFO: Processing standard module hook 'hook-PySide6.QtNfc.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14336 INFO: Analyzing hidden import 'PySide6.QtOpenGLWidgets' +14338 INFO: Processing standard module hook 'hook-PySide6.QtOpenGLWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14418 INFO: Analyzing hidden import 'PySide6.QtPdf' +14426 INFO: Processing standard module hook 'hook-PySide6.QtPdf.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14510 INFO: Analyzing hidden import 'PySide6.QtPdfWidgets' +14513 INFO: Processing standard module hook 'hook-PySide6.QtPdfWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14596 INFO: Analyzing hidden import 'PySide6.QtPrintSupport' +14605 INFO: Processing standard module hook 'hook-PySide6.QtPrintSupport.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14698 INFO: Analyzing hidden import 'PySide6.QtQuick3D' +14704 INFO: Processing standard module hook 'hook-PySide6.QtQuick3D.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14786 INFO: Analyzing hidden import 'PySide6.QtQuickControls2' +14787 INFO: Processing standard module hook 'hook-PySide6.QtQuickControls2.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14866 INFO: Analyzing hidden import 'PySide6.QtQuickTest' +14867 INFO: Analyzing hidden import 'PySide6.QtRemoteObjects' +14877 INFO: Processing standard module hook 'hook-PySide6.QtRemoteObjects.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +14959 INFO: Analyzing hidden import 'PySide6.QtScxml' +14968 INFO: Processing standard module hook 'hook-PySide6.QtScxml.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15052 INFO: Analyzing hidden import 'PySide6.QtSensors' +15081 INFO: Processing standard module hook 'hook-PySide6.QtSensors.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15169 INFO: Analyzing hidden import 'PySide6.QtSerialBus' +15186 INFO: Processing standard module hook 'hook-PySide6.QtSerialBus.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15301 INFO: Analyzing hidden import 'PySide6.QtSerialPort' +15306 INFO: Processing standard module hook 'hook-PySide6.QtSerialPort.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15381 INFO: Analyzing hidden import 'PySide6.QtSpatialAudio' +15387 INFO: Processing standard module hook 'hook-PySide6.QtSpatialAudio.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15469 INFO: Analyzing hidden import 'PySide6.QtSql' +15487 INFO: Processing standard module hook 'hook-PySide6.QtSql.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15670 INFO: Analyzing hidden import 'PySide6.QtStateMachine' +15678 INFO: Processing standard module hook 'hook-PySide6.QtStateMachine.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15758 INFO: Analyzing hidden import 'PySide6.QtSvg' +15762 INFO: Processing standard module hook 'hook-PySide6.QtSvg.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15841 INFO: Analyzing hidden import 'PySide6.QtSvgWidgets' +15843 INFO: Processing standard module hook 'hook-PySide6.QtSvgWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +15922 INFO: Analyzing hidden import 'PySide6.QtTest' +15932 INFO: Processing standard module hook 'hook-PySide6.QtTest.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16010 INFO: Analyzing hidden import 'PySide6.QtTextToSpeech' +16015 INFO: Processing standard module hook 'hook-PySide6.QtTextToSpeech.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16112 INFO: Analyzing hidden import 'PySide6.QtUiTools' +16114 INFO: Processing standard module hook 'hook-PySide6.QtUiTools.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16198 INFO: Analyzing hidden import 'PySide6.QtWebChannel' +16200 INFO: Processing standard module hook 'hook-PySide6.QtWebChannel.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16295 INFO: Analyzing hidden import 'PySide6.QtWebEngineCore' +16332 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineCore.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16522 INFO: Analyzing hidden import 'PySide6.QtWebEngineQuick' +16526 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineQuick.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16617 INFO: Analyzing hidden import 'PySide6.QtWebEngineWidgets' +16621 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineWidgets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16707 INFO: Analyzing hidden import 'PySide6.QtWebSockets' +16713 INFO: Processing standard module hook 'hook-PySide6.QtWebSockets.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16790 INFO: Analyzing hidden import 'PySide6.QtWebView' +16793 INFO: Analyzing hidden import 'PySide6.QtXml' +16804 INFO: Processing standard module hook 'hook-PySide6.QtXml.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16894 INFO: Analyzing hidden import 'PySide6._config' +16896 INFO: Analyzing hidden import 'PySide6._git_pyside_version' +16897 INFO: Analyzing hidden import 'PySide6.scripts' +16897 INFO: Analyzing hidden import 'PySide6.scripts.android_deploy' +16901 INFO: Analyzing hidden import 'PySide6.scripts.deploy' +16904 INFO: Analyzing hidden import 'PySide6.scripts.metaobjectdump' +16912 INFO: Analyzing hidden import 'PySide6.scripts.project' +16936 INFO: Analyzing hidden import 'PySide6.scripts.project_lib' +16972 INFO: Processing standard module hook 'hook-xml.etree.cElementTree.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +16978 INFO: Processing pre-safe-import-module hook 'hook-tomli.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/pre_safe_import_module' +17002 INFO: Analyzing hidden import 'PySide6.scripts.pyside_tool' +17016 INFO: Processing standard module hook 'hook-sysconfig.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +17020 INFO: Analyzing hidden import 'PySide6.scripts.qml' +17024 INFO: Analyzing hidden import 'PySide6.scripts.qtpy2cpp' +17026 INFO: Analyzing hidden import 'PySide6.support' +17027 INFO: Analyzing hidden import 'PySide6.support.deprecated' +17028 INFO: Analyzing hidden import 'PySide6.support.generate_pyi' +17030 INFO: Processing module hooks (post-graph stage)... +17104 INFO: Performing binary vs. data reclassification (3660 entries) +19240 INFO: Looking for ctypes DLLs +19242 INFO: Analyzing run-time hooks ... +19245 INFO: Including run-time hook 'pyi_rth_inspect.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/rthooks' +19246 INFO: Including run-time hook 'pyi_rth_pkgutil.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/rthooks' +19248 INFO: Including run-time hook 'pyi_rth_multiprocessing.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/rthooks' +19249 INFO: Including run-time hook 'pyi_rth_cryptography_openssl.py' from '/app/venv-linux/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/rthooks' +19250 INFO: Including run-time hook 'pyi_rth_pyside6.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/rthooks' +19251 INFO: Processing pre-find-module-path hook 'hook-_pyi_rth_utils.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks/pre_find_module_path' +19252 INFO: Processing standard module hook 'hook-_pyi_rth_utils.py' from '/app/venv-linux/lib/python3.10/site-packages/PyInstaller/hooks' +19329 INFO: Creating base_library.zip... +19340 INFO: Looking for dynamic libraries +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6FFmpegStub-crypto.so.3'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6FFmpegStub-ssl.so.3'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6FFmpegStub-va-drm.so.2'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6FFmpegStub-va-x11.so.2'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6FFmpegStub-va.so.2'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6Multimedia.so.6'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6MultimediaQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6MultimediaWidgets.so.6'. +23477 WARNING: Library not found: could not resolve 'libpcsclite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6Nfc.so.6'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6Quick3DSpatialAudio.so.6'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6SpatialAudio.so.6'. +23477 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6TextToSpeech.so.6'. +23477 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineCore.so.6'. +23477 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23477 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23478 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineQuick.so.6'. +23478 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6WebEngineWidgets.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-shape.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-render-util.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-icccm.so.4', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-render.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-util.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxkbcommon-x11.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-cursor.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-image.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libxcb-keysyms.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libQt6XcbQpa.so.6'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavcodec.so.61'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavcodec.so.61.19.101'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavformat.so.61'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavformat.so.61.7.100'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavutil.so.59'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavutil.so.59.39.100'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswresample.so.5'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswresample.so.5.3.100'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswscale.so.8'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswscale.so.8.3.100'. +23478 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/libexec/QtWebEngineProcess'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavcodec.so'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavformat.so'. +23478 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libavutil.so'. +23479 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswresample.so'. +23479 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/lib/libswscale.so'. +23479 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/designer/libqwebengineview.so'. +23479 WARNING: Library not found: could not resolve 'libQt6EglFsKmsGbmSupport.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/egldeviceintegrations/libqeglfs-kms-integration.so'. +23479 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/multimedia/libffmpegmediaplugin.so'. +23479 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/multimedia/libffmpegmediaplugin.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-shape.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-render-util.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-icccm.so.4', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-render.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-util.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxkbcommon-x11.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-cursor.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-image.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libxcb-keysyms.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so'. +23479 WARNING: Library not found: could not resolve 'libharfbuzz.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libcairo-gobject.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libpangocairo-1.0.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libcairo.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libgdk_pixbuf-2.0.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libpango-1.0.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libgtk-3.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libgdk-3.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libatk-1.0.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/platformthemes/libqgtk3.so'. +23479 WARNING: Library not found: could not resolve 'libcups.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/printsupport/libcupsprintersupport.so'. +23479 WARNING: Library not found: could not resolve 'libfbclient.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/sqldrivers/libqsqlibase.so'. +23479 WARNING: Library not found: could not resolve 'libmimerapi.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/sqldrivers/libqsqlmimer.so'. +23479 WARNING: Library not found: could not resolve 'libmysqlclient.so.21', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/sqldrivers/libqsqlmysql.so'. +23479 WARNING: Library not found: could not resolve 'libclntsh.so.23.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/sqldrivers/libqsqloci.so'. +23479 WARNING: Library not found: could not resolve 'libpq.so.5', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/sqldrivers/libqsqlpsql.so'. +23479 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/texttospeech/libqtexttospeech_mock.so'. +23479 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/texttospeech/libqtexttospeech_speechd.so'. +23480 WARNING: Library not found: could not resolve 'libspeechd.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/texttospeech/libqtexttospeech_speechd.so'. +23480 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/webview/libqtwebview_webengine.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-shape.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-render-util.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-icccm.so.4', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-render.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-util.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxkbcommon-x11.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-cursor.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-image.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-keysyms.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-egl-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-shape.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-render-util.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-icccm.so.4', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-render.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-util.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxkbcommon-x11.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-cursor.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-image.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libxcb-keysyms.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so'. +23480 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtMultimedia/libquickmultimediaplugin.so'. +23480 WARNING: Library not found: could not resolve 'libQt6QuickShapesDesignHelpers.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtQuick/Shapes/DesignHelpers/libqtquickshapesdesignhelpersplugin.so'. +23480 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtQuick/VirtualKeyboard/Components/libqtvkbcomponentsplugin.so'. +23480 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtQuick3D/SpatialAudio/libquick3dspatialaudioplugin.so'. +23480 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtTextToSpeech/libtexttospeechqmlplugin.so'. +23480 WARNING: Library not found: could not resolve 'libQt6WaylandCompositorIviapplication.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWayland/Compositor/IviApplication/libwaylandcompositoriviapplicationplugin.so'. +23480 WARNING: Library not found: could not resolve 'libQt6WaylandCompositorPresentationTime.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWayland/Compositor/PresentationTime/libwaylandcompositorpresentationtimeplugin.so'. +23480 WARNING: Library not found: could not resolve 'libQt6WaylandCompositorWLShell.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWayland/Compositor/WlShell/libwaylandcompositorwlshellplugin.so'. +23480 WARNING: Library not found: could not resolve 'libQt6WaylandCompositorXdgShell.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWayland/Compositor/XdgShell/libwaylandcompositorxdgshellplugin.so'. +23480 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23480 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23480 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/Qt/qml/QtWebEngine/libqtwebenginequickplugin.so'. +23481 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineQuick.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libasound.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXcomposite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXtst.so.6', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnspr4.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnss3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libxkbfile.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXrandr.so.2', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libXdamage.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libsmime3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libnssutil3.so', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtWebEngineCore.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtTextToSpeech.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtSpatialAudio.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libpcsclite.so.1', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtNfc.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtMultimediaWidgets.abi3.so'. +23481 WARNING: Library not found: could not resolve 'libpulse.so.0', dependency of '/app/venv-linux/lib/python3.10/site-packages/PySide6/QtMultimedia.abi3.so'. +23545 INFO: Warnings written to /tmp/cloudrestore-build/CloudRestoreAS/warn-CloudRestoreAS.txt +23570 INFO: Graph cross-reference written to /tmp/cloudrestore-build/CloudRestoreAS/xref-CloudRestoreAS.html +23670 INFO: checking PYZ +23670 INFO: Building PYZ because PYZ-00.toc is non existent +23670 INFO: Building PYZ (ZlibArchive) /tmp/cloudrestore-build/CloudRestoreAS/PYZ-00.pyz +23962 INFO: Building PYZ (ZlibArchive) /tmp/cloudrestore-build/CloudRestoreAS/PYZ-00.pyz completed successfully. +24040 INFO: checking PKG +24040 INFO: Building PKG because PKG-00.toc is non existent +24040 INFO: Building PKG (CArchive) CloudRestoreAS.pkg +93529 INFO: Building PKG (CArchive) CloudRestoreAS.pkg completed successfully. +93583 INFO: Bootloader /app/venv-linux/lib/python3.10/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run +93583 INFO: checking EXE +93583 INFO: Building EXE because EXE-00.toc is non existent +93583 INFO: Building EXE from EXE-00.toc +93583 INFO: Copying bootloader EXE to /app/dist/CloudRestoreAS +93585 INFO: Appending PKG archive to custom ELF section in EXE +99643 INFO: Building EXE from EXE-00.toc completed successfully. +99693 INFO: Build complete! The results are available in: /app/dist + +Build OK: /app/dist/CloudRestoreAS +Distribuir solo el binario; al ejecutar crea config/ automaticamente. +===LINUX_BUILD_EXIT=0=== diff --git a/build-windows.log b/build-windows.log new file mode 100644 index 0000000..0d62547 --- /dev/null +++ b/build-windows.log @@ -0,0 +1,326 @@ +=============================================== +CloudRestoreAS - Build Windows (onefile) +=============================================== +==> 7-Zip Extra (26.01) + 7-Zip ya existe +==> ODBC Driver 18 (extraer DLLs del MSI) + ODBC ya existe +Listo: \\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\packaging\bundled\windows +Python: C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\python.exe + +[notice] A new release of pip is available: 25.0.1 -> 26.1.2 +[notice] To update, run: \\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Scripts\python.exe -m pip install --upgrade pip +Ejecutando PyInstaller (onefile)... +Workpath: C:\Users\Hugo Reyes\AppData\Local\Temp\cloudrestore-build +739 INFO: PyInstaller: 6.20.0, contrib hooks: 2026.6 +739 INFO: Python: 3.13.3 +760 INFO: Platform: Windows-11-10.0.26200-SP0 +760 INFO: Python environment: \\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows +767 INFO: Removing temporary files and cleaning cache in C:\Users\Hugo Reyes\AppData\Local\pyinstaller +1995 WARNING: Failed to collect submodules for 'PySide6.scripts.deploy_lib' because importing 'PySide6.scripts.deploy_lib' raised: ModuleNotFoundError: No module named 'project_lib' +60908 INFO: Module search paths (PYTHONPATH): +['\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS', + 'C:\\Users\\Hugo ' + 'Reyes\\AppData\\Local\\Programs\\Python\\Python313\\python313.zip', + 'C:\\Users\\Hugo Reyes\\AppData\\Local\\Programs\\Python\\Python313\\DLLs', + 'C:\\Users\\Hugo Reyes\\AppData\\Local\\Programs\\Python\\Python313\\Lib', + 'C:\\Users\\Hugo Reyes\\AppData\\Local\\Programs\\Python\\Python313', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\win32', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\win32\\lib', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\pythonwin', + '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS'] +62173 INFO: Appending 'binaries' from .spec +63219 INFO: Appending 'datas' from .spec +73737 INFO: checking Analysis +73738 INFO: Building Analysis because Analysis-00.toc is non existent +73738 INFO: Looking for Python shared library... +73738 INFO: Using Python shared library: C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\python313.dll +73738 INFO: Running Analysis Analysis-00.toc +73738 INFO: Target bytecode optimization level: 0 +73738 INFO: Initializing module dependency graph... +73739 INFO: Initializing module graph hook caches... +73939 INFO: Analyzing modules for base_library.zip ... +79161 INFO: Processing standard module hook 'hook-encodings.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +83574 INFO: Processing standard module hook 'hook-pickle.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +86088 INFO: Processing standard module hook 'hook-heapq.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +89220 INFO: Caching module dependency graph... +89327 INFO: Analyzing \\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\runner.py +89562 INFO: Processing standard module hook 'hook-sqlite3.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +91855 INFO: Processing standard module hook 'hook-PySide6.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +92619 INFO: Processing standard module hook 'hook-shiboken6.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +92791 INFO: Processing standard module hook 'hook-PySide6.QtNetwork.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +97922 INFO: Processing standard module hook 'hook-PySide6.QtCore.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +102127 INFO: Processing standard module hook 'hook-PySide6.QtWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +109064 INFO: Processing standard module hook 'hook-PySide6.QtGui.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +140346 INFO: Processing standard module hook 'hook-platform.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +140386 INFO: Processing standard module hook 'hook-_ctypes.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +140584 INFO: Processing standard module hook 'hook-cryptography.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +144730 INFO: hook-cryptography: cryptography does not seem to be using dynamically linked OpenSSL. +145207 INFO: Processing standard module hook 'hook-pyodbc.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +145794 INFO: Processing standard module hook 'hook-urllib3.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +146377 INFO: Processing pre-safe-import-module hook 'hook-backports.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +146440 INFO: SetuptoolsInfo: initializing cached setuptools info... +150867 INFO: Setuptools: 'backports' appears to be a full setuptools-vendored copy - creating alias to 'setuptools._vendor.backports'! +150904 INFO: Processing standard module hook 'hook-setuptools.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +151016 INFO: Processing pre-safe-import-module hook 'hook-distutils.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +151182 INFO: Processing standard module hook 'hook-sysconfig.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +151201 INFO: Processing pre-safe-import-module hook 'hook-jaraco.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +151205 INFO: Setuptools: 'jaraco' appears to be a full setuptools-vendored copy - creating alias to 'setuptools._vendor.jaraco'! +151239 INFO: Processing pre-safe-import-module hook 'hook-more_itertools.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +151242 INFO: Setuptools: 'more_itertools' appears to be a setuptools-vendored copy - creating alias to 'setuptools._vendor.more_itertools'! +151523 INFO: Processing pre-safe-import-module hook 'hook-typing_extensions.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +151727 INFO: Processing pre-safe-import-module hook 'hook-packaging.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +152152 INFO: Processing standard module hook 'hook-setuptools._vendor.jaraco.text.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +152159 INFO: Processing pre-safe-import-module hook 'hook-importlib_resources.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +152946 INFO: Processing pre-safe-import-module hook 'hook-importlib_metadata.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +152950 INFO: Setuptools: 'importlib_metadata' appears to be a setuptools-vendored copy - creating alias to 'setuptools._vendor.importlib_metadata'! +152983 INFO: Processing standard module hook 'hook-setuptools._vendor.importlib_metadata.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +153157 INFO: Processing pre-safe-import-module hook 'hook-zipp.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +153161 INFO: Setuptools: 'zipp' appears to be a setuptools-vendored copy - creating alias to 'setuptools._vendor.zipp'! +153794 INFO: Processing pre-safe-import-module hook 'hook-tomli.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +153800 INFO: Setuptools: 'tomli' appears to be a setuptools-vendored copy - creating alias to 'setuptools._vendor.tomli'! +155363 INFO: Processing pre-safe-import-module hook 'hook-wheel.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module' +155367 INFO: Setuptools: 'wheel' appears to be a setuptools-vendored copy - creating alias to 'setuptools._vendor.wheel'! +156755 INFO: Processing standard module hook 'hook-certifi.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +157092 INFO: Processing standard module hook 'hook-charset_normalizer.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +159788 INFO: Processing standard module hook 'hook-bcrypt.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +162515 INFO: Processing standard module hook 'hook-difflib.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +162955 INFO: Processing standard module hook 'hook-multiprocessing.util.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +163298 INFO: Processing standard module hook 'hook-xml.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +165811 INFO: Processing standard module hook 'hook-nacl.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks' +168908 INFO: Analyzing hidden import 'PySide6.Qt3DAnimation' +168980 INFO: Processing standard module hook 'hook-PySide6.Qt3DAnimation.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +171050 INFO: Processing standard module hook 'hook-PySide6.Qt3DCore.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +173412 INFO: Processing standard module hook 'hook-PySide6.Qt3DRender.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +187367 INFO: Processing standard module hook 'hook-PySide6.QtOpenGL.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +193279 INFO: Analyzing hidden import 'PySide6.Qt3DExtras' +193358 INFO: Processing standard module hook 'hook-PySide6.Qt3DExtras.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +195332 INFO: Analyzing hidden import 'PySide6.Qt3DInput' +195365 INFO: Processing standard module hook 'hook-PySide6.Qt3DInput.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +196759 INFO: Analyzing hidden import 'PySide6.Qt3DLogic' +196773 INFO: Processing standard module hook 'hook-PySide6.Qt3DLogic.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +198361 INFO: Analyzing hidden import 'PySide6.QtAsyncio' +198435 INFO: Analyzing hidden import 'PySide6.QtAxContainer' +198462 INFO: Processing standard module hook 'hook-PySide6.QtAxContainer.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +199952 INFO: Analyzing hidden import 'PySide6.QtBluetooth' +200101 INFO: Processing standard module hook 'hook-PySide6.QtBluetooth.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +201249 INFO: Analyzing hidden import 'PySide6.QtCanvasPainter' +201572 INFO: Processing standard module hook 'hook-PySide6.QtQuick.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +204027 INFO: Processing standard module hook 'hook-PySide6.QtQml.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +--- Logging error --- +Traceback (most recent call last): + File "C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\Lib\logging\__init__.py", line 1150, in emit + msg = self.format(record) + File "C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\Lib\logging\__init__.py", line 998, in format + return fmt.format(record) + ~~~~~~~~~~^^^^^^^^ + File "C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\Lib\logging\__init__.py", line 711, in format + record.message = record.getMessage() + ~~~~~~~~~~~~~~~~~^^ + File "C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\Lib\logging\__init__.py", line 400, in getMessage + msg = msg % self.args + ~~~~^~~~~~~~~~~ +TypeError: not enough arguments for format string +Call stack: + File "", line 198, in _run_module_as_main + File "", line 88, in _run_code + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\__main__.py", line 321, in + run() + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\__main__.py", line 215, in run + run_build(pyi_config, spec_file, **vars(args)) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\__main__.py", line 70, in run_build + PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\building\build_main.py", line 1275, in main + build(specfile, distpath, workpath, clean_build) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\building\build_main.py", line 1213, in build + exec(code, spec_namespace) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\packaging\CloudRestoreAS.spec", line 59, in + a = Analysis( + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\building\build_main.py", line 584, in __init__ + self.__postinit__() + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\building\datastruct.py", line 184, in __postinit__ + self.assemble() + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\building\build_main.py", line 719, in assemble + self.graph.add_hiddenimports(self.hiddenimports) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 770, in add_hiddenimports + nodes = self.import_hook(modnm) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 477, in _safe_import_hook + ret_modules = super()._safe_import_hook( + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 2103, in _safe_import_hook + target_modules = self.import_hook( + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 477, in _safe_import_hook + ret_modules = super()._safe_import_hook( + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 2103, in _safe_import_hook + target_modules = self.import_hook( + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1279, in import_hook + submodule = self._safe_import_module(head, mname, submodule) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 539, in _safe_import_module + return super()._safe_import_module(module_basename, module_name, parent_package) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 1826, in _safe_import_module + self._process_imports(n) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\lib\modulegraph\modulegraph.py", line 2654, in _process_imports + target_modules = self._safe_import_hook(*import_info, **kwargs) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 385, in _safe_import_hook + excluded_imports = self._find_all_excluded_imports(source_module.identifier) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\analysis.py", line 373, in _find_all_excluded_imports + excluded_imports.update(module_hook.excludedimports) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\imphook.py", line 343, in __getattr__ + self._load_hook_module() + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\depend\imphook.py", line 422, in _load_hook_module + self._hook_module = importlib_load_source(self.hook_module_name, self.hook_filename) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\compat.py", line 566, in importlib_load_source + mod_loader.exec_module(mod) + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\hooks\hook-PySide6.QtQml.py", line 15, in + qml_binaries, qml_datas = pyside6_library_info.collect_qtqml_files() + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\utils\hooks\qt\__init__.py", line 963, in collect_qtqml_files + plugin_binaries, plugin_datas = self._process_qml_plugin(qmldir_file) + File "\\wsl.localhost\Debian\home\hugo_reyes\dev\CloudRecoveryAS\venv-windows\Lib\site-packages\PyInstaller\utils\hooks\qt\__init__.py", line 1014, in _process_qml_plugin + logger.warn("%s: QML plugin binary %r does not exist!", str(plugin_file)) +Message: '%s: QML plugin binary %r does not exist!' +Arguments: ('\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\qml\\Qt\\labs\\assetdownloader\\qmlassetdownloaderprivateplugin.dll',) +275746 INFO: Analyzing hidden import 'PySide6.QtCharts' +276105 INFO: Processing standard module hook 'hook-PySide6.QtCharts.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +279221 INFO: Analyzing hidden import 'PySide6.QtConcurrent' +279250 INFO: Processing standard module hook 'hook-PySide6.QtConcurrent.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +280311 INFO: Analyzing hidden import 'PySide6.QtDBus' +280357 INFO: Processing standard module hook 'hook-PySide6.QtDBus.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +281539 INFO: Analyzing hidden import 'PySide6.QtDataVisualization' +281715 INFO: Processing standard module hook 'hook-PySide6.QtDataVisualization.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +283429 INFO: Analyzing hidden import 'PySide6.QtDesigner' +283475 INFO: Processing standard module hook 'hook-PySide6.QtDesigner.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +287863 INFO: Analyzing hidden import 'PySide6.QtGraphs' +288258 INFO: Processing standard module hook 'hook-PySide6.QtGraphs.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +290498 INFO: Analyzing hidden import 'PySide6.QtGraphsWidgets' +290529 INFO: Processing standard module hook 'hook-PySide6.QtGraphsWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +291489 INFO: Processing standard module hook 'hook-PySide6.QtQuickWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +293266 INFO: Analyzing hidden import 'PySide6.QtHelp' +293297 INFO: Processing standard module hook 'hook-PySide6.QtHelp.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +294778 INFO: Analyzing hidden import 'PySide6.QtHttpServer' +294836 INFO: Processing standard module hook 'hook-PySide6.QtHttpServer.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +295978 INFO: Analyzing hidden import 'PySide6.QtLocation' +296080 INFO: Processing standard module hook 'hook-PySide6.QtLocation.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +299546 INFO: Processing standard module hook 'hook-PySide6.QtPositioning.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +302720 INFO: Analyzing hidden import 'PySide6.QtMultimedia' +302889 INFO: Processing standard module hook 'hook-PySide6.QtMultimedia.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +307961 INFO: Analyzing hidden import 'PySide6.QtMultimediaWidgets' +308000 INFO: Processing standard module hook 'hook-PySide6.QtMultimediaWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +310129 INFO: Analyzing hidden import 'PySide6.QtNetworkAuth' +310201 INFO: Processing standard module hook 'hook-PySide6.QtNetworkAuth.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +311724 INFO: Analyzing hidden import 'PySide6.QtNfc' +311759 INFO: Processing standard module hook 'hook-PySide6.QtNfc.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +312965 INFO: Analyzing hidden import 'PySide6.QtOpenGLWidgets' +312999 INFO: Processing standard module hook 'hook-PySide6.QtOpenGLWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +314755 INFO: Analyzing hidden import 'PySide6.QtPdf' +314785 INFO: Processing standard module hook 'hook-PySide6.QtPdf.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +316217 INFO: Analyzing hidden import 'PySide6.QtPdfWidgets' +316256 INFO: Processing standard module hook 'hook-PySide6.QtPdfWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +318721 INFO: Analyzing hidden import 'PySide6.QtPrintSupport' +318783 INFO: Processing standard module hook 'hook-PySide6.QtPrintSupport.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +321097 INFO: Analyzing hidden import 'PySide6.QtQuick3D' +321121 INFO: Processing standard module hook 'hook-PySide6.QtQuick3D.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +323189 INFO: Analyzing hidden import 'PySide6.QtQuickControls2' +323210 INFO: Processing standard module hook 'hook-PySide6.QtQuickControls2.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +324672 INFO: Analyzing hidden import 'PySide6.QtQuickTest' +324686 INFO: Analyzing hidden import 'PySide6.QtRemoteObjects' +324716 INFO: Processing standard module hook 'hook-PySide6.QtRemoteObjects.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +325812 INFO: Analyzing hidden import 'PySide6.QtScxml' +325844 INFO: Processing standard module hook 'hook-PySide6.QtScxml.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +327910 INFO: Analyzing hidden import 'PySide6.QtSensors' +327951 INFO: Processing standard module hook 'hook-PySide6.QtSensors.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +329877 INFO: Analyzing hidden import 'PySide6.QtSerialBus' +329978 INFO: Processing standard module hook 'hook-PySide6.QtSerialBus.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +333019 INFO: Analyzing hidden import 'PySide6.QtSerialPort' +333041 INFO: Processing standard module hook 'hook-PySide6.QtSerialPort.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +334172 INFO: Analyzing hidden import 'PySide6.QtSpatialAudio' +334196 INFO: Processing standard module hook 'hook-PySide6.QtSpatialAudio.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +335737 INFO: Analyzing hidden import 'PySide6.QtSql' +335786 INFO: Processing standard module hook 'hook-PySide6.QtSql.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +340576 INFO: Analyzing hidden import 'PySide6.QtStateMachine' +340648 INFO: Processing standard module hook 'hook-PySide6.QtStateMachine.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +342135 INFO: Analyzing hidden import 'PySide6.QtSvg' +342220 INFO: Processing standard module hook 'hook-PySide6.QtSvg.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +344343 INFO: Analyzing hidden import 'PySide6.QtSvgWidgets' +344415 INFO: Processing standard module hook 'hook-PySide6.QtSvgWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +346445 INFO: Analyzing hidden import 'PySide6.QtTest' +346515 INFO: Processing standard module hook 'hook-PySide6.QtTest.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +348584 INFO: Analyzing hidden import 'PySide6.QtTextToSpeech' +348616 INFO: Processing standard module hook 'hook-PySide6.QtTextToSpeech.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +351294 INFO: Analyzing hidden import 'PySide6.QtUiTools' +351319 INFO: Processing standard module hook 'hook-PySide6.QtUiTools.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +353034 INFO: Analyzing hidden import 'PySide6.QtWebChannel' +353050 INFO: Processing standard module hook 'hook-PySide6.QtWebChannel.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +354101 INFO: Analyzing hidden import 'PySide6.QtWebEngineCore' +354172 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineCore.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +358005 INFO: Analyzing hidden import 'PySide6.QtWebEngineQuick' +358049 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineQuick.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +359992 INFO: Analyzing hidden import 'PySide6.QtWebEngineWidgets' +360012 INFO: Processing standard module hook 'hook-PySide6.QtWebEngineWidgets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +361731 INFO: Analyzing hidden import 'PySide6.QtWebSockets' +361758 INFO: Processing standard module hook 'hook-PySide6.QtWebSockets.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +362821 INFO: Analyzing hidden import 'PySide6.QtWebView' +362842 INFO: Analyzing hidden import 'PySide6.QtXml' +362877 INFO: Processing standard module hook 'hook-PySide6.QtXml.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +364018 INFO: Analyzing hidden import 'PySide6._config' +364032 INFO: Analyzing hidden import 'PySide6._git_pyside_version' +364047 INFO: Analyzing hidden import 'PySide6.scripts' +364051 INFO: Analyzing hidden import 'PySide6.scripts.deploy' +364083 INFO: Analyzing hidden import 'PySide6.scripts.metaobjectdump' +364144 INFO: Analyzing hidden import 'PySide6.scripts.project' +364206 INFO: Analyzing hidden import 'PySide6.scripts.project_lib' +364407 INFO: Processing standard module hook 'hook-xml.etree.cElementTree.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +364588 INFO: Analyzing hidden import 'PySide6.scripts.pyside_tool' +364619 INFO: Analyzing hidden import 'PySide6.scripts.qml' +364648 INFO: Analyzing hidden import 'PySide6.scripts.qtpy2cpp' +364670 INFO: Analyzing hidden import 'PySide6.support' +364682 INFO: Analyzing hidden import 'PySide6.support.deprecated' +364697 INFO: Analyzing hidden import 'PySide6.support.generate_pyi' +364717 INFO: Processing module hooks (post-graph stage)... +366519 INFO: Performing binary vs. data reclassification (3704 entries) +434075 INFO: Looking for ctypes DLLs +434137 INFO: Analyzing run-time hooks ... +434140 INFO: Including run-time hook 'pyi_rth_inspect.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks' +434182 INFO: Including run-time hook 'pyi_rth_setuptools.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks' +434219 INFO: Including run-time hook 'pyi_rth_pkgutil.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks' +434274 INFO: Including run-time hook 'pyi_rth_multiprocessing.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks' +434306 INFO: Including run-time hook 'pyi_rth_cryptography_openssl.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks' +434323 INFO: Including run-time hook 'pyi_rth_pyside6.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks' +434347 INFO: Processing pre-find-module-path hook 'hook-_pyi_rth_utils.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks\\pre_find_module_path' +434407 INFO: Processing standard module hook 'hook-_pyi_rth_utils.py' from '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PyInstaller\\hooks' +434578 INFO: Creating base_library.zip... +434595 INFO: Looking for dynamic libraries +448674 INFO: Extra DLL search directories (AddDllDirectory): ['\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\shiboken6'] +448674 INFO: Extra DLL search directories (PATH): ['\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6'] +627416 WARNING: Library not found: could not resolve 'fbclient.dll', dependency of '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\plugins\\sqldrivers\\qsqlibase.dll'. +627645 WARNING: Library not found: could not resolve 'MIMAPI64.dll', dependency of '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\plugins\\sqldrivers\\qsqlmimer.dll'. +627727 WARNING: Library not found: could not resolve 'OCI.dll', dependency of '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\plugins\\sqldrivers\\qsqloci.dll'. +627756 WARNING: Library not found: could not resolve 'LIBPQ.dll', dependency of '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\plugins\\sqldrivers\\qsqlpsql.dll'. +628660 WARNING: Library not found: could not resolve 'Qt6QuickShapesDesignHelpers.dll', dependency of '\\\\wsl.localhost\\Debian\\home\\hugo_reyes\\dev\\CloudRecoveryAS\\venv-windows\\Lib\\site-packages\\PySide6\\qml\\QtQuick\\Shapes\\DesignHelpers\\qtquickshapesdesignhelpersplugin.dll'. +634420 INFO: Warnings written to C:\Users\Hugo Reyes\AppData\Local\Temp\cloudrestore-build\CloudRestoreAS\warn-CloudRestoreAS.txt +634496 INFO: Graph cross-reference written to C:\Users\Hugo Reyes\AppData\Local\Temp\cloudrestore-build\CloudRestoreAS\xref-CloudRestoreAS.html +634828 INFO: checking PYZ +634828 INFO: Building PYZ because PYZ-00.toc is non existent +634828 INFO: Building PYZ (ZlibArchive) C:\Users\Hugo Reyes\AppData\Local\Temp\cloudrestore-build\CloudRestoreAS\PYZ-00.pyz +635544 INFO: Building PYZ (ZlibArchive) C:\Users\Hugo Reyes\AppData\Local\Temp\cloudrestore-build\CloudRestoreAS\PYZ-00.pyz completed successfully. +635679 INFO: checking PKG +635679 INFO: Building PKG because PKG-00.toc is non existent +635679 INFO: Building PKG (CArchive) CloudRestoreAS.pkg diff --git a/build.ps1 b/build.ps1 index 65dc2c8..ef48013 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,72 +1,63 @@ -# Script de build para PyInstaller -# Genera un ejecutable único de la aplicación +# Build CloudRestoreAS — PyInstaller onefile portable +$ErrorActionPreference = "Stop" +$Root = $PSScriptRoot Write-Host "===============================================" -ForegroundColor Cyan -Write-Host "CloudRestoreAS - Build Script" -ForegroundColor Cyan +Write-Host "CloudRestoreAS - Build Windows (onefile)" -ForegroundColor Cyan Write-Host "===============================================" -ForegroundColor Cyan -Write-Host "" -# Verificar que PyInstaller esté instalado -Write-Host "Verificando PyInstaller..." -ForegroundColor Yellow -$pyinstallerCheck = pip show pyinstaller 2>$null -if (-not $pyinstallerCheck) { - Write-Host "PyInstaller no está instalado. Instalando..." -ForegroundColor Yellow - pip install pyinstaller +& "$Root\packaging\scripts\download-bundled-deps.ps1" + +# Python de Windows (evitar venv creado desde WSL) +$PythonCandidates = @( + "$env:LOCALAPPDATA\Programs\Python\Python313\python.exe", + "$env:LOCALAPPDATA\Programs\Python\Python312\python.exe", + "$env:LOCALAPPDATA\Programs\Python\Python311\python.exe" +) +$Python = $PythonCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 +if (-not $Python) { + throw "No se encontró Python 3.11+ en Windows. Instale desde python.org" } +Write-Host "Python: $Python" -ForegroundColor Gray -# Crear directorio de build -$buildDir = "build" -$distDir = "dist" - -Write-Host "" -Write-Host "Limpiando builds anteriores..." -ForegroundColor Yellow -if (Test-Path $buildDir) { - Remove-Item -Recurse -Force $buildDir -} -if (Test-Path $distDir) { - Remove-Item -Recurse -Force $distDir -} -if (Test-Path "*.spec") { - Remove-Item -Force *.spec +$venvDir = Join-Path $Root "venv-windows" +$venvPython = Join-Path $venvDir "Scripts\python.exe" +if (-not (Test-Path $venvPython)) { + if (Test-Path $venvDir) { Remove-Item -Recurse -Force $venvDir } + & $Python -m venv $venvDir } +& $venvPython -m pip install --upgrade pip -q +& $venvPython -m pip install -r (Join-Path $Root "requirements-windows.txt") -q -Write-Host "" -Write-Host "Ejecutando PyInstaller..." -ForegroundColor Yellow -Write-Host "" - -# Ejecutar PyInstaller -pyinstaller --name CloudRestoreAS ` - --onefile ` - --windowed ` - --icon=NONE ` - --add-data "app;app" ` - --hidden-import PySide6 ` - --hidden-import pyodbc ` - --hidden-import win32crypt ` - --collect-all PySide6 ` - runner.py - -if ($LASTEXITCODE -eq 0) { - Write-Host "" - Write-Host "===============================================" -ForegroundColor Green - Write-Host "Build completado exitosamente!" -ForegroundColor Green - Write-Host "===============================================" -ForegroundColor Green - Write-Host "" - Write-Host "El ejecutable se encuentra en:" -ForegroundColor Cyan - Write-Host " $PWD\dist\CloudRestoreAS.exe" -ForegroundColor White - Write-Host "" - Write-Host "IMPORTANTE:" -ForegroundColor Yellow - Write-Host " - Copia las carpetas 'data' y 'logs' junto al .exe" -ForegroundColor White - Write-Host " - Asegúrate de tener 7-Zip instalado" -ForegroundColor White - Write-Host " - Asegúrate de tener ODBC Driver 17 for SQL Server" -ForegroundColor White - Write-Host "" +$distDir = Join-Path $Root "dist" +$isUncRoot = $Root -like "\\*" +# PyInstaller no puede usar workpath en UNC (WSL); usar carpeta local en Windows +if ($isUncRoot) { + $buildDir = Join-Path $env:LOCALAPPDATA "Temp\cloudrestore-build" } else { - Write-Host "" - Write-Host "===============================================" -ForegroundColor Red - Write-Host "Error durante el build" -ForegroundColor Red - Write-Host "===============================================" -ForegroundColor Red - Write-Host "" + $buildDir = Join-Path $Root "build" +} +if (Test-Path $buildDir) { + Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue +} +New-Item -ItemType Directory -Force -Path $distDir, $buildDir | Out-Null +if (Test-Path (Join-Path $distDir "CloudRestoreAS.exe")) { + Remove-Item -Force (Join-Path $distDir "CloudRestoreAS.exe") -ErrorAction SilentlyContinue } -# Pausar para ver resultados -Read-Host "Presiona Enter para continuar..." +Write-Host "Ejecutando PyInstaller (onefile)..." -ForegroundColor Yellow +Write-Host "Workpath: $buildDir" -ForegroundColor Gray +& $venvPython -m PyInstaller (Join-Path $Root "packaging\CloudRestoreAS.spec") ` + --clean --noconfirm ` + --distpath $distDir ` + --workpath $buildDir + +$exe = Join-Path $Root "dist\CloudRestoreAS.exe" +if (Test-Path $exe) { + Write-Host "" + Write-Host "Build OK: $exe" -ForegroundColor Green + Write-Host "Distribuir solo el .exe; al ejecutar crea config/ automaticamente." -ForegroundColor Cyan +} else { + Write-Host "Error: no se genero el ejecutable" -ForegroundColor Red + exit 1 +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..8d71b8e --- /dev/null +++ b/build.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Build CloudRestoreAS — PyInstaller onefile portable (Linux) +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +cd "$ROOT" + +echo "===============================================" +echo "CloudRestoreAS - Build Linux (onefile)" +echo "===============================================" + +bash "$ROOT/packaging/scripts/download-bundled-deps.sh" + +VENV="$ROOT/venv-linux" +if [[ ! -x "$VENV/bin/python" ]]; then + rm -rf "$VENV" + python3 -m venv "$VENV" +fi +"$VENV/bin/pip" install --upgrade pip -q +"$VENV/bin/pip" install -r requirements.txt -q + +BUILD_DIR="${CLOUDRESTORE_BUILD_DIR:-/tmp/cloudrestore-build}" +DIST_DIR="${CLOUDRESTORE_DIST_DIR:-$ROOT/dist}" +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" "$DIST_DIR" +rm -f "$DIST_DIR/CloudRestoreAS" + +echo "Ejecutando PyInstaller (onefile)..." +"$VENV/bin/python" -m PyInstaller packaging/CloudRestoreAS.spec \ + --clean --noconfirm \ + --distpath "$DIST_DIR" \ + --workpath "$BUILD_DIR" + +BIN="$ROOT/dist/CloudRestoreAS" +if [[ -f "$BIN" ]]; then + chmod +x "$BIN" + echo "" + echo "Build OK: $BIN" + echo "Distribuir solo el binario; al ejecutar crea config/ automaticamente." +else + echo "Error: no se genero el binario" >&2 + exit 1 +fi diff --git a/packaging/CloudRestoreAS.spec b/packaging/CloudRestoreAS.spec new file mode 100644 index 0000000..7f372ed --- /dev/null +++ b/packaging/CloudRestoreAS.spec @@ -0,0 +1,97 @@ +# -*- mode: python ; coding: utf-8 -*- +"""PyInstaller spec — onefile portable (ejecutar en Windows o Linux según destino).""" +import sys +from pathlib import Path + +block_cipher = None +ROOT = Path(SPECPATH).resolve().parent +platform = "windows" if sys.platform == "win32" else "linux" +bundled = ROOT / "packaging" / "bundled" / platform + +_assets = ROOT / "packaging" / "assets" +datas = [ + (str(ROOT / "packaging" / "templates" / "env.default"), "packaging/templates"), +] +if _assets.is_dir(): + for _icon in _assets.iterdir(): + if _icon.is_file() and _icon.suffix.lower() in (".png", ".ico"): + datas.append((str(_icon), "packaging/assets")) + +binaries = [] + +seven_dir = bundled / "7zip" +if seven_dir.is_dir(): + for item in seven_dir.iterdir(): + if item.is_file(): + binaries.append((str(item), "bundled/7zip")) + +odbc_dir = bundled / "odbc" +if odbc_dir.is_dir(): + for item in odbc_dir.rglob("*"): + if not item.is_file(): + continue + if item.suffix.lower() in (".msi", ".deb"): + continue + rel = item.relative_to(odbc_dir) + dest = f"bundled/odbc/{rel.parent}".replace("\\", "/") + if str(rel.parent) == ".": + dest = "bundled/odbc" + binaries.append((str(item), dest)) + +hiddenimports = [ + "PySide6", + "pyodbc", + "requests", + "paramiko", + "dotenv", + "cryptography.fernet", +] +if platform == "windows": + hiddenimports.append("win32crypt") + +from PyInstaller.utils.hooks import collect_all + +pyside_datas, pyside_binaries, pyside_hidden = collect_all("PySide6") +datas += pyside_datas +binaries += pyside_binaries +hiddenimports += pyside_hidden + +a = Analysis( + [str(ROOT / "runner.py")], + pathex=[str(ROOT)], + binaries=binaries, + datas=datas, + hiddenimports=hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name="CloudRestoreAS", + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) diff --git a/packaging/LEEME.txt b/packaging/LEEME.txt new file mode 100644 index 0000000..557d89b --- /dev/null +++ b/packaging/LEEME.txt @@ -0,0 +1,18 @@ +CloudRestoreAS — despliegue portable +===================================== + +Windows: copie CloudRestoreAS.exe a la carpeta deseada. +Linux: copie CloudRestoreAS y ejecute chmod +x CloudRestoreAS si hace falta. + +1. Ejecute el programa (doble clic o ./CloudRestoreAS). + - Se abre la ventana principal y un icono en la bandeja del sistema. + - El motor arranca automaticamente (CLOUDRESTORE_AUTO_START=true por defecto). +2. Se crean automaticamente: config/, Entrada/, Procesados/, Fallados/, Temp/ +3. Edite config/.env con la URL, token e instancia del servicio de bases. +4. Reinicie la aplicacion si cambio la configuracion. + +Cerrar la ventana (X) minimiza a la bandeja; la app sigue en ejecucion. +Para salir por completo: clic derecho en el icono de bandeja -> Salir, +o menu Archivo -> Salir. + +No requiere Python ni instaladores adicionales en el equipo destino. diff --git a/packaging/bundled-versions.json b/packaging/bundled-versions.json new file mode 100644 index 0000000..3a81da1 --- /dev/null +++ b/packaging/bundled-versions.json @@ -0,0 +1,9 @@ +{ + "seven_zip": "26.01", + "seven_zip_windows_extra": "https://github.com/ip7z/7zip/releases/download/26.01/7z2601-extra.7z", + "seven_zip_windows_7zr": "https://github.com/ip7z/7zip/releases/download/26.01/7zr.exe", + "seven_zip_linux_x64": "https://github.com/ip7z/7zip/releases/download/26.01/7z2601-linux-x64.tar.xz", + "msodbcsql18_msi": "https://go.microsoft.com/fwlink/?linkid=2281204", + "msodbcsql18_deb": "https://packages.microsoft.com/ubuntu/22.04/prod/pool/main/m/msodbcsql18/msodbcsql18_18.5.1.1-1_amd64.deb", + "unixodbc_deb": "http://archive.ubuntu.com/ubuntu/pool/main/u/unixodbc/unixodbc_2.3.12-1ubuntu0.24.04.1_amd64.deb" +} diff --git a/packaging/scripts/build-all.sh b/packaging/scripts/build-all.sh new file mode 100644 index 0000000..3a0f4bd --- /dev/null +++ b/packaging/scripts/build-all.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Genera ambos artefactos: Linux (local/Docker) y Windows (PowerShell + Python en Windows). +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +RELEASE="$ROOT/dist/release" +mkdir -p "$RELEASE" + +echo "=== Build Linux ===" +if [[ -f "$ROOT/build.sh" ]]; then + bash "$ROOT/build.sh" + cp -f "$ROOT/dist/CloudRestoreAS" "$RELEASE/CloudRestoreAS-linux" + chmod +x "$RELEASE/CloudRestoreAS-linux" +fi + +echo "=== Build Windows (via PowerShell) ===" +WSL_PATH=$(wslpath -w "$ROOT" 2>/dev/null || echo "") +if [[ -n "$WSL_PATH" ]] && command -v powershell.exe >/dev/null 2>&1; then + powershell.exe -NoProfile -ExecutionPolicy Bypass -Command " + Set-Location '$WSL_PATH' + & 'C:\Users\Hugo Reyes\AppData\Local\Programs\Python\Python313\python.exe' -m pip install -q -r requirements-windows.txt 2>\$null + & .\build.ps1 + " + if [[ -f "$ROOT/dist/CloudRestoreAS.exe" ]]; then + cp -f "$ROOT/dist/CloudRestoreAS.exe" "$RELEASE/CloudRestoreAS.exe" + fi +else + echo "Omitido: ejecute build.ps1 en Windows manualmente" +fi + +cp -f "$ROOT/packaging/LEEME.txt" "$RELEASE/" +echo "" +echo "Artefactos en: $RELEASE" +ls -la "$RELEASE" diff --git a/packaging/scripts/docker-build-linux.sh b/packaging/scripts/docker-build-linux.sh new file mode 100755 index 0000000..eea822e --- /dev/null +++ b/packaging/scripts/docker-build-linux.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Build Linux onefile dentro de Docker con dependencias Qt del sistema. +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +docker run --rm \ + -v "$ROOT:/app" \ + -w /app \ + ubuntu:22.04 bash -c ' +set -e +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq \ + python3 python3-venv python3-pip curl dpkg-dev binutils unixodbc \ + libglib2.0-0 libdbus-1-3 libxkbcommon0 libfontconfig1 libxcb1 libx11-6 \ + libxcb-xkb1 libegl1 libgl1 libxi6 libxrender1 libxext6 \ + > /dev/null +chmod +x build.sh packaging/scripts/download-bundled-deps.sh +./build.sh +' diff --git a/packaging/scripts/download-bundled-deps.ps1 b/packaging/scripts/download-bundled-deps.ps1 new file mode 100644 index 0000000..b39ecbe --- /dev/null +++ b/packaging/scripts/download-bundled-deps.ps1 @@ -0,0 +1,93 @@ +# Descarga 7-Zip Extra y ODBC para empaquetar en CloudRestoreAS.exe +$ErrorActionPreference = "Stop" +$Root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent +$Versions = Get-Content (Join-Path $Root "packaging\bundled-versions.json") | ConvertFrom-Json +$Bundled = Join-Path $Root "packaging\bundled\windows" +$Seven = Join-Path $Bundled "7zip" +$Odbc = Join-Path $Bundled "odbc" + +New-Item -ItemType Directory -Force -Path $Seven, $Odbc | Out-Null + +Write-Host "==> 7-Zip Extra ($($Versions.seven_zip))" -ForegroundColor Cyan +if (-not (Test-Path (Join-Path $Seven "7z.exe"))) { + $tmp = Join-Path $env:TEMP "cloudrestore-7z-$(Get-Random)" + New-Item -ItemType Directory -Path $tmp | Out-Null + $sevenR = Join-Path $tmp "7zr.exe" + $extra = Join-Path $tmp "7z-extra.7z" + Invoke-WebRequest -Uri $Versions.seven_zip_windows_7zr -OutFile $sevenR + Invoke-WebRequest -Uri $Versions.seven_zip_windows_extra -OutFile $extra + & $sevenR x $extra ("-o" + $Seven) -y | Out-Null + $x64Exe = Join-Path $Seven "x64\7za.exe" + $x64Dll = Join-Path $Seven "x64\7za.dll" + if (Test-Path $x64Exe) { + Copy-Item $x64Exe (Join-Path $Seven "7z.exe") -Force + Copy-Item $x64Exe (Join-Path $Seven "7za.exe") -Force + } + if (Test-Path $x64Dll) { + Copy-Item $x64Dll (Join-Path $Seven "7z.dll") -Force + Copy-Item $x64Dll (Join-Path $Seven "7za.dll") -Force + } + Remove-Item -Recurse -Force $tmp + Write-Host " 7z.exe en $Seven" +} else { + Write-Host " 7-Zip ya existe" +} + +Write-Host "==> ODBC Driver 18 (extraer DLLs del MSI)" -ForegroundColor Cyan +$Msi = Join-Path $Odbc "msodbcsql18.msi" +if (-not (Test-Path (Join-Path $Odbc "msodbcsql18.dll"))) { + if (-not (Test-Path $Msi)) { + Invoke-WebRequest -Uri $Versions.msodbcsql18_msi -OutFile $Msi + } + $extract = Join-Path $Odbc "_msi_extract" + if (Test-Path $extract) { Remove-Item -Recurse -Force $extract } + New-Item -ItemType Directory -Path $extract | Out-Null + $msiArgs = @("/a", "`"$Msi`"", "/qn", "TARGETDIR=`"$extract`"", "IACCEPTMSODBCSQLLICENSETERMS=YES") + $proc = Start-Process msiexec.exe -ArgumentList $msiArgs -Wait -PassThru + if ($proc.ExitCode -ne 0) { + Write-Host " msiexec /a fallo (codigo $($proc.ExitCode)); intentando instalacion silenciosa local..." -ForegroundColor Yellow + $local = Join-Path $Odbc "driver_install" + New-Item -ItemType Directory -Force -Path $local | Out-Null + Start-Process msiexec.exe -ArgumentList "/i", "`"$Msi`"", "/qn", "IACCEPTMSODBCSQLLICENSETERMS=YES", "ADDLOCAL=ALL" -Wait + $sysOdbc = "${env:ProgramFiles}\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn" + if (Test-Path $sysOdbc) { + Get-ChildItem $sysOdbc -Filter "*.dll" | Copy-Item -Destination $Odbc -Force + } + $driverDir = "${env:ProgramFiles}\Microsoft SQL Server\Client SDK\ODBC\180\Tools\Binn" + if (Test-Path $driverDir) { + Get-ChildItem $driverDir -Filter "*.dll" | Copy-Item -Destination $Odbc -Force + } + foreach ($dll in @("msodbcsql18.dll", "msodbcsql17.dll")) { + $sys = Join-Path $env:SystemRoot "System32\$dll" + if (Test-Path $sys) { Copy-Item $sys $Odbc -Force } + } + } else { + Get-ChildItem -Path $extract -Recurse -Filter "msodbcsql*.dll" | ForEach-Object { + Copy-Item $_.FullName $Odbc -Force + } + Get-ChildItem -Path $extract -Recurse -Filter "*.dll" | Where-Object { + $_.Name -match "mso|odbc|ssl|crypto|bcp" + } | ForEach-Object { Copy-Item $_.FullName $Odbc -Force -ErrorAction SilentlyContinue } + Remove-Item -Recurse -Force $extract -ErrorAction SilentlyContinue + } + Write-Host " DLLs ODBC en $Odbc" +} else { + Write-Host " ODBC ya existe" +} + +if (-not (Test-Path (Join-Path $Odbc "odbcinst.ini"))) { + @" +[ODBC Driver 18 for SQL Server] +Description=Microsoft ODBC Driver 18 for SQL Server +Driver=msodbcsql18.dll +UsageCount=1 + +[ODBC Driver 17 for SQL Server] +Description=Microsoft ODBC Driver 17 for SQL Server +Driver=msodbcsql17.dll +UsageCount=1 +"@ | Set-Content (Join-Path $Odbc "odbcinst.ini") -Encoding ASCII + "[ODBC Data Sources]" | Set-Content (Join-Path $Odbc "odbc.ini") -Encoding ASCII +} + +Write-Host "Listo: $Bundled" -ForegroundColor Green diff --git a/packaging/scripts/download-bundled-deps.sh b/packaging/scripts/download-bundled-deps.sh new file mode 100755 index 0000000..1202db0 --- /dev/null +++ b/packaging/scripts/download-bundled-deps.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Descarga 7zz y bibliotecas ODBC para empaquetar en el binario Linux. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +VERSIONS="$ROOT/packaging/bundled-versions.json" +BUNDLED="$ROOT/packaging/bundled/linux" +SEVEN="$BUNDLED/7zip" +ODBC="$BUNDLED/odbc/lib" + +mkdir -p "$SEVEN" "$ODBC" + +read_url() { + python3 -c "import json; print(json.load(open('$VERSIONS'))['$1'])" +} + +LINUX_7Z_URL="$(read_url seven_zip_linux_x64)" +MSODBC_DEB="$(read_url msodbcsql18_deb)" + +echo "==> 7-Zip Linux (7zz)" +if [[ ! -f "$SEVEN/7zz" ]]; then + TMP=$(mktemp -d) + curl -fsSL -o "$TMP/7z.tar.xz" "$LINUX_7Z_URL" + tar -xJf "$TMP/7z.tar.xz" -C "$TMP" + cp "$TMP/7zz" "$SEVEN/7zz" + chmod +x "$SEVEN/7zz" + rm -rf "$TMP" + echo " 7zz instalado" +else + echo " 7zz ya existe" +fi + +echo "==> ODBC Driver 18 + unixODBC" +if [[ -z "$(ls -A "$ODBC" 2>/dev/null || true)" ]]; then + TMP=$(mktemp -d) + cd "$TMP" + curl -fsSL -o msodbcsql18.deb "$MSODBC_DEB" + + if command -v apt-get >/dev/null 2>&1; then + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -y -qq unixodbc dpkg-dev > /dev/null 2>&1 || true + if dpkg -l unixodbc 2>/dev/null | grep -q ^ii; then + find /usr/lib -name 'libodbc*.so*' -exec cp -P {} "$ODBC/" \; 2>/dev/null || true + find /usr/lib -name 'libodbcinst*.so*' -exec cp -P {} "$ODBC/" \; 2>/dev/null || true + fi + fi + + dpkg-deb -x msodbcsql18.deb msodbcsql + find msodbcsql -name '*.so*' -exec cp -P {} "$ODBC/" \; + cd "$ROOT" + rm -rf "$TMP" + echo " Bibliotecas ODBC en $ODBC" +else + echo " ODBC ya existe" +fi + +echo "Listo: $BUNDLED" diff --git a/packaging/scripts/package-release.sh b/packaging/scripts/package-release.sh new file mode 100644 index 0000000..a0041f2 --- /dev/null +++ b/packaging/scripts/package-release.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Empaqueta artefactos en dist/release/ +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +RELEASE="$ROOT/dist/release" +mkdir -p "$RELEASE" + +[[ -f "$ROOT/dist/CloudRestoreAS.exe" ]] && cp -f "$ROOT/dist/CloudRestoreAS.exe" "$RELEASE/" +[[ -f "$ROOT/dist/CloudRestoreAS" ]] && cp -f "$ROOT/dist/CloudRestoreAS" "$RELEASE/CloudRestoreAS-linux" && chmod +x "$RELEASE/CloudRestoreAS-linux" +cp -f "$ROOT/packaging/LEEME.txt" "$RELEASE/" + +python3 <<'PY' +import zipfile +from pathlib import Path +release = Path("dist/release") +if (release / "CloudRestoreAS.exe").exists(): + with zipfile.ZipFile(release / "CloudRestoreAS-win.zip", "w", zipfile.ZIP_DEFLATED) as z: + z.write(release / "CloudRestoreAS.exe", "CloudRestoreAS.exe") + z.write(release / "LEEME.txt", "LEEME.txt") +PY + +if [[ -f "$RELEASE/CloudRestoreAS-linux" ]]; then + tar czf "$RELEASE/CloudRestoreAS-linux.tar.gz" -C "$RELEASE" CloudRestoreAS-linux LEEME.txt +fi + +echo "Release en: $RELEASE" +ls -lh "$RELEASE" diff --git a/packaging/templates/env.default b/packaging/templates/env.default new file mode 100644 index 0000000..407ec96 --- /dev/null +++ b/packaging/templates/env.default @@ -0,0 +1,18 @@ +# CloudRestoreAS — configuración local (editar y reiniciar la app) + +# Arranca el motor al abrir (la ventana siempre se muestra salvo START_MINIMIZED=true) +CLOUDRESTORE_AUTO_START=true +CLOUDRESTORE_START_MINIMIZED=false +CLOUDRESTORE_REGISTER_AUTOSTART=true + +# Servicio PANEL_BASES_ANEXO24 +CLOUDRESTORE_PANEL_API_URL= +CLOUDRESTORE_PANEL_API_TOKEN= +CLOUDRESTORE_PANEL_INSTANCE_KEY= +CLOUDRESTORE_PANEL_VERIFY_SSL=false + +# Carpetas (se crean automáticamente junto al ejecutable) +CLOUDRESTORE_INPUT_FOLDER={APP_DIR}/Entrada +CLOUDRESTORE_PROCESSED_FOLDER={APP_DIR}/Procesados +CLOUDRESTORE_FAILED_FOLDER={APP_DIR}/Fallados +CLOUDRESTORE_EXTRACT_FOLDER={APP_DIR}/Temp diff --git a/requirements-windows.txt b/requirements-windows.txt new file mode 100644 index 0000000..c0444a2 --- /dev/null +++ b/requirements-windows.txt @@ -0,0 +1,2 @@ +-r requirements.txt +pywin32>=306 diff --git a/requirements.txt b/requirements.txt index ab8c6a0..b5f4bfe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,13 @@ -# CloudRestoreAS - Dependencias +# CloudRestoreAS - Dependencias base (Windows y Linux) -# PySide6 para la interfaz gráfica PySide6>=6.6.0 - -# pyodbc para SQL Server pyodbc>=5.0.0 - -# pywin32 para DPAPI (cifrado de passwords) -pywin32>=306 - -# requests para consultar al PANEL el servidor de restauración de cada base requests>=2.31.0 - -# paramiko para transferir el .bak por SFTP/SSH a los servidores SQL externos paramiko>=3.4.0 +python-dotenv>=1.0.0 +cryptography>=42.0.0 -# Para empaquetado (opcional) +# Empaquetado (opcional, solo build) pyinstaller>=6.0.0 # Pruebas (opcional) diff --git a/runner.py b/runner.py index 93a053a..5bdca07 100644 --- a/runner.py +++ b/runner.py @@ -1,51 +1,91 @@ """Punto de entrada principal de la aplicación.""" +import argparse import sys from pathlib import Path -# Agregar el directorio raíz al path -ROOT_DIR = Path(__file__).parent -sys.path.insert(0, str(ROOT_DIR)) +ROOT_DIR = Path(__file__).parent.resolve() +if not getattr(sys, "frozen", False): + sys.path.insert(0, str(ROOT_DIR)) + +# Bootstrap y ODBC antes de importar módulos que usan pyodbc +from app.config.bootstrap import ensure_runtime_layout +from app.config.odbc_setup import configure_odbc_environment + +ensure_runtime_layout() +configure_odbc_environment() from PySide6.QtWidgets import QApplication from PySide6.QtCore import Qt +from app.config import get_launch_options, initialize_env, is_panel_configured from app.ui.main_window import MainWindow from app.utils.logger import app_logger -from app.constants import DATA_DIR, LOGS_DIR + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="CloudRestoreAS") + parser.add_argument( + "--minimized", + action="store_true", + help="Inicia minimizado en la bandeja del sistema", + ) + parser.add_argument( + "--start-engine", + action="store_true", + help="Inicia el motor de restauración automáticamente", + ) + return parser.parse_args(argv) def main(): """Función principal.""" - # Crear directorios necesarios - DATA_DIR.mkdir(parents=True, exist_ok=True) - LOGS_DIR.mkdir(parents=True, exist_ok=True) - + args = parse_args() + launch = get_launch_options() + initialize_env() + + minimized = args.minimized or launch.minimized + panel_ok = is_panel_configured() + start_engine = args.start_engine or launch.start_engine + app_logger.info("=" * 80) app_logger.info("CloudRestoreAS - Iniciando aplicación") + app_logger.info(f"Directorio app: {ROOT_DIR}") + if minimized: + app_logger.info("Modo: ventana oculta (--minimized o START_MINIMIZED)") + else: + app_logger.info("Modo: ventana visible; cerrar minimiza a bandeja") + if start_engine: + app_logger.info("Modo: motor auto-inicio") + if not panel_ok: + app_logger.warning( + "Servicio PANEL_BASES_ANEXO24 no configurado en config/.env — " + "complete CLOUDRESTORE_PANEL_* para enrutamiento automático." + ) app_logger.info("=" * 80) - - # Configurar aplicación Qt + app = QApplication(sys.argv) app.setApplicationName("CloudRestoreAS") app.setOrganizationName("Aduanasoft") - - # Habilitar high DPI scaling app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling) - + app.setQuitOnLastWindowClosed(False) + try: - # Crear y mostrar ventana principal - window = MainWindow() - window.show() - - app_logger.info("Ventana principal mostrada") - - # Ejecutar aplicación + window = MainWindow( + minimized=minimized, + start_engine=start_engine, + panel_configured=panel_ok, + ) + if not minimized: + window.show() + app_logger.info("Ventana principal mostrada") + else: + app_logger.info("Ventana oculta; icono en bandeja del sistema") + exit_code = app.exec() - app_logger.info(f"Aplicación finalizada con código: {exit_code}") sys.exit(exit_code) - + except Exception as e: app_logger.critical(f"Error fatal en la aplicación: {e}", exc_info=True) sys.exit(1) diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py new file mode 100644 index 0000000..392659f --- /dev/null +++ b/tests/test_bootstrap.py @@ -0,0 +1,79 @@ +"""Pruebas de bootstrap y carga de .env.""" + +import os +from pathlib import Path + +import pytest + +from app.config.bootstrap import ensure_runtime_layout +from app.config.env_loader import ( + apply_env_overrides, + get_launch_options, + is_panel_configured, + render_env_template, +) +from app.constants import CONFIG_DIR, DEFAULT_CONFIG, DIR_ENTRADA, ENV_PATH + + +def test_render_env_template_contains_app_dir(tmp_path: Path): + text = render_env_template(tmp_path) + assert str(tmp_path) in text + assert "CLOUDRESTORE_PANEL_API_URL" in text + + +def test_ensure_runtime_layout_creates_structure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr("app.config.bootstrap.APP_DIR", tmp_path) + monkeypatch.setattr("app.config.bootstrap.CONFIG_DIR", tmp_path / "config") + monkeypatch.setattr("app.config.bootstrap.DATA_DIR", tmp_path / "config" / "data") + monkeypatch.setattr("app.config.bootstrap.LOGS_DIR", tmp_path / "config" / "logs") + monkeypatch.setattr("app.config.bootstrap.ODBC_DIR", tmp_path / "config" / "odbc") + monkeypatch.setattr("app.config.bootstrap.SEVEN_ZIP_DIR", tmp_path / "config" / "7zip") + monkeypatch.setattr("app.config.bootstrap.ENV_PATH", tmp_path / "config" / ".env") + monkeypatch.setattr("app.config.bootstrap.DIR_ENTRADA", tmp_path / "Entrada") + monkeypatch.setattr("app.config.bootstrap.DIR_PROCESADOS", tmp_path / "Procesados") + monkeypatch.setattr("app.config.bootstrap.DIR_FALLADOS", tmp_path / "Fallados") + monkeypatch.setattr("app.config.bootstrap.DIR_TEMP", tmp_path / "Temp") + monkeypatch.setattr("app.constants.DB_PATH", tmp_path / "config" / "data" / "app.db") + + ensure_runtime_layout() + + assert (tmp_path / "config" / ".env").is_file() + assert (tmp_path / "Entrada").is_dir() + assert (tmp_path / "config" / "data" / "app.db").is_file() + + +def test_is_panel_configured(monkeypatch: pytest.MonkeyPatch): + monkeypatch.delenv("CLOUDRESTORE_PANEL_API_URL", raising=False) + monkeypatch.delenv("CLOUDRESTORE_PANEL_API_TOKEN", raising=False) + monkeypatch.delenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", raising=False) + assert not is_panel_configured() + + monkeypatch.setenv("CLOUDRESTORE_PANEL_API_URL", "https://panel:3000") + monkeypatch.setenv("CLOUDRESTORE_PANEL_API_TOKEN", "secret") + monkeypatch.setenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", "srv1") + assert is_panel_configured() + + +def test_get_launch_options_auto_start_does_not_minimize(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("CLOUDRESTORE_AUTO_START", "true") + monkeypatch.delenv("CLOUDRESTORE_START_MINIMIZED", raising=False) + opts = get_launch_options() + assert opts.start_engine is True + assert opts.minimized is False + + +def test_get_launch_options_start_minimized(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("CLOUDRESTORE_START_MINIMIZED", "true") + opts = get_launch_options() + assert opts.minimized is True + + +def test_render_env_template_documents_start_minimized(tmp_path: Path): + text = render_env_template(tmp_path) + assert "CLOUDRESTORE_START_MINIMIZED=false" in text + + +def test_apply_env_overrides_paths(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("CLOUDRESTORE_INPUT_FOLDER", "/tmp/in") + cfg = apply_env_overrides(DEFAULT_CONFIG.copy()) + assert cfg["paths"]["input_folder"] == "/tmp/in"