RedFlag/installer/windows/build-desktop.ps1
Source publisher ffdccba10f source: advance the checked projection
Source-Sha: 194a59adfd6c901635da7a9c18253bb2210e2426
Policy-Sha: b9b0be0ee74e55f561a803b8aef6be3c2134f5a83b46816a069a372f2eb04f4d
Tree-Digest: 4697f2e281c1c6ecafff17cf6eeb391a0a1ac0fa6e5fc429f5b473f4579c252c
2026-09-21 14:39:08 +00:00

100 lines
5.2 KiB
PowerShell

# Native Windows CI entry point. Run from an x64 MSVC developer environment
# with Rust, Go and Qt 6 on PATH. Produces the complete Desktop payload;
# setup must package the entire directory, not just redflag-desktop.exe.
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$OutputDirectory,
[Parameter(Mandatory = $true)][ValidatePattern('^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?$')][string]$Version
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if ($env:OS -ne 'Windows_NT') { throw 'This build requires a native Windows runner.' }
foreach ($tool in @('cargo', 'rustc', 'go', 'cl', 'windeployqt')) {
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) { throw "Missing build tool: $tool" }
}
$rustInfo = & rustc -vV
if ($LASTEXITCODE -ne 0 -or -not ($rustInfo -match 'host: x86_64-pc-windows-msvc')) {
throw 'Select the x86_64-pc-windows-msvc Rust toolchain.'
}
$root = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path
$expectedVersion = $Version
$output = [IO.Path]::GetFullPath($OutputDirectory)
if (Test-Path $output) { throw 'Output directory must be new; refusing to mix payload versions.' }
New-Item -ItemType Directory -Path $output | Out-Null
Push-Location (Join-Path $root 'desktop')
$previousVersion = $env:REDFLAG_RELEASE_VERSION
try {
$env:REDFLAG_RELEASE_VERSION = $Version
& cargo build --release --locked
if ($LASTEXITCODE -ne 0) { throw 'Desktop build failed.' }
Copy-Item 'target/release/redflag-desktop.exe' $output
} finally { $env:REDFLAG_RELEASE_VERSION = $previousVersion; Pop-Location }
Push-Location (Join-Path $root 'agent')
try {
& go build -trimpath -ldflags "-X github.com/Fimeg/RedFlag/agent/internal/version.Version=$Version" -o (Join-Path $output 'redflag-agent.exe') ./cmd/agent/
if ($LASTEXITCODE -ne 0) { throw 'Agent build failed.' }
} finally { Pop-Location }
Push-Location (Join-Path $root 'helper')
$previousVersion = $env:REDFLAG_RELEASE_VERSION
try {
$env:REDFLAG_RELEASE_VERSION = $Version
& cargo build --release --locked
if ($LASTEXITCODE -ne 0) { throw 'Helper build failed.' }
Copy-Item 'target/release/redflag-helper.exe' $output
} finally { $env:REDFLAG_RELEASE_VERSION = $previousVersion; Pop-Location }
$desktop = Join-Path $output 'redflag-desktop.exe'
& windeployqt --release --qmldir (Join-Path $root 'desktop/qml') --dir $output $desktop
if ($LASTEXITCODE -ne 0) { throw 'Qt runtime deployment failed.' }
foreach ($relative in @('Qt6Core.dll', 'Qt6Gui.dll', 'Qt6Qml.dll', 'Qt6Quick.dll', 'platforms/qwindows.dll')) {
if (-not (Test-Path (Join-Path $output $relative))) { throw "Qt payload is missing $relative" }
}
Copy-Item (Join-Path $root 'LICENSE') (Join-Path $output 'LICENSE.txt')
Copy-Item (Join-Path $root 'THIRD_PARTY_LICENSES.md') $output
Copy-Item (Join-Path $root 'installer/windows/provision-helper.ps1') $output
# Remove the build toolchain from DLL lookup for the version check. A clean
# Windows VM must additionally launch QML and exercise the Agent pipe.
$buildPath = $env:PATH
try {
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot"
$reportedVersion = & $desktop --version
if ($LASTEXITCODE -ne 0 -or $reportedVersion -ne "RedFlag v$expectedVersion") {
throw 'Staged Desktop failed its version check without the Qt build PATH.'
}
$helper = Join-Path $output 'redflag-helper.exe'
$helperVersion = & $helper --version
if ($LASTEXITCODE -ne 0 -or $helperVersion -ne "RedFlag helper v$expectedVersion") {
throw 'Staged Helper failed its version check.'
}
& $helper execute-envelope
if ($LASTEXITCODE -ne 18) {
throw 'Windows Helper did not fail closed for unavailable execution.'
}
# The fixed task entry point takes no runtime arguments, and its exchange
# root is not negotiable. Both checks are read-only: this worker carries no
# RedFlag installation, so run-request can only fail to open the fixed root.
# Neither touches service identity, ACLs, or the task scheduler.
& $helper run-request extra-argument
if ($LASTEXITCODE -ne 10) {
throw 'Windows Helper accepted arguments on its fixed task entry point.'
}
& $helper run-request
if ($LASTEXITCODE -ne 26) {
throw 'Windows Helper did not refuse an unprovisioned exchange root.'
}
$helperRail = & (Join-Path $output 'provision-helper.ps1') -Describe | ConvertFrom-Json
if ($helperRail.principal -ne 'SYSTEM' -or
$helperRail.runnable_by -ne 'NT SERVICE\RedFlagAgent' -or
$helperRail.arguments -ne 'run-request') {
throw 'Staged Helper task description does not preserve the fixed authority boundary.'
}
} finally { $env:PATH = $buildPath }
$files = @(Get-ChildItem $output -Recurse -File | Sort-Object FullName | ForEach-Object {
[ordered]@{
path = $_.FullName.Substring($output.Length + 1).Replace('\', '/')
sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
size = $_.Length
}
})
[ordered]@{ desktop_version = $reportedVersion; helper_version = $helperVersion; files = $files } |
ConvertTo-Json -Depth 4 | Set-Content (Join-Path $output 'payload.json') -Encoding UTF8
Write-Output "Desktop payload staged at $output; this is not yet an installer or an installation test."