Back up User PATH inside Add-ToUserPath, before first mutation

Previously only studio/setup.ps1 took a one-time PATH backup, at script
top (line ~547). install.ps1 (the irm | iex entry point) had no backup,
so users who installed via that path had no recovery surface if anything
clobbered their PATH. The PR description's "one-time backup before any
modifications" promise only held for the studio installer flow.

Move the backup into Add-ToUserPath itself: just before the first actual
SetValue mutation, write the pristine raw PATH to
HKCU\Software\Unsloth\PathBackup if no backup already exists. This:

- Covers both entry points (install.ps1 and studio/setup.ps1).
- Captures the TRUE pristine PATH even when install.ps1 runs first and
  studio/setup.ps1 runs afterwards (the script-top backup in setup.ps1
  would otherwise see an already-modified PATH).
- Is idempotent: once a backup exists, subsequent calls preserve it.
- Skips when nothing would mutate (dedup match) or PATH is empty.

The script-top backup in studio/setup.ps1 is kept for defense in depth.
This commit is contained in:
Daniel Han 2026-04-16 07:17:17 +00:00
commit 7df4316250
2 changed files with 34 additions and 0 deletions

View file

@ -142,6 +142,22 @@ function Install-UnslothStudio {
return $false # already present
}
}
# One-time backup of the pristine User PATH before our first
# mutation. Stored under HKCU\Software\Unsloth so a wiped/clobbered
# PATH can be recovered. Idempotent: existing backup is preserved.
if ($rawPath) {
try {
$backupKey = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Software\Unsloth')
try {
$existingBackup = $backupKey.GetValue('PathBackup', $null)
if (-not $existingBackup) {
$backupKey.SetValue('PathBackup', $rawPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
}
} finally {
$backupKey.Close()
}
} catch { }
}
if (-not $rawPath) {
Write-Host "[WARN] User PATH is empty — initializing with $Directory" -ForegroundColor Yellow
}

View file

@ -112,6 +112,24 @@ function Add-ToUserPath {
return $false # already present
}
}
# One-time backup of the pristine User PATH before our first
# mutation. Stored under HKCU\Software\Unsloth so a wiped/clobbered
# PATH can be recovered. Idempotent: existing backup is preserved.
# The script-top backup at line ~547 covers the studio entry point;
# this in-helper backup also covers callers that bypass that block.
if ($rawPath) {
try {
$backupKey = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Software\Unsloth')
try {
$existingBackup = $backupKey.GetValue('PathBackup', $null)
if (-not $existingBackup) {
$backupKey.SetValue('PathBackup', $rawPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
}
} finally {
$backupKey.Close()
}
} catch { }
}
if (-not $rawPath) {
Write-Host "[WARN] User PATH is empty — initializing with $Directory" -ForegroundColor Yellow
}