studio/setup.ps1: mirror step/substep output to [Console]::Out for piped consumers

Follow-up to 47432b0b. The -Command + *>&1 redirect at the
powershell.exe invocation level is not enough on its own: PS 5.1's
Write-Host writes via $Host.UI.WriteLine, and the default ConsoleHost
does not always forward host-UI output to the inherited stdout
handle when there is no console attached (CREATE_NO_WINDOW) and
stdout is a pipe. Even with $InformationPreference = 'Continue',
the parent's `tee` saw nothing, so `unsloth studio update --local
2>&1 | tee logs/update.log` produced an empty update.log.

Add a small Write-StudioStdoutMirror helper and have step/substep
mirror the plain (no ANSI) form of each line to [Console]::Out
when [Console]::IsOutputRedirected is true. [Console]::Out always
lands on the OS-level stdout file handle, so the line propagates
through install.ps1 -> unsloth.exe -> python -> powershell.exe ->
setup.ps1 unaffected by host-UI vs information-stream quirks.

Gated on IsOutputRedirected so the interactive-console UX stays
unchanged (no double-printing of the colorized step lines).

Net effect: the Windows Studio Update CI's grep for "prebuilt up to
date and validated" / "prebuilt installed and validated" finds the
marker because step() now writes the plain text to stdout from
inside setup.ps1.
This commit is contained in:
Daniel Han 2026-05-08 03:04:28 +00:00
parent 112690d584
commit f2c2b3f327

View file

@ -530,12 +530,33 @@ function Write-LlamaFailureLog {
Write-Host " | $line" -ForegroundColor DarkGray
}
}
# Mirror the plain (no ANSI) form of step/substep messages to the
# OS-level stdout handle when a parent is consuming our stdout via
# a pipe (CI `tee`, Python subprocess.PIPE, CREATE_NO_WINDOW grandchild).
# Write-Host on PS 5.1 routes through $Host.UI / the Information
# stream, neither of which propagates reliably across the
# install.ps1 -> unsloth.exe -> python -> powershell.exe ->
# setup.ps1 process chain. [Console]::Out always lands on the OS
# stdout file handle. Gated on IsOutputRedirected so the
# interactive-console path keeps the colorized Write-Host output
# only (no double-print).
function Write-StudioStdoutMirror {
param([Parameter(Mandatory = $true)][string]$Line)
try {
if ([Console]::IsOutputRedirected) {
[Console]::Out.WriteLine($Line)
[Console]::Out.Flush()
}
} catch {}
}
function step {
param(
[Parameter(Mandatory = $true)][string]$Label,
[Parameter(Mandatory = $true)][string]$Value,
[string]$Color = "Green"
)
$padded = if ($Label.Length -ge 15) { $Label.Substring(0, 15) } else { $Label.PadRight(15) }
if ($script:StudioVtOk -and -not $env:NO_COLOR) {
$dim = Get-StudioAnsi Dim
$rst = Get-StudioAnsi Reset
@ -546,10 +567,8 @@ function step {
'DarkGray' { Get-StudioAnsi Dim }
default { Get-StudioAnsi Ok }
}
$padded = if ($Label.Length -ge 15) { $Label.Substring(0, 15) } else { $Label.PadRight(15) }
Write-Host (" {0}{1}{2}{3}{4}{2}" -f $dim, $padded, $rst, $val, $Value)
} else {
$padded = if ($Label.Length -ge 15) { $Label.Substring(0, 15) } else { $Label.PadRight(15) }
Write-Host (" {0}" -f $padded) -NoNewline -ForegroundColor DarkGray
$fc = switch ($Color) {
'Green' { 'DarkGreen' }
@ -560,6 +579,7 @@ function step {
}
Write-Host $Value -ForegroundColor $fc
}
Write-StudioStdoutMirror (" {0}{1}" -f $padded, $Value)
}
function substep {
@ -581,6 +601,7 @@ function substep {
}
Write-Host (" {0,-15}{1}" -f "", $Message) -ForegroundColor $fc
}
Write-StudioStdoutMirror (" {0,-15}{1}" -f "", $Message)
}
# ─────────────────────────────────────────────