From b4af8c77befb0a78a5e32397348d35bb130b3513 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Thu, 22 Oct 2026 13:59:45 +0200 Subject: [PATCH] Bootstrap integration tests read the publish script's output as text GitHub Actions runs powershell steps with $ErrorActionPreference = 'stop', and under Windows PowerShell 5.1 a stderr line of a native process redirected with 2>&1 then ends the test: the three publish refusals failed in CI although the publish refused as it should. The publish dry runs now start pwsh through System.Diagnostics.Process like every other scenario. Local run under 'stop': 199 of 199. [skip actions]: published by a dispatch of publish-scripts.yml, channel dev first. Co-Authored-By: Claude Opus 5.5 --- .../Bootstrap/Bootstrap.Integration.Tests.ps1 | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/Bootstrap/Bootstrap.Integration.Tests.ps1 b/scripts/Bootstrap/Bootstrap.Integration.Tests.ps1 index f018d823..a8732910 100644 --- a/scripts/Bootstrap/Bootstrap.Integration.Tests.ps1 +++ b/scripts/Bootstrap/Bootstrap.Integration.Tests.ps1 @@ -666,8 +666,27 @@ Describe 'publish pipeline and dev channel' { $script:rendered = Join-Path $TestDrive 'rendered' $script:pwsh = (Get-Command pwsh -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source $script:publish = Join-Path $script:repoRoot '.github\scripts\Publish-BootstrapScripts.ps1' - $script:publishOutput = @(& $script:pwsh -NoProfile -File $script:publish -DryRun -SignedOutputDir $script:rendered 2>&1 | ForEach-Object { "$_" }) - if ($LASTEXITCODE -ne 0) { throw "Publish dry run failed: $($script:publishOutput -join [Environment]::NewLine)" } + + # Output read as text from a process of its own: a stderr line must not become an error + # record, which ends the test under the 'Stop' preference CI runs with. + function Invoke-Publish { + param([string[]]$Arguments) + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $script:pwsh + $psi.Arguments = ('-NoProfile -File "{0}" {1}' -f $script:publish, ($Arguments -join ' ')).Trim() + $psi.UseShellExecute = $false + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $process = [System.Diagnostics.Process]::Start($psi) + $stdout = $process.StandardOutput.ReadToEndAsync() + $stderr = $process.StandardError.ReadToEndAsync() + $process.WaitForExit() + return [pscustomobject]@{ ExitCode = $process.ExitCode; Output = @(($stdout.Result + "`n" + $stderr.Result) -split "`r?`n") } + } + + $dryRun = Invoke-Publish @('-DryRun', '-SignedOutputDir', ('"' + $script:rendered + '"')) + if ($dryRun.ExitCode -ne 0) { throw "Publish dry run failed: $($dryRun.Output -join [Environment]::NewLine)" } + $script:publishOutput = $dryRun.Output } # A loader that went live first would refuse the installer still being served. @@ -689,11 +708,11 @@ Describe 'publish pipeline and dev channel' { $text.Contains($Old) | Should -BeTrue [System.IO.File]::WriteAllText((Join-Path $root 'scripts\Bootstrap\Install-AutopilotMonitor.ps1'), $text.Replace($Old, $New)) - $output = @(& $script:pwsh -NoProfile -File $script:publish -DryRun -Channel dev -RepoRoot $root 2>&1 | ForEach-Object { "$_" }) + $refused = Invoke-Publish @('-DryRun', '-Channel', 'dev', '-RepoRoot', ('"' + $root + '"')) - $LASTEXITCODE | Should -Not -Be 0 - ($output -join "`n") | Should -Match 'The loader would refuse Install-AutopilotMonitor-Dev\.ps1' - ($output -join "`n") | Should -Not -Match '\[dry-run\]' + $refused.ExitCode | Should -Not -Be 0 + ($refused.Output -join "`n") | Should -Match 'The loader would refuse Install-AutopilotMonitor-Dev\.ps1' + ($refused.Output -join "`n") | Should -Not -Match '\[dry-run\]' } It 'the dev loader fetches the dev installer, which installs the dev agent' {