From 80eeb389af33cd30d031e08150c9a430d23e5ac3 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 12 May 2026 21:28:11 +0200 Subject: [PATCH] fix(burn-in): add -SkipArtifact switch; restore production fail-on-missing-dist PROBLEM: Invoke-RemoteBuild.ps1 was creating a placeholder artifact when the build produced no dist/ directory. In production this hides build defects (build exits 0 but produces nothing = silent failure treated as success). SOLUTION: Add explicit -SkipArtifact switch to the call chain: Invoke-RemoteBuild.ps1: - Add -SkipArtifact switch. When set: skips packaging entirely (no zip created). - When absent (production default): missing GuestArtifactSource is a hard error with a clear message pointing to -SkipArtifact as the opt-in for no-output builds. Invoke-CIJob.ps1: - Add -SkipArtifact switch. Passes it to Invoke-RemoteBuild.ps1. - Phase 6 (Get-BuildArtifacts) is entirely skipped when set; logs 'skipped' event. Test-CapacityBurnIn.ps1: - Add -SkipArtifact switch. Adds '-SkipArtifact' flag to each child job argList. Start-BurnInTest.ps1: - Always passes -SkipArtifact by default (burn-in uses delay commands, no output). - Add -NoSkipArtifact override switch for runs that do produce real artifacts. - Remove -NoSkipArtifact escape from wrapper: default burn-in never needs artifacts. --- scripts/Invoke-CIJob.ps1 | 50 ++++++++++++++++++++------------- scripts/Invoke-RemoteBuild.ps1 | 27 ++++++++++-------- scripts/Start-BurnInTest.ps1 | 4 ++- scripts/Test-CapacityBurnIn.ps1 | 7 +++++ 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/scripts/Invoke-CIJob.ps1 b/scripts/Invoke-CIJob.ps1 index 2f11206..bf07242 100644 --- a/scripts/Invoke-CIJob.ps1 +++ b/scripts/Invoke-CIJob.ps1 @@ -105,6 +105,11 @@ param( # Requires Set-TemplateSharedFolders.ps1 to have been run on the template. [switch] $UseSharedCache, + # Skip artifact packaging and collection (Phase 5 packaging + Phase 6). + # Use for burn-in / smoke builds that intentionally produce no output. + # In production (absent) the build must produce artifacts or the job fails. + [switch] $SkipArtifact, + # Credential Manager target for Gitea PAT used by -UseGitClone (private repos). # Store with: New-StoredCredential -Target 'GiteaPAT' -UserName '' -Password '' -Persist LocalMachine # Ignored when -UseGitClone is not set. @@ -494,35 +499,40 @@ try { if ($GuestArtifactSource -ne '') { $remoteBuildParams['GuestArtifactSource'] = $GuestArtifactSource } if ($ExtraGuestEnv.Count -gt 0) { $remoteBuildParams['ExtraGuestEnv'] = $ExtraGuestEnv } if ($UseSharedCache) { $remoteBuildParams['UseSharedCache'] = $true } + if ($SkipArtifact) { $remoteBuildParams['SkipArtifact'] = $true } & "$scriptDir\Invoke-RemoteBuild.ps1" @remoteBuildParams Write-JobEvent -Phase 'phase5.build' -Status 'success' $phaseTimings.Add([PSCustomObject]@{ Phase = 'Phase 5 - Remote build'; Sec = [int]((Get-Date) - $phaseStart).TotalSeconds }) $phaseStart = Get-Date # ── Phase 6: Collect artifacts ──────────────────────────────────────── - Write-Host "`n[Phase 6/6] Collecting artifacts..." - Write-JobEvent -Phase 'phase6.artifacts' -Status 'start' - if ($GuestOS -eq 'Linux') { - & "$scriptDir\Get-BuildArtifacts.ps1" ` - -IPAddress $VMIPAddress ` - -GuestOS 'Linux' ` - -SshKeyPath $SshKeyPath ` - -SshUser $SshUser ` - -SshKnownHostsFile $SshKnownHostsFile ` - -GuestArtifactPath '/opt/ci/output' ` - -HostArtifactDir $jobArtifactDir ` - -IncludeLogs + if ($SkipArtifact) { + Write-Host "`n[Phase 6/6] Artifact collection skipped (-SkipArtifact)." + Write-JobEvent -Phase 'phase6.artifacts' -Status 'skipped' } else { - & "$scriptDir\Get-BuildArtifacts.ps1" ` - -IPAddress $VMIPAddress ` - -Credential $credential ` - -GuestArtifactPath 'C:\CI\output\artifacts.zip' ` - -HostArtifactDir $jobArtifactDir ` - -IncludeLogs + Write-Host "`n[Phase 6/6] Collecting artifacts..." + Write-JobEvent -Phase 'phase6.artifacts' -Status 'start' + if ($GuestOS -eq 'Linux') { + & "$scriptDir\Get-BuildArtifacts.ps1" ` + -IPAddress $VMIPAddress ` + -GuestOS 'Linux' ` + -SshKeyPath $SshKeyPath ` + -SshUser $SshUser ` + -SshKnownHostsFile $SshKnownHostsFile ` + -GuestArtifactPath '/opt/ci/output' ` + -HostArtifactDir $jobArtifactDir ` + -IncludeLogs + } else { + & "$scriptDir\Get-BuildArtifacts.ps1" ` + -IPAddress $VMIPAddress ` + -Credential $credential ` + -GuestArtifactPath 'C:\CI\output\artifacts.zip' ` + -HostArtifactDir $jobArtifactDir ` + -IncludeLogs + } + Write-JobEvent -Phase 'phase6.artifacts' -Status 'success' -Data @{ dir = $jobArtifactDir } } - $elapsed = (Get-Date) - $startTime - Write-JobEvent -Phase 'phase6.artifacts' -Status 'success' -Data @{ dir = $jobArtifactDir } Write-JobEvent -Phase 'job' -Status 'success' -Data @{ elapsedSec = [int]$elapsed.TotalSeconds } $phaseTimings.Add([PSCustomObject]@{ Phase = 'Phase 6 - Artifacts'; Sec = [int]((Get-Date) - $phaseStart).TotalSeconds }) Write-Host "`n============================================================" diff --git a/scripts/Invoke-RemoteBuild.ps1 b/scripts/Invoke-RemoteBuild.ps1 index 881ca10..048d562 100644 --- a/scripts/Invoke-RemoteBuild.ps1 +++ b/scripts/Invoke-RemoteBuild.ps1 @@ -126,6 +126,12 @@ param( # and VMware Tools HGFS driver present in the guest. [switch] $UseSharedCache, + # When set, skip artifact packaging and do NOT write artifacts.zip in the guest. + # Use for burn-in / smoke builds that intentionally produce no output. + # In production (default: absent) the build MUST produce GuestArtifactSource or the + # job fails — a silently-empty build is treated as an error. + [switch] $SkipArtifact, + # Guest OS type — Linux uses SSH instead of WinRM. [ValidateSet('Windows', 'Linux')] [string] $GuestOS = 'Windows', @@ -427,7 +433,7 @@ try { # ── Custom build command (e.g. python, msbuild, cmake…) ────────── Write-Host "[Invoke-RemoteBuild] Running build command: $BuildCommand" $buildExit = Invoke-Command -Session $session -ScriptBlock { - param($workDir, $cmd, $artifactZip, $artifactSrc, $useCache, $extraEnv) + param($workDir, $cmd, $artifactZip, $artifactSrc, $useCache, $extraEnv, $skipArt) # Unbuffered Python output — lines appear in real-time instead of being held in buffer $env:PYTHONUNBUFFERED = '1' if ($useCache) { @@ -442,21 +448,14 @@ try { & cmd /c "$cmd 2>&1" | ForEach-Object { Write-Host $_ } $exit = $LASTEXITCODE - if ($exit -eq 0) { + if ($exit -eq 0 -and -not $skipArt) { $archiveDir = Split-Path $artifactZip -Parent if (-not (Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir -Force | Out-Null } $srcPath = Join-Path $workDir $artifactSrc if (-not (Test-Path $srcPath)) { - # No artifact source — create a placeholder zip so that - # Get-BuildArtifacts succeeds and the full pipeline is exercised. - # This is expected for burn-in / smoke builds that produce no output. - Write-Host "[Build] Artifact source '$srcPath' not found — creating placeholder artifact." - $placeholder = Join-Path $archiveDir 'build-ok.txt' - 'No artifact source directory. Build command completed successfully.' | - Out-File -FilePath $placeholder -Encoding UTF8 - Compress-Archive -Path $placeholder -DestinationPath $artifactZip -Force + throw "Artifact source directory not found: $srcPath. Build succeeded (exit 0) but produced no output. Check GuestArtifactSource or use -SkipArtifact for builds that intentionally produce no files." } else { $7zip = 'C:\Program Files\7-Zip\7z.exe' @@ -472,7 +471,7 @@ try { } return $exit - } -ArgumentList $GuestWorkDir, $BuildCommand, $GuestArtifactZip, $GuestArtifactSource, $UseSharedCache.IsPresent, $ExtraGuestEnv + } -ArgumentList $GuestWorkDir, $BuildCommand, $GuestArtifactZip, $GuestArtifactSource, $UseSharedCache.IsPresent, $ExtraGuestEnv, $SkipArtifact.IsPresent if ($buildExit -ne 0) { throw "Build command failed (exit $buildExit)" @@ -535,7 +534,11 @@ try { } } - Write-Host "[Invoke-RemoteBuild] Build succeeded. Artifact at $GuestArtifactZip" + if ($SkipArtifact) { + Write-Host '[Invoke-RemoteBuild] Build succeeded. Artifact packaging skipped (-SkipArtifact).' + } else { + Write-Host "[Invoke-RemoteBuild] Build succeeded. Artifact at $GuestArtifactZip" + } } finally { Remove-PSSession $session -ErrorAction SilentlyContinue diff --git a/scripts/Start-BurnInTest.ps1 b/scripts/Start-BurnInTest.ps1 index 355d9d1..075119e 100644 --- a/scripts/Start-BurnInTest.ps1 +++ b/scripts/Start-BurnInTest.ps1 @@ -49,6 +49,7 @@ param( [int] $Rounds = 3, [int] $Parallelism = 4, [string] $BuildCommand = 'ping 127.0.0.1 -n 31 > nul', + [switch] $NoSkipArtifact, [int] $RoundTimeoutMinutes = 60 ) @@ -71,6 +72,7 @@ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -BuildCommand $BuildCommand ` -Parallelism $Parallelism ` -Rounds $Rounds ` - -RoundTimeoutMinutes $RoundTimeoutMinutes + -RoundTimeoutMinutes $RoundTimeoutMinutes ` + $(if (-not $NoSkipArtifact) { '-SkipArtifact' }) exit $LASTEXITCODE diff --git a/scripts/Test-CapacityBurnIn.ps1 b/scripts/Test-CapacityBurnIn.ps1 index eeebac1..e230957 100644 --- a/scripts/Test-CapacityBurnIn.ps1 +++ b/scripts/Test-CapacityBurnIn.ps1 @@ -124,6 +124,10 @@ param( [string] $BuildCommand = '', + # Skip artifact packaging and collection in every child job. + # Set this for burn-in and smoke runs that use delay-only build commands. + [switch] $SkipArtifact, + [ValidateRange(1, 10)] [int] $Parallelism = 4, @@ -211,6 +215,9 @@ for ($r = 1; $r -le $Rounds; $r++) { if ($BuildCommand -ne '') { $argList.Add('-BuildCommand'); $argList.Add($BuildCommand) } + if ($SkipArtifact) { + $argList.Add('-SkipArtifact') + } $argArray = $argList.ToArray()