Use direct process streaming in scratch runner

This commit is contained in:
master
2026-03-13 13:54:01 +02:00
parent 481231e685
commit 7c955dce3a

View File

@@ -67,16 +67,6 @@ function Format-ProcessArgument {
return '"' + ($Value -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"' return '"' + ($Value -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"'
} }
function ConvertTo-PowerShellLiteral {
param(
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[string]$Value
)
return "'" + ($Value -replace "'", "''") + "'"
}
function Invoke-External { function Invoke-External {
param( param(
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
@@ -88,9 +78,6 @@ function Invoke-External {
) )
Push-Location $WorkingDirectory Push-Location $WorkingDirectory
$stdoutPath = New-LogCapturePath -Suffix '.stdout.log'
$stderrPath = New-LogCapturePath -Suffix '.stderr.log'
$wrapperPath = New-LogCapturePath -Suffix '.invoke.ps1'
try { try {
$resolvedFilePath = $FilePath $resolvedFilePath = $FilePath
if (Test-Path $FilePath) { if (Test-Path $FilePath) {
@@ -99,60 +86,85 @@ function Invoke-External {
$startFilePath = $resolvedFilePath $startFilePath = $resolvedFilePath
$startArgumentList = @($ArgumentList) $startArgumentList = @($ArgumentList)
$powershellPath = (Get-Command powershell.exe -ErrorAction Stop).Source
if ([System.IO.Path]::GetExtension($resolvedFilePath).Equals('.ps1', [System.StringComparison]::OrdinalIgnoreCase)) { if ([System.IO.Path]::GetExtension($resolvedFilePath).Equals('.ps1', [System.StringComparison]::OrdinalIgnoreCase)) {
$powershellPath = (Get-Command powershell.exe -ErrorAction Stop).Source
$startFilePath = $powershellPath $startFilePath = $powershellPath
$startArgumentList = @('-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $resolvedFilePath) + @($ArgumentList) $startArgumentList = @('-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $resolvedFilePath) + @($ArgumentList)
} }
$invocationExpression = '& ' + (ConvertTo-PowerShellLiteral -Value $startFilePath) $startInfo = [System.Diagnostics.ProcessStartInfo]::new()
if ($startArgumentList.Count -gt 0) { $startInfo.FileName = $startFilePath
$invocationExpression += ' ' + (($startArgumentList | ForEach-Object { ConvertTo-PowerShellLiteral -Value $_ }) -join ' ') $startInfo.WorkingDirectory = $WorkingDirectory
$startInfo.UseShellExecute = $false
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
foreach ($argument in $startArgumentList) {
[void]$startInfo.ArgumentList.Add([string]$argument)
} }
$wrapperContent = @( $process = [System.Diagnostics.Process]::new()
'$ErrorActionPreference = ''Stop''' $process.StartInfo = $startInfo
"$invocationExpression 1> $(ConvertTo-PowerShellLiteral -Value $stdoutPath) 2> $(ConvertTo-PowerShellLiteral -Value $stderrPath)"
'exit $LASTEXITCODE'
''
)
Set-Content -Path $wrapperPath -Value $wrapperContent -Encoding UTF8
$formattedArgumentList = @(@('-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $wrapperPath) | ForEach-Object { Format-ProcessArgument -Value $_ }) $stdoutQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
$stderrQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
$process = Start-Process ` $stdoutHandler = [System.Diagnostics.DataReceivedEventHandler]{
-FilePath $powershellPath ` param($sender, $eventArgs)
-ArgumentList $formattedArgumentList ` if ($null -ne $eventArgs.Data) {
-WorkingDirectory $WorkingDirectory ` $stdoutQueue.Enqueue($eventArgs.Data)
-PassThru }
}
$stdoutIndex = 0 $stderrHandler = [System.Diagnostics.DataReceivedEventHandler]{
$stderrIndex = 0 param($sender, $eventArgs)
if ($null -ne $eventArgs.Data) {
$stderrQueue.Enqueue($eventArgs.Data)
}
}
$process.add_OutputDataReceived($stdoutHandler)
$process.add_ErrorDataReceived($stderrHandler)
if (-not $process.Start()) {
throw "Failed to start process: $resolvedFilePath"
}
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
$dequeuedLine = $null
while (-not $process.HasExited) { while (-not $process.HasExited) {
Write-LogCaptureDelta -Path $stdoutPath -LineIndex ([ref]$stdoutIndex) while ($stdoutQueue.TryDequeue([ref]$dequeuedLine)) {
Write-LogCaptureDelta -Path $stderrPath -LineIndex ([ref]$stderrIndex) $dequeuedLine | Out-Host
$dequeuedLine = $null
}
while ($stderrQueue.TryDequeue([ref]$dequeuedLine)) {
$dequeuedLine | Out-Host
$dequeuedLine = $null
}
Start-Sleep -Milliseconds 500 Start-Sleep -Milliseconds 500
$process.Refresh()
} }
$process.WaitForExit() $process.WaitForExit()
$process.Refresh()
Write-LogCaptureDelta -Path $stdoutPath -LineIndex ([ref]$stdoutIndex) while ($stdoutQueue.TryDequeue([ref]$dequeuedLine)) {
Write-LogCaptureDelta -Path $stderrPath -LineIndex ([ref]$stderrIndex) $dequeuedLine | Out-Host
$dequeuedLine = $null
}
while ($stderrQueue.TryDequeue([ref]$dequeuedLine)) {
$dequeuedLine | Out-Host
$dequeuedLine = $null
}
if ($process.ExitCode -ne 0) { if ($process.ExitCode -ne 0) {
throw "Command failed: $resolvedFilePath $($ArgumentList -join ' ')" throw "Command failed: $resolvedFilePath $($ArgumentList -join ' ')"
} }
} }
finally { finally {
foreach ($capturePath in @($stdoutPath, $stderrPath, $wrapperPath)) {
if (Test-Path $capturePath) {
Remove-Item $capturePath -Force -ErrorAction SilentlyContinue
}
}
Pop-Location Pop-Location
} }
} }