Repository navigation
200 lines (178 loc) · 8.69 KB
/
Copy pathdocs.yml
File metadata and controls
200 lines (178 loc) · 8.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# Build the Sphinx docs + JupyterLite ("Try it Now") site, and deploy the
# combined output to GitHub Pages when run against main.
#
# Reusable workflow (workflow_call) -- called from ci.yml, gated on the
# test matrix passing, and from docs-publish.yml, a manual workflow_dispatch
# escape hatch for a docs-only change that doesn't need the full suite to
# re-run first. Keeping the build/deploy steps in one place means the two
# trigger paths can never drift out of sync with each other.
name: Docs
on:
workflow_call:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
MPLBACKEND: Agg
QT_QPA_PLATFORM: offscreen
jobs:
docs-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.12"
cache: pip
- name: Check notebook bootstrap cells and outputs are in sync
run: |
python docs/notebooks/sync_bootstrap.py --check
python docs/notebooks/clear_outputs.py --check
- name: Install docs dependencies
run: |
pip install .[docs]
pip install sympy python-flint # for the analytic IK examples
sudo apt-get install -y graphviz
# See the "Cache robot_descriptions assets" step in ci.yml's test-core
# job -- intro.rst's YuMi runblock example loads via robot_descriptions
# too.
- name: Cache robot_descriptions assets
uses: actions/cache@v6
with:
path: ~/.cache/robot_descriptions
key: robot-descriptions-v2-${{ runner.os }}-3.12
- name: Build docs
# TODO: add -W once warning count reaches zero
run: |
cd docs && make SPHINXOPTS="--keep-going" html
touch build/html/.nojekyll
- name: Build pure-Python wheel for JupyterLite
# RTB_PURE_WHEEL=1 (see pyproject.toml's scikit-build-core override)
# skips _fknm_c/_frne_c and forces a py3-none-any tag -- both have
# complete, tested pure-Python fallbacks, so nothing is missing at
# runtime. This used to require fetching a wasm32 wheel
# cross-compiled by release.yml's build_pyodide job (via
# cibuildwheel's Pyodide platform) from the latest GitHub Release,
# since building that wheel here on every docs push would've been
# slow, and PyPI rejected its old-format tag outright. A pure wheel
# needs neither: it's fast enough to build fresh on every push (no
# compilation at all, see build_pyodide's comment in release.yml),
# so "Try it Now" now tracks bleeding-edge main instead of lagging
# behind the last release, and py3-none-any needs no cross-repo
# GitHub Release fetch at all.
run: |
mkdir -p docs/lite/pypi
pip install build
RTB_PURE_WHEEL=1 python3 -m build --wheel --outdir docs/lite/pypi
- uses: actions/setup-node@v7
with:
node-version: "22"
cache: npm
cache-dependency-path: docs/lite/wasm_check/package-lock.json
- name: Check the JupyterLite install path actually works (real Pyodide, real WASM)
# Runs the notebooks' bootstrap in Pyodide under Node against the wheel
# just built, so a dependency that cannot be installed in the browser
# fails here and not on the published site.
run: |
npm --prefix docs/lite/wasm_check ci
node docs/lite/wasm_check/check_pyodide_install.mjs docs/lite/pypi/*.whl
- name: Sync notebooks into JupyterLite content
# Copies *.ipynb plus any local asset directories (e.g.
# docs/notebooks/figs/, referenced by relative path from markdown
# cells) -- but not the Makefile-generated *.py script conversions
# or other loose files. ik-symbolic.ipynb is included: SymPy is one of Pyodide's own packages
# and the derivation takes 1 to 10 s without python-flint (timed in
# docs/lite/wasm_check, which checks it on every build).
run: |
mkdir -p docs/lite/files
rsync -a --exclude '.ipynb_checkpoints' --exclude '__pycache__' \
--exclude 'Untitled*.ipynb' --exclude '*.py' --exclude 'Makefile' --exclude 'README.md' \
docs/notebooks/ docs/lite/files/
- name: Adapt staged notebooks for JupyterLite
run: |
python - <<'PY'
import json
from pathlib import Path
root = Path("docs/lite/files")
for nb_path in root.rglob("*.ipynb"):
with nb_path.open("r", encoding="utf-8") as f:
nb = json.load(f)
meta = nb.setdefault("metadata", {})
meta["kernelspec"] = {
"name": "python",
"display_name": "Python (Pyodide)",
"language": "python",
}
meta.setdefault("language_info", {})
meta["language_info"]["name"] = "python"
with nb_path.open("w", encoding="utf-8") as f:
json.dump(nb, f, indent=1)
f.write("\n")
PY
- name: Build JupyterLite site
# jupyterlite-pyodide-kernel is pinned deliberately -- NOT just for
# wasm-ABI matching with the cp312 wheel fetched above, but because
# newer kernel releases bundle Pyodide >=0.27.7, which turned
# WebAssembly JSPI ("stack switching") on by default for
# run_until_complete(). Browsers without JSPI support (Safari has
# none as of 2026-08; Firefox only behind a flag) either crash the
# kernel outright or, with the enableRunUntilComplete: false
# workaround, turn run_until_complete into a fire-and-forget no-op --
# letting a cell's package/import machinery run before an install has
# actually finished landing (confirmed live on bdsim: intermittent
# ModuleNotFoundError despite a successful-looking install cell).
# jupyterlite-pyodide-kernel==0.6.1 bundles Pyodide 0.27.6, genuinely
# predating this whole mechanism -- matches MVTB's and bdsim's
# existing pins. See the toolbox-maintainer skill's JupyterLite
# version-pinning section for the full story before bumping this.
# Once Safari (and Firefox out-of-flag) ship JSPI, revisit this pin
# across RTB/MVTB/bdsim together.
# ipympl is installed here, in the build environment, so that the
# jupyter-matplotlib extension is bundled into the site: the notebooks
# use %matplotlib ipympl, and without the extension a figure is blank.
# (The notebooks install ipympl itself into the in-browser kernel.)
run: |
pip install jupyterlite-core "jupyterlite-pyodide-kernel==0.6.1" jupyter-server ipympl
cd docs/lite && jupyter lite build --output-dir ../build/html/lite
- name: Check JupyterLite in a real browser (install, widgets, animation)
# Builds a small site with the wheel built above and one notebook, opens it in
# Chromium, and checks that the site installs that wheel (not the last release
# from PyPI), draws an ipywidgets slider and an ipympl figure, and that a
# trajectory animates while its cell is running. See
# docs/lite/browser_check/. Informational (continue-on-error) until it has
# run green on main, then remove that line so that it blocks the deploy.
continue-on-error: true
timeout-minutes: 20
run: |
pip install playwright
playwright install --with-deps chromium
python docs/lite/browser_check/check_lite_in_browser.py --screenshot lite_check_failed.png
- name: Keep the screenshot if the browser check failed
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: lite-browser-check
path: lite_check_failed.png
if-no-files-found: ignore
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/build/html
# Separate job so the github-pages environment (and its deployment
# protection rules restricting it to main) is only ever touched on main —
# a job-level `environment:` triggers a deployment attempt as soon as the
# job starts, regardless of any step-level `if:`, so PR/dependabot runs
# (or a manual docs-publish.yml dispatch from a feature branch) would
# otherwise show a false "failed to deploy to github-pages".
docs-deploy:
needs: docs-build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v5