Files
CloudRecoveryAS/BUILD.md

13 KiB

BUILD.md — Compilación, empaquetado y despliegue

Referencia de todos los comandos, ejecutables y scripts para generar, empaquetar, instalar y verificar CloudRestoreAS en Windows y Linux.

El ejecutable es autocontenido: no requiere Python, 7-Zip, driver ODBC ni librerías Qt instaladas en el equipo destino. Todo se embebe dentro del binario en tiempo de build.


1. Todo en una tarea (recomendado) — build-all.sh

Orquestador único. Se ejecuta desde WSL (bash). Genera Windows + Linux + los paquetes.

./build-all.sh                 # Windows + Linux + paquetes versionados en dist/release/
./build-all.sh --linux-only    # solo Linux (Docker)
./build-all.sh --windows-only  # solo Windows (PowerShell + build.ps1)
./build-all.sh --no-package    # compila sin generar .tar.gz/.zip
./build-all.sh --clean         # rebuild desde cero (borra venvs, bundled, dist)
./build-all.sh --publish       # además publica en Gitea (ver §5; requiere GITEA_TOKEN)
./build-all.sh --help

Requisitos:

  • Linux: Docker (usa docker-build-linux.sh en ubuntu:22.04).
  • Windows: powershell.exe accesible desde WSL + Python 3.11+ instalado en Windows (build.ps1 lo auto-detecta en %LOCALAPPDATA%\Programs\Python\Python31X).
  • Es fail-soft: si no hay powershell.exe, compila solo Linux y avisa.

El build de Windows se hace sobre la ruta \\wsl.localhost\..., por eso tarda (~15-18 min: pip install de PySide6 + PyInstaller). El de Linux (Docker, con deps cacheadas) es rápido.

packaging/scripts/build-all.sh es un wrapper que delega en este mismo script.


2. Builds individuales

Objetivo Comando Salida Notas
Linux (Docker, recomendado) bash packaging/scripts/docker-build-linux.sh dist/CloudRestoreAS ubuntu:22.04 con todas las deps + patchelf; garantiza autocontención
Linux (host) ./build.sh dist/CloudRestoreAS Requiere que el host tenga las libs Qt/ODBC; usar solo si no hay Docker
Windows .\build.ps1 (en Windows/PowerShell) dist\CloudRestoreAS.exe Auto-detecta Python 3.11+; crea venv-windows

Ambos usan el mismo spec: packaging/CloudRestoreAS.spec (PyInstaller onefile, console=False).


3. Dependencias embebidas (build-time)

Se descargan e integran al binario. No se instalan en el destino.

Script Qué embebe
packaging/scripts/download-bundled-deps.sh (Linux) 7zz (7-Zip Linux), driver MS ODBC 18 + unixODBC + Kerberos/GSSAPI + libltdl + OpenSSL, y cluster Qt xcb/X11 + EGL. Aplica patchelf --set-rpath '$ORIGIN' a las .so del cluster ODBC para que resuelvan entre sí.
packaging/scripts/download-bundled-deps.ps1 (Windows) 7z.exe y msodbcsql18.dll.

Versiones/URLs en packaging/bundled-versions.json. Los binarios quedan en packaging/bundled/{linux,windows}/ (git-ignored, se generan en el build).

Ubicación en runtime (creada por el bootstrap en la primera ejecución):

  • config/7zip/7zz — extractor.
  • config/odbc/lib/ — driver ODBC + toda su cadena (resuelven por $ORIGIN).
  • Cluster Qt xcb/EGL — dentro del onefile, en PySide6/Qt/lib.

4. Empaquetado — package-release.sh

bash packaging/scripts/package-release.sh

La versión sale de app/__init__.py (fuente única) y va en el nombre de cada paquete. El script aborta si la versión no es puntos-y-números: el PANEL las compara como tuplas de enteros y otro formato rompería en silencio la detección de "hay versión nueva".

Genera en dist/release/:

Archivo Contenido
CloudRestoreAS-<version>-linux-<arch>.tar.gz CloudRestoreAS/ → binario + install.sh + packaging/linux/cloudrestoreas.service + LEEME.txt
CloudRestoreAS-<version>-win-<arch>.zip CloudRestoreAS/CloudRestoreAS.exe + install.ps1 + LEEME.txt
SHA256SUMS Checksums de los dos paquetes
release.json Manifiesto: versión, fecha, artefactos (platform/arch/tamaño/sha256) y deps embebidas. Es lo que lee publish-release.sh para saber qué subir

Además deja copias crudas sin versión (CloudRestoreAS.exe, CloudRestoreAS-linux) para la verificación de autocontención de la §9. Esas no se publican.

arch se declara con CLOUDRESTORE_TARGET_ARCH (default x86_64): el .exe lo compila el host Windows y desde WSL no hay forma de inferir su arquitectura.


5. Publicación a Gitea — publish-release.sh

Los binarios viven en el registro de paquetes genéricos de Gitea, que es la fuente de verdad que consume el PANEL. Gitea calcula y expone el sha256 de cada archivo, así que no hace falta mantener un manifiesto de integridad propio: el PANEL verifica sus descargas contra ese hash.

# Requiere un PAT de Gitea con scope write:package
export GITEA_TOKEN=xxxxxxxx

bash packaging/scripts/publish-release.sh --dry-run   # lista qué subiría
bash packaging/scripts/publish-release.sh             # sube y verifica
bash packaging/scripts/publish-release.sh --notify-panel   # y avisa al PANEL

Destino: https://git.aduanasoft.com/api/packages/ADUANASOFT/generic/cloudrestoreas/<version>/

Tras subir, el script relee la API de Gitea y compara el sha256 y el tamaño de cada artefacto contra release.json. Si no coinciden falla: una publicación a medias no debe pasar por buena, porque el PANEL rechazaría la descarga por hash y el error aparecería mucho después, al intentar instalar.

Los paquetes genéricos son inmutables: reintentar la misma versión da HTTP 409. Lo correcto es subir una versión nueva; --force borra y reemplaza, y solo aplica cuando la versión anterior nunca se instaló en ningún servidor.

Todo en una sola tarea:

GITEA_TOKEN=xxxx ./build-all.sh --publish --notify-panel

--publish exige el build de ambas plataformas: publicar una versión a la que le falta una dejaría en el PANEL un release que no se le puede instalar a la mitad de los servidores, y corregirlo obligaría a quemar el número de versión.

Variable Default Para qué
GITEA_TOKEN Requerida. PAT con scope write:package
GITEA_BASE_URL https://git.aduanasoft.com Instancia de Gitea
GITEA_OWNER ADUANASOFT Organización dueña del paquete
CRAS_PACKAGE cloudrestoreas Nombre del paquete genérico
PANEL_API_URL Solo con --notify-panel
CLOUDRESTORE_API_TOKEN Solo con --notify-panel (token de servicio del PANEL)

Siguiente paso, en el PANEL: /versiones-cras → Sincronizar con Gitea → Activar, y de ahí Instalar / Actualizar por servidor.


6. Instalación / despliegue

Lo normal es que el PANEL instale por SSH desde /versiones-cras, sembrando además las credenciales. Lo de abajo es el camino manual y lo que el PANEL ejecuta por dentro.

Linux — install.sh (no instala nada del sistema)

tar xzf CloudRestoreAS-<version>-linux-x86_64.tar.gz && cd CloudRestoreAS

sudo ./install.sh --service     # servicio systemd 24/7 headless (recomendado en servidor)
     ./install.sh --desktop      # autostart .desktop (requiere sesión gráfica)
     ./install.sh                # solo instala + bootstrap; lo corres a mano
     ./install.sh --help

Variables: PREFIX=/opt/cloudrestoreas (destino), SERVICE_USER=<usuario> (usuario del servicio).

Detiene el servicio antes de reemplazar el binario (un ELF en ejecución da ETXTBSY) y lo vuelve a levantar si estaba activo.

Servicio systemd:

systemctl status cloudrestoreas
journalctl -u cloudrestoreas -f
sudo systemctl restart cloudrestoreas    # tras editar config/.env

Windows — install.ps1 (autocontenido, sin NSSM ni descargas)

Expand-Archive CloudRestoreAS-<version>-win-x86_64.zip -DestinationPath .
cd CloudRestoreAS

.\install.ps1 -Service      # tarea programada ONSTART como SYSTEM (24/7 headless)
.\install.ps1 -Desktop      # arranque al iniciar sesión (tarea ONLOGON de la app)
.\install.ps1               # solo instala + bootstrap
Get-Help .\install.ps1 -Detailed

Parámetros: -Prefix (default C:\Aduanasoft\CloudRestoreAS), -PanelEnvFile.

-Service requiere PowerShell como Administrador (la tarea corre como SYSTEM). El arranque 24/7 se resuelve con una tarea programada, no con NSSM: descargarlo violaría la regla de que en el servidor destino no se instala ni se baja nada.

scripts/dev-setup.ps1 es otra cosa: prepara el entorno de desarrollo (Python, venv, requirements.txt) para correr python runner.py. No sirve para desplegar el binario.

Siembra de credenciales del PANEL

Ambos instaladores aceptan un archivo KEY=valor con las claves CLOUDRESTORE_PANEL_*, que fusionan en config/.env (replace-or-append, idempotente, con lista blanca) y luego borran:

./install.sh --service --panel-env-file /tmp/panel.env
.\install.ps1 -Service -PanelEnvFile C:\Temp\panel.env

Va por archivo y no por argumentos a propósito: un token en la línea de comandos queda visible en ps y en el historial del servidor destino.


7. Ejecución manual y flags del binario

# Linux servidor sin pantalla (headless): motor de restauración sin GUI
QT_QPA_PLATFORM=offscreen ./CloudRestoreAS --start-engine --headless

# Linux con escritorio o Windows: abre la GUI normal
./CloudRestoreAS
Flag / variable Efecto
--start-engine Inicia el motor de restauración al arrancar
--headless Fuerza modo sin interfaz (Qt offscreen), para servidores sin display
--minimized Inicia minimizado en la bandeja
--version Imprime CloudRestoreAS <version> (<platform>/<arch>) y termina. En Windows requiere consola del padre (el .exe es console=False); el instalador remoto prefiere leer config/.version
QT_QPA_PLATFORM=offscreen Plataforma Qt sin display (el binario ya cae a esto automáticamente si no hay DISPLAY/WAYLAND_DISPLAY en Linux)

En Linux sin DISPLAY, el binario selecciona offscreen solo; con display usa xcb.


8. Configuración (primera ejecución)

El binario crea automáticamente: config/, config/.env, Entrada/, Procesados/, Fallados/, Temp/. Editar config/.env:

CLOUDRESTORE_AUTO_START=true            # el motor arranca solo
CLOUDRESTORE_PANEL_API_URL=...          # servicio PANEL_BASES_ANEXO24 (enrutamiento)
CLOUDRESTORE_PANEL_API_TOKEN=...
CLOUDRESTORE_PANEL_INSTANCE_KEY=...

El PANEL entrega el servidor SQL destino y su data_folder. Con SQL Server sobre Linux serán rutas POSIX (p. ej. /var/opt/mssql/data); el RESTORE ... MOVE adapta el separador automáticamente según el formato del data_folder.


9. Verificación de autocontención (contenedor pelado)

Confirma que el binario Linux corre sin instalar NADA del sistema:

docker run --rm -v "$PWD/dist:/dist:ro" debian:12-slim bash -c '
  cp /dist/CloudRestoreAS /root/app && cd /root
  QT_QPA_PLATFORM=offscreen timeout 12 ./app --headless --start-engine 2>&1 | \
    grep -iE "offscreen|Ventana principal|Traceback|platform plugin"
  # ODBC: driver + cadena resuelven por $ORIGIN (debe dar 0)
  ldd /root/config/odbc/lib/libmsodbcsql-18*.so* 2>&1 | grep -c "not found"
  # 7-Zip embebido
  /root/config/7zip/7zz | head -1
'

Esperado: arranca en offscreen sin errores Qt, 0 deps ODBC faltantes, 7zz ejecuta.


10. Artefactos y ubicaciones

Ruta Qué es
dist/CloudRestoreAS Binario Linux onefile
dist/CloudRestoreAS.exe Ejecutable Windows onefile
dist/release/CloudRestoreAS-<version>-*.{tar.gz,zip} Paquetes de despliegue publicables (binario + instalador + docs)
dist/release/release.json Manifiesto de la versión (lo consume publish-release.sh)
dist/release/SHA256SUMS Checksums de los paquetes
config/.version Sello de la versión que corrió (lo lee el instalador remoto por SFTP)
config/.bundled_deps Sello del bundled-versions.json desplegado; si cambia, se re-copian config/7zip y config/odbc
packaging/bundled/{linux,windows}/ Deps embebidas (generadas en build; git-ignored)
venv-linux/, venv-windows/ Entornos virtuales de build (git-ignored)

11. Scripts de referencia rápida

Script Propósito
build-all.sh Todo en uno (Windows + Linux + paquetes) desde WSL
build.sh / build.ps1 Build individual Linux / Windows
packaging/scripts/docker-build-linux.sh Build Linux en contenedor controlado
packaging/scripts/download-bundled-deps.sh / .ps1 Descarga+embebe deps
packaging/scripts/package-release.sh Genera los paquetes versionados + SHA256SUMS + release.json
packaging/scripts/publish-release.sh Publica en los paquetes genéricos de Gitea y verifica el sha256
install.sh / install.ps1 Instalador de despliegue Linux / Windows (autocontenidos)
scripts/dev-setup.ps1 Entorno de desarrollo en Windows (Python + venv). No sirve para desplegar
packaging/linux/cloudrestoreas.service Unit systemd (24/7 headless)