Mise en place d'un installeur pour la version bureau sans passer par Docker.
Some checks failed
E2E Tests / e2e (push) Has been cancelled
Build & Push Images / build (brain) (push) Successful in 1m25s
Build & Push Images / build (core) (push) Successful in 2m55s
Build & Push Images / build (web) (push) Successful in 1m49s
Build & Push Images / build-switcher (push) Successful in 39s
Some checks failed
E2E Tests / e2e (push) Has been cancelled
Build & Push Images / build (brain) (push) Successful in 1m25s
Build & Push Images / build (core) (push) Successful in 2m55s
Build & Push Images / build (web) (push) Successful in 1m49s
Build & Push Images / build-switcher (push) Successful in 39s
Permet d'utiliser Loremind sans passer par Docker et sans lancer tous les conteneurs Passage en v0.15.0
This commit is contained in:
214
installers/desktop/build-windows.ps1
Normal file
214
installers/desktop/build-windows.ps1
Normal file
@@ -0,0 +1,214 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Construit l'installeur de BUREAU Windows de LoreMind (.msi), sans Docker.
|
||||
|
||||
.DESCRIPTION
|
||||
Pipeline complet "local-first" :
|
||||
1. Build du front Angular (web/ -> web/dist/web)
|
||||
2. Prep du Brain (Python embeddable) (brain/ -> dist-embed : python.exe signe PSF + deps + sources)
|
||||
3. Build du Core en fat jar + front (core/ -> target/*.jar, profil Maven "desktop")
|
||||
4. Assemblage de la charge utile (jar + brain) dans un dossier d'entree jpackage
|
||||
5. jpackage -> installeur .msi avec JRE embarque
|
||||
|
||||
L'app resultante se lance d'un double-clic : le Core demarre en profil Spring
|
||||
"local" (H2 fichier + stockage filesystem) et lance lui-meme le Brain en sidecar.
|
||||
Aucune dependance externe a installer cote utilisateur (ni Docker, ni Java, ni Python).
|
||||
L'IA fonctionne via le cloud (1min.ai / Gemini / Mistral...) ou via Ollama si present.
|
||||
|
||||
.PARAMETER Version
|
||||
Version de l'installeur (X.Y.Z, numerique). Defaut : derivee de core/pom.xml
|
||||
(le suffixe -beta est retire car les MSI n'acceptent qu'une version numerique).
|
||||
|
||||
.PARAMETER SkipFront / SkipBrain / SkipJar
|
||||
Sauter une etape (build incrementaux pendant la mise au point).
|
||||
|
||||
.PREREQUIS (sur la machine de build uniquement, PAS chez l'utilisateur final)
|
||||
- JDK 21+ avec jpackage dans le PATH (Temurin OK).
|
||||
- WiX Toolset v3 (https://github.com/wixtoolset/wix3/releases) — requis par
|
||||
jpackage pour produire un .msi sur Windows.
|
||||
- Node.js + npm (build Angular).
|
||||
- Python + pip (telecharge les wheels cp312 du Brain ; toute version 3.x convient,
|
||||
on cible explicitement 3.12 via --python-version) + acces Internet (python.org).
|
||||
|
||||
.NOTES
|
||||
Projet : LoreMind — assistant pour Maitres de Jeu de JDR
|
||||
Licence : AGPL-3.0
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Version,
|
||||
[switch]$SkipFront,
|
||||
[switch]$SkipBrain,
|
||||
[switch]$SkipJar
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-Step($m) { Write-Host "==> $m" -ForegroundColor Cyan }
|
||||
function Write-Ok($m) { Write-Host " OK $m" -ForegroundColor Green }
|
||||
function Write-Err($m) { Write-Host " XX $m" -ForegroundColor Red }
|
||||
|
||||
# --- Chemins ---------------------------------------------------------------
|
||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
||||
$WebDir = Join-Path $RepoRoot 'web'
|
||||
$BrainDir = Join-Path $RepoRoot 'brain'
|
||||
$CoreDir = Join-Path $RepoRoot 'core'
|
||||
$StageDir = Join-Path $CoreDir 'target\dist-input' # charge utile jpackage
|
||||
$OutDir = Join-Path $CoreDir 'target\dist-out' # .msi produit
|
||||
|
||||
# --- Version (numerique pour MSI) ------------------------------------------
|
||||
if (-not $Version) {
|
||||
$pom = Get-Content (Join-Path $CoreDir 'pom.xml') -Raw
|
||||
# IMPORTANT : viser la version DU PROJET, pas le premier <version> du fichier
|
||||
# (qui est celui du parent spring-boot-starter-parent). On l'ancre sur
|
||||
# l'artifactId du projet, puis on garde la partie numerique (MSI = X.Y.Z).
|
||||
if ($pom -match '<artifactId>loremind-core</artifactId>\s*<version>([0-9]+\.[0-9]+\.[0-9]+)') {
|
||||
$Version = $Matches[1]
|
||||
} else {
|
||||
$Version = '0.0.0'
|
||||
}
|
||||
}
|
||||
Write-Host ""
|
||||
Write-Host "============================================================" -ForegroundColor Magenta
|
||||
Write-Host " LoreMind - Build installeur bureau Windows (v$Version)" -ForegroundColor Magenta
|
||||
Write-Host "============================================================" -ForegroundColor Magenta
|
||||
|
||||
# --- Verif outils ----------------------------------------------------------
|
||||
if (-not (Get-Command jpackage -ErrorAction SilentlyContinue)) {
|
||||
Write-Err "jpackage introuvable dans le PATH. Installez un JDK 21+ (Temurin)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- 1. Front Angular ------------------------------------------------------
|
||||
if (-not $SkipFront) {
|
||||
Write-Step "Build du front Angular"
|
||||
Push-Location $WebDir
|
||||
try {
|
||||
if (-not (Test-Path 'node_modules')) { npm ci }
|
||||
npm run build
|
||||
if ($LASTEXITCODE -ne 0) { throw "Echec du build Angular" }
|
||||
} finally { Pop-Location }
|
||||
Write-Ok "Front construit (web/dist/web)"
|
||||
} else { Write-Step "Front : saute (--SkipFront)" }
|
||||
|
||||
# --- 2. Brain (Python embeddable officiel, PAS d'exe gele) -----------------
|
||||
# On embarque le Python *embeddable* de python.org (python.exe SIGNE par la PSF)
|
||||
# + les dependances + les sources .py. Aucun executable "gele" type PyInstaller
|
||||
# -> plus de faux positif antivirus (le bootloader packe etait pris pour un
|
||||
# trojan). Le Core lancera : brain\python\python.exe brain\run_local.py
|
||||
$PyVersion = '3.12.8' # doit matcher python:3.12 du Docker
|
||||
$PyTag = 'python312' # prefixe du fichier ._pth
|
||||
$BrainEmbed = Join-Path $BrainDir 'dist-embed' # staging du brain empaquete
|
||||
if (-not $SkipBrain) {
|
||||
Write-Step "Preparation du Brain (Python embeddable $PyVersion)"
|
||||
if (Test-Path $BrainEmbed) { Remove-Item $BrainEmbed -Recurse -Force }
|
||||
$PyDir = Join-Path $BrainEmbed 'python'
|
||||
New-Item -ItemType Directory -Force -Path $PyDir | Out-Null
|
||||
|
||||
# a) Telecharger + extraire le Python embeddable (zip officiel).
|
||||
$zip = Join-Path $BrainEmbed 'python-embed.zip'
|
||||
$url = "https://www.python.org/ftp/python/$PyVersion/python-$PyVersion-embed-amd64.zip"
|
||||
Write-Host " Telechargement $url"
|
||||
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
|
||||
Expand-Archive -Path $zip -DestinationPath $PyDir -Force
|
||||
Remove-Item $zip -Force
|
||||
|
||||
# b) Activer site-packages dans le ._pth (sinon les deps ne sont pas importees).
|
||||
$pth = Join-Path $PyDir "$PyTag._pth"
|
||||
Set-Content -Path $pth -Encoding ascii -Value @(
|
||||
"$PyTag.zip", '.', 'Lib\site-packages', 'import site'
|
||||
)
|
||||
|
||||
# c) Installer les deps en wheels cp312 dans python\Lib\site-packages.
|
||||
# --python-version 3.12 + --only-binary : on telecharge les roues 3.12
|
||||
# meme si le pip courant tourne sous une autre version de Python.
|
||||
$site = Join-Path $PyDir 'Lib\site-packages'
|
||||
New-Item -ItemType Directory -Force -Path $site | Out-Null
|
||||
Push-Location $BrainDir
|
||||
try {
|
||||
python -m pip install --upgrade pip *> $null
|
||||
python -m pip install --target $site --only-binary=:all: --python-version 3.12 -r requirements.txt
|
||||
if ($LASTEXITCODE -ne 0) { throw "Echec pip install (deps Brain)" }
|
||||
} finally { Pop-Location }
|
||||
|
||||
# d) Copier les sources du Brain + le point d'entree.
|
||||
Copy-Item (Join-Path $BrainDir 'app') (Join-Path $BrainEmbed 'app') -Recurse
|
||||
Copy-Item (Join-Path $BrainDir 'run_local.py') (Join-Path $BrainEmbed 'run_local.py')
|
||||
|
||||
Write-Ok "Brain prepare (brain/dist-embed : python embeddable + deps + sources)"
|
||||
} else { Write-Step "Brain : saute (--SkipBrain)" }
|
||||
|
||||
# --- 3. Core (fat jar avec front embarque) ---------------------------------
|
||||
if (-not $SkipJar) {
|
||||
Write-Step "Build du Core (fat jar, profil desktop)"
|
||||
Push-Location $CoreDir
|
||||
try {
|
||||
# -Pdesktop : copie web/dist/web dans le jar (classpath:/static).
|
||||
cmd /c "mvn -q -Pdesktop -DskipTests clean package"
|
||||
if ($LASTEXITCODE -ne 0) { throw "Echec du build Maven" }
|
||||
} finally { Pop-Location }
|
||||
Write-Ok "Core construit (core/target)"
|
||||
} else { Write-Step "Core : saute (--SkipJar)" }
|
||||
|
||||
# --- 4. Assemblage de la charge utile --------------------------------------
|
||||
Write-Step "Assemblage de la charge utile jpackage"
|
||||
if (Test-Path $StageDir) { Remove-Item $StageDir -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path $StageDir | Out-Null
|
||||
|
||||
# Le jar repackage Spring Boot (executable) — on ignore le *.jar.original.
|
||||
$jar = Get-ChildItem (Join-Path $CoreDir 'target') -Filter 'loremind-core-*.jar' |
|
||||
Where-Object { $_.Name -notlike '*.original' } | Select-Object -First 1
|
||||
if (-not $jar) { Write-Err "Jar introuvable dans core/target. Lancez sans --SkipJar."; exit 1 }
|
||||
Copy-Item $jar.FullName (Join-Path $StageDir 'loremind-core.jar')
|
||||
|
||||
# Le Brain (python embeddable + deps + sources) -> stage/brain/
|
||||
# stage/brain/python/python.exe , stage/brain/app , stage/brain/run_local.py
|
||||
if (-not (Test-Path $BrainEmbed)) { Write-Err "Brain introuvable (brain/dist-embed). Lancez sans --SkipBrain."; exit 1 }
|
||||
$stageBrain = Join-Path $StageDir 'brain'
|
||||
New-Item -ItemType Directory -Force -Path $stageBrain | Out-Null
|
||||
Copy-Item (Join-Path $BrainEmbed '*') $stageBrain -Recurse
|
||||
Write-Ok "Charge utile prete ($StageDir)"
|
||||
|
||||
# --- 5. jpackage -> .msi ---------------------------------------------------
|
||||
Write-Step "Generation de l'installeur (.msi) via jpackage"
|
||||
if (Test-Path $OutDir) { Remove-Item $OutDir -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
# $APPDIR est substitue par jpackage au LANCEMENT par le dossier 'app' de
|
||||
# l'install (qui contient le jar ET le dossier brain copie depuis --input).
|
||||
# Le Brain se lance via le python embeddable + run_local.py. brain.sidecar.command
|
||||
# est une liste : les deux chemins separes par une virgule (aucun chemin Windows
|
||||
# ne contient de virgule) sont bindes en List<String> par Spring.
|
||||
$brainCmd = '$APPDIR\brain\python\python.exe,$APPDIR\brain\run_local.py'
|
||||
|
||||
# Note : pas de --win-console (app de bureau). Les logs Spring/Brain peuvent
|
||||
# etre rediriges vers un fichier via une option ulterieure si besoin de debug.
|
||||
jpackage `
|
||||
--type msi `
|
||||
--name LoreMind `
|
||||
--app-version $Version `
|
||||
--vendor 'IGML Creation' `
|
||||
--input $StageDir `
|
||||
--main-jar loremind-core.jar `
|
||||
--main-class org.springframework.boot.loader.launch.JarLauncher `
|
||||
--dest $OutDir `
|
||||
--java-options '-Dspring.profiles.active=local' `
|
||||
--java-options "-Dbrain.sidecar.command=$brainCmd" `
|
||||
--win-per-user-install `
|
||||
--win-menu --win-menu-group 'LoreMind' `
|
||||
--win-shortcut --win-dir-chooser
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Err "Echec jpackage. Cause probable : WiX Toolset v3 absent."
|
||||
Write-Err "Installez-le : https://github.com/wixtoolset/wix3/releases"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$msi = Get-ChildItem $OutDir -Filter '*.msi' | Select-Object -First 1
|
||||
Write-Host ""
|
||||
Write-Host "============================================================" -ForegroundColor Green
|
||||
Write-Host " Installeur genere !" -ForegroundColor Green
|
||||
Write-Host " $($msi.FullName)" -ForegroundColor Green
|
||||
Write-Host "============================================================" -ForegroundColor Green
|
||||
Reference in New Issue
Block a user