From f2c2b3f32743dc16fd213b672fc0b52362f631df Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Fri, 8 May 2026 03:04:28 +0000 Subject: [PATCH] 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. --- studio/setup.ps1 | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/studio/setup.ps1 b/studio/setup.ps1 index f2753d5c88..40788a0ecb 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -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) } # ─────────────────────────────────────────────