param( [Parameter(Mandatory = $true)] [string]$AppDir, [Parameter(Mandatory = $true)] [string]$OutputExe, [Parameter(Mandatory = $true)] [string]$Version, [string]$BuildNumber = "dev", [long]$MinInstallerBytes = 10485760 ) $ErrorActionPreference = "Stop" function Find-Makensis() { $command = Get-Command makensis -ErrorAction SilentlyContinue if ($command) { return $command.Source } $searchRoots = @( "${env:ProgramFiles(x86)}\NSIS\makensis.exe", "${env:ProgramFiles}\NSIS\makensis.exe" ) foreach ($candidate in $searchRoots) { if (Test-Path $candidate) { return $candidate } } throw "Could not find makensis. Install NSIS 3.x on the Windows build agent." } function Get-DirectoryBytes { param([string]$Path) $total = 0 foreach ($item in Get-ChildItem -LiteralPath $Path -Recurse -File -Force) { $total += $item.Length } return $total } $rootDir = Split-Path -Parent $PSScriptRoot $nsiPath = Join-Path $rootDir "packaging/windows/farmcontrol.nsi" $installerInclude = Join-Path $rootDir "packaging/windows/installer.nsh" $fileListGenerator = Join-Path $rootDir "scripts/generate-nsis-file-list.ps1" $workDir = Join-Path $env:TEMP "farmcontrol-nsis" $localInstallerName = "farmcontrol-installer.exe" if (Test-Path $workDir) { Remove-Item $workDir -Recurse -Force } New-Item -ItemType Directory -Path $workDir | Out-Null $appDirPath = (Resolve-Path -LiteralPath $AppDir).Path $stagingAppDir = Join-Path $workDir "app" Write-Host "Staging application files for NSIS from $appDirPath" Copy-Item -LiteralPath $appDirPath -Destination $stagingAppDir -Recurse -Force $stagedAppBytes = Get-DirectoryBytes $stagingAppDir Write-Host "Staged application size: $([math]::Round($stagedAppBytes / 1MB, 2)) MB" if ($stagedAppBytes -lt 5242880) { throw "Staged application is only $([math]::Round($stagedAppBytes / 1KB, 1)) KiB. Expected a full Electrobun app bundle before building the installer." } $requiredExe = Join-Path $stagingAppDir "bin\FarmControl.exe" $requiredDeeplinkScript = Join-Path $stagingAppDir "bin\deeplink.js" if (-not (Test-Path $requiredExe)) { throw "Staged application is missing bin\FarmControl.exe" } if (-not (Test-Path $requiredDeeplinkScript)) { throw "Staged application is missing bin\deeplink.js" } Copy-Item -LiteralPath $nsiPath -Destination (Join-Path $workDir "farmcontrol.nsi") Copy-Item -LiteralPath $installerInclude -Destination (Join-Path $workDir "installer.nsh") Copy-Item -LiteralPath $fileListGenerator -Destination (Join-Path $workDir "generate-nsis-file-list.ps1") $iconPath = Join-Path $rootDir "assets\installer.ico" if (Test-Path $iconPath) { Copy-Item -LiteralPath $iconPath -Destination (Join-Path $workDir "icon.ico") -Force Write-Host "Using installer icon from $iconPath" } $uninstallerIconPath = Join-Path $rootDir "assets\uninstaller.ico" if (Test-Path $uninstallerIconPath) { Copy-Item -LiteralPath $uninstallerIconPath -Destination (Join-Path $workDir "uninstaller.ico") -Force Write-Host "Using uninstaller icon from $uninstallerIconPath" } $makensis = Find-Makensis $outputExePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputExe) $outputDir = Split-Path $outputExePath -Parent if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir | Out-Null } $makensisArgs = @( "/V3" "/NOCD" "/DOUTFILE=$localInstallerName" "/DVERSION=$Version" "/DBUILD_NUMBER=$BuildNumber" "/DAPP_SOURCE_DIR=app" "/DAPP_FILE_LIST_GENERATOR=generate-nsis-file-list.ps1" ) if (Test-Path (Join-Path $workDir "icon.ico")) { $makensisArgs += "/DINSTALLER_ICON=icon.ico" } if (Test-Path (Join-Path $workDir "uninstaller.ico")) { $makensisArgs += "/DUNINSTALLER_ICON=uninstaller.ico" } $makensisArgs += (Join-Path $workDir "farmcontrol.nsi") Push-Location $workDir try { & $makensis @makensisArgs if ($LASTEXITCODE -ne 0) { throw "makensis failed with exit code $LASTEXITCODE" } } finally { Pop-Location } $localInstallerPath = Join-Path $workDir $localInstallerName if (-not (Test-Path $localInstallerPath)) { throw "NSIS installer was not created at $localInstallerPath" } $installerBytes = (Get-Item -LiteralPath $localInstallerPath).Length Write-Host "NSIS installer size: $([math]::Round($installerBytes / 1MB, 2)) MB" if ($installerBytes -lt $MinInstallerBytes) { throw "NSIS installer is only $([math]::Round($installerBytes / 1KB, 1)) KiB at $localInstallerPath. The application files were not packaged. Check NSIS logs above." } Move-Item -LiteralPath $localInstallerPath -Destination $outputExePath -Force Write-Host "Created NSIS installer at $outputExePath"