fix: address Codex review on WoA deferral, uninstall port-kill, and build preservation

Three valid findings from the 06-22 Codex review:

1. provision_llama_cuda.sh: when $LLAMA_DIR holds a .git checkout (a prior CPU
   source build), the whole-dir backup was skipped, so a failed CUDA rebuild's
   'rm -rf build' destroyed the working CPU server with nothing to restore --
   leaving NO llama-server despite the 'keeps the existing server' promise (a
   thermal shutdown mid-build is a real failure mode on this hardware). Back up
   build/bin before the rebuild and restore it on total failure; idempotent and
   self-cleaning (never overwrites a freshly built server). Verified both paths.

2. uninstall.ps1: 'fuser -k 8888/tcp' killed ANY listener on 8888 (Jupyter et al.
   default to it), not just Studio. Now only kills a PID whose /proc/cmdline is
   under /root/.unsloth -- matching the adjacent pkill scoping.

3. setup.sh: the 'defer to background CUDA build' branch fired even on a direct
   in-WSL 'unsloth studio update', where install.ps1 never launched a background
   builder -- so the footer claimed a build was running while nothing built. Gate
   it on UNSLOTH_WSL_LLAMA_DEFERRED=1 (set only by install.ps1, and already read
   elsewhere in setup.sh); a direct run now falls through to a real CPU build.

bash -n + PS parse clean; the common install.ps1 WoA path (prebuilt success,
deferred flag set) is unaffected.
This commit is contained in:
Daniel Han 2026-06-21 22:24:41 -07:00
commit fa44cb8c44
3 changed files with 45 additions and 13 deletions

View file

@ -474,12 +474,13 @@ function Uninstall-UnslothStudio {
# Probe candidates by exit code ('' = default distro) since `wsl --list` emits UTF-16 PS
# mis-parses. rm runs FIRST (the kills could SIGKILL this shell) and drops the dangling
# /root/.local/bin/unsloth symlink. Scope STRICTLY to /root (the fallback's install dir);
# /home/*/.unsloth may be another user's. The 8888 kill is gated on an Unsloth install
# existing (checked BEFORE rm deletes the marker) so an unrelated listener survives. pkill
# matches argv containing /root/.unsloth/ (not bare names that would hit a user's own
# llama-server); the backslash + [h]-bracket in '/root/\.unslot[h]/' keep it from matching
# this command's own argv.
$_clean = '_had=0; if [ -d /root/.unsloth ] || [ -L /root/.local/bin/unsloth ]; then _had=1; fi; rm -rf /root/.unsloth /root/llama-cuda /root/provision_llama_cuda.sh /root/llama_cuda_build.log 2>/dev/null; rm -f /root/.local/bin/unsloth 2>/dev/null; if [ $_had -eq 1 ]; then fuser -k 8888/tcp 2>/dev/null; fi; pkill -9 -f ''/root/\.unslot[h]/'' 2>/dev/null; true'
# /home/*/.unsloth may be another user's. The 8888 kill only targets a listener whose
# process cmdline is under /root/.unsloth (Studio's bind), so an unrelated service on 8888
# -- Jupyter et al. default to it -- is NOT killed; it's also gated on an Unsloth install
# having existed (checked BEFORE rm deletes the marker). pkill matches argv containing
# /root/.unsloth/ (not bare names that would hit a user's own llama-server); the backslash
# + [h]-bracket in '/root/\.unslot[h]/' keep it from matching this command's own argv.
$_clean = '_had=0; if [ -d /root/.unsloth ] || [ -L /root/.local/bin/unsloth ]; then _had=1; fi; rm -rf /root/.unsloth /root/llama-cuda /root/provision_llama_cuda.sh /root/llama_cuda_build.log 2>/dev/null; rm -f /root/.local/bin/unsloth 2>/dev/null; if [ $_had -eq 1 ]; then for _p in $(fuser 8888/tcp 2>/dev/null); do grep -qa /root/\.unsloth/ /proc/$_p/cmdline 2>/dev/null && kill -9 $_p 2>/dev/null; done; fi; pkill -9 -f ''/root/\.unslot[h]/'' 2>/dev/null; true'
# Clean only distros with evidence of a fallback install: the wsl-distro.txt marker or an
# explicit UNSLOTH_WSL_DISTRO. The broad candidate probe is only for legacy marker-less
# installs (ARM64 only); on x86 it would delete distros this installer never touched

View file

@ -172,6 +172,30 @@ if [ ! -d "$LLAMA_DIR/.git" ]; then
fi
cd "$LLAMA_DIR" || { _restore_prev; exit 0; }
# When rebuilding in-place over an existing git checkout, the whole-dir backup above
# was skipped (_LLAMA_BAK empty) -- but build/ may already hold a working (e.g. CPU)
# llama-server from a prior setup.sh source build. The wipe-on-failure paths below
# would destroy it with nothing to restore, leaving NO server despite the "keeps the
# existing server" promise (a thermal shutdown mid-CUDA-build is a real failure mode
# here). Back up the existing binaries so a failed rebuild can put them back. Only
# bin/ (server + dlopen-ed backends) is needed; cheap since any pre-existing server
# here is the non-CUDA fallback (a CUDA one would have exited at step 0).
_BUILD_BAK=""
if [ -z "$_LLAMA_BAK" ] && [ -x "$SERVER" ]; then
_BUILD_BAK="${LLAMA_DIR}.binbak.$$"
rm -rf "$_BUILD_BAK" 2>/dev/null
cp -a "$LLAMA_DIR/build/bin" "$_BUILD_BAK" 2>/dev/null || _BUILD_BAK=""
fi
_restore_build() {
if [ -n "$_BUILD_BAK" ] && [ -e "$_BUILD_BAK" ] && [ ! -x "$SERVER" ]; then
mkdir -p "$LLAMA_DIR/build" 2>/dev/null
rm -rf "$LLAMA_DIR/build/bin" 2>/dev/null
mv "$_BUILD_BAK" "$LLAMA_DIR/build/bin" 2>/dev/null && log "restored previous llama-server (rebuild failed)"
fi
[ -n "$_BUILD_BAK" ] && rm -rf "$_BUILD_BAK" 2>/dev/null
_BUILD_BAK=""
}
log "building CUDA llama.cpp (arch=$CUDA_ARCH, host=$HCXX) - this takes a few minutes..."
_cmake_configure() {
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
@ -185,7 +209,7 @@ _cmake_configure() {
if ! _cmake_configure; then
log "stale/incompatible CMake cache detected; wiping build dir for a clean CUDA configure"
rm -rf build
_cmake_configure || { log "cmake configure failed"; cd /; _restore_prev; exit 0; }
_cmake_configure || { log "cmake configure failed"; cd /; _restore_build; _restore_prev; exit 0; }
fi
# Also builds the targets unsloth-zoo's GGUF exporter needs (llama-mtmd-cli,
# llama-gguf-split). Jobs default to ~half the cores (full -j(nproc) CUDA builds
@ -226,10 +250,13 @@ if ! _cmake_build; then
# (undefined ggml_cuda_op_* refs); wipe and rebuild clean.
log "build failed (likely interrupted/partial); wiping build dir and rebuilding clean"
rm -rf build
_cmake_configure || { log "cmake configure failed"; cd /; _restore_prev; exit 0; }
_cmake_build || { log "cmake build failed"; cd /; _restore_prev; exit 0; }
_cmake_configure || { log "cmake configure failed"; cd /; _restore_build; _restore_prev; exit 0; }
_cmake_build || { log "cmake build failed"; cd /; _restore_build; _restore_prev; exit 0; }
fi
_cmake_build_extras
# Drop the backup on a successful build, or restore the prior server if the rebuild
# yielded none (idempotent; only restores when $SERVER is missing).
_restore_build
if is_cuda_server "$SERVER"; then
log "CUDA llama-server ready: $SERVER"

View file

@ -1253,11 +1253,15 @@ if [ "$_NEED_LLAMA_SOURCE_BUILD" = true ] && \
fi
# ── WSL2 aarch64 + NVIDIA, no nvcc yet: defer to the background CUDA build ──
# install.ps1 builds the CUDA llama-server in the background; without nvcc,
# section 9 could only make a slow CPU server that build discards. With nvcc we
# fall through to section 9; opted out (UNSLOTH_NO_LLAMA_CUDA=1) the CPU build is
# the only server.
# install.ps1 builds the CUDA llama-server in the background AND signals that by
# exporting UNSLOTH_WSL_LLAMA_DEFERRED=1 into this install. ONLY defer when that
# flag is set: a direct in-WSL `unsloth studio update` has no background builder,
# so deferring there would report "CUDA build running in background" while nothing
# builds -- hiding a no-server state. Without the flag we fall through to section 9
# (a slow CPU server, still better than a phantom background build). With nvcc we
# fall through too; opted out (UNSLOTH_NO_LLAMA_CUDA=1) the CPU build is the only server.
if [ "$_NEED_LLAMA_SOURCE_BUILD" = true ] \
&& [ "${UNSLOTH_WSL_LLAMA_DEFERRED:-0}" = "1" ] \
&& [ "$_LLAMA_FORCE_COMPILE" != "1" ] \
&& [ -z "$_LLAMA_PR" ] \
&& [ "${UNSLOTH_NO_LLAMA_CUDA:-0}" != "1" ] \