docker: close four holes the review found

docker-publish.yml: the llama.cpp tag resolver was the one step in the prepare
job still using `curl | sed` without pipefail. The runner's default `bash -e`
shell takes sed's exit status, so an unreachable github.com left TAG empty and
the step published the mutable `latest`. Both arch legs re-resolve that through
fetch_llama_prebuilt.py and Dockerfile.studio resolves it a third time, so a
release cut mid-run can put different llama.cpp bundles under one manifest.
Capture the redirect first and fail the job when it is missing or does not land
on a release tag, matching the three ref resolvers below it.

unsloth_nb_strip_colab.py: strip_notebook read, parsed and then unconditionally
os.replace'd. The refresh child re-arms finalize after the entrypoint has execed
the container command, so JupyterLab is already serving the tree and a save
landing in that window was destroyed, after which migrate recorded the cleaned
hash and marked the notebook pristine forever. Re-read the hash once the staged
copy is complete and drop it when the file moved, the same rule the refresh
publish in unsloth_sync_notebooks.sh already follows.

unsloth_nb_view.py: ownership for the view teardown accepted any symlink target
under DEST, but every link the tool creates points at DEST/nb. A shortcut the
user made in the landing dir to their own file elsewhere in the checkout was
therefore classified as ours and deleted on the next boot. Key ownership on
DEST/nb instead.

cellNav.ts: the edit-mode boundary test compared the cursor line against
editor.lineCount, both logical, while JupyterLab wraps markdown and raw editors
by default (StaticNotebook.defaultEditorConfig). A one-line markdown header
renders as several visual rows, so every arrow left the cell and the wrapped
rows could not be reached. Ask CodeMirror whether it can still move one visual
line (EditorView.moveVertically, compared by coordsAtPos top) and keep the
logical test as the fallback for a non-CodeMirror editor.

New tests: 12 passed / 8 failed before, 20 passed / 0 failed after.
This commit is contained in:
Daniel Han 2026-07-27 16:06:56 +00:00
commit 5906a9feb6
8 changed files with 497 additions and 24 deletions

View file

@ -82,12 +82,26 @@ jobs:
run: |
TAG="$INPUT_TAG"
if [ -z "$TAG" ]; then
TAG="$(curl -fsSL -o /dev/null -w '%{url_effective}' \
https://github.com/unslothai/llama.cpp/releases/latest \
| sed -n 's#.*/releases/tag/##p')"
# Same rule as the three ref resolvers below. This step has no
# explicit `shell:`, so it runs under `bash -e` WITHOUT pipefail and
# a failing curl inside `curl | sed` is lost: the step exited 0 and
# published tag=latest. Every consumer resolves that MUTABLE tag
# again -- fetch_llama_prebuilt.py once per arch leg, Dockerfile.
# studio once more -- so a release cut mid-run can put different
# llama.cpp bundles under one manifest. Fail the job instead.
if ! REDIRECT="$(curl -fsSL -o /dev/null -w '%{url_effective}' \
https://github.com/unslothai/llama.cpp/releases/latest)"; then
echo "::error::unslothai/llama.cpp unreachable; cannot resolve the newest prebuilt tag"
exit 1
fi
TAG="$(printf '%s\n' "$REDIRECT" | sed -n 's#.*/releases/tag/##p')"
if [ -z "$TAG" ]; then
echo "::error::/releases/latest did not redirect to a release tag (landed on ${REDIRECT})"
exit 1
fi
fi
echo "tag=${TAG:-latest}" >> "$GITHUB_OUTPUT"
echo "llama.cpp prebuilt tag: ${TAG:-latest}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "llama.cpp prebuilt tag: ${TAG}"
# Requested-ref precedence: dispatch input, else pushed tag, else trigger
# sha, else main -- then frozen to one sha per the job header.