From 921ab186188838f955acff3bd17a86644a4faec3 Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Tue, 16 Jun 2026 06:14:33 +0000 Subject: [PATCH] docker: heal deleted notebooks on boot + fix notebooks helper dockerignore The boot-time notebook sync now restores notebooks the user deleted, on every boot, from the baked template (offline, even when upstream has not advanced). It only restores files that are missing, so it never resurrects or overwrites an edited notebook, and the GitHub refresh still bumps a restored file to the latest upstream. Opt out with UNSLOTH_KEEP_DELETED_NOTEBOOKS=1. Also add unsloth_nb_content_sig.py to docker/.dockerignore's allowlist; it was referenced by the Dockerfile COPY but excluded from the build context, which broke the image build. --- docker/.dockerignore | 1 + docker/unsloth_sync_notebooks.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docker/.dockerignore b/docker/.dockerignore index df3d08f8c8..1b6d68223c 100644 --- a/docker/.dockerignore +++ b/docker/.dockerignore @@ -10,3 +10,4 @@ !unsloth_ipython_startup.py !unsloth_run.py !unsloth_sync_notebooks.sh +!unsloth_nb_content_sig.py diff --git a/docker/unsloth_sync_notebooks.sh b/docker/unsloth_sync_notebooks.sh index cce87fb6f5..7e251574aa 100644 --- a/docker/unsloth_sync_notebooks.sh +++ b/docker/unsloth_sync_notebooks.sh @@ -16,6 +16,8 @@ # UNSLOTH_SKIP_NOTEBOOK_SYNC=1 do nothing (no populate, no refresh) # UNSLOTH_SKIP_NOTEBOOK_REFRESH=1 populate from the baked template only; # never touch the network +# UNSLOTH_KEEP_DELETED_NOTEBOOKS=1 do not restore notebooks the user deleted +# (default: deleted files are healed back) # UNSLOTH_NOTEBOOKS_DIR= target dir (default /workspace/unsloth-notebooks) # UNSLOTH_NOTEBOOKS_REPO= source repo (default unslothai/notebooks) # UNSLOTH_NOTEBOOK_FETCH_TIMEOUT=N seconds for each network op (default 60) @@ -87,6 +89,35 @@ if [ ! -f "$STATE" ]; then echo "[unsloth-nb] notebooks ready at $DEST" fi +# 1b) Every-boot OFFLINE restore of deleted notebooks. A file we previously wrote +# that the user has since DELETED is restored from the baked template -- works +# with no network and even when upstream has not advanced. Files that still exist +# (edited or not) are never touched, so this cannot resurrect or clobber an edit; +# the GitHub refresh below then bumps any restored file to the latest upstream. +# The restored file's recorded hash is reset to the template's so the refresh +# treats it as pristine (not as a user edit). Opt out with +# UNSLOTH_KEEP_DELETED_NOTEBOOKS=1 (for users who prune notebooks on purpose). +if [ -f "$STATE" ] && [ "${UNSLOTH_KEEP_DELETED_NOTEBOOKS:-0}" != "1" ]; then + restored=0 + RS_TMP="$(mktemp)" + while IFS= read -r line; do + h="${line%% *}"; rel="${line#* }" + if [ -n "$rel" ] && [ "$rel" != "$line" ] \ + && [ ! -e "$DEST/$rel" ] && [ -f "$TEMPLATE/$rel" ]; then + mkdir -p "$DEST/$(dirname "$rel")" 2>/dev/null || true + if cp -a "$TEMPLATE/$rel" "$DEST/$rel" 2>/dev/null; then + printf '%s %s\n' "$(hash_of "$DEST/$rel")" "$rel" >> "$RS_TMP" + restored=$((restored + 1)) + continue + fi + fi + printf '%s\n' "$line" >> "$RS_TMP" + done < "$STATE" + mv "$RS_TMP" "$STATE" 2>/dev/null || rm -f "$RS_TMP" + [ "$restored" -gt 0 ] \ + && echo "[unsloth-nb] restored $restored deleted notebook(s) from the baked set" +fi + # 2) Best-effort GitHub refresh -- only when upstream has advanced. Edits win. [ "${UNSLOTH_SKIP_NOTEBOOK_REFRESH:-0}" = "1" ] && exit 0 command -v git >/dev/null 2>&1 || exit 0