Skip to content

Commit 94244ff

Browse files
committed
feat: add CommonMark ipynb export and CI workflow
- Add `markdown: commonmark` to all 24 lecture export configs so ipynb exports produce plain CommonMark markdown cells compatible with vanilla Jupyter Notebook, JupyterLab, and Google Colab - Add build-ipynb.yml workflow that clones QuantEcon/mystmd@myst-to-ipynb, builds from source, exports all ipynb files, and audits for MyST leaks - All 24 notebooks pass audit (0 MyST syntax leaks) Uses QuantEcon/mystmd myst-to-ipynb branch which adds: - CommonMark AST pre-transform (admonitions, math, figures, exercises, etc.) - Identifier/label stripping to prevent (label)= prefixes - Image directive stripping for plain ![alt](url) output - Empty cell filtering and block marker removal
1 parent f704c4b commit 94244ff

25 files changed

Lines changed: 103 additions & 0 deletions

.github/workflows/build-ipynb.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Build ipynb [using QuantEcon/mystmd]
2+
on: [pull_request]
3+
jobs:
4+
build-ipynb:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
9+
- uses: actions/setup-node@v4
10+
with:
11+
node-version: 22.x
12+
13+
- name: Install Python
14+
uses: actions/setup-python@v5
15+
with:
16+
python-version: '3.12'
17+
cache: 'pip'
18+
cache-dependency-path: 'myst_requirements.txt'
19+
20+
- name: Install execution requirements
21+
run: python -m pip install -r myst_requirements.txt
22+
23+
- name: Clone QuantEcon/mystmd (myst-to-ipynb branch)
24+
run: |
25+
git clone --depth 1 --branch myst-to-ipynb https://github.com/QuantEcon/mystmd.git /tmp/mystmd
26+
27+
- name: Build mystmd from source
28+
working-directory: /tmp/mystmd
29+
run: |
30+
npm ci
31+
npx turbo run build
32+
33+
- name: Build HTML with custom mystmd
34+
working-directory: ./lectures
35+
run: /tmp/mystmd/packages/mystmd/dist/myst.cjs build --html
36+
37+
- name: Build ipynb exports with custom mystmd
38+
working-directory: ./lectures
39+
run: /tmp/mystmd/packages/mystmd/dist/myst.cjs build --ipynb
40+
41+
- name: Audit notebooks for MyST syntax leaks
42+
working-directory: ./lectures
43+
run: |
44+
python3 -c "
45+
import json, glob, re, sys
46+
PATTERNS = [
47+
(r'^\s*:::', 'directive fence'),
48+
(r'\{math\}\`', 'math role'),
49+
(r'^\+\+\+', 'block marker'),
50+
(r'\{doc\}\`', 'doc role'),
51+
(r'\{index\}', 'index directive'),
52+
(r'\`\`\`\{', 'fenced directive'),
53+
(r'^\([a-z_][a-z0-9_-]*\)=\$', 'target label'),
54+
]
55+
issues = 0
56+
for nb_path in sorted(glob.glob('exports/*.ipynb')):
57+
with open(nb_path) as f:
58+
nb = json.load(f)
59+
for i, cell in enumerate(nb.get('cells', []), 1):
60+
if cell.get('cell_type') != 'markdown':
61+
continue
62+
source = ''.join(cell.get('source', []))
63+
for line in source.split('\n'):
64+
for pat, desc in PATTERNS:
65+
if re.search(pat, line.strip()):
66+
print(f'{nb_path} cell {i}: [{desc}] {line.strip()[:80]}')
67+
issues += 1
68+
if issues:
69+
print(f'\n{issues} MyST syntax leaks found!')
70+
sys.exit(1)
71+
else:
72+
print(f'All {len(glob.glob(\"exports/*.ipynb\"))} notebooks clean.')
73+
"
74+
75+
- name: Upload ipynb exports
76+
uses: actions/upload-artifact@v4
77+
with:
78+
name: ipynb-exports
79+
path: './lectures/exports/'

lectures/about_py.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/about_py.ipynb
1314
downloads:
1415
- file: ./about_py.md

lectures/debugging.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/debugging.ipynb
1314
downloads:
1415
- file: ./debugging.md

lectures/functions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/functions.ipynb
1314
downloads:
1415
- file: ./functions.md

lectures/getting_started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/getting_started.ipynb
1314
downloads:
1415
- file: ./getting_started.md

lectures/intro.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/intro.ipynb
1314
downloads:
1415
- file: ./intro.md

lectures/jax_intro.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ kernelspec:
1111
name: python3
1212
exports:
1313
- format: ipynb
14+
markdown: commonmark
1415
output: exports/jax_intro.ipynb
1516
downloads:
1617
- file: ./jax_intro.md

lectures/matplotlib.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/matplotlib.ipynb
1314
downloads:
1415
- file: ./matplotlib.md

lectures/names.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/names.ipynb
1314
downloads:
1415
- file: ./names.md

lectures/need_for_speed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelspec:
99
name: python3
1010
exports:
1111
- format: ipynb
12+
markdown: commonmark
1213
output: exports/need_for_speed.ipynb
1314
downloads:
1415
- file: ./need_for_speed.md

0 commit comments

Comments
 (0)