Permet de distribuer l'application en executable Windows double-clic, sans Python/Node/terminal pour l'utilisateur final. - `desktop.py` : demarre le serveur sur un port libre dans un thread et ouvre une fenetre native (pywebview). `PLESNA_DEBUG=1` active les DevTools. Commande `plesna-gerance desktop` (import paresseux). - `paths.py` : resolution centralisee des chemins selon le contexte (dev, executable PyInstaller, surcharge par variables d'env). Les donnees vont dans un dossier utilisateur inscriptible (%APPDATA%), les ressources dans le bundle. connection.py/storage.py utilisent `get_data_dir()`, app.py `resource_path()`. - app.py : type MIME `.mjs` force (modules ES / worker PDF.js sous Windows). - Groupes de deps optionnels desktop / desktop-linux / build. - packaging/ : spec PyInstaller, build_windows.ps1, installeur Inno Setup. Workflow GitHub build-windows.yml (runner Windows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
2.3 KiB
Python
84 lines
2.3 KiB
Python
# -*- mode: python ; coding: utf-8 -*-
|
|
"""Spec PyInstaller pour Plesna Gérance (application de bureau autonome).
|
|
|
|
À lancer depuis la racine du projet :
|
|
|
|
pyinstaller packaging/plesna_gerance.spec
|
|
|
|
Produit un exécutable unique (onefile, sans console) qui embarque :
|
|
- le code Python et ses dépendances (FastAPI, uvicorn, pdfplumber, pywebview) ;
|
|
- le frontend déjà buildé (``frontend/dist``).
|
|
|
|
Le frontend doit être buildé AVANT (``cd frontend && npm run build``).
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
from PyInstaller.utils.hooks import collect_all, collect_submodules
|
|
|
|
# Racine du projet (le .spec est dans packaging/).
|
|
ROOT = Path(SPECPATH).resolve().parent
|
|
|
|
# --- Ressources embarquées ------------------------------------------------
|
|
datas = [
|
|
# Frontend buildé -> extrait dans _MEIPASS/frontend/dist (cf. paths.resource_path)
|
|
(str(ROOT / "frontend" / "dist"), "frontend/dist"),
|
|
]
|
|
binaries = []
|
|
hiddenimports = []
|
|
|
|
# Dépendances à embarquer en totalité (modules dynamiques mal détectés).
|
|
for pkg in ("uvicorn", "pdfplumber", "pdfminer", "pypdfium2"):
|
|
pkg_datas, pkg_binaries, pkg_hidden = collect_all(pkg)
|
|
datas += pkg_datas
|
|
binaries += pkg_binaries
|
|
hiddenimports += pkg_hidden
|
|
|
|
# uvicorn charge ses protocoles/boucles par import dynamique.
|
|
hiddenimports += collect_submodules("uvicorn")
|
|
|
|
block_cipher = None
|
|
|
|
a = Analysis(
|
|
[str(ROOT / "packaging" / "app_entry.py")],
|
|
pathex=[str(ROOT / "src")],
|
|
binaries=binaries,
|
|
datas=datas,
|
|
hiddenimports=hiddenimports,
|
|
hookspath=[],
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
excludes=["tkinter"],
|
|
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="PlesnaGerance",
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip=False,
|
|
upx=True,
|
|
upx_exclude=[],
|
|
runtime_tmpdir=None,
|
|
console=False, # application fenêtrée, pas de console
|
|
disable_windowed_traceback=False,
|
|
argv_emulation=False,
|
|
target_arch=None,
|
|
codesign_identity=None,
|
|
entitlements_file=None,
|
|
icon=str(ROOT / "packaging" / "icon.ico")
|
|
if (ROOT / "packaging" / "icon.ico").exists()
|
|
else None,
|
|
)
|