setup.sh + setup.ps1: canonicalize both sides of legacy-equality check

Proactive audit pass found one real asymmetry the cycle-by-cycle
review process had not yet flagged:

- install.sh:704 / install.ps1:469 are gated on env-mode and only
  run when STUDIO_HOME has already been canonicalized (cycle 24).
  Symmetric.
- studio/setup.sh:577 / studio/setup.ps1:1829 run UNCONDITIONALLY,
  including in default mode. In default mode STUDIO_HOME is set to
  the bare logical \$HOME/.unsloth/studio (setup.sh:416) or
  Join-Path \$env:USERPROFILE ".unsloth\\studio" (setup.ps1:1480).
  Cycle 25 canonicalized only the legacy side, creating an
  asymmetry under symlinked \$HOME / junctioned %USERPROFILE%.

Result of the asymmetry: a default-mode install on a host with
\$HOME=/tmp/link -> /tmp/real treats the legacy default as a custom
root, putting llama.cpp at \$STUDIO_HOME/llama.cpp instead of
~/.unsloth/llama.cpp -- and the Python backend's _find_llama_server_binary
(which uses .resolve() on both sides) then can't find the install.

Fix: canonicalize STUDIO_HOME on the fly at the comparison site, in
both setup.sh and setup.ps1. Symmetric with the now-canonicalized
legacy side from cycle 25, regardless of which mode set STUDIO_HOME.

The other two comparison sites (install.sh:704, install.ps1:469) are
already symmetric because they only run when STUDIO_HOME comes from
the env-override resolution path that already does pwd -P / Resolve-Path.

unsloth_cli/commands/studio.py + studio/backend/run.py + main.py +
llama_cpp.py already use .resolve() on both sides -- symmetric.
This commit is contained in:
Daniel Han 2026-04-26 23:31:51 +00:00
parent 185853e608
commit 2ea2c91178
2 changed files with 20 additions and 6 deletions

View file

@ -1827,11 +1827,18 @@ step "transformers" "5.5.0 pre-installed"
# stale UNSLOTH_STUDIO_HOME pointing at the legacy default does not
# accidentally relocate llama.cpp.
$LegacyStudioHome = Join-Path $env:USERPROFILE ".unsloth\studio"
# Canonicalize the legacy side to match $StudioHome's normalized form.
# Canonicalize BOTH sides. $StudioHome is the resolved env-override path
# in env-mode (line 1474) but the bare logical path in default mode
# (line 1480). Canonicalizing on the fly handles a junctioned/symlinked
# %USERPROFILE% the same in both modes.
$_studioHomeCanon = $StudioHome
if (Test-Path -LiteralPath $_studioHomeCanon -PathType Container) {
$_studioHomeCanon = (Resolve-Path -LiteralPath $_studioHomeCanon).Path
}
if (Test-Path -LiteralPath $LegacyStudioHome -PathType Container) {
$LegacyStudioHome = (Resolve-Path -LiteralPath $LegacyStudioHome).Path
}
if ($StudioHome -eq $LegacyStudioHome) {
if ($_studioHomeCanon -eq $LegacyStudioHome) {
$UnslothHome = Join-Path $env:USERPROFILE ".unsloth"
} else {
$UnslothHome = $StudioHome

View file

@ -575,14 +575,21 @@ fi
# of env-var presence avoids regressing default installs that incidentally
# inherit UNSLOTH_STUDIO_HOME from a parent process or the CLI.
_LEGACY_STUDIO_HOME="$HOME/.unsloth/studio"
# Canonicalize the legacy side so a symlinked $HOME doesn't make the
# comparison fail when STUDIO_HOME (already canonicalized) and the
# legacy path point at the same directory.
# Canonicalize BOTH sides under symlinked $HOME. STUDIO_HOME is logical
# in default-mode (line 416 sets it from the bare $HOME path) and
# canonical in env-mode (line 413 uses pwd -P). Canonicalizing on the
# fly here means default-mode under symlinked $HOME still recognizes
# the legacy default and keeps llama.cpp at ~/.unsloth/llama.cpp.
_studio_home_canon="$STUDIO_HOME"
if [ -d "$_studio_home_canon" ]; then
_studio_home_canon=$(CDPATH= cd -P -- "$_studio_home_canon" 2>/dev/null && pwd -P) \
|| _studio_home_canon="$STUDIO_HOME"
fi
if [ -d "$_LEGACY_STUDIO_HOME" ]; then
_LEGACY_STUDIO_HOME=$(CDPATH= cd -P -- "$_LEGACY_STUDIO_HOME" 2>/dev/null && pwd -P) \
|| _LEGACY_STUDIO_HOME="$HOME/.unsloth/studio"
fi
if [ "$STUDIO_HOME" = "$_LEGACY_STUDIO_HOME" ]; then
if [ "$_studio_home_canon" = "$_LEGACY_STUDIO_HOME" ]; then
UNSLOTH_HOME="$HOME/.unsloth"
else
UNSLOTH_HOME="$STUDIO_HOME"