95 lines
4.9 KiB
PowerShell
95 lines
4.9 KiB
PowerShell
# Build the three Windows installer artifacts with PyInstaller:
|
|
#
|
|
# dist/installer/GTIFiscalAgent-Setup-<version>.exe (onefile, --uac-admin) <- main download artifact
|
|
# dist/installer/GTIFiscalAgentService/<host> (onedir) <- embedded in Setup, extracted to Program Files
|
|
# dist/installer/Uninstall.exe (onefile, --uac-admin) <- embedded in Setup, ARP uninstaller
|
|
# dist/installer/GTIFiscalAgent-Setup-<version>.exe.sha256
|
|
#
|
|
# Reproducibility = automated, repeatable, deterministic inputs + immutable
|
|
# PUBLISHED release (byte-identical hashes are NOT required).
|
|
#
|
|
# Requires (fails fast if missing): python with PyInstaller, pywin32, fdb,
|
|
# httpx; icacls for the ACL on credentials.bin.
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$Python = "python",
|
|
[switch]$SkipSetup = $false,
|
|
[switch]$SkipService = $false,
|
|
[switch]$SkipUninstall = $false
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
$Version = (Get-Content (Join-Path $Root "release\VERSION") -Raw).Trim()
|
|
$Out = Join-Path $Root "dist\installer"
|
|
$Work = Join-Path $Root "build\installer"
|
|
$SpecDir = Join-Path $Root "build\installer"
|
|
|
|
function Invoke-Step([string]$Title, [scriptblock]$Body) {
|
|
Write-Host "== $Title ==" -ForegroundColor Cyan
|
|
& $Body
|
|
if ($LASTEXITCODE -ne 0) { throw "$Title failed with exit code $LASTEXITCODE" }
|
|
}
|
|
|
|
Write-Host "Building GTI Fiscal Agent installer v$Version" -ForegroundColor Green
|
|
|
|
$py = (Get-Command $Python).Source
|
|
& $py -c "import PyInstaller, fdb, httpx, win32serviceutil; print('deps ok, pyinstaller', PyInstaller.__version__)"
|
|
if ($LASTEXITCODE -ne 0) { throw "build environment is missing PyInstaller/fdb/httpx/pywin32" }
|
|
if (-not (Get-Command "icacls" -ErrorAction SilentlyContinue)) { throw "missing required tool: icacls" }
|
|
|
|
$Paths = @("--paths", (Join-Path $Root "agent\src"), "--paths", (Join-Path $Root "agent\scripts"),
|
|
"--paths", (Join-Path $Root "agent\service"), "--paths", (Join-Path $Root "central"))
|
|
$Common = @("--noconfirm", "--clean", "--distpath", $Out, "--workpath", $Work, "--specpath", $SpecDir,
|
|
"--log-level", "WARN")
|
|
# win32crypt is imported lazily inside credential_store (function body), so the
|
|
# AST scan misses it; the agent service modules also need the pywin32 service libs.
|
|
$Hidden = @("--hidden-import=win32timezone", "--hidden-import=win32crypt",
|
|
"--hidden-import=win32serviceutil", "--hidden-import=win32service",
|
|
"--hidden-import=win32event", "--hidden-import=servicemanager")
|
|
# None of the reachable runtime modules import these (verified by tracing
|
|
# __import__ while loading run_agent_service's full chain); they only add
|
|
# ~100MB+ of dead weight via hook-gathered submodules. Exclude them.
|
|
$Excludes = @("--exclude-module=numpy", "--exclude-module=PIL", "--exclude-module=yaml",
|
|
"--exclude-module=scipy", "--exclude-module=pandas", "--exclude-module=matplotlib",
|
|
"--exclude-module=openpyxl", "--exclude-module=jinja2")
|
|
|
|
if (-not $SkipService) {
|
|
Invoke-Step "Build service host (onedir)" -Body {
|
|
& $py -m PyInstaller @Common @Paths @Hidden @Excludes "--onedir" `
|
|
"--name" "GTIFiscalAgentService" (Join-Path $Root "installer\service_main.py")
|
|
}
|
|
}
|
|
|
|
if (-not $SkipUninstall) {
|
|
Invoke-Step "Build Uninstall.exe (onefile, uac-admin)" -Body {
|
|
& $py -m PyInstaller @Common @Paths @Hidden @Excludes "--onefile" "--uac-admin" "--name" "Uninstall" `
|
|
(Join-Path $Root "installer\uninstall_main.py")
|
|
}
|
|
}
|
|
|
|
if (-not $SkipSetup) {
|
|
$ServiceData = (Join-Path $Out "GTIFiscalAgentService") + ";" + "GTIFiscalAgentService"
|
|
$UninstallData = (Join-Path $Out "Uninstall.exe") + ";" + "."
|
|
$ExecutorData = (Join-Path $Root "scripts\firebird_write_executor.py") + ";" + "scripts"
|
|
Invoke-Step "Build Setup.exe (onefile, uac-admin)" -Body {
|
|
& $py -m PyInstaller @Common @Paths @Hidden @Excludes "--onefile" "--uac-admin" "--name" "GTIFiscalAgent-Setup" `
|
|
"--add-data" $ServiceData "--add-data" $UninstallData "--add-data" $ExecutorData `
|
|
(Join-Path $Root "installer\setup_main.py")
|
|
}
|
|
}
|
|
|
|
$BuiltSetup = Join-Path $Out "GTIFiscalAgent-Setup.exe"
|
|
$FinalSetup = Join-Path $Out "GTIFiscalAgent-Setup-$Version.exe"
|
|
if (Test-Path $BuiltSetup) {
|
|
Move-Item -Force $BuiltSetup $FinalSetup
|
|
$Digest = (Get-FileHash -Algorithm SHA256 -Path $FinalSetup).Hash.ToLowerInvariant()
|
|
Set-Content -Path "$FinalSetup.sha256" -Value "$Digest $([IO.Path]::GetFileName($FinalSetup))`n" -NoNewline -Encoding Ascii
|
|
Write-Host "Installer: $FinalSetup" -ForegroundColor Green
|
|
Write-Host "sha256: $Digest" -ForegroundColor Green
|
|
} else {
|
|
Write-Host "Setup artifact not built (skipped); service host + Uninstall.exe still available under $Out" -ForegroundColor Yellow
|
|
}
|
|
|
|
Get-ChildItem $Out -Recurse | Select-Object FullName, Length | Format-Table -AutoSize
|
|
Write-Host "Build complete." -ForegroundColor Green |