* studio: regenerate desktop launcher on `unsloth studio update`
Today `unsloth studio update` only mutates the venv. The macOS .app bundle,
the Linux .desktop file, and the shared launch-studio.sh stub bake their
paths and `studio_install_id` at install time and never refresh. Users who
update an existing Studio install report the Dock / Applications icon still
pointing at the old launcher; only a fresh `curl ... install.sh | sh`
fixes it because that path re-enters install.sh's create_studio_shortcuts.
Wire the same logic into the update path:
- install.sh: add --shortcuts-only. Skips the heavy install steps, resolves
STUDIO_HOME / OS / DATA_DIR through the existing _resolve_studio_destinations
+ platform detection, then calls create_studio_shortcuts and exits.
- unsloth_cli/commands/studio.py: after setup.sh succeeds, call install.sh
with --shortcuts-only. Prefers a local checkout's install.sh (when
STUDIO_LOCAL_REPO is set) or one shipped under _PACKAGE_ROOT, and falls
back to fetching the upstream installer from https://unsloth.ai/install.sh
for PyPI-installed users (the wheel does not ship install.sh).
Net effect: `unsloth studio update` now refreshes the macOS .app stub,
launcher script, studio.conf, and Linux .desktop entry on every update, so
the desktop icon stays in sync with the venv that setup.sh just updated.
Env-override and Tauri modes keep their existing behavior (no persistent
menu shortcuts, but the launch-studio.sh is still regenerated).
Windows is unchanged here; setup.ps1 already handles its own Start Menu /
Desktop .lnk creation on update.
* studio: also regenerate Windows .lnk shortcuts on update
Mirror the macOS fix: install.ps1 gains --shortcuts-only that short-circuits
to New-StudioShortcuts, and unsloth studio update calls it after setup.ps1
the same way it now does on macOS / Linux.
PyPI installs do not ship install.ps1, so the Python helper fetches the
upstream script from https://unsloth.ai/install.ps1 and pipes it into
powershell.exe -Command - with an explicit Install-UnslothStudio call
appended (irm | iex relies on the trailing @args, which is empty when
launched from stdin).
setup.ps1 alone never recreates the Start Menu / Desktop .lnk targets or
the launch-studio.{ps1,vbs} scripts, so without this update users on
Windows hit the same stale-icon regression that triggered the macOS PR.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* studio: rename unsloth.exe to .deleteme before update on Windows
Pip's editable reinstall calls uninstall first, which deletes every RECORD
entry. unsloth.exe is one of them, and Windows refuses to delete a file
whose image is mapped into the running process tree. The first
unsloth studio update after install therefore fails with:
OSError: [WinError 32] The process cannot access the file because it
is being used by another process: ...\Scripts\unsloth.exe
Windows does allow renaming an in-use exe, so move it aside before
_run_setup_script kicks pip. pip then drops a fresh unsloth.exe at the
original path; the *.exe.deleteme left behind is cleaned up at the start
of the next update once the previous shim has exited.
* studio: rename unsloth.exe from setup.ps1 to reliably bypass exe lock
* studio: print python -m workaround when Windows exe lock blocks update
* studio: use python -c hint (unsloth_cli has no __main__)
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* install.sh: reshape --shortcuts-only Tauri guard to pass exit-order test
* shorter comments in update / launcher regen logic
* studio update: env-mode passthrough + non-silent shortcuts-only error
* studio update: address codex/gemini PR review
- Strip install.ps1's `Install-UnslothStudio @args` auto-invoke before
appending an explicit `--shortcuts-only` call so PyPI Windows installs
don't re-run the full installer over stdin.
- subprocess.run(input=wrapper, ...) now uses encoding="utf-8" so box
drawing chars in install.ps1 don't UnicodeEncodeError on CP1252.
- Wrap _run_setup_script in try/except to restore unsloth.exe from
.deleteme if setup fails, and mirror that rollback inside setup.ps1
when install_python_stack.py exits non-zero.
- Capture subprocess return codes in _refresh_desktop_shortcuts and
echo a one-line warning on non-zero so silent stale-shortcut failures
surface.
- Drop --local from the Windows lock-recovery hint so users on PyPI
installs don't accidentally switch into editable-checkout mode.
- Quote $VENV_ABS_BIN/unsloth in the install.sh shortcuts-only error
so paths with spaces print legibly.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* studio update: harden Windows refresh per multi-reviewer pass
- PowerShell stdin path now writes the wrapper to a UTF-8 BOM tempfile
and runs it via `-File`. `powershell.exe -Command -` decodes stdin
with the OEM code page, which mangles box-drawing chars in the
fetched install.ps1; -File reads the BOM and decodes UTF-8 cleanly.
- _restore_self_exe_lock_windows now treats a zero-byte unsloth.exe as
a partial-write and prefers the .deleteme copy. setup.ps1 mirrors
the same check.
- _release_self_exe_lock_windows uses os.replace for atomic overwrite
so a stale .deleteme from an aborted prior update doesn't break the
rename.
- Lock-recovery hint mentions that --local should be re-added when
the user installed from a repo checkout.
* studio update: respect Tauri context and tidy Windows .deleteme
Tauri's update.rs spawns `unsloth studio update`; without a signal,
the CLI's _refresh_desktop_shortcuts would call install.{sh,ps1}
--shortcuts-only and create duplicate ~/Applications/Unsloth Studio.app
(or .desktop / .lnk) entries that collide with the Tauri bundle.
- update.rs now sets UNSLOTH_TAURI_UPDATE=1 on the spawned child.
- studio.py's update() skips _refresh_desktop_shortcuts when that env
var is set; Tauri owns its own bundle entries.
- After a successful Windows update, drop the .deleteme orphan so
repeated updates don't accumulate stale binaries that could later
be promoted by _restore_self_exe_lock_windows on a cross-version
failure.
- Tempfile for the PyPI-fallback PowerShell path now uses an
unsloth-studio-refresh- prefix so AV/EDR rules and user greps can
identify it.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* studio update: drop obsolete WinError 32 hint, echo Tauri skip
The rename trick in _release_self_exe_lock_windows + setup.ps1's
restore now handle the .exe-lock case in-flow; the printed hint
suggested re-running update via venv python, but that just re-enters
the same update() and hits the same failure if the rename didn't help.
Removing the misleading hint and its helper.
Also surface a one-line typer.echo when refresh is skipped under
UNSLOTH_TAURI_UPDATE so --verbose logs make the branch visible.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>