fix(install): address Codex round-2 review (WSL1 distros, build-failure restore, libcurl, shim quoting, opt-out forwarding)

- install.ps1: detect a PRE-EXISTING WSL1 distro up-front (kernel string +
  libcuda probe inside the distro; encoding-proof vs UTF-16 `wsl -l -v`) and
  convert it with `wsl --set-version 2`, failing early with instructions if
  conversion does not take -- instead of completing a full install that only
  fails at the final torch.cuda check (no GPU passthrough under WSL1).
- install.ps1: quote the distro name in the generated unsloth.cmd shim and in
  the copy-pasteable hint commands so UNSLOTH_WSL_DISTRO values with spaces
  ("Ubuntu Preview") keep working.
- install.ps1: forward UNSLOTH_NO_LLAMA_CUDA=1 into the WSL install env; the
  inner setup.sh otherwise defers its llama.cpp build to a background builder
  this script then never dispatches (the same opt-out skips it), leaving no
  llama-server and a misleading "building in background" footer. Also add
  libcurl4-openssl-dev to the WSL bootstrap apt line.
- provision_llama_cuda.sh: install libcurl4-openssl-dev with the base tools --
  _cmake_configure forces -DLLAMA_CURL=ON and on the deferred WSL path this
  script is the only build path (setup.sh's GGUF dep install was skipped), so
  configure failed on fresh hosts without the headers.
- provision_llama_cuda.sh: keep the pre-existing llama.cpp backup until the
  fresh build is CONFIRMED (was: dropped right after a successful clone), and
  restore it on configure/build failure or when no server binary was produced
  -- a failed CUDA build no longer destroys a previously working (CPU) server.
- setup.sh: when provisioning fails and NO llama-server is present, set
  _LLAMA_CPP_DEGRADED=true so the arm64 CPU-prebuilt last resort and the
  installer failure exit fire instead of reporting a working install.

Round-2 comments verified already fixed in ad77ae6 (anchored to its parent
d161ff5): the torch probe already passes --reinstall; the WSL uninstall is
already scoped to /root only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Han 2026-06-09 20:07:32 -07:00
commit 8e51d18a6c
3 changed files with 68 additions and 19 deletions

View file

@ -1563,6 +1563,29 @@ shell.Run cmd, 0, False
if (-not $haveDistro) {
substep "installing WSL distro '$distro' (first time only)..." "Cyan"
try { & wsl.exe --install -d $distro --no-launch } catch {}
} else {
# A PRE-EXISTING distro may be WSL1, which has no GPU passthrough: the existence
# probe passes but the full install would only fail at the final torch.cuda
# check. Detect WSL1 up-front from inside the distro (kernel string + libcuda --
# encoding-proof, unlike parsing UTF-16 `wsl -l -v` output) and convert in place;
# `wsl --set-version` preserves the distro's files. Freshly installed distros
# are WSL2 (default version 2), so only the pre-existing case needs this.
$_wsl2Probe = 'grep -qiE ''microsoft-standard|WSL2'' /proc/version 2>/dev/null || test -e /usr/lib/wsl/lib/libcuda.so'
$_isWsl2 = $false
$global:LASTEXITCODE = -1
try { & wsl.exe -d $distro -u root -- bash -c $_wsl2Probe *> $null; $_isWsl2 = ($LASTEXITCODE -eq 0) } catch {}
if (-not $_isWsl2) {
substep "distro '$distro' looks like WSL1 (no GPU passthrough) -- converting to WSL2 (one-time; can take a few minutes)..." "Yellow"
$global:LASTEXITCODE = -1
try { & wsl.exe --set-version $distro 2 } catch {}
$global:LASTEXITCODE = -1
try { & wsl.exe -d $distro -u root -- bash -c $_wsl2Probe *> $null; $_isWsl2 = ($LASTEXITCODE -eq 0) } catch {}
if (-not $_isWsl2) {
Restore-StudioVenvRollback
return (Exit-InstallFailure "WSL distro '$distro' is WSL1 and automatic conversion failed; NVIDIA GPU passthrough needs WSL2. Convert it, then re-run the installer: wsl --set-version `"$distro`" 2" 1)
}
substep "'$distro' converted to WSL2." "Green"
}
}
substep "installing Unsloth Studio inside WSL '$distro' with full GPU (this downloads PyTorch)..." "Cyan"
# For a non-main ref, fetch + export THAT ref so the WSL venv gets the branch's
@ -1575,10 +1598,16 @@ shell.Run cmd, 0, False
# itself instead of leaving them with no GGUF server.)
# apt stderr is kept visible (only stdout -> /dev/null) so network/DNS/repo failures inside
# WSL are diagnosable rather than silently swallowed.
# Forward the CUDA llama.cpp opt-out into WSL: without it the inner setup.sh would
# defer its build to a background builder this script then never starts (the same
# opt-out skips the dispatch below), leaving no llama-server and a misleading
# "building in background" footer. Forwarded, setup.sh keeps its own build instead.
$_fwdEnv = ''
if ($env:UNSLOTH_NO_LLAMA_CUDA -eq '1') { $_fwdEnv = 'export UNSLOTH_NO_LLAMA_CUDA=1; ' }
if ($_instRef -eq 'main') {
$wslInstall = 'export DEBIAN_FRONTEND=noninteractive UNSLOTH_WSL_LLAMA_DEFERRED=1; apt-get update -y >/dev/null; apt-get install -y build-essential cmake git curl pciutils >/dev/null; curl -fsSL https://unsloth.ai/install.sh | sh'
$wslInstall = $_fwdEnv + 'export DEBIAN_FRONTEND=noninteractive UNSLOTH_WSL_LLAMA_DEFERRED=1; apt-get update -y >/dev/null; apt-get install -y build-essential cmake git curl pciutils libcurl4-openssl-dev >/dev/null; curl -fsSL https://unsloth.ai/install.sh | sh'
} else {
$wslInstall = 'export DEBIAN_FRONTEND=noninteractive UNSLOTH_WSL_LLAMA_DEFERRED=1; export UNSLOTH_INSTALL_REF=' + $_instRef + '; apt-get update -y >/dev/null; apt-get install -y build-essential cmake git curl pciutils >/dev/null; curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/' + $_instRef + '/install.sh | sh'
$wslInstall = $_fwdEnv + 'export DEBIAN_FRONTEND=noninteractive UNSLOTH_WSL_LLAMA_DEFERRED=1; export UNSLOTH_INSTALL_REF=' + $_instRef + '; apt-get update -y >/dev/null; apt-get install -y build-essential cmake git curl pciutils libcurl4-openssl-dev >/dev/null; curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/' + $_instRef + '/install.sh | sh'
}
# install.sh may exit non-zero on the optional llama.cpp prebuilt step (no aarch64 prebuilt)
# though torch + unsloth + Studio still install, so lower EAP so it doesn't abort under Stop.
@ -1651,7 +1680,9 @@ shell.Run cmd, 0, False
New-Item -ItemType Directory -Force -Path $shimDir *> $null
$shimLines = @(
'@echo off',
"wsl.exe -d $distro -u root -- /root/.unsloth/studio/unsloth_studio/bin/unsloth %*"
# Quote the distro: an UNSLOTH_WSL_DISTRO with spaces (e.g. "Ubuntu Preview")
# would otherwise split after -d and break every `unsloth ...` invocation.
"wsl.exe -d `"$distro`" -u root -- /root/.unsloth/studio/unsloth_studio/bin/unsloth %*"
)
Set-Content -LiteralPath (Join-Path $shimDir "unsloth.cmd") -Value $shimLines -Encoding ASCII
# A fresh Windows profile may have no HKCU 'Path' value at all -> $userPath is null
@ -1668,7 +1699,7 @@ shell.Run cmd, 0, False
substep " unsloth studio # runs in WSL; opens http://localhost:8888" "Cyan"
substep " unsloth studio run # also forwarded into WSL" "Cyan"
} catch {
substep "(shim creation failed; launch manually): wsl -d $distro -u root -- bash -lic 'unsloth studio -p 8888'" "Yellow"
substep "(shim creation failed; launch manually): wsl -d `"$distro`" -u root -- bash -lic 'unsloth studio -p 8888'" "Yellow"
}
# Desktop + Start Menu shortcuts: launch the WSL Studio and open the browser when ready.
try {
@ -1768,13 +1799,13 @@ shell.Run cmd, 0, False
Start-Process -WindowStyle Hidden -FilePath 'wsl.exe' -ArgumentList @('-d', $distro, '--cd', '/root', '-u', 'root', '--', 'bash', '/root/.unsloth/run_llama_build.sh') | Out-Null
step "llama.cpp" "building CUDA llama.cpp for GGUF inference in the background (a few min); log: ~/.unsloth/llama_cuda_build.log" "Green"
} else {
substep "(GGUF inference needs a CUDA llama.cpp build; build later: wsl -d $distro -u root -- bash ~/.unsloth/provision_llama_cuda.sh)" "Yellow"
substep "(GGUF inference needs a CUDA llama.cpp build; build later: wsl -d `"$distro`" -u root -- bash ~/.unsloth/provision_llama_cuda.sh)" "Yellow"
}
} catch {} finally { $ErrorActionPreference = $prevEapL }
}
} else {
step "wsl" "WSL Studio install did not finish cleanly (torch.cuda not detected; inner exit $wslRc) -- see log above." "Yellow"
substep "retry, or launch manually: wsl -d $distro -u root -- bash -lic 'unsloth studio -p 8888'" "Cyan"
substep "retry, or launch manually: wsl -d `"$distro`" -u root -- bash -lic 'unsloth studio -p 8888'" "Cyan"
}
if ($torchOk) {
# WSL GPU install succeeded. On this path the Windows venv is vestigial (everything

View file

@ -49,8 +49,11 @@ HAVE_APT=0; command -v apt-get >/dev/null 2>&1 && HAVE_APT=1
# without the basic build tools needed to clone/configure llama.cpp.
if [ "$HAVE_APT" -eq 1 ]; then
$SUDO apt-get update -y >/dev/null 2>&1 || true
# libcurl4-openssl-dev: _cmake_configure forces -DLLAMA_CURL=ON, and on the WSL
# deferred path this script is the only build path -- setup.sh's GGUF dep install
# (which covers libcurl) was skipped, so configure would fail without the headers.
$SUDO apt-get install -y --no-install-recommends \
build-essential cmake git curl ca-certificates >/dev/null 2>&1 || true
build-essential cmake git curl ca-certificates libcurl4-openssl-dev >/dev/null 2>&1 || true
$SUDO apt-get install -y --no-install-recommends gcc-14 g++-14 >/dev/null 2>&1 || true
fi
@ -112,11 +115,17 @@ if [ -n "$CC_CAP" ]; then CUDA_ARCH="$CC_CAP"; else CUDA_ARCH="native"; fi
# the user's request instead of always tracking ggml-org main.
mkdir -p "$(dirname "$LLAMA_DIR")"
_LLAMA_REF="${UNSLOTH_LLAMA_TAG:-}"
# Preserve any existing (e.g. CPU-only) llama.cpp so a failed clone OR a failed CUDA
# build doesn't leave the user with NO server: the backup is restored on any failure
# exit and only dropped once a server from the fresh build is confirmed.
_LLAMA_BAK=""
_restore_prev() {
if [ -n "$_LLAMA_BAK" ] && [ -e "$_LLAMA_BAK" ]; then
rm -rf "$LLAMA_DIR" 2>/dev/null
mv "$_LLAMA_BAK" "$LLAMA_DIR" 2>/dev/null && log "restored previous llama.cpp install"
fi
}
if [ ! -d "$LLAMA_DIR/.git" ]; then
# Preserve any existing (e.g. CPU-only) llama.cpp so a FAILED clone doesn't leave the user
# with NO server -- restore it on clone failure. A successful clone makes it obsolete (the
# fresh CUDA build replaces it), so the backup is dropped then.
_LLAMA_BAK=""
if [ -e "$LLAMA_DIR" ]; then
_LLAMA_BAK="${LLAMA_DIR}.prev.$$"
rm -rf "$_LLAMA_BAK" 2>/dev/null
@ -131,12 +140,11 @@ if [ ! -d "$LLAMA_DIR/.git" ]; then
fi
if [ "$_clone_ok" -ne 1 ]; then
log "git clone failed"
[ -n "$_LLAMA_BAK" ] && mv "$_LLAMA_BAK" "$LLAMA_DIR" 2>/dev/null # restore previous server
_restore_prev
exit 0
fi
[ -n "$_LLAMA_BAK" ] && rm -rf "$_LLAMA_BAK" 2>/dev/null
fi
cd "$LLAMA_DIR" || exit 0
cd "$LLAMA_DIR" || { _restore_prev; exit 0; }
log "building CUDA llama.cpp (arch=$CUDA_ARCH, host=$HCXX) - this takes a few minutes..."
_cmake_configure() {
@ -153,7 +161,7 @@ _cmake_configure() {
if ! _cmake_configure; then
log "stale/incompatible CMake cache detected; wiping build dir for a clean CUDA configure"
rm -rf build
_cmake_configure || { log "cmake configure failed"; exit 0; }
_cmake_configure || { log "cmake configure failed"; cd /; _restore_prev; exit 0; }
fi
# Build the full target set unsloth-zoo's GGUF exporter also needs (llama-mtmd-cli,
# llama-gguf-split) so one build serves both Studio inference and save_pretrained_gguf.
@ -192,13 +200,19 @@ if ! _cmake_build; then
# Wipe build/ and rebuild clean once before giving up.
log "build failed (likely interrupted/partial); wiping build dir and rebuilding clean"
rm -rf build
_cmake_configure || { log "cmake configure failed"; exit 0; }
_cmake_build || { log "cmake build failed"; exit 0; }
_cmake_configure || { log "cmake configure failed"; cd /; _restore_prev; exit 0; }
_cmake_build || { log "cmake build failed"; cd /; _restore_prev; exit 0; }
fi
if is_cuda_server "$SERVER"; then
log "CUDA llama-server ready: $SERVER"
else
[ -n "$_LLAMA_BAK" ] && rm -rf "$_LLAMA_BAK" 2>/dev/null
elif [ -x "$SERVER" ]; then
# A server exists but isn't CUDA-confirmed; still better than the old backup.
log "build finished but CUDA llama-server could not be confirmed"
[ -n "$_LLAMA_BAK" ] && rm -rf "$_LLAMA_BAK" 2>/dev/null
else
log "build finished but no llama-server was produced"
cd /; _restore_prev
fi
exit 0

View file

@ -1488,7 +1488,11 @@ if [ "$_HOST_SYSTEM" = "Linux" ] \
elif [ -f "$LLAMA_SERVER_BIN" ]; then
substep "CUDA build unavailable; keeping existing (CPU) llama-server" "$C_WARN"
else
substep "CUDA build unavailable; see ~/.unsloth/llama.cpp build output" "$C_WARN"
substep "CUDA build unavailable and no llama-server present; see $LLAMA_CPP_DIR build output" "$C_WARN"
# No server at all (e.g. the provisioner replaced a previous build and then
# failed): mark degraded so the arm64 CPU-prebuilt last resort below and the
# installer failure exit fire instead of reporting a working install.
_LLAMA_CPP_DEGRADED=true
fi
fi
fi