unsloth/install.ps1
Manan Shah 6f129a214b
Fix Install commands for Windows + 1 line installs (#4447)
* One liner setup for unsloth studio

* Fix install scripts: system deps, activation bugs, curl/wget support

- install.sh: detect platform (macOS/Linux/WSL) and check for missing
  system dependencies (cmake, git, build-essential, libcurl4-openssl-dev).
  Prompt user once for permission to install all missing packages via
  brew (macOS) or sudo apt-get (Linux/WSL). Add wget fallback via
  download() helper since curl is not always present on minimal Linux
  installs. Fix nested curl|sh stdin stealing by downloading uv installer
  to a tempfile first. Replace venv activation (no-op in a pipe subshell)
  with explicit --python flag for uv pip install and direct venv binary
  invocation. Add idempotency guard for venv creation. Redirect stdin
  on unsloth studio setup to prevent pipe consumption. On macOS, check
  for Xcode Command Line Tools and trigger install if missing.

- install.ps1: wrap script body in Install-UnslothStudio function so
  that errors use return instead of exit (exit kills the terminal when
  run via irm|iex). Remove activate.ps1 invocation entirely -- use
  explicit --python path for uv pip install and & $UnslothExe for
  studio setup. This avoids both the child-scope activation bug (& vs
  dot-source) and the execution policy error on default Windows systems.
  Add winget availability check with clear error message. Fix PATH
  refresh to append registry paths instead of replacing the session PATH.
  Add uv installer fallback via astral.sh PowerShell script if winget
  install does not put uv on PATH. Broaden Python version check to
  accept 3.11-3.13. Add idempotency guard for venv creation.

- README.md: add wget one-liner alternative for systems without curl.

* Fix Tailwind CSS v4 .gitignore bug on Windows (#4444)

- Add .gitignore hiding workaround to setup.ps1 (matching existing
  setup.sh logic) so venv .gitignore files containing "*" don't prevent
  Tailwind's oxide scanner from finding .tsx source files
- Add CSS size validation to setup.sh, setup.ps1, and build.sh to catch
  truncated Tailwind builds early
- Remove stray force-rebuild overrides that made the "skip build if
  current" cache check dead code in both setup scripts
- Add rm -rf dist to build.sh to force clean rebuilds for wheel packaging

* Change default port 8000 to 8888, fix installer bugs, improve UX

- Change default Studio port from 8000 to 8888 across all entry points
  (run.py, studio.py, ui.py, colab.py, vite.config.ts, setup scripts)
- Update launch banner: "Launching with studio venv..." to
  "Launching Unsloth Studio... Please wait..."
- Add "Open your web browser" banner and rename labels
  (Local -> Local Access, External -> Worldwide Web Address)
- Fix venv idempotency: check for bin/python instead of just directory
  existence, clean up partial venvs on retry
- Fix build.sh CSS validation: handle empty CSS case that silently
  bypassed the check with "integer expression expected"
- Fix install.sh sudo handling: try apt-get without sudo first (works
  when root), then escalate with per-package tracking and user prompt
- Fix install.ps1: check exit code from studio setup, fail on error
- Add pciutils to WSL GGUF build dependencies
- Apply same smart apt-get escalation pattern to studio/setup.sh

* Use detected Python version for venv, abort on non-apt Linux

- install.ps1: detect existing Python 3.11/3.12/3.13 and use that
  version for venv creation instead of always forcing 3.13
- install.sh: exit with error on non-apt Linux distros when required
  packages cannot be auto-installed, instead of silently continuing

* Make sudo permission prompt more prominent with warning banner

* Add Accept [Y/n] sudo prompt to studio/setup.sh for consistency

* Fix native command exit code handling and sudo decline flow

install.ps1: Add $LASTEXITCODE checks after winget (Python), uv venv,
and uv pip install calls. $ErrorActionPreference only catches PowerShell
cmdlet errors, not native executable failures. The Python check also
handles winget returning non-zero for "already installed".

setup.sh: Skip llama-server build when user declines sudo or sudo is
unavailable. Previously the script continued to section 8 which would
fail with confusing errors (e.g. "gcc: command not found") since
build-essential was never installed.

* Move rm -rf llama.cpp inside build branch to preserve existing install

When _SKIP_GGUF_BUILD is set (user declined sudo or sudo unavailable),
the previous rm -rf would destroy an already-working llama-server before
the skip check ran. Move it inside the else branch so existing builds
are preserved when the rebuild is skipped.

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-19 02:09:09 -07:00

119 lines
5.1 KiB
PowerShell

# Unsloth Studio Installer for Windows PowerShell
# Usage: irm https://raw.githubusercontent.com/unslothai/unsloth/main/install.ps1 | iex
# Local: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; .\install.ps1
function Install-UnslothStudio {
$ErrorActionPreference = "Stop"
$VenvName = "unsloth_studio"
$PythonVersion = "3.13"
Write-Host ""
Write-Host "========================================="
Write-Host " Unsloth Studio Installer (Windows)"
Write-Host "========================================="
Write-Host ""
# ── Helper: refresh PATH from registry (preserving current session entries) ──
function Refresh-SessionPath {
$machine = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
$user = [System.Environment]::GetEnvironmentVariable("Path", "User")
$env:Path = "$machine;$user;$env:Path"
}
# ── Check winget ──
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
Write-Host "Error: winget is not available." -ForegroundColor Red
Write-Host " Install it from https://aka.ms/getwinget" -ForegroundColor Yellow
Write-Host " or install Python $PythonVersion and uv manually, then re-run." -ForegroundColor Yellow
return
}
# ── Install Python if no compatible version (3.11-3.13) found ──
$DetectedPythonVersion = ""
if (Get-Command python -ErrorAction SilentlyContinue) {
$pyVer = python --version 2>&1
if ($pyVer -match "Python (3\.1[1-3])\.\d+") {
Write-Host "==> Python already installed: $pyVer"
$DetectedPythonVersion = $Matches[1]
}
}
if (-not $DetectedPythonVersion) {
Write-Host "==> Installing Python ${PythonVersion}..."
winget install -e --id Python.Python.3.13 --accept-package-agreements --accept-source-agreements
Refresh-SessionPath
if ($LASTEXITCODE -ne 0) {
# winget returns non-zero for "already installed" -- only fail if python is truly missing
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
Write-Host "[ERROR] Python installation failed (exit code $LASTEXITCODE)" -ForegroundColor Red
return
}
}
$DetectedPythonVersion = $PythonVersion
}
# ── Install uv if not present ──
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host "==> Installing uv package manager..."
winget install --id=astral-sh.uv -e --accept-package-agreements --accept-source-agreements
Refresh-SessionPath
# Fallback: if winget didn't put uv on PATH, try the PowerShell installer
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host " Trying alternative uv installer..."
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Refresh-SessionPath
}
}
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host "Error: uv could not be installed." -ForegroundColor Red
Write-Host " Install it from https://docs.astral.sh/uv/" -ForegroundColor Yellow
return
}
# ── Create venv (skip if it already exists and has a valid interpreter) ──
$VenvPython = Join-Path $VenvName "Scripts\python.exe"
if (-not (Test-Path $VenvPython)) {
if (Test-Path $VenvName) { Remove-Item -Recurse -Force $VenvName }
Write-Host "==> Creating Python ${DetectedPythonVersion} virtual environment (${VenvName})..."
uv venv $VenvName --python $DetectedPythonVersion
if ($LASTEXITCODE -ne 0) {
Write-Host "[ERROR] Failed to create virtual environment (exit code $LASTEXITCODE)" -ForegroundColor Red
return
}
} else {
Write-Host "==> Virtual environment ${VenvName} already exists, skipping creation."
}
# ── Install unsloth directly into the venv (no activation needed) ──
Write-Host "==> Installing unsloth (this may take a few minutes)..."
uv pip install --python $VenvPython unsloth --torch-backend=auto
if ($LASTEXITCODE -ne 0) {
Write-Host "[ERROR] Failed to install unsloth (exit code $LASTEXITCODE)" -ForegroundColor Red
return
}
# ── Run studio setup ──
# setup.ps1 will handle installing Git, CMake, Visual Studio Build Tools,
# CUDA Toolkit, Node.js, and other dependencies automatically via winget.
Write-Host "==> Running unsloth studio setup..."
$UnslothExe = Join-Path $VenvName "Scripts\unsloth.exe"
& $UnslothExe studio setup
if ($LASTEXITCODE -ne 0) {
Write-Host "[ERROR] unsloth studio setup failed (exit code $LASTEXITCODE)" -ForegroundColor Red
return
}
Write-Host ""
Write-Host "========================================="
Write-Host " Unsloth Studio installed!"
Write-Host "========================================="
Write-Host ""
Write-Host " To launch, run:"
Write-Host ""
Write-Host " .\${VenvName}\Scripts\activate"
Write-Host " unsloth studio -H 0.0.0.0 -p 8888"
Write-Host ""
}
Install-UnslothStudio