Merge branch 'main' into ci/frozen-lockfile-installs
This commit is contained in:
commit
888347d551
119 changed files with 14019 additions and 1177 deletions
34
.github/workflows/studio-mac-ui-smoke.yml
vendored
34
.github/workflows/studio-mac-ui-smoke.yml
vendored
|
|
@ -183,16 +183,16 @@ jobs:
|
|||
# available to llama.cpp from CI; gemma-3-270m turn latency
|
||||
# has been observed to crowd the 180s default. Triple it.
|
||||
STUDIO_UI_TURN_TIMEOUT_MS: '540000'
|
||||
# Retry up to 3 times to absorb the racy Playwright Node 24
|
||||
# pipeTransport.js 'Unexpected end of JSON input' crash that
|
||||
# fires intermittently on macos-14 free runners (Chromium
|
||||
# browser process dies mid-test → driver Node process can't
|
||||
# parse the truncated JSON-RPC line and exits). The retry
|
||||
# FULLY resets Studio (kill, reset-password, reboot, wait
|
||||
# /api/health, re-export bootstrap pw) before re-running the
|
||||
# script so the change-password flow finds a fresh bootstrap.
|
||||
# A real test failure (assertion / timeout) does NOT match the
|
||||
# JSON pattern so it bypasses retry and surfaces immediately.
|
||||
# Retry up to 3 times to absorb known macos-14 free-runner
|
||||
# flakes: (1) Playwright Node 24 pipeTransport.js 'Unexpected
|
||||
# end of JSON input' crash when the Chromium browser process
|
||||
# dies mid-test, and (2) Chromium net::ERR_NO_BUFFER_SPACE
|
||||
# when the runner's kernel briefly runs out of socket buffers.
|
||||
# The retry FULLY resets Studio (kill, reset-password, reboot,
|
||||
# wait /api/health, re-export bootstrap pw) before re-running
|
||||
# the script. A real test failure (assertion / timeout) does
|
||||
# NOT match either pattern so it bypasses retry and surfaces
|
||||
# immediately.
|
||||
run: |
|
||||
mkdir -p logs/playwright
|
||||
attempt=1
|
||||
|
|
@ -205,9 +205,10 @@ jobs:
|
|||
if [ "$rc" -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
if grep -q "Unexpected end of JSON input" logs/playwright_attempt_${attempt}.log \
|
||||
if { grep -q "Unexpected end of JSON input" logs/playwright_attempt_${attempt}.log \
|
||||
|| grep -q "ERR_NO_BUFFER_SPACE" logs/playwright_attempt_${attempt}.log; } \
|
||||
&& [ "$attempt" -lt "$max_attempts" ]; then
|
||||
echo "::warning::Playwright pipeTransport JSON crash on attempt ${attempt}; resetting Studio and retrying..."
|
||||
echo "::warning::Playwright flake on attempt ${attempt}; resetting Studio and retrying..."
|
||||
kill "${STUDIO_PID}" 2>/dev/null || true
|
||||
sleep 2
|
||||
unsloth studio reset-password
|
||||
|
|
@ -280,8 +281,8 @@ jobs:
|
|||
STUDIO_UI_TURN_TIMEOUT_MS: '540000'
|
||||
GGUF_REPO: ${{ env.GGUF_REPO }}
|
||||
GGUF_VARIANT: ${{ env.GGUF_VARIANT }}
|
||||
# Same pipeTransport JSON-crash retry shape as "Drive the chat
|
||||
# UI with Playwright" -- see comment there.
|
||||
# Same flake-retry shape as "Drive the chat UI with Playwright"
|
||||
# -- catches pipeTransport JSON crash and ERR_NO_BUFFER_SPACE.
|
||||
run: |
|
||||
mkdir -p logs/playwright_extra
|
||||
attempt=1
|
||||
|
|
@ -294,9 +295,10 @@ jobs:
|
|||
if [ "$rc" -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
if grep -q "Unexpected end of JSON input" logs/playwright_extra_attempt_${attempt}.log \
|
||||
if { grep -q "Unexpected end of JSON input" logs/playwright_extra_attempt_${attempt}.log \
|
||||
|| grep -q "ERR_NO_BUFFER_SPACE" logs/playwright_extra_attempt_${attempt}.log; } \
|
||||
&& [ "$attempt" -lt "$max_attempts" ]; then
|
||||
echo "::warning::Playwright pipeTransport JSON crash on attempt ${attempt}; resetting Studio and retrying..."
|
||||
echo "::warning::Playwright flake on attempt ${attempt}; resetting Studio and retrying..."
|
||||
kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true
|
||||
sleep 2
|
||||
unsloth studio reset-password
|
||||
|
|
|
|||
34
.github/workflows/studio-mac-update-smoke.yml
vendored
34
.github/workflows/studio-mac-update-smoke.yml
vendored
|
|
@ -21,6 +21,7 @@ on:
|
|||
pull_request:
|
||||
paths:
|
||||
- 'install.sh'
|
||||
- 'uninstall.sh'
|
||||
- 'studio/setup.sh'
|
||||
- 'studio/install_python_stack.py'
|
||||
- 'studio/install_llama_prebuilt.py'
|
||||
|
|
@ -137,6 +138,38 @@ jobs:
|
|||
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
|
||||
|
|
@ -147,4 +180,5 @@ jobs:
|
|||
logs/update.log
|
||||
logs/update2.log
|
||||
logs/studio.log
|
||||
logs/uninstall.log
|
||||
retention-days: 7
|
||||
|
|
|
|||
55
.github/workflows/studio-ui-smoke.yml
vendored
55
.github/workflows/studio-ui-smoke.yml
vendored
|
|
@ -229,12 +229,55 @@ jobs:
|
|||
kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# IME + multilingual paste regression (issue #5318 / PR #5327).
|
||||
# Third Studio on its own port so a hang here cannot poison the
|
||||
# earlier UI tests. No GGUF -- the bug surface is the composer.
|
||||
- name: Reset auth + boot Studio for IME / i18n tests (port 18896)
|
||||
run: |
|
||||
unsloth studio reset-password
|
||||
mkdir -p logs
|
||||
UNSLOTH_API_ONLY=1 unsloth studio -H 127.0.0.1 -p 18896 \
|
||||
> logs/studio_ime.log 2>&1 &
|
||||
echo "STUDIO_IME_PID=$!" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Wait for /api/health on 18896
|
||||
run: |
|
||||
for i in $(seq 1 180); do
|
||||
if curl -fs "http://127.0.0.1:18896/api/health" > /tmp/health3.json; then
|
||||
jq -e '.status == "healthy"' /tmp/health3.json && break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
jq -e '.status == "healthy"' /tmp/health3.json
|
||||
|
||||
- name: Pass bootstrap pw for IME / i18n test
|
||||
# IME smoke does the change-password against the bootstrap that
|
||||
# Studio's frontend injects into the page, so it only needs the
|
||||
# NEW password.
|
||||
run: |
|
||||
NEW="CIIme-$(python -c 'import secrets; print(secrets.token_urlsafe(16))')"
|
||||
echo "::add-mask::$NEW"
|
||||
echo "STUDIO_IME_NEW_PW=$NEW" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Drive IME + multilingual paste regression with Playwright
|
||||
env:
|
||||
BASE_URL: http://127.0.0.1:18896
|
||||
STUDIO_NEW_PW: ${{ env.STUDIO_IME_NEW_PW }}
|
||||
PW_ART_DIR: logs/playwright_ime
|
||||
STUDIO_UI_STRICT: '1'
|
||||
run: |
|
||||
mkdir -p logs/playwright_ime
|
||||
python tests/studio/playwright_chat_ime_i18n.py
|
||||
|
||||
- name: Stop third Studio
|
||||
if: always()
|
||||
run: |
|
||||
kill "${STUDIO_IME_PID}" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
- name: Upload Playwright artifacts
|
||||
# Always upload (not just failure) so a green run's screenshots
|
||||
# are reviewable in the Actions UI -- catches "passed but the
|
||||
# UI is silently broken" regressions that would be invisible
|
||||
# otherwise. Both Studio's logs (chat + extra) and BOTH
|
||||
# Playwright artifact dirs are bundled.
|
||||
# Always upload so a green run's screenshots stay reviewable --
|
||||
# catches "passed but the UI is silently broken" regressions.
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
|
@ -242,7 +285,9 @@ jobs:
|
|||
path: |
|
||||
logs/studio.log
|
||||
logs/studio_extra.log
|
||||
logs/studio_ime.log
|
||||
logs/install.log
|
||||
logs/playwright
|
||||
logs/playwright_extra
|
||||
logs/playwright_ime
|
||||
retention-days: 7
|
||||
|
|
|
|||
39
.github/workflows/studio-update-smoke.yml
vendored
39
.github/workflows/studio-update-smoke.yml
vendored
|
|
@ -15,6 +15,7 @@ on:
|
|||
pull_request:
|
||||
paths:
|
||||
- 'install.sh'
|
||||
- 'uninstall.sh'
|
||||
- 'studio/setup.sh'
|
||||
- 'studio/install_python_stack.py'
|
||||
- 'studio/install_llama_prebuilt.py'
|
||||
|
|
@ -139,9 +140,44 @@ jobs:
|
|||
kill "$PID" 2>/dev/null || true
|
||||
echo "post-update Studio /api/health OK"
|
||||
|
||||
- name: Uninstall and verify clean
|
||||
# Round-trip the installer through uninstall.sh: confirms the
|
||||
# uninstaller actually finds and removes everything install.sh +
|
||||
# update wrote. Safety-guard scenarios (refuse-$HOME etc.) belong
|
||||
# in a separate fast smoke job; this is the happy-path cleanup
|
||||
# assertion that catches regressions where install.sh starts
|
||||
# writing to a new location and uninstall.sh hasn't caught up.
|
||||
# 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/Desktop/Unsloth Studio.desktop" \
|
||||
"$HOME/.local/bin/unsloth"; do
|
||||
if [ -e "$p" ] || [ -L "$p" ]; then
|
||||
echo "::error::leak: $p"
|
||||
ls -la "$p" 2>&1 | head -3
|
||||
leak=$((leak + 1))
|
||||
fi
|
||||
done
|
||||
[ "$leak" -eq 0 ] || exit 1
|
||||
# Idempotent: re-runs exit 0 on an empty $HOME.
|
||||
sh uninstall.sh 2>&1 | tail -5
|
||||
sh uninstall.sh 2>&1 | tail -5
|
||||
echo "PASS: install -> update -> uninstall round-trip clean"
|
||||
|
||||
- name: Upload update logs
|
||||
# Always upload so a green run still leaves the install + two
|
||||
# update logs reviewable.
|
||||
# update logs + uninstall log reviewable.
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
|
@ -151,4 +187,5 @@ jobs:
|
|||
logs/update.log
|
||||
logs/update2.log
|
||||
logs/studio.log
|
||||
logs/uninstall.log
|
||||
retention-days: 7
|
||||
|
|
|
|||
107
.github/workflows/studio-windows-inference-smoke.yml
vendored
107
.github/workflows/studio-windows-inference-smoke.yml
vendored
|
|
@ -258,11 +258,26 @@ jobs:
|
|||
|
||||
- name: Load the GGUF (HF repo + variant, served from HF_HOME cache)
|
||||
run: |
|
||||
curl -fs -X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 600 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}" \
|
||||
| jq '{status, display_name, is_gguf, context_length}'
|
||||
# Retry the load step a few times so a transient TCP RST during
|
||||
# llama-server warm-up (Windows runner image churn,
|
||||
# windows-latest -> windows-2025-vs2026 rollout) doesn't fail
|
||||
# the whole job. The Studio backend's _wait_for_health now
|
||||
# catches httpx.ReadError too; this retry layer covers the
|
||||
# cases the backend can't recover from on its own.
|
||||
LOAD_OK=0
|
||||
for attempt in 1 2 3; do
|
||||
HTTP=$(curl -s -o /tmp/load.json -w '%{http_code}' \
|
||||
-X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 600 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}")
|
||||
if [ "$HTTP" = "200" ]; then LOAD_OK=1; break; fi
|
||||
echo "::warning::/api/inference/load attempt $attempt returned $HTTP; response:"
|
||||
cat /tmp/load.json || true
|
||||
sleep 10
|
||||
done
|
||||
[ "$LOAD_OK" = "1" ] || { echo "::error::/api/inference/load failed 3 attempts"; exit 22; }
|
||||
jq '{status, display_name, is_gguf, context_length}' /tmp/load.json
|
||||
|
||||
- name: Multi-turn determinism via OpenAI + Anthropic SDKs
|
||||
env:
|
||||
|
|
@ -350,6 +365,19 @@ jobs:
|
|||
shell: cmd
|
||||
run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end)
|
||||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
# upload-artifact can pick it up. Crucial for diagnosing a
|
||||
# subprocess crash where Studio's traceback only shows the
|
||||
# symptom (httpx ReadError) but not the cause.
|
||||
run: |
|
||||
mkdir -p logs/llama-server
|
||||
cp -v ~/.unsloth/studio/logs/llama-server/*.log logs/llama-server/ 2>/dev/null || \
|
||||
echo "no llama-server logs to collect"
|
||||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
|
@ -358,6 +386,7 @@ jobs:
|
|||
path: |
|
||||
logs/studio.log
|
||||
logs/install.log
|
||||
logs/llama-server/*.log
|
||||
retention-days: 7
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -561,11 +590,21 @@ jobs:
|
|||
# a normal path.
|
||||
GGUF_PATH="${GITHUB_WORKSPACE//\\//}/gguf-cache/${GGUF_FILE}"
|
||||
ls -lh "$GGUF_PATH"
|
||||
curl -fs -X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 600 \
|
||||
-d "{\"model_path\":\"$GGUF_PATH\",\"is_lora\":false,\"max_seq_length\":2048}" \
|
||||
| jq '{status, display_name}'
|
||||
# Retry: same rationale as the OpenAI/Anthropic job.
|
||||
LOAD_OK=0
|
||||
for attempt in 1 2 3; do
|
||||
HTTP=$(curl -s -o /tmp/load.json -w '%{http_code}' \
|
||||
-X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 600 \
|
||||
-d "{\"model_path\":\"$GGUF_PATH\",\"is_lora\":false,\"max_seq_length\":2048}")
|
||||
if [ "$HTTP" = "200" ]; then LOAD_OK=1; break; fi
|
||||
echo "::warning::/api/inference/load attempt $attempt returned $HTTP; response:"
|
||||
cat /tmp/load.json || true
|
||||
sleep 10
|
||||
done
|
||||
[ "$LOAD_OK" = "1" ] || { echo "::error::/api/inference/load failed 3 attempts"; exit 22; }
|
||||
jq '{status, display_name}' /tmp/load.json
|
||||
|
||||
- name: Tool calling, server-side tools, thinking on/off
|
||||
env:
|
||||
|
|
@ -768,6 +807,19 @@ jobs:
|
|||
shell: cmd
|
||||
run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end)
|
||||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
# upload-artifact can pick it up. Crucial for diagnosing a
|
||||
# subprocess crash where Studio's traceback only shows the
|
||||
# symptom (httpx ReadError) but not the cause.
|
||||
run: |
|
||||
mkdir -p logs/llama-server
|
||||
cp -v ~/.unsloth/studio/logs/llama-server/*.log logs/llama-server/ 2>/dev/null || \
|
||||
echo "no llama-server logs to collect"
|
||||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
|
@ -776,6 +828,7 @@ jobs:
|
|||
path: |
|
||||
logs/studio.log
|
||||
logs/install.log
|
||||
logs/llama-server/*.log
|
||||
retention-days: 7
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -970,11 +1023,21 @@ jobs:
|
|||
-H 'content-type: application/json' \
|
||||
-d "{\"username\":\"unsloth\",\"password\":\"$NEW\"}" | jq -r .access_token)
|
||||
echo "API_KEY=$TOKEN" >> "$GITHUB_ENV"
|
||||
curl -fs -X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 900 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}" \
|
||||
| jq '{status, display_name, is_vision}'
|
||||
# Retry: same rationale as the OpenAI/Anthropic and Tool calling jobs.
|
||||
LOAD_OK=0
|
||||
for attempt in 1 2 3; do
|
||||
HTTP=$(curl -s -o /tmp/load.json -w '%{http_code}' \
|
||||
-X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 900 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}")
|
||||
if [ "$HTTP" = "200" ]; then LOAD_OK=1; break; fi
|
||||
echo "::warning::/api/inference/load attempt $attempt returned $HTTP; response:"
|
||||
cat /tmp/load.json || true
|
||||
sleep 10
|
||||
done
|
||||
[ "$LOAD_OK" = "1" ] || { echo "::error::/api/inference/load failed 3 attempts"; exit 22; }
|
||||
jq '{status, display_name, is_vision}' /tmp/load.json
|
||||
|
||||
- name: JSON schema decoding + image input
|
||||
env:
|
||||
|
|
@ -1156,6 +1219,19 @@ jobs:
|
|||
shell: cmd
|
||||
run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end)
|
||||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
# upload-artifact can pick it up. Crucial for diagnosing a
|
||||
# subprocess crash where Studio's traceback only shows the
|
||||
# symptom (httpx ReadError) but not the cause.
|
||||
run: |
|
||||
mkdir -p logs/llama-server
|
||||
cp -v ~/.unsloth/studio/logs/llama-server/*.log logs/llama-server/ 2>/dev/null || \
|
||||
echo "no llama-server logs to collect"
|
||||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
|
@ -1164,4 +1240,5 @@ jobs:
|
|||
path: |
|
||||
logs/studio.log
|
||||
logs/install.log
|
||||
logs/llama-server/*.log
|
||||
retention-days: 7
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ on:
|
|||
pull_request:
|
||||
paths:
|
||||
- 'install.ps1'
|
||||
- 'uninstall.ps1'
|
||||
- 'studio/setup.ps1'
|
||||
- 'studio/setup.bat'
|
||||
- 'studio/install_python_stack.py'
|
||||
|
|
@ -266,6 +267,39 @@ jobs:
|
|||
kill "$PID" 2>/dev/null || true
|
||||
echo "post-update Studio /api/health OK"
|
||||
|
||||
- name: Uninstall and verify clean
|
||||
# Round-trip through uninstall.ps1 against the default install
|
||||
# tree at %USERPROFILE%\.unsloth\studio. Catches regressions
|
||||
# where install.ps1 starts writing under a new key (registry,
|
||||
# Start Menu, %APPDATA%) and uninstall.ps1 has not been updated
|
||||
# to match. Skips gracefully if uninstall.ps1 has not landed yet
|
||||
# (lets this workflow merge before #5513).
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path logs | Out-Null
|
||||
if (-not (Test-Path "$PWD\uninstall.ps1")) {
|
||||
Write-Host "uninstall.ps1 not present in this tree; skipping round-trip"
|
||||
"" | Set-Content logs/uninstall.log
|
||||
exit 0
|
||||
}
|
||||
pwsh -NoProfile -File "$PWD\uninstall.ps1" *>&1 | Tee-Object -FilePath logs/uninstall.log
|
||||
$leak = 0
|
||||
foreach ($p in @(
|
||||
"$env:USERPROFILE\.unsloth\studio",
|
||||
"$env:USERPROFILE\.unsloth\studio\unsloth_studio",
|
||||
"$env:USERPROFILE\.unsloth\studio\bin\unsloth.exe"
|
||||
)) {
|
||||
if (Test-Path -LiteralPath $p) {
|
||||
Write-Host "::error::leak: $p"
|
||||
$leak++
|
||||
}
|
||||
}
|
||||
if ($leak -gt 0) { exit 1 }
|
||||
# Idempotency.
|
||||
pwsh -NoProfile -File "$PWD\uninstall.ps1" *>&1 | Select-Object -Last 5
|
||||
pwsh -NoProfile -File "$PWD\uninstall.ps1" *>&1 | Select-Object -Last 5
|
||||
Write-Host "PASS: windows install -> update -> uninstall round-trip clean"
|
||||
|
||||
- name: Upload update logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
|
@ -276,4 +310,5 @@ jobs:
|
|||
logs/update.log
|
||||
logs/update2.log
|
||||
logs/studio.log
|
||||
logs/uninstall.log
|
||||
retention-days: 7
|
||||
|
|
|
|||
1
.github/workflows/version-compat-ci.yml
vendored
1
.github/workflows/version-compat-ci.yml
vendored
|
|
@ -127,6 +127,7 @@ jobs:
|
|||
run: |
|
||||
PYTHONPATH=. python -m pytest \
|
||||
tests/version_compat/test_peft_pinned_symbols.py \
|
||||
tests/version_compat/test_unsloth_zoo_save_merged_pinned_symbols.py \
|
||||
-v --tb=short
|
||||
|
||||
st-pinned-symbols:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue