Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ee9413b
add initial mystmd setup
kp992 May 13, 2025
6552217
Remove div block headers
kp992 May 16, 2025
10b32d5
remove doc derivates
kp992 May 16, 2025
01eef6b
fix tags errors
kp992 May 16, 2025
c501899
Add myst CI
kp992 May 18, 2025
fd99ff1
use netlify for preview
kp992 May 18, 2025
e4bbe73
fix CI
kp992 May 18, 2025
61721ca
fix path
kp992 May 18, 2025
4761e44
fix scale with width
kp992 May 20, 2025
15c6a8b
update CI
kp992 May 20, 2025
601f6b1
update main CI and test it with pull request preview
kp992 May 23, 2025
ff3cd3a
remove myst ci file
kp992 May 23, 2025
debd62a
remove all index derivatives
kp992 May 26, 2025
ac58b7f
fix code reference
kp992 Jun 6, 2025
522cea2
fix code linking reference issues
kp992 Jun 6, 2025
0793a74
update theme template
kp992 Jun 13, 2025
7b0eaf3
use jupyter book 2.0 for building
kp992 Jun 20, 2025
0844963
Merge 'main' into myst_build
kp992 Jun 20, 2025
f5af297
check notebook download
kp992 Jul 6, 2025
8b29f5e
try other way of installing
kp992 Jul 6, 2025
e85f807
use npm link
kp992 Jul 6, 2025
0df0b62
add download buttons for notebook and md file
kp992 Jul 6, 2025
34538aa
try building ipynb and html separately
kp992 Jul 6, 2025
f704c4b
use jupyter 2.0
kp992 Jul 23, 2025
94244ff
feat: add CommonMark ipynb export and CI workflow
mmcky Feb 25, 2026
ecae752
ci: trigger build
mmcky Feb 25, 2026
3720552
ci: add push and workflow_dispatch triggers for ipynb workflow
mmcky Feb 25, 2026
088e2d5
ci: use npm install instead of npm ci for mystmd build
mmcky Feb 25, 2026
109804f
feat: enable image attachment embedding for all lecture exports
mmcky Feb 25, 2026
e07c2a0
merge: bring in latest sources from main
mmcky Feb 26, 2026
8c51f78
feat: add ipynb export config for numpy_vs_numba_vs_jax and update my…
mmcky Feb 26, 2026
987f72e
trigger rebuild: test latest mystmd improvements
mmcky Feb 26, 2026
9fd57e1
trigger rebuild: test latest mystmd improvements
mmcky Feb 26, 2026
420f847
trigger rebuild: test mystmd gated directive fix
mmcky Feb 26, 2026
1682fdf
trigger rebuild: test updated mystmd gated directive fix
mmcky Feb 26, 2026
43ebcf5
trigger rebuild: test epigraph fix in mystmd
mmcky Feb 26, 2026
f301be5
ci: enable MYST_DEBUG_XREF=1 and upload xref debug log for #8
mmcky Feb 27, 2026
39d5099
trigger rebuild: test updated mystmd xref fix
mmcky Feb 27, 2026
84cf8aa
fix: join multi-line {ref} role in jax_intro to single line
mmcky Feb 27, 2026
8271cce
ci: remove xref debug logging from workflow
mmcky Feb 27, 2026
c6779ad
trigger rebuild: validate latest mystmd changes
mmcky Feb 27, 2026
2fb0389
ci: update build-ipynb to use QuantEcon/mystmd quantecon branch via bun
mmcky May 7, 2026
6f5dbee
ci: install QuantEcon/mystmd quantecon branch before jupyter book build
mmcky May 7, 2026
68e4fb8
Merge branch 'main' into jb2
mmcky May 7, 2026
c474a0b
ci: install QuantEcon/mystmd from main and log build identifier
mmcky May 14, 2026
994262c
Merge remote-tracking branch 'origin/main' into jb2
mmcky May 14, 2026
c8c47eb
Merge branch 'main' into jb2
mmcky Jul 31, 2026
3d092f3
Point the myst build at the QuantEcon theme release and refresh CI to…
mmcky Jul 31, 2026
4314bc1
Drop execution artifacts that were committed on this branch
mmcky Jul 31, 2026
b9cc649
Build the HTML preview with the fork's myst CLI, not jupyter-book's
mmcky Jul 31, 2026
9a78294
FIX: update GitHub pull requests doc link to final destination (#590)
mmcky Aug 3, 2026
f8a3e85
Merge remote-tracking branch 'origin/main' into jb2-update
mmcky Aug 3, 2026
8af1015
CI: instrument the cold-cache execution failure, pin the mystmd engine
mmcky Aug 3, 2026
94a7e52
CI: disable JAX GPU preallocation for concurrent notebook execution
mmcky Aug 3, 2026
f541ccc
CI: make the execution cache engine-aware, pin JAX
mmcky Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/build-ipynb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Build ipynb [using QuantEcon/mystmd]
on:
push:
branches: [jb2]
pull_request:
workflow_dispatch:
jobs:
build-ipynb:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

# Node 24 matches the theme's .nvmrc (quantecon-theme.mystmd), which runs
# as a Remix server during `myst build --html`.
- uses: actions/setup-node@v7
with:
node-version: '24'

- uses: oven-sh/setup-bun@v2

- name: Install Python
uses: actions/setup-python@v7
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: 'myst_requirements.txt'

- name: Install execution requirements
run: python -m pip install -r myst_requirements.txt

- name: Install mystmd (QuantEcon fork)
run: |
git clone --branch main --depth 1 \
https://github.com/QuantEcon/mystmd.git /tmp/qe-mystmd
cd /tmp/qe-mystmd
bun install
bun run build
npm install -g /tmp/qe-mystmd/packages/mystmd

- name: Log QuantEcon/mystmd build identifier
run: cat /tmp/qe-mystmd/quantecon/VERSION.yml

- name: Verify mystmd version
run: myst --version

- name: Build HTML with custom mystmd
working-directory: ./lectures
run: myst build --html

- name: Build ipynb exports with custom mystmd
working-directory: ./lectures
run: myst build --ipynb

- name: Audit notebooks for MyST syntax leaks
working-directory: ./lectures
run: |
python3 -c "
import json, glob, re, sys
PATTERNS = [
(r'^\s*:::', 'directive fence'),
(r'\{math\}\`', 'math role'),
(r'^\+\+\+', 'block marker'),
(r'\{doc\}\`', 'doc role'),
(r'\{index\}', 'index directive'),
(r'\`\`\`\{', 'fenced directive'),
(r'^\([a-z_][a-z0-9_-]*\)=\$', 'target label'),
]
issues = 0
for nb_path in sorted(glob.glob('exports/*.ipynb')):
with open(nb_path) as f:
nb = json.load(f)
for i, cell in enumerate(nb.get('cells', []), 1):
if cell.get('cell_type') != 'markdown':
continue
source = ''.join(cell.get('source', []))
for line in source.split('\n'):
for pat, desc in PATTERNS:
if re.search(pat, line.strip()):
print(f'{nb_path} cell {i}: [{desc}] {line.strip()[:80]}')
issues += 1
if issues:
print(f'\n{issues} MyST syntax leaks found!')
sys.exit(1)
else:
print(f'All {len(glob.glob(\"exports/*.ipynb\"))} notebooks clean.')
"

- name: Upload ipynb exports
uses: actions/upload-artifact@v7
with:
name: ipynb-exports
path: './lectures/exports/'


199 changes: 125 additions & 74 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,96 +1,147 @@
name: Build Project [using jupyter-book]
name: Build Project [using QuantEcon/mystmd]
on: [pull_request]
jobs:
preview:
runs-on: "runs-on=${{ github.run_id }}/family=g4dn.2xlarge/image=quantecon_ubuntu2404/volume=80gb/spot=false"
steps:
- uses: actions/checkout@v7

# The key hashes everything cached execution outputs depend on: the
# lectures themselves, the Python requirements, and this workflow file
# (which pins the mystmd engine SHA, the JAX version and the XLA env).
# Hashing only the lectures is how the b9cc649 engine swap shipped
# green without ever executing a notebook: the cache replayed outputs
# from the old engine. Any engine/env/requirements change now forces a
# cold build automatically.
- name: Cache Notebook Execution
uses: actions/cache@v6
id: cache-execution
with:
fetch-depth: 0 # Fetch full git history for changelog feature
- name: Setup Anaconda
uses: conda-incubator/setup-miniconda@v4
path: ./lectures/_build/execute
key: ${{ runner.os }}-execute-cache-${{ hashFiles('lectures/**/*.md', 'myst_requirements.txt', '.github/workflows/ci.yml') }}

# Node 24 matches the theme's .nvmrc (quantecon-theme.mystmd), which runs
# as a Remix server during `myst build --html`.
- uses: actions/setup-node@v7
with:
auto-update-conda: true
auto-activate-base: true
miniconda-version: 'latest'
python-version: "3.13"
environment-file: environment.yml
activate-environment: quantecon
- name: Check nvidia Drivers
shell: bash -l {0}
run: nvidia-smi
- name: Install JAX and Numpyro
shell: bash -l {0}
node-version: '24'

- uses: oven-sh/setup-bun@v2

- name: Install Python
if: steps.cache-execution.outputs.cache-hit != 'true'
uses: actions/setup-python@v7
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: 'myst_requirements.txt'

- name: Install execution requirements
if: steps.cache-execution.outputs.cache-hit != 'true'
run: python -m pip install -r myst_requirements.txt

# The GPU lectures (jax_intro, autodiff, numpy_vs_numba_vs_jax) need JAX
# on the g4dn runner. Without it those notebooks execute to
# ModuleNotFoundError cells. Pinned to the version validated on
# 2026-08-03; unpinned, the executed environment drifts between runs of
# the same commit. Bumping it here busts the execution cache (the key
# hashes this file), so the bump is validated by a cold build.
- name: Install JAX
if: steps.cache-execution.outputs.cache-hit != 'true'
run: |
pip install -U "jax[cuda13]"
pip install numpyro
pip install "jax[cuda13]==0.11.0"
python scripts/test-jax-install.py
- name: Install latex dependencies
run: |
sudo apt-get -qq update
sudo apt-get install -y \
texlive-latex-recommended \
texlive-latex-extra \
texlive-fonts-recommended \
texlive-fonts-extra \
texlive-xetex \
latexmk \
xindy \
dvipng \
cm-super
- name: Display Conda Environment Versions
shell: bash -l {0}
run: conda list
- name: Display Pip Versions
shell: bash -l {0}
run: pip list
- name: Download "build" folder (cache)
uses: dawidd6/action-download-artifact@v21
with:
workflow: cache.yml
branch: main
name: build-cache
path: _build
# Build Assets (Download Notebooks and PDF via LaTeX)
- name: Build Download Notebooks (sphinx-tojupyter)
shell: bash -l {0}

# Pinned to the exact SHA of the fork's main (unmoved since 2026-06-12,
# so this is the same engine the 2026-08-03 cold-run failures used).
# A branch name here makes the engine a moving target between runs of
# the same lecture commit, which makes failures impossible to bisect.
- name: Install mystmd (QuantEcon fork)
run: |
# This is the FIRST `jb build` in the workflow, and `execute_notebooks: "cache"`
# means only the first build actually executes notebooks — the later HTML build
# reads the cache. So this is the step where a CellExecutionError surfaces, and
# therefore the step that has to gate.
#
# `set -eo pipefail` is required as well as `-W`: `shell: bash -l {0}` is a
# custom shell spec, so GitHub does not inject `-eo pipefail` (it only does
# that for the bare `shell: bash` shorthand). Without it a failing `jb build`
# would be masked by the trailing mkdir/cp, whose exit code becomes the
# step's — and `--keep-going` guarantees the .ipynb files exist for `cp`
# to succeed on.
set -eo pipefail
jb build lectures --path-output ./ --builder=custom --custom-builder=jupyter -n -W --keep-going
mkdir -p _build/html/_notebooks
cp -u _build/jupyter/*.ipynb _build/html/_notebooks
- name: Build PDF from LaTeX
shell: bash -l {0}
mkdir -p /tmp/qe-mystmd
cd /tmp/qe-mystmd
git init -q
git remote add origin https://github.com/QuantEcon/mystmd.git
git fetch -q --depth 1 origin 96ee78f550ff2e0410730f328352e98d3e29d338
git checkout -q FETCH_HEAD
git rev-parse HEAD
bun install
bun run build
npm install -g /tmp/qe-mystmd/packages/mystmd

- name: Log QuantEcon/mystmd build identifier
run: cat /tmp/qe-mystmd/quantecon/VERSION.yml

- name: Verify mystmd version
run: myst --version

# Standing telemetry: sample host RAM, top-RSS processes and GPU memory
# every 5s for the rest of the job. The nohup'd loop survives step
# boundaries; the runner reaps it at job end. Read back in the always()
# step after the build. This is what diagnosed the 2026-08-03 jax_intro
# kernel death (GPU preallocation race — see the Build HTML env below);
# it costs nothing, so it stays for the next one.
- name: Start memory monitor
run: |
jb build lectures --builder pdflatex --path-output ./ -n -W --keep-going
mkdir _build/html/_pdf
cp -u _build/latex/*.pdf _build/html/_pdf
# Final Build of HTML
nohup bash -c 'while true; do
echo "=== $(date -u +%H:%M:%S)"
free -m | sed -n "2,3p"
ps -eo pid,rss,etime,comm --sort=-rss | head -8
nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader 2>/dev/null
sleep 5
done' >/tmp/memmon.log 2>&1 &
echo "monitor started"

# Built with the fork's `myst` CLI rather than `jupyter book build`.
# jupyter-book 2.x is a Python shim that execs a mystmd bundle vendored
# inside its own wheel (jupyter_book/dist/jupyter-book.cjs) and offers
# no supported way to point it at another CLI — so `jupyter book build`
# silently ignored the fork we install above and built with upstream
# mystmd (visible as 23 `unknown export output extension: *.ipynb`
# errors, since ipynb export is a fork-only capability).
- name: Build HTML
shell: bash -l {0}
working-directory: ./lectures
env:
# Three JAX lectures (jax_intro, autodiff, numpy_vs_numba_vs_jax)
# execute concurrently on the g4dn's single T4. By default the first
# JAX process to touch the GPU preallocates 75% of it (11.3GiB of
# 15.3GiB); the 2026-08-03 diagnostics showed jax_intro's kernel then
# clamped to the ~3.4GiB remainder and aborted with no Python
# traceback once a later cell outgrew that pool (dmesg had no
# OOM-killer or segfault records — an XLA fatal, not a host kill).
# On-demand allocation lets the concurrent kernels share the GPU.
XLA_PYTHON_CLIENT_PREALLOCATE: 'false'
run: myst build --html --execute

# Reads the telemetry back. dmesg covers the whole job because runs-on
# boots a fresh instance per run — an OOM-killed process appears there
# with its RSS, so this cleanly separates host OOM kills from in-process
# aborts (the 2026-08-03 death was the latter: dmesg was clean while the
# GPU sampler showed the preallocation collision). Start from this
# output for any future kernel death.
- name: Execution diagnostics
if: always()
run: |
jb build lectures --path-output ./ -n -W --keep-going
- name: Upload Execution Reports
echo "::group::memory timeline (5s samples)"
cat /tmp/memmon.log || true
echo "::endgroup::"
echo "::group::kernel log (OOM killer / segfaults)"
(sudo dmesg -T || dmesg -T) 2>/dev/null | grep -iE "out of memory|oom|killed process|segfault" || echo "no OOM/segfault records"
echo "::endgroup::"
echo "::group::final state"
free -m
nvidia-smi 2>/dev/null || true
echo "::endgroup::"

- name: Upload build output
uses: actions/upload-artifact@v7
if: failure()
with:
name: execution-reports
path: _build/html/reports
path: './lectures/_build/html'

- name: Preview Deploy to Netlify
uses: nwtgck/actions-netlify@v4
with:
publish-dir: '_build/html/'
publish-dir: './lectures/_build/html'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Preview Deploy from GitHub Actions"
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ lectures/sine_wave.py
lectures/mathfoo.py
lectures/mod.py
lectures/test.py
lectures/exports/
lectures/foo.py
lectures/*.txt
lectures/*.csv
lectures/*.csv
Binary file added lectures/_static/ccbysa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading