Skip to content

Commit bf51897

Browse files
feat: enable downloadable .ipynb from unstable .md notebooks
- Generate .ipynb from .md at build time via jupytext (md:myst format) - Update myst.yml TOC to reference generated .ipynb for unstable section - Add jupytext conversion step to GitHub Actions CI pipeline - Gitignore unstable/*.ipynb (only .md tracked in repo) - Document new pipeline in AGENTS.md Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 3649899 commit bf51897

4 files changed

Lines changed: 52 additions & 13 deletions

File tree

.github/workflows/notebook_to_html.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ jobs:
3434
# Checks out your repository under $GITHUB_WORKSPACE, so your job can access it
3535
- uses: actions/checkout@v4
3636

37+
# Convert unstable/*.md → unstable/*.ipynb so the site provides "Download notebook" buttons
38+
# https://jupytext.readthedocs.io/ — supports md:myst format natively
39+
# Must run BEFORE build so myst/jupyter-book can pick up the generated notebooks
40+
- name: Generate downloadable notebooks
41+
run: |
42+
pip install jupytext
43+
for f in unstable/lesson*_tutorial.md unstable/lesson*_exercise_answers.md; do
44+
[ -f "$f" ] || continue
45+
out="${f%.md}.ipynb"
46+
python -m jupytext --from md:myst --to notebook --output "$out" "$f"
47+
echo "Generated: $out"
48+
done
49+
3750
# Runs a set of commands using the runner's shell
3851
# https://mystmd.org/guide/deployment-github-pages#fn-except-custom-domains
3952
- name: Install dependencies and build page

AGENTS.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ This script:
9696
- **`%%capture` magic IS supported** in MyST text notebooks. MyST uses a Jupyter Server with an IPython kernel to execute code cells ([Execute Notebooks at Build Time](https://mystmd.org/guide/execute-notebooks)). The `%%capture` magic is a built-in IPython cell magic ([Built-in magic commands — IPython](https://ipython.readthedocs.io/en/stable/interactive/magics.html)) and works correctly during MyST execution.
9797
- If output suppression is needed without `%%capture`, the MyST-native approach is to use cell tags like `remove-stdout` and `remove-stderr` on the `{code-cell}` directive.
9898

99+
### Downloadable `.ipynb` from `.md` notebooks
100+
101+
The `unstable/` `.md` files are the canonical source. `.ipynb` files are generated at build time so visitors can download them:
102+
103+
1. **CI pipeline** (`.github/workflows/notebook_to_html.yml`) runs `jupytext --from md:myst --to notebook` before the MyST build, converting each `unstable/*.md``unstable/*.ipynb`.
104+
2. **`myst.yml` TOC** references the generated `.ipynb` for the unstable section — MyST renders these identically to the `.md` but provides native "Download notebook" buttons.
105+
3. **`unstable/.gitignore`** excludes `*.ipynb` so only `.md` is tracked in git.
106+
107+
To generate locally (e.g. for testing):
108+
```bash
109+
pip install jupytext
110+
for f in unstable/lesson*_tutorial.md unstable/lesson*_exercise_answers.md; do
111+
python -m jupytext --from md:myst --to notebook --output "${f%.md}.ipynb" "$f"
112+
done
113+
```
114+
99115
---
100116

101117
## Building & Testing
@@ -108,7 +124,7 @@ python3 -m venv venv
108124
source venv/bin/activate
109125

110126
# For MyST text notebook builds:
111-
pip install mystmd jupyter-server ipykernel
127+
pip install mystmd jupyter-server ipykernel jupytext
112128

113129
# For legacy jupyter-book builds:
114130
pip install jupyter-book --pre
@@ -118,6 +134,13 @@ pip install jupyter-book --pre
118134

119135
**MyST build (root — includes all lessons + unstable):**
120136
```bash
137+
# Step 1: Generate .ipynb from .md (required for unstable section)
138+
pip install jupytext
139+
for f in unstable/lesson*_tutorial.md unstable/lesson*_exercise_answers.md; do
140+
python -m jupytext --from md:myst --to notebook --output "${f%.md}.ipynb" "$f"
141+
done
142+
143+
# Step 2: Build the site
121144
myst build --html
122145
```
123146
- `--execute` runs all code cells and caches results in `_build/execute/`

myst.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,17 @@ project:
4949
- title: "Unstable"
5050
children:
5151
- file: unstable/README.md
52-
- file: unstable/lesson0_tutorial.md
53-
- file: unstable/lesson1_tutorial.md
54-
- file: unstable/lesson2_tutorial.md
55-
- file: unstable/lesson3_tutorial.md
56-
- file: unstable/lesson4_tutorial.md
57-
- file: unstable/lesson5_tutorial.md
58-
- file: unstable/lesson1_exercise_answers.md
59-
- file: unstable/lesson2_exercise_answers.md
60-
- file: unstable/lesson3_exercise_answers.md
61-
- file: unstable/lesson4_exercise_answers.md
62-
- file: unstable/lesson5_exercise_answers.md
52+
- file: unstable/lesson0_tutorial.ipynb
53+
- file: unstable/lesson1_tutorial.ipynb
54+
- file: unstable/lesson2_tutorial.ipynb
55+
- file: unstable/lesson3_tutorial.ipynb
56+
- file: unstable/lesson4_tutorial.ipynb
57+
- file: unstable/lesson5_tutorial.ipynb
58+
- file: unstable/lesson1_exercise_answers.ipynb
59+
- file: unstable/lesson2_exercise_answers.ipynb
60+
- file: unstable/lesson3_exercise_answers.ipynb
61+
- file: unstable/lesson4_exercise_answers.ipynb
62+
- file: unstable/lesson5_exercise_answers.ipynb
6363

6464
site:
6565
template: book-theme

unstable/.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
# MyST build artifacts
2-
_build/
2+
_build/
3+
4+
# Generated .ipynb files (converted from .md at build time via jupytext)
5+
*.ipynb

0 commit comments

Comments
 (0)