build(windows): scripts build one-click + installer + doc
- build_windows_oneclick.bat / build_windows_installer_oneclick.bat : wrappers - scripts/build_windows_oneclick.ps1 / build_windows_installer_only.ps1 / install_inno_setup_build_dep.ps1 - build_signing.example.ps1 : exemple protocole signing (sans secret) - docs/build-windows-oneclick.md : documentation du build Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
17
build_signing.example.ps1
Normal file
17
build_signing.example.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copier ce fichier en build_signing.local.ps1 sur la machine Windows de build.
|
||||
# Ne pas versionner build_signing.local.ps1 : il peut contenir des secrets.
|
||||
|
||||
# Active la signature Authenticode pendant build_windows_oneclick.bat.
|
||||
$BuildSigningEnabled = $true
|
||||
|
||||
# Option recommandée si le certificat est installé dans le magasin Windows.
|
||||
# Récupérer l'empreinte avec :
|
||||
# Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
|
||||
$BuildSigningCertThumbprint = "REMPLACER_PAR_L_EMPREINTE_DU_CERTIFICAT"
|
||||
|
||||
# Alternative si vous disposez d'un fichier PFX.
|
||||
# $BuildSigningPfxPath = "C:\chemin\certificat-code-signing.pfx"
|
||||
# $BuildSigningPfxPassword = "MOT_DE_PASSE_PFX"
|
||||
|
||||
# Serveur d'horodatage RFC 3161.
|
||||
$BuildSigningTimestampServer = "http://timestamp.digicert.com"
|
||||
28
build_windows_installer_oneclick.bat
Normal file
28
build_windows_installer_oneclick.bat
Normal file
@@ -0,0 +1,28 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "PS_SCRIPT=%SCRIPT_DIR%scripts\build_windows_oneclick.ps1"
|
||||
|
||||
if not exist "%PS_SCRIPT%" (
|
||||
echo Script PowerShell introuvable : %PS_SCRIPT%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Lancement du build Windows avec installateur...
|
||||
powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%"
|
||||
set "EXITCODE=%ERRORLEVEL%"
|
||||
|
||||
if not "%EXITCODE%"=="0" (
|
||||
echo.
|
||||
echo Le build installateur a echoue. Code retour : %EXITCODE%
|
||||
pause
|
||||
exit /b %EXITCODE%
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build installateur termine avec succes.
|
||||
echo Sortie attendue : release\Anonymisation-Setup.exe
|
||||
pause
|
||||
exit /b 0
|
||||
27
build_windows_oneclick.bat
Normal file
27
build_windows_oneclick.bat
Normal file
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
set "PS_SCRIPT=%SCRIPT_DIR%scripts\build_windows_oneclick.ps1"
|
||||
|
||||
if not exist "%PS_SCRIPT%" (
|
||||
echo Script PowerShell introuvable : %PS_SCRIPT%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Lancement du build Windows one-click...
|
||||
powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%"
|
||||
set "EXITCODE=%ERRORLEVEL%"
|
||||
|
||||
if not "%EXITCODE%"=="0" (
|
||||
echo.
|
||||
echo Le build a echoue. Code retour : %EXITCODE%
|
||||
pause
|
||||
exit /b %EXITCODE%
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build termine avec succes.
|
||||
pause
|
||||
exit /b 0
|
||||
119
docs/build-windows-oneclick.md
Normal file
119
docs/build-windows-oneclick.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Build Windows One-Click
|
||||
|
||||
Le packaging Windows standard du projet repose sur :
|
||||
|
||||
- `build_windows_oneclick.bat`
|
||||
- `build_windows_installer_oneclick.bat`
|
||||
- `scripts/build_windows_oneclick.ps1`
|
||||
- `anonymisation_onefile.spec`
|
||||
- `installer/Anonymisation.iss`
|
||||
|
||||
## Usage
|
||||
|
||||
Sur la machine Windows de build :
|
||||
|
||||
1. ouvrir le dossier du projet
|
||||
2. double-cliquer sur `build_windows_oneclick.bat`
|
||||
|
||||
Le script :
|
||||
|
||||
- crée un venv de build local `.venv_build_win`
|
||||
- installe les dépendances nécessaires au packaging
|
||||
- génère `build_info.py`
|
||||
- lance `PyInstaller` avec `anonymisation_onefile.spec`
|
||||
- vérifie la présence de l'exécutable final
|
||||
- prépare un dossier de livraison et une archive ZIP
|
||||
- crée `release\Anonymisation-Setup.exe` si Inno Setup 6 est installé
|
||||
|
||||
## Sorties attendues
|
||||
|
||||
- exécutable : `dist\Anonymisation.exe`
|
||||
- dossier de livraison : `release\Anonymisation-Windows\`
|
||||
- archive : `release\Anonymisation-Windows.zip`
|
||||
- installateur : `release\Anonymisation-Setup.exe`
|
||||
- hash : `release\Anonymisation.exe.sha256.txt`
|
||||
|
||||
## Installateur Windows
|
||||
|
||||
L'installateur est généré avec Inno Setup 6. Il fournit :
|
||||
|
||||
- choix du dossier d'installation
|
||||
- installation utilisateur par défaut sans droits administrateur
|
||||
- raccourci menu Démarrer
|
||||
- option d'icône sur le bureau
|
||||
- désinstallation Windows standard
|
||||
|
||||
Si Inno Setup n'est pas présent sur la machine de build, le script conserve le
|
||||
build EXE/ZIP et affiche un avertissement. Installer Inno Setup 6 depuis le site
|
||||
officiel puis relancer le build.
|
||||
|
||||
Installation automatisée de la dépendance de build Inno Setup :
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\install_inno_setup_build_dep.ps1
|
||||
```
|
||||
|
||||
Recompiler uniquement l'installateur à partir de `release\Anonymisation-Windows\Anonymisation.exe` :
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows_installer_only.ps1
|
||||
```
|
||||
|
||||
Pour ne générer que l'exécutable et le ZIP :
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows_oneclick.ps1 -SkipInstaller
|
||||
```
|
||||
|
||||
## Important
|
||||
|
||||
- les utilisateurs finaux n'ont pas besoin d'installer Python
|
||||
- le build doit être lancé depuis Windows
|
||||
- le modèle ONNX embarqué requis doit exister localement dans :
|
||||
`models\camembert-bio-deid\onnx\model.onnx`
|
||||
|
||||
## Blocage Windows / SmartScreen
|
||||
|
||||
Un exécutable PyInstaller non signé peut déclencher Microsoft Defender SmartScreen, surtout s'il est téléchargé depuis Internet ou envoyé par e-mail. La signature réduit fortement le risque et évite l'éditeur inconnu, mais elle ne garantit pas toujours l'absence totale d'avertissement SmartScreen pour une toute nouvelle version : Windows tient aussi compte de la réputation du fichier et de son hash.
|
||||
|
||||
Pour une diffusion à des utilisateurs novices, la voie recommandée est :
|
||||
|
||||
- signer `Anonymisation.exe` avec un certificat Authenticode
|
||||
- horodater la signature
|
||||
- diffuser par partage réseau interne, Intune, GPO ou portail établissement
|
||||
- conserver le hash `release\Anonymisation.exe.sha256.txt`
|
||||
- éviter de demander aux utilisateurs de cliquer sur `Exécuter quand même`
|
||||
|
||||
Le script prend en charge la signature si un certificat est disponible.
|
||||
|
||||
### Signature automatique avec configuration locale
|
||||
|
||||
Sur la machine Windows de build :
|
||||
|
||||
1. copier `build_signing.example.ps1` en `build_signing.local.ps1`
|
||||
2. renseigner l'empreinte du certificat ou le chemin du PFX
|
||||
3. double-cliquer comme d'habitude sur `build_windows_oneclick.bat`
|
||||
|
||||
`build_signing.local.ps1` est ignoré par Git pour éviter de versionner des secrets.
|
||||
|
||||
### Signature manuelle via PowerShell
|
||||
|
||||
Avec un certificat installé dans le magasin Windows :
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows_oneclick.ps1 -Sign -CertThumbprint "EMPREINTE_CERTIFICAT"
|
||||
```
|
||||
|
||||
Avec un fichier PFX :
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows_oneclick.ps1 -Sign -PfxPath "C:\chemin\certificat.pfx" -PfxPassword "mot-de-passe"
|
||||
```
|
||||
|
||||
Si aucun certificat n'est disponible, le build reste possible, mais Windows peut afficher un avertissement de réputation au premier lancement.
|
||||
|
||||
Références Microsoft :
|
||||
|
||||
- SmartScreen reputation : https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/smartscreen-reputation
|
||||
- SignTool : https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool
|
||||
- Authenticode timestamping : https://learn.microsoft.com/en-us/windows/win32/seccrypto/time-stamping-authenticode-signatures
|
||||
61
scripts/build_windows_installer_only.ps1
Normal file
61
scripts/build_windows_installer_only.ps1
Normal file
@@ -0,0 +1,61 @@
|
||||
param(
|
||||
[string]$AppVersion = (Get-Date -Format "yyyy.MM.dd.HHmm")
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Resolve-InnoCompiler {
|
||||
$command = Get-Command ISCC.exe -ErrorAction SilentlyContinue
|
||||
if ($command) {
|
||||
return $command.Source
|
||||
}
|
||||
|
||||
$candidates = @()
|
||||
if (${env:ProgramFiles(x86)}) {
|
||||
$candidates += (Join-Path ${env:ProgramFiles(x86)} "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:ProgramFiles) {
|
||||
$candidates += (Join-Path $env:ProgramFiles "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:LOCALAPPDATA) {
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Programs\Inno Setup 6\ISCC.exe")
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
|
||||
foreach ($candidate in $candidates) {
|
||||
if ($candidate -and (Test-Path $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
throw "ISCC.exe introuvable. Installer Inno Setup 6 puis relancer."
|
||||
}
|
||||
|
||||
function Require-Path {
|
||||
param(
|
||||
[string]$PathValue,
|
||||
[string]$Label
|
||||
)
|
||||
if (-not (Test-Path $PathValue)) {
|
||||
throw "$Label introuvable: $PathValue"
|
||||
}
|
||||
}
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$ProjectRoot = (Resolve-Path (Join-Path $ScriptDir "..")).Path
|
||||
$InstallerScriptPath = Join-Path $ProjectRoot "installer\Anonymisation.iss"
|
||||
$PackageExePath = Join-Path $ProjectRoot "release\Anonymisation-Windows\Anonymisation.exe"
|
||||
$InstallerPath = Join-Path $ProjectRoot "release\Anonymisation-Setup.exe"
|
||||
|
||||
Require-Path -PathValue $InstallerScriptPath -Label "Script Inno Setup"
|
||||
Require-Path -PathValue $PackageExePath -Label "Executable package"
|
||||
|
||||
$innoCompiler = Resolve-InnoCompiler
|
||||
Write-Host "Inno Setup Compiler : $innoCompiler"
|
||||
& $innoCompiler "/DAppVersion=$AppVersion" $InstallerScriptPath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Inno Setup a echoue avec le code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
Require-Path -PathValue $InstallerPath -Label "Installateur Windows"
|
||||
$installerSizeMb = [math]::Round((Get-Item $InstallerPath).Length / 1MB, 1)
|
||||
Write-Host "Installateur pret : $InstallerPath ($installerSizeMb MB)"
|
||||
369
scripts/build_windows_oneclick.ps1
Normal file
369
scripts/build_windows_oneclick.ps1
Normal file
@@ -0,0 +1,369 @@
|
||||
param(
|
||||
[switch]$SkipZip,
|
||||
[switch]$SkipInstaller,
|
||||
[switch]$SkipRequirements,
|
||||
[switch]$Sign,
|
||||
[string]$CertThumbprint,
|
||||
[string]$PfxPath,
|
||||
[string]$PfxPassword,
|
||||
[string]$TimestampServer = "http://timestamp.digicert.com"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$script:SignatureSummary = "Non signé"
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-Host ""
|
||||
Write-Host "=== $Message ===" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Require-Path {
|
||||
param(
|
||||
[string]$PathValue,
|
||||
[string]$Label
|
||||
)
|
||||
if (-not (Test-Path $PathValue)) {
|
||||
throw "$Label introuvable: $PathValue"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-BootstrapPython {
|
||||
param([string[]]$Arguments)
|
||||
if ($script:PythonBootstrap[0] -eq "py") {
|
||||
& py $script:PythonBootstrap[1] @Arguments
|
||||
} else {
|
||||
& $script:PythonBootstrap[0] @Arguments
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-BootstrapPython {
|
||||
if (Get-Command py -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
& py -3.11 --version | Out-Host
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
return @("py", "-3.11")
|
||||
}
|
||||
} catch {}
|
||||
try {
|
||||
& py -3 --version | Out-Host
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
return @("py", "-3")
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (Get-Command python -ErrorAction SilentlyContinue) {
|
||||
& python --version | Out-Host
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
return @("python")
|
||||
}
|
||||
}
|
||||
throw "Python introuvable sur la machine de build Windows."
|
||||
}
|
||||
|
||||
function Resolve-SignTool {
|
||||
$command = Get-Command signtool.exe -ErrorAction SilentlyContinue
|
||||
if ($command) {
|
||||
return $command.Source
|
||||
}
|
||||
|
||||
$programFilesX86 = ${env:ProgramFiles(x86)}
|
||||
if ($programFilesX86) {
|
||||
$kitsRoot = Join-Path $programFilesX86 "Windows Kits\10\bin"
|
||||
if (Test-Path $kitsRoot) {
|
||||
$candidates = @(
|
||||
Get-ChildItem -Path $kitsRoot -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match "\\x64\\signtool\.exe$" } |
|
||||
Sort-Object FullName -Descending
|
||||
)
|
||||
if ($candidates.Count -gt 0) {
|
||||
return $candidates[0].FullName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw "signtool.exe introuvable. Installer Windows SDK ou ajouter signtool.exe au PATH."
|
||||
}
|
||||
|
||||
function Resolve-InnoCompiler {
|
||||
$command = Get-Command ISCC.exe -ErrorAction SilentlyContinue
|
||||
if ($command) {
|
||||
return $command.Source
|
||||
}
|
||||
|
||||
$candidates = @()
|
||||
if (${env:ProgramFiles(x86)}) {
|
||||
$candidates += (Join-Path ${env:ProgramFiles(x86)} "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:ProgramFiles) {
|
||||
$candidates += (Join-Path $env:ProgramFiles "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:LOCALAPPDATA) {
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Programs\Inno Setup 6\ISCC.exe")
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
foreach ($candidate in $candidates) {
|
||||
if ($candidate -and (Test-Path $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Invoke-CodeSigning {
|
||||
param([string]$FilePath)
|
||||
|
||||
if (-not $Sign) {
|
||||
Write-Host "Signature Authenticode ignorée. Utiliser -Sign pour signer l'exécutable."
|
||||
return
|
||||
}
|
||||
|
||||
Require-Path -PathValue $FilePath -Label "Fichier à signer"
|
||||
if ($PfxPath) {
|
||||
Require-Path -PathValue $PfxPath -Label "Certificat PFX"
|
||||
}
|
||||
|
||||
$signTool = Resolve-SignTool
|
||||
Write-Host "SignTool : $signTool"
|
||||
|
||||
if ($CertThumbprint -eq "REMPLACER_PAR_L_EMPREINTE_DU_CERTIFICAT") {
|
||||
throw "Empreinte de certificat non renseignée dans build_signing.local.ps1."
|
||||
}
|
||||
|
||||
$args = @("sign", "/fd", "SHA256", "/tr", $TimestampServer, "/td", "SHA256", "/d", "Anonymisation")
|
||||
if ($PfxPath) {
|
||||
$args += @("/f", $PfxPath)
|
||||
if ($PfxPassword) {
|
||||
$args += @("/p", $PfxPassword)
|
||||
}
|
||||
} elseif ($CertThumbprint) {
|
||||
$args += @("/sha1", ($CertThumbprint -replace "\s", ""))
|
||||
} else {
|
||||
$args += @("/a")
|
||||
}
|
||||
$args += $FilePath
|
||||
|
||||
& $signTool @args
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "La signature Authenticode a échoué."
|
||||
}
|
||||
|
||||
& $signTool verify /pa /v $FilePath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "La vérification Authenticode a échoué."
|
||||
}
|
||||
|
||||
$signature = Get-AuthenticodeSignature $FilePath
|
||||
$subject = ""
|
||||
if ($signature.SignerCertificate) {
|
||||
$subject = $signature.SignerCertificate.Subject
|
||||
}
|
||||
$script:SignatureSummary = "$($signature.Status) - $subject"
|
||||
Write-Host "Signature : $script:SignatureSummary"
|
||||
|
||||
if ($signature.Status -ne "Valid") {
|
||||
throw "Signature Authenticode non valide : $($signature.Status)"
|
||||
}
|
||||
}
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$ProjectRoot = (Resolve-Path (Join-Path $ScriptDir "..")).Path
|
||||
$SigningConfigPath = Join-Path $ProjectRoot "build_signing.local.ps1"
|
||||
$SpecPath = Join-Path $ProjectRoot "anonymisation_onefile.spec"
|
||||
$InstallerScriptPath = Join-Path $ProjectRoot "installer\Anonymisation.iss"
|
||||
$BuildInfoPath = Join-Path $ProjectRoot "build_info.py"
|
||||
$ModelPath = Join-Path $ProjectRoot "models\camembert-bio-deid\onnx\model.onnx"
|
||||
$VenvDir = Join-Path $ProjectRoot ".venv_build_win"
|
||||
$VenvPython = Join-Path $VenvDir "Scripts\python.exe"
|
||||
$DistDir = Join-Path $ProjectRoot "dist"
|
||||
$BuildDir = Join-Path $ProjectRoot "build"
|
||||
$ReleaseDir = Join-Path $ProjectRoot "release"
|
||||
$ExePath = Join-Path $DistDir "Anonymisation.exe"
|
||||
$PackageDir = Join-Path $ReleaseDir "Anonymisation-Windows"
|
||||
$ZipPath = Join-Path $ReleaseDir "Anonymisation-Windows.zip"
|
||||
$HashPath = Join-Path $ReleaseDir "Anonymisation.exe.sha256.txt"
|
||||
$InstallerPath = Join-Path $ReleaseDir "Anonymisation-Setup.exe"
|
||||
$ReadmePath = Join-Path $PackageDir "README.txt"
|
||||
$RequiredSourceFiles = @(
|
||||
"launcher.py",
|
||||
"Pseudonymisation_Gui_V5.py",
|
||||
"anonymizer_core_refactored_onnx.py",
|
||||
"admin_rules.py",
|
||||
"config_defaults.py",
|
||||
"profile_defaults.py",
|
||||
"gui_batch_paths.py",
|
||||
"manual_masking.py",
|
||||
"pdf_mask_designer.py",
|
||||
"format_converter.py",
|
||||
"camembert_ner_manager.py"
|
||||
)
|
||||
|
||||
Write-Step "Préparation du build Windows"
|
||||
Write-Host "Projet : $ProjectRoot"
|
||||
|
||||
Require-Path -PathValue $SpecPath -Label "Spec PyInstaller"
|
||||
Require-Path -PathValue $InstallerScriptPath -Label "Script installateur Inno Setup"
|
||||
Require-Path -PathValue $ModelPath -Label "Modèle ONNX embarqué"
|
||||
foreach ($RelativeSourceFile in $RequiredSourceFiles) {
|
||||
Require-Path -PathValue (Join-Path $ProjectRoot $RelativeSourceFile) -Label "Module source requis"
|
||||
}
|
||||
|
||||
if (Test-Path $SigningConfigPath) {
|
||||
Write-Step "Configuration locale de signature"
|
||||
. $SigningConfigPath
|
||||
if ($BuildSigningEnabled) { $Sign = $true }
|
||||
if ($BuildSigningCertThumbprint -and -not $CertThumbprint) { $CertThumbprint = $BuildSigningCertThumbprint }
|
||||
if ($BuildSigningPfxPath -and -not $PfxPath) { $PfxPath = $BuildSigningPfxPath }
|
||||
if ($BuildSigningPfxPassword -and -not $PfxPassword) { $PfxPassword = $BuildSigningPfxPassword }
|
||||
if ($BuildSigningTimestampServer -and $TimestampServer -eq "http://timestamp.digicert.com") {
|
||||
$TimestampServer = $BuildSigningTimestampServer
|
||||
}
|
||||
if ($Sign) {
|
||||
Write-Host "Signature activée depuis build_signing.local.ps1"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Step "Détection de Python"
|
||||
$script:PythonBootstrap = Resolve-BootstrapPython
|
||||
Write-Host "Bootstrap Python : $($script:PythonBootstrap -join ' ')"
|
||||
|
||||
Write-Step "Environnement virtuel de build"
|
||||
if (-not (Test-Path $VenvPython)) {
|
||||
Write-Host "Création du venv : $VenvDir"
|
||||
Invoke-BootstrapPython -Arguments @("-m", "venv", $VenvDir)
|
||||
}
|
||||
Require-Path -PathValue $VenvPython -Label "Python du venv"
|
||||
|
||||
Push-Location $ProjectRoot
|
||||
try {
|
||||
Write-Step "Installation des dépendances de build"
|
||||
& $VenvPython -m pip install --upgrade pip setuptools wheel
|
||||
if (-not $SkipRequirements) {
|
||||
& $VenvPython -m pip install -r requirements.txt
|
||||
}
|
||||
& $VenvPython -m pip install pyinstaller
|
||||
|
||||
Write-Step "Génération de build_info.py"
|
||||
$commit = "local"
|
||||
$branch = "local"
|
||||
if (Get-Command git -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
$gitCommit = (git rev-parse --short HEAD 2>$null | Out-String).Trim()
|
||||
if ($gitCommit) { $commit = $gitCommit }
|
||||
$gitBranch = (git rev-parse --abbrev-ref HEAD 2>$null | Out-String).Trim()
|
||||
if ($gitBranch) { $branch = $gitBranch }
|
||||
} catch {}
|
||||
}
|
||||
$buildDate = Get-Date -Format "yyyy-MM-dd HH:mm"
|
||||
$buildInfo = @"
|
||||
"""Métadonnées de build - généré automatiquement par build_windows_oneclick.ps1."""
|
||||
BUILD_DATE = "$buildDate"
|
||||
BUILD_COMMIT = "$commit"
|
||||
BUILD_BRANCH = "$branch"
|
||||
"@
|
||||
Set-Content -Path $BuildInfoPath -Value $buildInfo -Encoding UTF8
|
||||
Write-Host "Build info : $buildDate / $branch / $commit"
|
||||
|
||||
Write-Step "Nettoyage des anciens artefacts"
|
||||
foreach ($PathValue in @($BuildDir, $DistDir, $PackageDir)) {
|
||||
if (Test-Path $PathValue) {
|
||||
Remove-Item -Recurse -Force $PathValue -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
if (Test-Path $ZipPath) {
|
||||
Remove-Item -Force $ZipPath -ErrorAction SilentlyContinue
|
||||
}
|
||||
if (Test-Path $HashPath) {
|
||||
Remove-Item -Force $HashPath -ErrorAction SilentlyContinue
|
||||
}
|
||||
if (Test-Path $InstallerPath) {
|
||||
Remove-Item -Force $InstallerPath -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Step "Compilation PyInstaller"
|
||||
& $VenvPython -m PyInstaller --clean --noconfirm $SpecPath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "PyInstaller a échoué avec le code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
Write-Step "Vérification de l'exécutable"
|
||||
Require-Path -PathValue $ExePath -Label "Exécutable Windows"
|
||||
$exeSizeMb = [math]::Round((Get-Item $ExePath).Length / 1MB, 1)
|
||||
Write-Host "EXE créé : $ExePath ($exeSizeMb MB)"
|
||||
|
||||
Write-Step "Signature Authenticode"
|
||||
Invoke-CodeSigning -FilePath $ExePath
|
||||
|
||||
Write-Step "Préparation du dossier de livraison"
|
||||
New-Item -ItemType Directory -Force -Path $PackageDir | Out-Null
|
||||
Copy-Item $ExePath (Join-Path $PackageDir "Anonymisation.exe")
|
||||
|
||||
$readme = @"
|
||||
Anonymisation - paquet Windows
|
||||
================================
|
||||
|
||||
Fichier principal :
|
||||
- Anonymisation.exe
|
||||
|
||||
Conseils de diffusion :
|
||||
- Aucune installation de Python n'est nécessaire pour l'utilisateur final.
|
||||
- Conservez le fichier dans un dossier en écriture (par exemple Bureau ou Documents).
|
||||
- Privilégiez une diffusion par partage réseau interne, Intune, GPO ou portail établissement.
|
||||
- Évitez l'envoi direct par e-mail ou téléchargement public non signé.
|
||||
- Le journal applicatif s'écrit à côté de l'exécutable : anonymisation.log
|
||||
|
||||
Build :
|
||||
- Date : $buildDate
|
||||
- Branche : $branch
|
||||
- Commit : $commit
|
||||
- Signature : $script:SignatureSummary
|
||||
"@
|
||||
Set-Content -Path $ReadmePath -Value $readme -Encoding UTF8
|
||||
|
||||
$hash = (Get-FileHash -Algorithm SHA256 $ExePath).Hash
|
||||
Set-Content -Path $HashPath -Value "SHA256 Anonymisation.exe $hash" -Encoding UTF8
|
||||
Write-Host "SHA256 : $hash"
|
||||
|
||||
if (-not $SkipZip) {
|
||||
Write-Step "Création de l'archive de livraison"
|
||||
Compress-Archive -Path (Join-Path $PackageDir "*") -DestinationPath $ZipPath -CompressionLevel Optimal
|
||||
Write-Host "Archive créée : $ZipPath"
|
||||
}
|
||||
|
||||
if (-not $SkipInstaller) {
|
||||
Write-Step "Création de l'installateur Windows"
|
||||
$innoCompiler = Resolve-InnoCompiler
|
||||
if ($innoCompiler) {
|
||||
Write-Host "Inno Setup Compiler : $innoCompiler"
|
||||
$installerVersion = (Get-Date -Format "yyyy.MM.dd.HHmm")
|
||||
& $innoCompiler "/DAppVersion=$installerVersion" $InstallerScriptPath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Inno Setup a échoué avec le code $LASTEXITCODE."
|
||||
}
|
||||
Require-Path -PathValue $InstallerPath -Label "Installateur Windows"
|
||||
$installerSizeMb = [math]::Round((Get-Item $InstallerPath).Length / 1MB, 1)
|
||||
Write-Host "Installateur créé : $InstallerPath ($installerSizeMb MB)"
|
||||
|
||||
if ($Sign) {
|
||||
Write-Step "Signature Authenticode de l'installateur"
|
||||
Invoke-CodeSigning -FilePath $InstallerPath
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Inno Setup 6 introuvable. Installateur ignoré. Installer Inno Setup puis relancer le build."
|
||||
Write-Warning "Téléchargement officiel : https://jrsoftware.org/isdl.php"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Step "Build terminé"
|
||||
Write-Host "EXE final : $ExePath" -ForegroundColor Green
|
||||
if (-not $SkipZip) {
|
||||
Write-Host "Archive prête : $ZipPath" -ForegroundColor Green
|
||||
}
|
||||
if ((-not $SkipInstaller) -and (Test-Path $InstallerPath)) {
|
||||
Write-Host "Installateur prêt : $InstallerPath" -ForegroundColor Green
|
||||
}
|
||||
Write-Host "Hash SHA256 : $HashPath" -ForegroundColor Green
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
57
scripts/install_inno_setup_build_dep.ps1
Normal file
57
scripts/install_inno_setup_build_dep.ps1
Normal file
@@ -0,0 +1,57 @@
|
||||
param(
|
||||
[string]$DownloadUrl = "https://jrsoftware.org/download.php/is.exe"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-Host ""
|
||||
Write-Host "=== $Message ===" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Find-InnoCompiler {
|
||||
$candidates = @()
|
||||
if (${env:ProgramFiles(x86)}) {
|
||||
$candidates += (Join-Path ${env:ProgramFiles(x86)} "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:ProgramFiles) {
|
||||
$candidates += (Join-Path $env:ProgramFiles "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
if ($env:LOCALAPPDATA) {
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Programs\Inno Setup 6\ISCC.exe")
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Inno Setup 6\ISCC.exe")
|
||||
}
|
||||
foreach ($candidate in $candidates) {
|
||||
if ($candidate -and (Test-Path $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
$existing = Find-InnoCompiler
|
||||
if ($existing) {
|
||||
Write-Host "Inno Setup deja disponible : $existing"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Step "Telechargement Inno Setup"
|
||||
$installerPath = Join-Path $env:TEMP "innosetup-build-dep.exe"
|
||||
Invoke-WebRequest -Uri $DownloadUrl -OutFile $installerPath
|
||||
Write-Host "Installeur telecharge : $installerPath"
|
||||
|
||||
Write-Step "Installation Inno Setup utilisateur"
|
||||
$args = @("/SP-", "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/CURRENTUSER")
|
||||
$process = Start-Process -FilePath $installerPath -ArgumentList $args -Wait -PassThru
|
||||
Write-Host "Code retour : $($process.ExitCode)"
|
||||
if ($process.ExitCode -ne 0) {
|
||||
throw "Installation Inno Setup echouee avec le code $($process.ExitCode)."
|
||||
}
|
||||
|
||||
$compiler = Find-InnoCompiler
|
||||
if (-not $compiler) {
|
||||
throw "ISCC.exe introuvable apres installation Inno Setup."
|
||||
}
|
||||
|
||||
Write-Host "Inno Setup pret : $compiler"
|
||||
Reference in New Issue
Block a user