From bd45848dea77cf5ff285ec0fa8394ecbcda7887d Mon Sep 17 00:00:00 2001 From: KellyMichels Date: Wed, 2 Sep 2026 16:40:30 -0500 Subject: [PATCH] fix(zdeploy): -Scan must not block on a redirected stdin Read-Host throws under -NonInteractive - already caught - but with stdin merely redirected (a pipe, a scheduled task, powershell.exe launched from another shell) it blocks, waiting on a pipe that never answers. The first scan run that way hung for ten minutes with its table already printed. The test is [Console]::IsInputRedirected, checked before the prompt. Co-Authored-By: Claude Fable 5.1 --- zdeploy.ps1 | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/zdeploy.ps1 b/zdeploy.ps1 index ae556a5..2b29f22 100644 --- a/zdeploy.ps1 +++ b/zdeploy.ps1 @@ -303,11 +303,19 @@ if ($Scan) { Write-Host "" if (-not $Yes) { - # Read-Host throws in a -NonInteractive host, and the raw exception - # reads as "the scan crashed" rather than "nobody could answer the - # question". Caught rather than predicted: [Environment]::UserInteractive - # is still $true under -NonInteractive, so asking first does not work - - # only attempting it tells the truth. Scheduled tasks land here. + # Two different ways a prompt can have nobody to answer it, and they + # fail differently: + # - a -NonInteractive host: Read-Host THROWS. Caught below. + # - redirected stdin (a pipe, a scheduled task, powershell.exe launched + # from another shell): Read-Host does NOT throw - it BLOCKS, waiting + # on a pipe that never answers. The first scan run this way sat for + # ten minutes with its table already printed but withheld behind the + # blocked pipeline. [Environment]::UserInteractive is $true in both + # cases, so it cannot be the test; IsInputRedirected can. + if ([Console]::IsInputRedirected) { + Write-Host " stdin is not a terminal - cannot prompt. Re-run with -Yes to deploy these $($pending.Count).`n" -ForegroundColor Yellow + Stop-ZTracking; exit 0 + } $answer = $null try { $answer = Read-Host " Deploy these $($pending.Count)? [y/N]" } catch {