mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-04 21:30:36 +00:00
* Replace standalone Studio wording with Unsloth Replace the single word Studio with Unsloth wherever it is used as shorthand for Unsloth Studio in docs, CLI output, UI strings, i18n locales, workflow display names, comments and docstrings. Kept unchanged: the full name Unsloth Studio, third party product names (LM Studio, Visual Studio, Mac Studio), feature names (Recipe Studio, Fine-tuning Studio and its translations), and all identifiers such as env vars, commands, paths and filenames. * Address review feedback on the Studio wording rename Use "an" before Unsloth where the rename left the article as "a". Restore the split brand where Unsloth and Studio render as two halves of the full product name: the onboarding sidebar subtitle and the IPv6 localhost warning. Scope two messages to the full name Unsloth Studio where plain Unsloth was misleading: the AMD README bullet and the CLI studio setup error.
427 lines
21 KiB
Bash
Executable file
427 lines
21 KiB
Bash
Executable file
#!/usr/bin/env sh
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
#
|
|
# Unsloth Studio uninstaller (macOS / Linux / WSL).
|
|
# Stops running servers and removes install dir, launcher data,
|
|
# CLI shim, desktop shortcut, .app bundle, and Launch Services entry.
|
|
# Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME at
|
|
# install time (read back from studio.conf).
|
|
#
|
|
# Usage: curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.sh | sh
|
|
|
|
set -e
|
|
|
|
# Stop an Unsloth server via its PID file (written by install.sh's _spawn_terminal).
|
|
_kill_pid_file() {
|
|
_pid_file="$1"
|
|
[ -f "$_pid_file" ] || return 0
|
|
_pid=$(sed -n '1s/[^0-9].*//p' "$_pid_file" 2>/dev/null || true)
|
|
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
|
|
kill -TERM "$_pid" 2>/dev/null || true
|
|
# Wait up to 10s for graceful shutdown.
|
|
_i=0
|
|
while kill -0 "$_pid" 2>/dev/null && [ "$_i" -lt 20 ]; do
|
|
sleep 0.5
|
|
_i=$((_i + 1))
|
|
done
|
|
kill -0 "$_pid" 2>/dev/null && kill -KILL "$_pid" 2>/dev/null || true
|
|
fi
|
|
rm -f "$_pid_file" 2>/dev/null || true
|
|
}
|
|
|
|
# BRE-escape a path so it can be embedded in a pkill -f regex.
|
|
_pkill_escape() {
|
|
printf '%s' "$1" | sed -e 's:[][\\.^$*+?{|}()/]:\\&:g'
|
|
}
|
|
|
|
_pkill_studio() {
|
|
# Prefer PID files written by _spawn_terminal so we only touch our own installs.
|
|
for _data_dir in "$HOME/.local/share/unsloth" $(_custom_studio_data_dirs); do
|
|
[ -d "$_data_dir" ] || continue
|
|
for _pf in "$_data_dir"/studio-*.pid; do
|
|
[ -f "$_pf" ] && _kill_pid_file "$_pf"
|
|
done
|
|
done
|
|
|
|
command -v pkill >/dev/null 2>&1 || return 0
|
|
|
|
# Scope fallback patterns to the install roots we are removing so a
|
|
# different Unsloth install (different UNSLOTH_STUDIO_HOME) is not touched.
|
|
_kill_roots="$HOME/.unsloth/studio"
|
|
_roots_from_conf=$(_custom_studio_roots 2>/dev/null || true)
|
|
[ -n "$_roots_from_conf" ] && _kill_roots="$_kill_roots
|
|
$_roots_from_conf"
|
|
|
|
printf '%s\n' "$_kill_roots" | while IFS= read -r _root; do
|
|
[ -n "$_root" ] || continue
|
|
[ -d "$_root" ] || continue
|
|
_re=$(_pkill_escape "$_root")
|
|
# `unsloth studio` (default port) + `-p N` + `--port N` forms, all
|
|
# anchored on the install root's venv path.
|
|
for _pat in \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio( |\$|.*-p[ =][0-9])" \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio.*--port[ =][0-9]" \
|
|
"${_re}/.*studio/backend/run\.py"
|
|
do
|
|
pkill -TERM -f "$_pat" 2>/dev/null || true
|
|
done
|
|
done
|
|
sleep 0.5
|
|
printf '%s\n' "$_kill_roots" | while IFS= read -r _root; do
|
|
[ -n "$_root" ] || continue
|
|
[ -d "$_root" ] || continue
|
|
_re=$(_pkill_escape "$_root")
|
|
for _pat in \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio( |\$|.*-p[ =][0-9])" \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio.*--port[ =][0-9]" \
|
|
"${_re}/.*studio/backend/run\.py"
|
|
do
|
|
pkill -KILL -f "$_pat" 2>/dev/null || true
|
|
done
|
|
done
|
|
}
|
|
|
|
_remove_path() {
|
|
_p="$1"
|
|
if [ -e "$_p" ] || [ -L "$_p" ]; then
|
|
rm -rf "$_p" 2>/dev/null && echo " removed: $_p" || echo " could not remove: $_p" >&2
|
|
fi
|
|
}
|
|
|
|
# Accept as Unsloth root only if Unsloth sentinels exist (matches install.sh's
|
|
# env-mode ownership guard at install.sh:1358-1361). A bare unsloth_studio/
|
|
# directory is NOT enough -- require the install-time owner marker so a user
|
|
# directory that happens to contain a folder named "unsloth_studio" is safe.
|
|
_is_studio_root() {
|
|
_r="$1"
|
|
[ -n "$_r" ] || return 1
|
|
[ -f "$_r/share/studio.conf" ] && return 0
|
|
[ -f "$_r/unsloth_studio/.unsloth-studio-owned" ] && return 0
|
|
if [ -L "$_r/bin/unsloth" ]; then
|
|
_t=$(readlink "$_r/bin/unsloth" 2>/dev/null || true)
|
|
case "$_t" in *unsloth_studio/bin/unsloth) return 0 ;; esac
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Hard deny list: never delete /, $HOME, $HOME's parent, or system paths.
|
|
_is_unsafe_root() {
|
|
_r="$1"
|
|
[ -z "$_r" ] && return 0
|
|
case "$_r" in /|""|"$HOME"|"$HOME/") return 0 ;; esac
|
|
case "$_r" in /bin|/sbin|/etc|/usr|/usr/*|/var|/var/*|/opt|/opt/*|/Library|/Library/*|/System|/System/*|/Applications|/Applications/*) return 0 ;; esac
|
|
_parent=$(dirname "$HOME" 2>/dev/null || echo "")
|
|
[ -n "$_parent" ] && [ "$_r" = "$_parent" ] && return 0
|
|
return 1
|
|
}
|
|
|
|
# Print share/ dirs of known custom roots (where PID files live).
|
|
_custom_studio_data_dirs() {
|
|
_custom_studio_roots 2>/dev/null | while IFS= read -r _r; do
|
|
[ -d "$_r/share" ] && printf '%s\n' "$_r/share"
|
|
done
|
|
}
|
|
|
|
# Resolve a custom install root from any of:
|
|
# 1. UNSLOTH_STUDIO_HOME / STUDIO_HOME env vars at uninstall time
|
|
# 2. Default-mode studio.conf at $HOME/.local/share/unsloth/studio.conf
|
|
# 3. Env-mode studio.conf at $<root>/share/studio.conf (discovered via 1)
|
|
# install.sh writes UNSLOTH_EXE='<root>/unsloth_studio/bin/unsloth', so
|
|
# the install root is three dirnames up. Prints each discovered non-default
|
|
# root on its own line; the caller iterates and de-duplicates.
|
|
_custom_studio_roots() {
|
|
_seen=""
|
|
_emit() {
|
|
_r="$1"
|
|
[ -z "$_r" ] && return 0
|
|
# Tilde expansion (env vars are not subject to it on quoted assignment),
|
|
# matches install.sh's _resolve_studio_destinations. The literal "~/"
|
|
# pattern is intentional; SC2088 is a false positive here.
|
|
# shellcheck disable=SC2088
|
|
case "$_r" in
|
|
"~") _r="$HOME" ;;
|
|
"~/"*) _r="$HOME/${_r#'~/'}" ;;
|
|
esac
|
|
# Canonicalize so syntactic variants ($HOME/../$USER, trailing slash)
|
|
# resolve to the same path and hit the _is_unsafe_root deny list.
|
|
# shellcheck disable=SC1007
|
|
_canon=$(CDPATH= cd -P -- "$_r" 2>/dev/null && pwd -P)
|
|
[ -n "$_canon" ] && _r="$_canon"
|
|
case "$_r" in "$HOME/.unsloth/studio"|/|"") return 0 ;; esac
|
|
case ":$_seen:" in *":$_r:"*) return 0 ;; esac
|
|
_seen="$_seen:$_r"
|
|
printf '%s\n' "$_r"
|
|
}
|
|
_from_conf() {
|
|
[ -f "$1" ] || return 0
|
|
# Tolerate paths containing apostrophes (install.sh emits '\'' for them).
|
|
_exe=$(sed -n "s/^UNSLOTH_EXE='\(.*\)'\$/\1/p" "$1" | head -n1)
|
|
_exe=$(printf '%s' "$_exe" | sed "s/'\\\\''/'/g")
|
|
[ -n "$_exe" ] || return 0
|
|
_emit "$(dirname "$(dirname "$(dirname "$_exe")")")"
|
|
}
|
|
# Mirror install.sh's precedence: UNSLOTH_STUDIO_HOME wins, STUDIO_HOME is
|
|
# ignored when both are set. Otherwise uninstalling install A could also
|
|
# delete install B if the user has STUDIO_HOME left over from B.
|
|
if [ -n "${UNSLOTH_STUDIO_HOME:-}" ]; then
|
|
_emit "$UNSLOTH_STUDIO_HOME"
|
|
_from_conf "$UNSLOTH_STUDIO_HOME/share/studio.conf"
|
|
elif [ -n "${STUDIO_HOME:-}" ]; then
|
|
_emit "$STUDIO_HOME"
|
|
_from_conf "$STUDIO_HOME/share/studio.conf"
|
|
fi
|
|
# Default-mode conf.
|
|
_from_conf "$HOME/.local/share/unsloth/studio.conf"
|
|
}
|
|
|
|
# Remove $HOME/.local/bin/unsloth only if it's an Unsloth-managed symlink.
|
|
# Unsloth's install.sh writes this as a symlink into the studio venv
|
|
# (install.sh: `ln -sfn "$VENV_DIR/bin/unsloth" "$_shim_path"`). A
|
|
# pip-installed `unsloth` CLI is a regular file — leave it alone to avoid
|
|
# wiping an unrelated install.
|
|
_remove_cli_shim() {
|
|
_shim="$HOME/.local/bin/unsloth"
|
|
[ -L "$_shim" ] || return 0
|
|
_target=$(readlink "$_shim" 2>/dev/null || true)
|
|
case "$_target" in
|
|
*/unsloth_studio/bin/unsloth) _remove_path "$_shim" ;;
|
|
*) ;;
|
|
esac
|
|
}
|
|
|
|
_uid=$(id -u 2>/dev/null || echo 0)
|
|
_os=$(uname 2>/dev/null || echo unknown)
|
|
_is_wsl=0
|
|
[ "$_os" = "Linux" ] && grep -qi microsoft /proc/version 2>/dev/null && _is_wsl=1
|
|
|
|
echo "Stopping any running Unsloth Studio servers..."
|
|
_pkill_studio
|
|
|
|
echo "Removing data and install directories..."
|
|
_custom_studio_roots | while IFS= read -r _custom_root; do
|
|
[ -n "$_custom_root" ] || continue
|
|
if _is_unsafe_root "$_custom_root"; then
|
|
echo " refusing to remove unsafe path: $_custom_root" >&2
|
|
continue
|
|
fi
|
|
if ! _is_studio_root "$_custom_root"; then
|
|
echo " refusing to remove non-Unsloth path: $_custom_root" >&2
|
|
continue
|
|
fi
|
|
_remove_path "$_custom_root"
|
|
done
|
|
_remove_path "$HOME/.unsloth/studio"
|
|
# Default-mode shared llama.cpp build + cache are siblings of studio (not removed
|
|
# by deleting it). No-op in env/custom mode (they nest under the custom root) and
|
|
# when absent. A user-set UNSLOTH_LLAMA_CPP_PATH is intentionally kept.
|
|
_remove_path "$HOME/.unsloth/llama.cpp"
|
|
_remove_path "$HOME/.unsloth/.cache"
|
|
# Isolated Node.js runtime (install_node_prebuilt.py), a sibling of studio in
|
|
# default mode. No-op in env/custom mode (nested under the custom root) and absent.
|
|
_remove_path "$HOME/.unsloth/node"
|
|
# llama.cpp atomic-install staging root (install_llama_prebuilt.py .staging).
|
|
# Normally pruned after activate, but an interrupted build can leave it behind;
|
|
# removing it lets the rmdir below succeed. No-op in env/custom mode and absent.
|
|
_remove_path "$HOME/.unsloth/.staging"
|
|
# llama.cpp install lock (serializes the shared build); a stray one keeps ~/.unsloth
|
|
# from being pruned below. No-op in env/custom mode and when absent.
|
|
_remove_path "$HOME/.unsloth/.llama.cpp.install.lock"
|
|
# ROCm-on-WSL helper artifacts (librocdxg build clone + smoke-test venv). No-op
|
|
# where they don't exist; removing them lets the rmdir below succeed.
|
|
_remove_path "$HOME/.unsloth/librocdxg"
|
|
_remove_path "$HOME/.unsloth/rocm-smoketest"
|
|
# Drop ~/.unsloth only if now empty (rmdir refuses non-empty, so user content is kept).
|
|
rmdir "$HOME/.unsloth" 2>/dev/null || true
|
|
_remove_path "$HOME/.local/share/unsloth"
|
|
# CLI shim: only the symlink Unsloth created, never a pip-installed file.
|
|
_remove_cli_shim
|
|
|
|
echo "Removing desktop shortcut and launcher lock..."
|
|
# install.sh creates Desktop/Unsloth Studio as a symlink. If the user has an
|
|
# unrelated regular directory by that name, leave it alone.
|
|
_desktop_link="$HOME/Desktop/Unsloth Studio"
|
|
if [ -L "$_desktop_link" ] || [ ! -e "$_desktop_link" ]; then
|
|
_remove_path "$_desktop_link"
|
|
else
|
|
echo " refusing to remove non-symlink Desktop path: $_desktop_link" >&2
|
|
fi
|
|
_remove_path "$HOME/Desktop/unsloth-studio.desktop"
|
|
# Locks are namespaced per-uid; env-mode adds an extra suffix.
|
|
_lock_glob="${XDG_RUNTIME_DIR:-/tmp}/unsloth-studio-launcher-${_uid}"
|
|
for _lock in "$_lock_glob".lock "$_lock_glob"-*.lock; do
|
|
[ -e "$_lock" ] && _remove_path "$_lock"
|
|
done
|
|
|
|
case "$_os" in
|
|
Darwin)
|
|
echo "Removing macOS .app bundle and Launch Services entry..."
|
|
_remove_path "$HOME/Applications/Unsloth Studio.app"
|
|
_lsr="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
|
|
if [ -x "$_lsr" ]; then
|
|
"$_lsr" -u "$HOME/Applications/Unsloth Studio.app" 2>/dev/null || true
|
|
fi
|
|
;;
|
|
Linux)
|
|
if [ "$_is_wsl" = "1" ]; then
|
|
echo "Removing WSL Windows-side shortcuts..."
|
|
# install.sh creates per-distro 'Unsloth Studio (WSL - <distro>).lnk'
|
|
# on the Windows Desktop + Start Menu via powershell.exe. Scope removal
|
|
# to THIS distro (passed as $args[0]) so a multi-distro install keeps the
|
|
# other distros' launchers; the TARGET=wsl.exe check still spares a
|
|
# native install's "Unsloth Studio.lnk". Prefer powershell.exe; test it
|
|
# can EXECUTE (`command -v` succeeds even with interop OFF -- .exe then
|
|
# fails "Exec format error", common on systemd-enabled distros).
|
|
_wsl_distro="${WSL_DISTRO_NAME:-}"
|
|
_ps_ran=0
|
|
if command -v powershell.exe >/dev/null 2>&1 && \
|
|
powershell.exe -NoProfile -Command "exit 0" >/dev/null 2>&1; then
|
|
_ps_ran=1
|
|
# Inject the distro into the command: a -Command string does not
|
|
# receive trailing tokens as $args. WSL distro names are safe to
|
|
# embed (no quotes/$/backtick).
|
|
# shellcheck disable=SC2016
|
|
powershell.exe -NoProfile -Command '$distro = "'"$_wsl_distro"'";
|
|
$dirs = @(
|
|
[Environment]::GetFolderPath("Desktop"),
|
|
(Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs")
|
|
);
|
|
$ws = New-Object -ComObject WScript.Shell;
|
|
foreach ($d in $dirs) {
|
|
if (-not $d -or -not (Test-Path -LiteralPath $d)) { continue }
|
|
Get-ChildItem -LiteralPath $d -Filter "Unsloth Studio*.lnk" -ErrorAction SilentlyContinue | ForEach-Object {
|
|
try {
|
|
$sc = $ws.CreateShortcut($_.FullName);
|
|
if ("$($sc.TargetPath) $($sc.Arguments)" -notmatch "wsl\.exe") { return }
|
|
# When the distro is known, require the per-distro
|
|
# name for this distro or its -d "<distro>" argument
|
|
# so launchers for other distros are not removed.
|
|
if ($distro) {
|
|
$nameMatch = ($_.Name -eq "Unsloth Studio (WSL - $distro).lnk");
|
|
$argMatch = ($sc.Arguments -match ("-d\s+`"?" + [regex]::Escape($distro) + "`"?"));
|
|
if (-not ($nameMatch -or $argMatch)) { return }
|
|
}
|
|
Remove-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue
|
|
} catch { }
|
|
}
|
|
}
|
|
# Keep the shared icon while any Unsloth shortcut still uses it (native
|
|
# install or another WSL distro); drop it only with the last one.
|
|
$iconInUse = $false;
|
|
foreach ($d in $dirs) {
|
|
if (-not $d -or -not (Test-Path -LiteralPath $d)) { continue }
|
|
if (Get-ChildItem -LiteralPath $d -Filter "Unsloth Studio*.lnk" -ErrorAction SilentlyContinue) { $iconInUse = $true; break }
|
|
}
|
|
# Guard LOCALAPPDATA: empty on a service/SYSTEM account makes
|
|
# Join-Path throw, aborting the icon cleanup (mirror uninstall.ps1).
|
|
if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) {
|
|
$iconDir = Join-Path $env:LOCALAPPDATA "Unsloth Studio";
|
|
$ico = Join-Path $iconDir "unsloth.ico";
|
|
if ((-not $iconInUse) -and (Test-Path -LiteralPath $ico)) { Remove-Item -LiteralPath $ico -Force -ErrorAction SilentlyContinue }
|
|
if ((Test-Path -LiteralPath $iconDir) -and -not (Get-ChildItem -LiteralPath $iconDir -Force -ErrorAction SilentlyContinue)) { Remove-Item -LiteralPath $iconDir -Recurse -Force -ErrorAction SilentlyContinue }
|
|
}' >/dev/null 2>&1 || true
|
|
fi
|
|
# Remove $1's shared unsloth.ico only if no Unsloth shortcut (native install
|
|
# or another WSL distro) still uses it, then drop the dir if empty. Reciprocal
|
|
# of uninstall.ps1's _RemoveDataDirKeepingWslIcon (keeps the icon for a
|
|
# surviving WSL shortcut when the native side is removed).
|
|
_drop_shared_icon_if_unused() {
|
|
_du="$1"
|
|
_icodir="$_du/AppData/Local/Unsloth Studio"
|
|
_icon_in_use=0
|
|
for _sd in \
|
|
"$_du/Desktop" \
|
|
"$_du/OneDrive/Desktop" \
|
|
"$_du"/OneDrive*/Desktop \
|
|
"$_du/AppData/Roaming/Microsoft/Windows/Start Menu/Programs"; do
|
|
[ -d "$_sd" ] || continue
|
|
for _any in "$_sd"/"Unsloth Studio"*.lnk; do
|
|
[ -e "$_any" ] && { _icon_in_use=1; break; }
|
|
done
|
|
[ "$_icon_in_use" = "1" ] && break
|
|
done
|
|
if [ "$_icon_in_use" = "0" ]; then
|
|
[ -f "$_icodir/unsloth.ico" ] && rm -f "$_icodir/unsloth.ico" 2>/dev/null || true
|
|
fi
|
|
[ -d "$_icodir" ] && rmdir "$_icodir" 2>/dev/null || true
|
|
}
|
|
# Fallback when powershell.exe can't run (interop disabled): remove WSL .lnk
|
|
# files via drvfs. The "Unsloth Studio (WSL..." name is WSL-specific, so a
|
|
# native install's "Unsloth Studio.lnk" never matches.
|
|
if [ "$_ps_ran" = "0" ]; then
|
|
for _drive in /mnt/c /mnt/d /mnt/e; do
|
|
[ -d "$_drive/Users" ] || continue
|
|
for _udir in "$_drive"/Users/*; do
|
|
[ -d "$_udir" ] || continue
|
|
for _scdir in \
|
|
"$_udir/Desktop" \
|
|
"$_udir/OneDrive/Desktop" \
|
|
"$_udir"/OneDrive*/Desktop \
|
|
"$_udir/AppData/Roaming/Microsoft/Windows/Start Menu/Programs"; do
|
|
[ -d "$_scdir" ] || continue
|
|
if [ -n "$_wsl_distro" ]; then
|
|
# Exact per-distro name (no glob) so other distros survive.
|
|
_lnk="$_scdir/Unsloth Studio (WSL - ${_wsl_distro}).lnk"
|
|
[ -e "$_lnk" ] && rm -f "$_lnk" 2>/dev/null && echo " removed: $_lnk" || true
|
|
else
|
|
# Distro unknown: fall back to the broad WSL prefix.
|
|
for _lnk in "$_scdir"/"Unsloth Studio (WSL"*.lnk; do
|
|
[ -e "$_lnk" ] && rm -f "$_lnk" 2>/dev/null && echo " removed: $_lnk" || true
|
|
done
|
|
fi
|
|
done
|
|
# Drop the shared icon only when no shortcut still needs it.
|
|
_drop_shared_icon_if_unused "$_udir"
|
|
done
|
|
done
|
|
fi
|
|
# ── ROCm-on-WSL config (install_rocm_wsl_strixhalo.sh) ──
|
|
# Remove Unsloth's own ROCDXG config (the env it persisted). The system
|
|
# ROCm userspace is a shared prereq (like CUDA) and is LEFT IN PLACE by
|
|
# default; set UNSLOTH_UNINSTALL_ROCM=1 to remove it too.
|
|
echo "Removing ROCm-on-WSL config..."
|
|
_sudo=""
|
|
if [ "$_uid" != "0" ] && command -v sudo >/dev/null 2>&1; then _sudo="sudo"; fi
|
|
$_sudo rm -f /etc/profile.d/unsloth-rocm-wsl.sh 2>/dev/null || true
|
|
if [ -f "$HOME/.bashrc" ] && grep -q "Unsloth ROCm-on-WSL" "$HOME/.bashrc" 2>/dev/null; then
|
|
_bk=$(mktemp 2>/dev/null || echo "$HOME/.bashrc.unsloth.tmp")
|
|
if sed '/# >>> Unsloth ROCm-on-WSL/,/# <<< Unsloth ROCm-on-WSL/d' "$HOME/.bashrc" > "$_bk" 2>/dev/null; then
|
|
cat "$_bk" > "$HOME/.bashrc" 2>/dev/null || true
|
|
echo " cleaned ROCm-on-WSL block from ~/.bashrc"
|
|
fi
|
|
rm -f "$_bk" 2>/dev/null || true
|
|
fi
|
|
if [ "${UNSLOTH_UNINSTALL_ROCM:-0}" = "1" ]; then
|
|
echo " removing system ROCm (UNSLOTH_UNINSTALL_ROCM=1)..."
|
|
$_sudo rm -f /etc/apt/sources.list.d/rocm.list /etc/apt/preferences.d/rocm-pin-600 \
|
|
/etc/apt/keyrings/rocm.gpg /etc/ld.so.conf.d/rocm.conf 2>/dev/null || true
|
|
$_sudo sh -c 'rm -rf /opt/rocm /opt/rocm-*' 2>/dev/null || true
|
|
if command -v ldconfig >/dev/null 2>&1; then $_sudo ldconfig 2>/dev/null || true; fi
|
|
elif [ -d /opt/rocm ]; then
|
|
echo " Note: ROCm userspace (/opt/rocm*) left in place (shared prereq)."
|
|
echo " Remove it by re-running with UNSLOTH_UNINSTALL_ROCM=1, or manually:"
|
|
echo " sudo rm -rf /opt/rocm /opt/rocm-* && sudo ldconfig"
|
|
fi
|
|
fi
|
|
echo "Removing Linux .desktop entry..."
|
|
_remove_path "$HOME/.local/share/applications/unsloth-studio.desktop"
|
|
if command -v update-desktop-database >/dev/null 2>&1; then
|
|
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Unsloth Studio uninstalled."
|
|
echo "Note: Hugging Face model cache at ~/.cache/huggingface was left in place."
|
|
echo "Remove it manually with 'rm -rf ~/.cache/huggingface/hub' if desired."
|
|
# Env-mode installs leave no breadcrumb in $HOME, so a custom root can
|
|
# only be located if the user re-exports the variable. Print a hint when
|
|
# neither var is set so the bare `curl | sh` flow doesn't silently miss.
|
|
if [ -z "${UNSLOTH_STUDIO_HOME:-}" ] && [ -z "${STUDIO_HOME:-}" ]; then
|
|
echo ""
|
|
echo "If you installed Unsloth Studio with UNSLOTH_STUDIO_HOME or STUDIO_HOME"
|
|
echo "pointing at a custom directory, re-run this script with the same variable"
|
|
echo "set to also remove that install tree, e.g.:"
|
|
echo " UNSLOTH_STUDIO_HOME=/your/path sh -c \"\$(curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.sh)\""
|
|
fi
|