feature/generador-instaladores-linux-windows
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
"""PyInstaller spec — onefile portable (ejecutar en Windows o Linux según destino)."""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -8,9 +9,27 @@ ROOT = Path(SPECPATH).resolve().parent
|
||||
platform = "windows" if sys.platform == "win32" else "linux"
|
||||
bundled = ROOT / "packaging" / "bundled" / platform
|
||||
|
||||
|
||||
def _read_app_version() -> str:
|
||||
"""
|
||||
Lee __version__ de app/__init__.py sin importar el paquete: el spec corre en el
|
||||
intérprete de PyInstaller y no debe cargar las dependencias de la app.
|
||||
"""
|
||||
text = (ROOT / "app" / "__init__.py").read_text(encoding="utf-8")
|
||||
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', text, re.MULTILINE)
|
||||
if not match:
|
||||
raise SystemExit("No se pudo leer __version__ de app/__init__.py")
|
||||
return match.group(1)
|
||||
|
||||
|
||||
APP_VERSION = _read_app_version()
|
||||
|
||||
_assets = ROOT / "packaging" / "assets"
|
||||
datas = [
|
||||
(str(ROOT / "packaging" / "templates" / "env.default"), "packaging/templates"),
|
||||
# bundled-versions.json va embebido para que el bootstrap sepa qué versiones de
|
||||
# 7-Zip/ODBC trae este build y re-despliegue config/ cuando cambien.
|
||||
(str(ROOT / "packaging" / "bundled-versions.json"), "packaging"),
|
||||
]
|
||||
if _assets.is_dir():
|
||||
for _icon in _assets.iterdir():
|
||||
@@ -89,6 +108,82 @@ pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
_icon_file = ROOT / "packaging" / "assets" / "tray-icon.ico"
|
||||
_icon = str(_icon_file) if _icon_file.is_file() else None
|
||||
|
||||
|
||||
def _version_quad(version: str) -> tuple:
|
||||
"""VSVersionInfo exige exactamente 4 enteros; se rellena o recorta lo que haga falta."""
|
||||
parts = []
|
||||
for chunk in version.split(".")[:4]:
|
||||
digits = "".join(c for c in chunk if c.isdigit())
|
||||
parts.append(int(digits) if digits else 0)
|
||||
while len(parts) < 4:
|
||||
parts.append(0)
|
||||
return tuple(parts)
|
||||
|
||||
|
||||
def _version_info_dir():
|
||||
"""
|
||||
Directorio donde escribir el recurso de versión.
|
||||
|
||||
Se prefiere `workpath`, el directorio de trabajo que PyInstaller inyecta en el namespace
|
||||
del spec, por dos razones concretas de este proyecto:
|
||||
1. build.ps1 lo redirige a %LOCALAPPDATA%\\Temp cuando el repo está en una ruta UNC
|
||||
(\\\\wsl.localhost\\...), que es el caso al compilar Windows desde WSL.
|
||||
2. `build/` en la raíz del repo lo crea el build de Linux DENTRO DE DOCKER, así que
|
||||
queda propiedad de root; el Python de Windows escribe por SMB como el usuario
|
||||
normal y recibe PermissionError.
|
||||
"""
|
||||
candidate = globals().get("workpath")
|
||||
if candidate:
|
||||
return Path(candidate)
|
||||
return ROOT / "build"
|
||||
|
||||
|
||||
def _write_version_info(version: str) -> str:
|
||||
"""
|
||||
Genera el recurso de versión de Windows. Sin esto el .exe sale sin metadatos y las
|
||||
Propiedades del archivo no muestran nada, lo que complica auditar qué versión está
|
||||
instalada en un servidor.
|
||||
"""
|
||||
quad = _version_quad(version)
|
||||
out = _version_info_dir() / "version_info.txt"
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(
|
||||
f"""VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
filevers={quad},
|
||||
prodvers={quad},
|
||||
mask=0x3f,
|
||||
flags=0x0,
|
||||
OS=0x40004,
|
||||
fileType=0x1,
|
||||
subtype=0x0,
|
||||
date=(0, 0),
|
||||
),
|
||||
kids=[
|
||||
StringFileInfo([
|
||||
StringTable('080904B0', [
|
||||
StringStruct('CompanyName', 'Aduanasoft'),
|
||||
StringStruct('FileDescription', 'CloudRestoreAS - Restauracion automatica SQL Server'),
|
||||
StringStruct('FileVersion', '{version}'),
|
||||
StringStruct('InternalName', 'CloudRestoreAS'),
|
||||
StringStruct('LegalCopyright', 'Aduanasoft'),
|
||||
StringStruct('OriginalFilename', 'CloudRestoreAS.exe'),
|
||||
StringStruct('ProductName', 'CloudRestoreAS'),
|
||||
StringStruct('ProductVersion', '{version}'),
|
||||
])
|
||||
]),
|
||||
VarFileInfo([VarStruct('Translation', [0x0809, 1200])]),
|
||||
],
|
||||
)
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return str(out)
|
||||
|
||||
|
||||
# El recurso de versión solo existe en PE/Windows; en Linux PyInstaller lo ignoraría.
|
||||
_version_info = _write_version_info(APP_VERSION) if platform == "windows" else None
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
@@ -98,6 +193,7 @@ exe = EXE(
|
||||
[],
|
||||
name="CloudRestoreAS",
|
||||
icon=_icon,
|
||||
version=_version_info,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
Reference in New Issue
Block a user