* Studio update CI: round-trip install -> update -> uninstall
Adds an "Uninstall and verify clean" step to the three existing
studio-{,-mac-,-windows-}update-smoke.yml workflows so each one ends by
running uninstall.sh / uninstall.ps1 against the install it just
produced, then asserting that the install dir, launcher data dir,
desktop shortcut, CLI shim (and on Mac, the .app bundle) are all gone.
Two trailing reruns confirm idempotency. The uninstall log is added to
the existing artifact bundle.
Catches regressions where install.sh / install.ps1 starts writing to a
new path (registry key, Start Menu entry, %APPDATA% subdir, etc.) and
uninstall.{sh,ps1} has not been updated to match. Safety-guard
scenarios (refuse-\$HOME, refuse-non-Studio, tilde expansion, etc.) are
intentionally NOT exercised here -- those belong in a dedicated fast
smoke job that does not have to wait on a 5-15 min install.
Wall-clock overhead is ~30-45 s on each runner. Path filters extended
to include uninstall.sh / uninstall.ps1 so a pure uninstaller change
also triggers the round-trip check.
* Skip round-trip step when uninstall.{sh,ps1} are not in tree
---------
Co-authored-by: Daniel Han <info@unsloth.ai>
184 lines
7 KiB
YAML
184 lines
7 KiB
YAML
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
|
|
|
# Mac counterpart to studio-update-smoke.yml. Verifies that on a real
|
|
# Apple Silicon (macos-14, M1) runner:
|
|
#
|
|
# 1. install.sh --local --no-torch installs Studio AND auto-fetches
|
|
# the prebuilt llama.cpp Mac binary (llama-bNNNN-bin-macos-arm64
|
|
# from ggml-org/llama.cpp). Hitting the source-build fallback is
|
|
# treated as an Unsloth bug -- Studio must always pick the
|
|
# prebuilt on Mac.
|
|
# 2. unsloth studio update --local is idempotent. Two consecutive
|
|
# runs both report "prebuilt up to date and validated", no
|
|
# source-build fallback.
|
|
# 3. The installed Studio still boots and /api/health returns
|
|
# healthy after the update path.
|
|
|
|
name: Mac Studio Update CI
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'install.sh'
|
|
- 'uninstall.sh'
|
|
- 'studio/setup.sh'
|
|
- 'studio/install_python_stack.py'
|
|
- 'studio/install_llama_prebuilt.py'
|
|
- 'studio/backend/requirements/**'
|
|
- 'unsloth_cli/commands/studio.py'
|
|
- 'pyproject.toml'
|
|
- '.github/workflows/studio-mac-update-smoke.yml'
|
|
push:
|
|
branches: [main, pip]
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
update-idempotency:
|
|
name: Studio Updating Tests
|
|
runs-on: macos-14
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: '22'
|
|
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
|
|
- name: Install Studio (--local, --no-torch)
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
mkdir -p logs
|
|
set -o pipefail
|
|
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
|
|
|
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
|
run: |
|
|
# Mac install must take the prebuilt path. Source-build
|
|
# fallback here is an Unsloth bug.
|
|
if grep -q "falling back to source build" logs/install.log; then
|
|
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
|
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
|
exit 1
|
|
fi
|
|
if ! grep -qE "prebuilt installed and validated|prebuilt up to date and validated|bin-macos-arm64" logs/install.log; then
|
|
echo "::error::no Mac prebuilt llama.cpp marker in install.log."
|
|
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
|
exit 1
|
|
fi
|
|
echo "install.sh installed the Mac prebuilt llama.cpp"
|
|
|
|
- name: First update should be a no-op (prebuilt already validated)
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -o pipefail
|
|
unsloth studio update --local 2>&1 | tee logs/update.log
|
|
if grep -q "falling back to source build" logs/update.log; then
|
|
echo "::error::studio update fell back to source-build llama.cpp on Mac."
|
|
grep -E "llama-prebuilt|llama.cpp" logs/update.log | tail -60
|
|
exit 1
|
|
fi
|
|
if ! grep -qE "prebuilt up to date and validated|prebuilt installed and validated" logs/update.log; then
|
|
echo "::error::no prebuilt up-to-date marker in update.log."
|
|
grep -E "llama-prebuilt|llama.cpp" logs/update.log | tail -60
|
|
exit 1
|
|
fi
|
|
echo "update path took the prebuilt fast path"
|
|
|
|
- name: Second update must also be a no-op
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -o pipefail
|
|
unsloth studio update --local 2>&1 | tee logs/update2.log
|
|
grep -q "falling back to source build" logs/update2.log && {
|
|
echo "::error::second update fell back to source build on Mac"
|
|
tail -60 logs/update2.log; exit 1; } || true
|
|
grep -qE "prebuilt up to date and validated|prebuilt installed and validated" logs/update2.log
|
|
echo "second update was clean"
|
|
|
|
- name: Boot Studio briefly to confirm the install is still usable
|
|
run: |
|
|
mkdir -p logs
|
|
UNSLOTH_API_ONLY=1 unsloth studio -H 127.0.0.1 -p 18891 \
|
|
> logs/studio.log 2>&1 &
|
|
PID=$!
|
|
HEALTHY=""
|
|
for i in $(seq 1 60); do
|
|
if curl -fs http://127.0.0.1:18891/api/health > /tmp/health.json; then
|
|
if python3 -c "import json,sys; d=json.load(open('/tmp/health.json')); sys.exit(0 if d.get('status')=='healthy' else 1)"; then
|
|
HEALTHY=1
|
|
break
|
|
fi
|
|
fi
|
|
sleep 1
|
|
done
|
|
if [ -z "$HEALTHY" ]; then
|
|
echo "Studio failed to come up after \`update\`"
|
|
tail -200 logs/studio.log
|
|
kill "$PID" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
kill "$PID" 2>/dev/null || true
|
|
echo "post-update Studio /api/health OK"
|
|
|
|
- name: Uninstall and verify clean
|
|
# Round-trip through uninstall.sh on real macOS. As a side effect
|
|
# this exercises the macOS-only .app bundle + Launch Services
|
|
# removal path (~/Applications/Unsloth Studio.app, lsregister -u)
|
|
# which is not testable from a Linux runner. Skips gracefully if
|
|
# uninstall.sh has not landed yet (lets this workflow merge
|
|
# before #5497).
|
|
run: |
|
|
set -o pipefail
|
|
if [ ! -f uninstall.sh ]; then
|
|
echo "uninstall.sh not present in this tree; skipping round-trip"
|
|
: > logs/uninstall.log
|
|
exit 0
|
|
fi
|
|
sh uninstall.sh 2>&1 | tee logs/uninstall.log
|
|
leak=0
|
|
for p in \
|
|
"$HOME/.unsloth/studio" \
|
|
"$HOME/.local/share/unsloth" \
|
|
"$HOME/Applications/Unsloth Studio.app" \
|
|
"$HOME/Desktop/Unsloth Studio.app" \
|
|
"$HOME/.local/bin/unsloth"; do
|
|
if [ -e "$p" ] || [ -L "$p" ]; then
|
|
echo "::error::leak: $p"
|
|
leak=$((leak + 1))
|
|
fi
|
|
done
|
|
[ "$leak" -eq 0 ] || exit 1
|
|
sh uninstall.sh 2>&1 | tail -5
|
|
sh uninstall.sh 2>&1 | tail -5
|
|
echo "PASS: mac install -> update -> uninstall round-trip clean"
|
|
|
|
- name: Upload update logs
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: mac-studio-update-log
|
|
path: |
|
|
logs/install.log
|
|
logs/update.log
|
|
logs/update2.log
|
|
logs/studio.log
|
|
logs/uninstall.log
|
|
retention-days: 7
|