From eea0433f0e6e8551d3d75f2a016223f790340d6d Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Tue, 28 Jul 2026 15:13:48 +0000 Subject: [PATCH] Make the Windows and Linux clean-machine legs honest The Windows scrub only touched PATH, so the legs were green while not clean: run 30365014702 logged "python ABSENT" and then "Python 3.13 already installed" with uv resolving C:\hostedtoolcache\windows\Python\3.13.14\arm64\python.exe. py.exe lives in C:\Windows and uv discovers interpreters itself, so take the toolcache off disk and fail when tooling survives, instead of only printing it. The Linux desktop legs never stripped anything, and the tauri.log step was all || true so it could not fail. Run the bundled installer the way install.rs does, with --tauri alone, and assert torch: passing --no-torch skipped the slowest half of first launch and let the venv check pass over it. Pin the WSL rootfs to a dated build; current/ is a rolling alias and the digest next to it is fixed. --- .../workflows/clean-machine-install-ci.yml | 26 +++++++++++++++++-- .../desktop-app-clean-machine-ci.yml | 12 ++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clean-machine-install-ci.yml b/.github/workflows/clean-machine-install-ci.yml index 4afc55c45e..4012276f42 100644 --- a/.github/workflows/clean-machine-install-ci.yml +++ b/.github/workflows/clean-machine-install-ci.yml @@ -404,7 +404,7 @@ jobs: run: | # WSL2 is present on windows-2022+ runner images; only a distro is missing. wsl --set-default-version 2 - $url = 'https://cloud-images.ubuntu.com/wsl/releases/24.04/current/ubuntu-noble-wsl-amd64-24.04lts.rootfs.tar.gz' + $url = 'https://cloud-images.ubuntu.com/wsl/releases/24.04/20240423/ubuntu-noble-wsl-amd64-24.04lts.rootfs.tar.gz' $expected = '2a790896740b14d637dbdc583cce1ba081ac53b9e9cdb46dc09a2f73abbd9934' New-Item -ItemType Directory -Force -Path wsl-dist, logs | Out-Null Invoke-WebRequest -Uri $url -OutFile wsl-dist/rootfs.tar.gz -UseBasicParsing -TimeoutSec 900 @@ -542,6 +542,17 @@ jobs: -Value "@`"$($wingetCmd.Source)`" %*" $kept = @($shim) + $kept } + # Take the toolcache Python off disk, not just off PATH. py.exe lives in + # C:\Windows (which must stay) and uv does its own interpreter discovery, so + # both reach the toolcache no matter what PATH says -- which is how a leg + # printing `python ABSENT` still installed with the runner's 3.13.14. + foreach ($tc in @("$env:AGENT_TOOLSDIRECTORY\Python", 'C:\hostedtoolcache\windows\Python')) { + if ($tc -and (Test-Path $tc)) { + try { Rename-Item -LiteralPath $tc -NewName 'Python.masked' -ErrorAction Stop + Write-Host "masked toolcache python: $tc" } + catch { Write-Host "::error::could not mask $tc ($($_.Exception.Message)); the leg would not be clean"; exit 1 } + } + } $newPath = ($kept -join ';') "PATH=$newPath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 # install.ps1 calls Refresh-SessionPath (defined install.ps1:318-337, called at @@ -570,9 +581,20 @@ jobs: - name: Verify the simulation took effect shell: pwsh run: | - foreach ($t in 'python','git','cmake','cl') { + $leaked = @() + # `py` too: the launcher lives in C:\Windows, which the scrub keeps, and it + # finds the toolcache Python the scrub just removed from PATH. + foreach ($t in 'python','py','git','cmake','cl') { $f = Get-Command $t -ErrorAction SilentlyContinue Write-Host ("{0,-8} {1}" -f $t, $(if ($f) { $f.Source } else { 'ABSENT' })) + if ($f -and $t -ne 'py') { $leaked += "$t -> $($f.Source)" } + } + # Printing alone could not fail, and the leg was green while not clean: + # run 30365014702 logged `python ABSENT` and then `Python 3.13 already + # installed` / `Using CPython ... C:\hostedtoolcache\windows\Python\...`. + if ($leaked) { + Write-Host "::error::developer tooling survived the scrub: $($leaked -join '; ')" + exit 1 } $winget = Get-Command winget -ErrorAction SilentlyContinue Write-Host ("winget {0}" -f $(if ($winget) { $winget.Source } else { 'ABSENT' })) diff --git a/.github/workflows/desktop-app-clean-machine-ci.yml b/.github/workflows/desktop-app-clean-machine-ci.yml index 190dd3bdf9..aaa9c3a19a 100644 --- a/.github/workflows/desktop-app-clean-machine-ci.yml +++ b/.github/workflows/desktop-app-clean-machine-ci.yml @@ -166,11 +166,15 @@ jobs: # does: --tauri, stdin closed, no tty. --tauri rejects a custom studio # home (install.sh:102-114), so drop the workspace-scoped override. env -u UNSLOTH_STUDIO_HOME \ - bash "$APP/Contents/Resources/install.sh" --tauri --no-torch \ + bash "$APP/Contents/Resources/install.sh" --tauri \ < /dev/null 2>&1 | tee logs/bundled-install.log PY="$HOME/.unsloth/studio/unsloth_studio/bin/python" [ -x "$PY" ] || { echo "::error::bundled installer left no venv at $PY"; exit 1; } "$PY" -V + # install.rs passes only --tauri, so torch is part of first launch. Dropping + # --no-torch here and asserting torch keeps the venv check from passing over + # a bundle whose only failure is the torch install. + "$PY" -c "import torch; print('torch', torch.__version__)" - name: Launch and prove it stays up run: | @@ -204,7 +208,13 @@ jobs: tail -60 "$f" # The two fields the bug report turned on. grep -E "disposition=|can_auto_repair=|Xcode Command Line|ModuleNotFoundError" "$f" || true + found=1 done + # Everything above is `|| true`, so on its own this step could not fail while + # the header sells the tauri.log disposition as an acceptance criterion. + # setup_logging (src-tauri/src/main.rs:50-67) opens tauri.log unconditionally + # at process start, so no log at all means the binary never got that far. + [ "${found:-0}" = "1" ] || { echo "::error::the app wrote no tauri.log; it never reached setup_logging"; exit 1; } - name: Restore the runner if: always()