5c8075996f
Acceptance-check finding (concurrency): the worktree lock in windev-worker-ci.ps1 guarded the build stage, but run-windev-ci.sh's bootstrap did git fetch + git checkout on the shared C:\build\mxaccessgw-ci clone BEFORE that lock was taken. Two concurrent runs therefore collided on .git/index.lock at the bootstrap stage (the second failed 'Another git process seems to be running', exit 1) instead of the second waiting — defeating the lock's purpose for manual/degraded-mode overlap or a future second runner. (The single Gitea runner serializes jobs, so CI pushes never actually overlapped; this is defense-in-depth being restored.) Fix: the bootstrap now acquires the same mkdir worktree lock around its fetch/ checkout and exports MXGW_CI_LOCK_HELD; windev-worker-ci.ps1 re-uses that lock (skips re-acquire/release) when the env is set, and still self-locks for a standalone/manual run. Now the second concurrent run waits at the bootstrap.
157 lines
6.5 KiB
PowerShell
157 lines
6.5 KiB
PowerShell
<#
|
|
.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) ---
|
|
# run-windev-ci.sh's bootstrap already holds this lock around its pre-hand-off fetch/checkout
|
|
# (MXGW_CI_LOCK_HELD=1) so that stage can't race a concurrent run; re-use it rather than
|
|
# re-acquire (and leave releasing to the bootstrap). A standalone/manual run (degraded mode)
|
|
# has no such env and self-locks here as before.
|
|
$deadline = (Get-Date).Add($lockTimeout)
|
|
$haveLock = $false
|
|
if ($env:MXGW_CI_LOCK_HELD -eq '1') {
|
|
Write-Log "Worktree lock already held by bootstrap (mode=$Mode sha=$Sha)"
|
|
}
|
|
else {
|
|
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"
|
|
}
|
|
}
|