From 5d41a03b40341995dc6f99ceb292fc87ee582412 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sun, 5 Jul 2026 14:09:48 +0000 Subject: [PATCH] docker: categorize AMD/domain notebooks and wire the feature validation into CI unsloth_nb_view.parse_readme only reset the folder section on level-3 (###) headings. The notebooks README carries level-1 domain headers (# AMD Notebooks, # Kaggle Notebooks) with their own nb/*.ipynb link tables and no intervening ###, so those notebooks were mis-filed under the previous stale section (all 148 AMD notebooks landed in Other Notebooks on an --amd build). Reset on any heading level and strip a leading emoji/symbol run so the domain notebooks get their own clean folder. Also run tests/validate_studio_features.py explicitly in the repo CPU job. It is named validate_* (not test_*) so pytest never collected it, which meant a regression in the notebook view, Colab compat, strip, JupyterLab defaults or login branding failed CI only when run by hand. --- .github/workflows/studio-backend-ci.yml | 6 ++++++ docker/unsloth_nb_view.py | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/studio-backend-ci.yml b/.github/workflows/studio-backend-ci.yml index 3022127a2b..c0abd78d74 100644 --- a/.github/workflows/studio-backend-ci.yml +++ b/.github/workflows/studio-backend-ci.yml @@ -238,3 +238,9 @@ jobs: echo "::endgroup::" done + - name: Docker JupyterLab/notebook feature validation + # Named validate_studio_features.py (not test_*.py) so pytest's default + # discovery skips it; run it explicitly here so a regression in the + # notebook view, Colab compat, strip, JupyterLab defaults or login + # branding fails CI instead of only when someone runs it by hand. + run: python tests/validate_studio_features.py diff --git a/docker/unsloth_nb_view.py b/docker/unsloth_nb_view.py index 9c74081999..0d6bb5a4f3 100644 --- a/docker/unsloth_nb_view.py +++ b/docker/unsloth_nb_view.py @@ -51,6 +51,10 @@ def clean_section(title): # Drop a trailing run of '#', surrounding whitespace and any emoji/symbols # that sometimes lead a header; keep ASCII text, digits and a few separators. title = title.strip().strip("#").strip() + # Strip a leading run of emoji / symbols / punctuation that some domain + # headers lead with (e.g. "🐧 AMD Notebooks", "📒 Kaggle Notebooks") so the + # folder label is clean text. + title = re.sub(r"^[^\w]+", "", title) title = title.replace("-", " ").replace("/", " ") title = re.sub(r"\s+", " ", title).strip() return title @@ -74,8 +78,13 @@ def parse_readme(readme_path): rows = [] seen_pairs = set() # (section, filename) already emitted section = None + # Reset on ANY markdown heading, not just `###`. The catalog uses `#`/`##` + # domain headers (e.g. "# AMD Notebooks", "# Kaggle Notebooks") that carry + # their own `nb/*.ipynb` link tables directly, with no intervening `###`. + # Matching only `###` left `section` stale, so those links were mis-filed + # under the previous section instead of getting their own folder. for line in text.splitlines(): - m = re.match(r"^###\s+(.*)$", line) + m = re.match(r"^#{1,6}\s+(.*)$", line) if m: section = clean_section(m.group(1)) continue