build: add repeatable local Windows debug runner

This commit is contained in:
Hamza Ayed
2026-10-04 02:58:54 +03:00
parent 83199c1b78
commit 3e978d44ba
2 changed files with 112 additions and 2 deletions
@@ -0,0 +1,108 @@
[CmdletBinding()]
param(
[switch]$BuildOnly,
[switch]$SyncOnly,
[string]$WorkRoot
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$sourceApp = Join-Path $repositoryRoot 'flutter_app'
if (-not (Test-Path -LiteralPath (Join-Path $sourceApp 'pubspec.yaml') -PathType Leaf)) {
throw "Flutter project was not found under '$sourceApp'."
}
$localAppData = [IO.Path]::GetFullPath($env:LOCALAPPDATA).TrimEnd('\')
$tempDirectory = [IO.Path]::GetFullPath($env:TEMP).TrimEnd('\')
if ([string]::IsNullOrWhiteSpace($WorkRoot)) {
$WorkRoot = Join-Path $localAppData 'SovereignAI-Starter\windows-dev\flutter_app'
}
$workRootPath = [IO.Path]::GetFullPath($WorkRoot).TrimEnd('\')
$allowedRoots = @($localAppData, $tempDirectory)
$isAllowedRoot = $false
foreach ($allowedRoot in $allowedRoots) {
if ($workRootPath.StartsWith($allowedRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
$isAllowedRoot = $true
break
}
}
if (-not $isAllowedRoot) {
throw 'WorkRoot must be a child directory of LOCALAPPDATA or TEMP.'
}
if (Test-Path -LiteralPath $workRootPath) {
$workRootItem = Get-Item -LiteralPath $workRootPath -Force
if (($workRootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
throw "Refusing to use a reparse-point work directory: $workRootPath"
}
} else {
New-Item -ItemType Directory -Path $workRootPath | Out-Null
}
$destinationAppPath = $workRootPath
if (-not (Test-Path -LiteralPath $destinationAppPath)) {
New-Item -ItemType Directory -Path $destinationAppPath | Out-Null
}
$destinationAppPath = (Resolve-Path -LiteralPath $destinationAppPath).Path
$destinationItem = Get-Item -LiteralPath $destinationAppPath -Force
if (($destinationItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
throw "Refusing to mirror source into a reparse-point directory: $destinationAppPath"
}
if (-not $SyncOnly) {
$runningApps = @(Get-Process -Name 'flutter_app' -ErrorAction SilentlyContinue)
if ($BuildOnly) {
$runningApps = @($runningApps | Where-Object { $_.Path -and $_.Path.StartsWith($workRootPath + '\', [StringComparison]::OrdinalIgnoreCase) })
}
if ($runningApps.Count -gt 0) {
$processIds = ($runningApps | ForEach-Object Id) -join ', '
throw "Close the running Mithqal AI Debug window(s) from this build workspace first (PID: $processIds), then run this script again."
}
}
# Keep native build outputs and package caches in the local copy for fast,
# incremental rebuilds. They are deliberately excluded from mirroring.
$excludedDirectories = @(
(Join-Path $sourceApp 'build'),
(Join-Path $sourceApp '.dart_tool'),
(Join-Path $sourceApp 'windows\flutter\ephemeral'),
(Join-Path $sourceApp 'linux\flutter\ephemeral'),
(Join-Path $sourceApp 'macos\Flutter\ephemeral')
)
$excludedFiles = @(
'.flutter-plugins',
'.flutter-plugins-dependencies',
'.packages',
'.env',
'.env.*'
)
& robocopy $sourceApp $destinationAppPath /MIR /XJ /R:1 /W:1 /NFL /NDL /NJH /NJS /NP /XD $excludedDirectories /XF $excludedFiles
if ($LASTEXITCODE -ge 8) {
throw "Could not sync Flutter sources to the local build directory (robocopy $LASTEXITCODE)."
}
Write-Host "Local Windows build workspace: $destinationAppPath"
if ($SyncOnly) {
exit 0
}
Push-Location $destinationAppPath
try {
& flutter pub get --offline
if ($LASTEXITCODE -ne 0) {
throw 'Flutter dependencies are not available in the local pub cache. Run flutter pub get once with network access, then retry.'
}
if ($BuildOnly) {
& flutter build windows --debug --no-pub
} else {
& flutter run -d windows --debug --no-pub
}
if ($LASTEXITCODE -ne 0) {
throw "Flutter exited with code $LASTEXITCODE."
}
} finally {
Pop-Location
}