]> vilimpoc.org git repositories - dotfiles/commitdiff
dotfiles: stop the ssh.exe check from deleting the ssh.exe it verifies
authorMax Vilimpoc <max@vilimpoc.org>
Sun, 6 Sep 2026 15:44:24 +0000 (17:44 +0200)
committerMax Vilimpoc <max@vilimpoc.org>
Sun, 6 Sep 2026 15:44:24 +0000 (17:44 +0200)
The rsync step's run-it-to-verify check has been throwing away a working
ssh.exe on almost every run since it was added, on every architecture:

    try { $sshVer = (& $SshExe -V 2>&1 | Select-Object -First 1) } catch { }

Select-Object stops the pipeline the instant it has its one object, and
that is when ssh.exe has WRITTEN its banner - not when it has exited.
PowerShell then tears the pipeline down, kills the still-running process,
and reports -1. Measured on a healthy binary on the x64 box: 24 of 25 runs
reported exit -1 through Select-Object, 25 of 25 reported 0 when the output
was collected first. So collect, then take the first line.

The bug hid behind its own warning. A killed process looks exactly like the
failure the check exists to catch, the warning names the release's ssh.exe
rather than the test, and the resulting fallback to the in-box client - the
~17MB/s one this whole step exists to avoid - reads as a property of the
release. This box had been running on the slow client with core.sshCommand
pointed at System32 because of it. It also made the check's stated purpose
untestable: it fired whether or not the libcrypto mismatch it was written
for was present.

Verified by re-running the elevated half: ssh.exe now survives and the
per-user half repoints core.sshCommand at C:/Tools/rsync/ssh.exe.

Also teach the architecture audit where NASM actually lands. Its installer
falls back to %LOCALAPPDATA%\bin\NASM when it cannot write to Program Files
- already documented in setup-windows.bat, but never taught to the audit -
so a present, native nasm.exe reported "- not installed" while the run
still concluded that everything had resolved. winget cannot see it there
either, which is the same reason it re-downloads and re-runs that installer
on every single run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4qCpesQL1UwJxgQy77ttG

setup-windows-no-uac.ps1
setup-windows-with-uac.ps1

index 27f7a0ea856bf8bab085eaf6374080186d843627..2a104fb37b9bccf319424ced85323c5be36c10a2 100644 (file)
@@ -625,7 +625,18 @@ function Invoke-ArchAudit {
         'BinSkim.exe'         = @((Join-Path $BinSkimDir 'BinSkim.exe'))
         'rsync.exe'           = @((Join-Path $RsyncDir 'rsync.exe'))
         'ssh.exe'             = @((Join-Path $env:WINDIR 'System32\OpenSSH\ssh.exe'))
-        'nasm.exe'            = @()
+        # NASM's installer falls back to a per-user directory when it cannot
+        # write to Program Files - see the NASM comment in setup-windows.bat. It
+        # registers nothing there, so winget cannot see it (and re-downloads and
+        # re-runs the installer on every run), and it puts nothing on the PATH.
+        # Without these candidates the audit reported "- not installed" for a
+        # nasm.exe that was on disk all along, and then still concluded that
+        # everything had resolved.
+        'nasm.exe'            = @(
+            (Join-Path $env:LOCALAPPDATA 'bin\NASM\nasm.exe')
+            (Join-Path $env:ProgramFiles 'NASM\nasm.exe')
+            (Join-Path ${env:ProgramFiles(x86)} 'NASM\nasm.exe')
+        )
         'iperf3.exe'          = @()
         'OpenCppCoverage.exe' = @((Join-Path $env:ProgramFiles 'OpenCppCoverage\OpenCppCoverage.exe'), (Join-Path ${env:ProgramFiles(x86)} 'OpenCppCoverage\OpenCppCoverage.exe'))
         'vswhere.exe'         = @($vsWhereExe)
index a11e1752ddba9ad4995d8797966e0ef8fdb018a8..4b7ea948dccce4a7c6b51b0b256499c10fd77716 100644 (file)
@@ -436,13 +436,26 @@ try {
     # $global:LASTEXITCODE is cleared first because an exe that cannot start at\r
     # all throws without setting one, and the stale 0 from the previous native\r
     # command would otherwise read as success.\r
+    #\r
+    # DRAIN THE OUTPUT BEFORE READING $LASTEXITCODE. `... | Select-Object -First 1`\r
+    # was the original spelling here, and it is a trap: Select-Object stops the\r
+    # pipeline the instant it has its one object, and that is when ssh.exe has\r
+    # WRITTEN its banner - not when it has exited. PowerShell then tears the\r
+    # pipeline down, kills the still-running process, and reports -1. Measured on a\r
+    # healthy binary on an x64 box: 24 of 25 runs reported exit -1 through\r
+    # Select-Object, and 25 of 25 reported 0 when the output was collected first.\r
+    #\r
+    # So this check was deleting a perfectly good ssh.exe on almost every run, on\r
+    # every architecture, and the resulting fallback to the in-box client - the\r
+    # ~17MB/s one this whole step exists to avoid - looked like a property of the\r
+    # release rather than a bug in the test. Collect, THEN take the first line.\r
     $SshExe = Join-Path $RsyncDir 'ssh.exe'\r
     if ($WantSsh -and (Test-Path $SshExe)) {\r
         $prevEap = $ErrorActionPreference\r
         $ErrorActionPreference = 'Continue'\r
         $global:LASTEXITCODE = $null\r
         $sshVer = $null\r
-        try { $sshVer = (& $SshExe -V 2>&1 | Select-Object -First 1) } catch { }\r
+        try { $sshOut = (& $SshExe -V 2>&1); $sshVer = $sshOut | Select-Object -First 1 } catch { }\r
         finally { $ErrorActionPreference = $prevEap }\r
         if ($LASTEXITCODE -eq 0) {\r
             Write-Host "    ssh.exe runs: $sshVer"\r