<# .SYNOPSIS Windows/x86 Worker CI stage — runs ON windev (10.100.0.48), invoked over SSH by scripts/ci/run-windev-ci.sh at the exact SHA under test. Restores the x86/net48 Worker test tier that the removed `windows` CI job used to cover (TST-25). .DESCRIPTION The Linux `windows-x86` / nightly CI jobs cannot build the x86/net48 Worker, so they ssh here and run this script inside the isolated CI clone C:\build\mxaccessgw-ci. The script is deliberately self-contained and versioned with the code under test: the SSH bootstrap only fetches + checks out the SHA (enough to load *this* file), then this script re-fetches and re-checks-out $Sha INSIDE the worktree lock so the checkout+build are fully serialized against a concurrent run and can never stomp each other's tree. Modes (cumulative): build x86 Worker build only (net48/CS0246/x86 compile guard — always fast). test build + Worker.Tests (x86). The per-push default. live test + the live-MXAccess smoke (MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1) and a full slnx build (surfaces Windows-only analyzer diagnostics, e.g. xUnit1030). Nightly. Runs under Windows PowerShell 5.1 (`powershell.exe`), so it avoids PS7-only syntax and checks $LASTEXITCODE after every native command rather than relying on stderr-as-error (git writes progress to stderr — the known windev gotcha). Exits nonzero on the first failure so the SSH exit code propagates to the CI job. .PARAMETER Sha The commit SHA under test. HEAD is force-checked-out to it and verified. .PARAMETER Mode build | test | live. .PARAMETER RepoRoot The CI clone root. Defaults to C:\build\mxaccessgw-ci. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Sha, [Parameter(Mandatory = $true)][ValidateSet('build', 'test', 'live')][string]$Mode, [string]$RepoRoot = 'C:\build\mxaccessgw-ci' ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $lockDir = "$RepoRoot.lock" $logFile = Join-Path $RepoRoot 'ci.log' $lockTimeout = [TimeSpan]::FromMinutes(20) function Write-Log { param([string]$Message) $line = ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message) Write-Host $line # Best-effort file log for the "is the tier down?" diagnosis in docs/GatewayTesting.md. try { Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue } catch { } } # Run a native command and throw on nonzero exit. Native stderr (git progress) does NOT # set $LASTEXITCODE, so it is not treated as failure — only a real nonzero exit is. function Invoke-Native { param([Parameter(Mandatory = $true)][string]$What, [Parameter(Mandatory = $true)][scriptblock]$Command) Write-Log "RUN: $What" & $Command if ($LASTEXITCODE -ne 0) { throw "$What failed with exit code $LASTEXITCODE" } } # --- acquire the worktree lock (mkdir is atomic; retry until timeout) --- $deadline = (Get-Date).Add($lockTimeout) $haveLock = $false Write-Log "Acquiring worktree lock $lockDir (mode=$Mode sha=$Sha)" while (-not $haveLock) { try { New-Item -Path $lockDir -ItemType Directory -ErrorAction Stop | Out-Null $haveLock = $true } catch { if ((Get-Date) -ge $deadline) { throw "Timed out after $($lockTimeout.TotalMinutes) min waiting for worktree lock $lockDir. A stuck run may have left it; if no CI job is active, remove it by hand." } Start-Sleep -Seconds 10 } } try { Push-Location $RepoRoot try { # Re-fetch + re-checkout under the lock so a concurrent run's bootstrap checkout # (done before we held the lock) cannot leave us on the wrong tree. Invoke-Native 'git fetch origin' { git fetch --prune origin } Invoke-Native "git checkout --force $Sha" { git checkout --force --detach $Sha } # Drop any stray tracked/untracked residue from a prior run so the build is clean. Invoke-Native 'git reset --hard' { git reset --hard $Sha } Invoke-Native 'git clean -fdx (build outputs)' { git clean -fdx -e ci.log } $head = (& git rev-parse HEAD).Trim() if ($head -ne $Sha) { throw "Worktree HEAD $head does not match requested SHA $Sha after checkout." } Write-Log "Worktree at $head" $workerProj = 'src/ZB.MOM.WW.MxGateway.Worker/ZB.MOM.WW.MxGateway.Worker.csproj' $workerTests = 'src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj' $integrationTests = 'src/ZB.MOM.WW.MxGateway.IntegrationTests/ZB.MOM.WW.MxGateway.IntegrationTests.csproj' # build: always. The x86/net48 compile is the floor guard (catches CS0246 on # unregenerated Generated/, x86-only breaks) even when the test step is demoted. Invoke-Native 'x86 Worker build' { dotnet build $workerProj -c Release -p:Platform=x86 } if ($Mode -eq 'test' -or $Mode -eq 'live') { Invoke-Native 'x86 Worker.Tests' { dotnet test $workerTests -c Release -p:Platform=x86 } } if ($Mode -eq 'live') { # Full-solution build surfaces Windows-only analyzer diagnostics (xUnit1030 etc.) # that never fire on the macOS/Linux NonWindows build. Invoke-Native 'full slnx build' { dotnet build src/ZB.MOM.WW.MxGateway.slnx -c Release } $env:MXGATEWAY_RUN_LIVE_MXACCESS_TESTS = '1' try { Invoke-Native 'live-MXAccess smoke' { dotnet test $integrationTests -c Release --filter 'FullyQualifiedName~WorkerLiveMxAccessSmokeTests' } } finally { Remove-Item Env:\MXGATEWAY_RUN_LIVE_MXACCESS_TESTS -ErrorAction SilentlyContinue } } Write-Log "SUCCESS mode=$Mode sha=$Sha" } finally { Pop-Location } } catch { Write-Log "FAILURE mode=$Mode sha=$Sha : $($_.Exception.Message)" throw } finally { if ($haveLock) { Remove-Item -Path $lockDir -Recurse -Force -ErrorAction SilentlyContinue Write-Log "Released worktree lock $lockDir" } }