From 1a8ad4ae0662b6f00cef87a05d5baed8f86bf102 Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Tue, 28 Jul 2026 23:49:18 +0000 Subject: [PATCH] Judge the probes on the desktop's deadline, and interrupt the host it uses Preflight gives each managed probe ten seconds and nothing more: managed.rs:337 wraps `unsloth -h` and managed.rs:390 wraps `studio desktop-capabilities --json` in a tokio timeout, kills the child on expiry, and returns Stale as "cli_unusable" or "desktop_capability_probe_failed". The probe allowed three minutes, so a venv torn badly enough that its CLI only answers after half a minute of retries was recorded HEALTHY here while the real app shows it as repairable. That skips the re-run assertion the leg exists to make, which is the same false-HEALTHY hole the studio_install_ok and -h gating already closed. Both calls now use the desktop's ten seconds, and the elapsed time is recorded so a leg that flips for timing reasons says so in the artefact. On Windows the installer child now runs where the desktop runs it. install.rs 325-339 spawns the bundled install.ps1 as powershell.exe with -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File, so Windows PowerShell 5.1 is the only host a real desktop install ever uses. The interrupted run and the repair re-run both used pwsh 7, and every other Windows job in .github runs install.ps1 under pwsh too, so the installer's behaviour on 5.1 was covered by nothing: .NET Framework instead of .NET, OEM console encoding instead of UTF-8, and different native-command and OSArchitecture reporting are all real sources of divergence. A workflow whose point is to reproduce what the app does cannot run a different interpreter than the app does. The driver itself stays under pwsh; only the installer child and the repair invocation change. --- .github/scripts/interrupt-install.ps1 | 21 +++++++++++++++++--- .github/scripts/interrupted_install_probe.py | 19 ++++++++++++++++-- .github/workflows/interrupted-install-ci.yml | 7 ++++++- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/.github/scripts/interrupt-install.ps1 b/.github/scripts/interrupt-install.ps1 index 92f6019c31..917cef32cd 100644 --- a/.github/scripts/interrupt-install.ps1 +++ b/.github/scripts/interrupt-install.ps1 @@ -41,10 +41,25 @@ foreach ($dir in @($env:UNSLOTH_STUDIO_HOME, (Join-Path $HOME '.unsloth\studio') } catch { Write-Host "[interrupt] could not seed install marker in ${dir}: $_" } } -# Run the installer in its own pwsh so stdout can be redirected to the log while we poll. -$argList = @('-NoProfile', '-NonInteractive', '-File', 'install.ps1') +# Run the installer in its own host so stdout can be redirected to the log while we +# poll. That host is WINDOWS PowerShell, not pwsh: the desktop app spawns the bundled +# install.ps1 as `powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden +# -ExecutionPolicy Bypass -File` (install.rs:325-339), so 5.1 with those flags is the +# only host a real desktop install ever uses. Every other Windows job in .github runs +# install.ps1 under the runner's pwsh 7, which leaves the installer's behaviour on 5.1 +# -- .NET Framework rather than .NET, OEM/ANSI console encoding rather than UTF-8, +# different native-command and OSArchitecture reporting -- covered by nothing. An +# interruption test that runs a different interpreter than the app cannot claim to +# reproduce what the app does. The driver itself stays under pwsh; only the installer +# child and the repair re-run change. +$argList = @( + '-NoLogo', '-NoProfile', '-NonInteractive', + '-WindowStyle', 'Hidden', + '-ExecutionPolicy', 'Bypass', + '-File', 'install.ps1' +) if ($InstallArgs) { $argList += $InstallArgs.Split(' ') } -$proc = Start-Process -FilePath 'pwsh' -ArgumentList $argList ` +$proc = Start-Process -FilePath 'powershell.exe' -ArgumentList $argList ` -RedirectStandardOutput $LogPath -RedirectStandardError "$LogPath.err" ` -PassThru -NoNewWindow Write-Host "[interrupt] installer pid=$($proc.Id) marker='$Marker' deadline=${KillAtSeconds}s" diff --git a/.github/scripts/interrupted_install_probe.py b/.github/scripts/interrupted_install_probe.py index f05b466fa3..7699ade872 100644 --- a/.github/scripts/interrupted_install_probe.py +++ b/.github/scripts/interrupted_install_probe.py @@ -95,18 +95,33 @@ def main(argv: list[str]) -> int: print(f"[probe] {k:28} = {v}") # ── the two probes Tauri preflight actually runs ───────────────────────── - r = run([binp, "-h"], timeout = 180) + # Both under the DESKTOP's deadline, not a generous CI one. preflight wraps each + # call in a 10 second tokio timeout (managed.rs:337 for `-h`, managed.rs:390 for + # desktop-capabilities) and on expiry kills the child and reports Stale -- + # "cli_unusable" or "desktop_capability_probe_failed" (managed.rs:471, :521). A + # torn venv whose CLI still answers, but only after 30 seconds of import retries, + # is therefore an install the app sends to repair; waiting three minutes for it + # here would call the same install HEALTHY and skip the re-run assertion. run() + # reports a timeout as a non-zero rc, which lands in the same REPAIRABLE arm the + # desktop's Stale maps to. + PREFLIGHT_TIMEOUT = 10 + + t0 = time.time() + r = run([binp, "-h"], timeout = PREFLIGHT_TIMEOUT) (out / "cli-h.log").write_text(merged(r), encoding = "utf-8", errors = "replace") say("cli_h_ok", r[0] == 0) + say("cli_h_seconds", round(time.time() - t0, 2)) + t0 = time.time() caps_rc, caps_out, caps_err = run( - [binp, "studio", "desktop-capabilities", "--json"], timeout = 180 + [binp, "studio", "desktop-capabilities", "--json"], timeout = PREFLIGHT_TIMEOUT ) (out / "desktop-capabilities.json").write_text(caps_out, encoding = "utf-8", errors = "replace") (out / "desktop-capabilities.stderr.log").write_text( caps_err, encoding = "utf-8", errors = "replace" ) say("capabilities_ok", caps_rc == 0) + say("capabilities_seconds", round(time.time() - t0, 2)) # Parse EXACTLY as the desktop does: managed.rs:414 hands the whole stdout buffer # to serde_json, which rejects any leading or trailing non-JSON, and stderr was diff --git a/.github/workflows/interrupted-install-ci.yml b/.github/workflows/interrupted-install-ci.yml index 94de2249a9..f73094b89a 100644 --- a/.github/workflows/interrupted-install-ci.yml +++ b/.github/workflows/interrupted-install-ci.yml @@ -293,7 +293,12 @@ jobs: if: always() && steps.probe.outputs.verdict != 'HEALTHY' shell: pwsh run: | - pwsh -NoProfile -NonInteractive -File install.ps1 ${{ matrix.installArgs }} *>&1 | + # powershell.exe with install.rs:325-339's flags, matching the interrupted + # run: the desktop repairs by re-running the same bundled script in Windows + # PowerShell 5.1, so a repair that only works under pwsh 7 would pass here + # and still strand the user. + powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden ` + -ExecutionPolicy Bypass -File install.ps1 ${{ matrix.installArgs }} *>&1 | Tee-Object -FilePath logs/repair.log $bin = Join-Path $env:USERPROFILE '.unsloth\studio\unsloth_studio\Scripts\unsloth.exe' if (-not (Test-Path $bin)) {