installer v1 (C+D): Windows EXE installer trio + build script + local Hub E2E
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user