Skip to content

Commit 5dc511b

Browse files
CI: create a CI for testing docs updates
1 parent ae23cc6 commit 5dc511b

7 files changed

Lines changed: 170 additions & 1 deletion

File tree

.github/workflows/docs.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Documentation
2+
3+
on:
4+
# Only PRs targeting master (base branch = master) and pushes to master.
5+
# This is the gate: a PR cannot be merged into master without this check.
6+
pull_request:
7+
types: [opened, synchronize, reopened, ready_for_review]
8+
branches: [master]
9+
paths:
10+
- "docs/**"
11+
- "rocketpy/**" # docstrings feed the autodoc API reference
12+
- "pyproject.toml"
13+
- "requirements*"
14+
- ".readthedocs.yaml"
15+
- ".github/workflows/docs.yml"
16+
push:
17+
branches: [master]
18+
paths:
19+
- "docs/**"
20+
- "rocketpy/**"
21+
- "pyproject.toml"
22+
- "requirements*"
23+
- ".readthedocs.yaml"
24+
- ".github/workflows/docs.yml"
25+
26+
defaults:
27+
run:
28+
shell: bash
29+
30+
jobs:
31+
build-docs:
32+
runs-on: ubuntu-latest
33+
strategy:
34+
matrix:
35+
python-version: ["3.12"] # match the Read the Docs build environment
36+
env:
37+
MPLBACKEND: Agg
38+
# Render jupyter-execute cells as static code blocks instead of running
39+
# them. This keeps the check fast and deterministic: no simulations and,
40+
# crucially, no live network calls to external weather/data servers (which
41+
# make the full build slow and flaky). Read the Docs still executes the
42+
# cells to render live outputs; this job only validates the docs structure
43+
# (reStructuredText, cross-references, toctrees, autodoc API reference).
44+
DOCS_SKIP_EXECUTE: "1"
45+
steps:
46+
- uses: actions/checkout@main
47+
48+
- name: Install pandoc (required by nbsphinx)
49+
run: sudo apt-get update && sudo apt-get install -y pandoc
50+
51+
- name: Set up Python ${{ matrix.python-version }}
52+
uses: actions/setup-python@main
53+
with:
54+
python-version: ${{ matrix.python-version }}
55+
cache: "pip"
56+
cache-dependency-path: |
57+
docs/requirements.txt
58+
requirements.txt
59+
60+
- name: Install dependencies (mirrors .readthedocs.yaml)
61+
run: |
62+
python -m pip install --upgrade pip
63+
pip install -r docs/requirements.txt
64+
pip install -r requirements.txt
65+
pip install .[all]
66+
67+
- name: Cache Sphinx doctrees
68+
uses: actions/cache@main
69+
with:
70+
path: docs/_build/doctrees
71+
key: sphinx-doctrees-${{ github.ref }}-${{ hashFiles('docs/**', 'rocketpy/**') }}
72+
restore-keys: |
73+
sphinx-doctrees-${{ github.ref }}-
74+
sphinx-doctrees-
75+
76+
- name: Build docs (warnings-as-errors)
77+
working-directory: docs
78+
run: |
79+
sphinx-build -b html -W --keep-going \
80+
-d _build/doctrees \
81+
-w _build/sphinx-warnings.txt \
82+
. _build/html
83+
84+
- name: Surface Sphinx warnings as annotations
85+
if: always()
86+
run: |
87+
if [ -s docs/_build/sphinx-warnings.txt ]; then
88+
echo "::group::Sphinx warnings"
89+
cat docs/_build/sphinx-warnings.txt
90+
echo "::endgroup::"
91+
while IFS= read -r line; do
92+
echo "::warning::${line}"
93+
done < docs/_build/sphinx-warnings.txt
94+
else
95+
echo "No Sphinx warnings 🎉"
96+
fi
97+
98+
- name: Upload built HTML
99+
if: always()
100+
uses: actions/upload-artifact@main
101+
with:
102+
name: docs-html
103+
path: docs/_build/html
104+
if-no-files-found: error

docs/conf.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@
6767
"allow_errors": True, # Continue building even if cells raise errors
6868
}
6969

70+
# When DOCS_SKIP_EXECUTE=1, ``jupyter-execute`` directives are rendered as
71+
# static (non-executing) code blocks instead of running live code. This makes
72+
# structural doc builds fast and deterministic (no simulations, no network),
73+
# which is what the GitHub Actions docs check relies on. Read the Docs leaves
74+
# this unset, so it still executes the cells and renders their live outputs.
75+
skip_jupyter_execute = os.environ.get("DOCS_SKIP_EXECUTE") == "1"
76+
77+
# Some notebook cells use IPython shell escapes (e.g. ``!pip install rocketpy``)
78+
# which the Pygments Python lexer cannot tokenize. These are purely cosmetic
79+
# syntax-highlighting failures, so don't let them fail a warnings-as-errors build.
80+
suppress_warnings = ["misc.highlighting_failure"]
81+
7082
# Add any paths that contain templates here, relative to this directory.
7183
templates_path = ["_templates"]
7284

@@ -150,3 +162,40 @@
150162
html_file_suffix = ".html"
151163

152164
htmlhelp_basename = "rocketpy"
165+
166+
167+
def setup(app):
168+
"""Sphinx entry point for optional build-time customizations."""
169+
if skip_jupyter_execute:
170+
from docutils.parsers.rst import directives
171+
from sphinx.directives.code import CodeBlock
172+
173+
class StaticJupyterExecute(CodeBlock):
174+
"""Render ``jupyter-execute`` as a non-executing Python code block.
175+
176+
Accepts (and ignores) the ``jupyter-execute`` directive options so
177+
existing pages parse unchanged, but produces a plain highlighted
178+
code block instead of a live-executed cell.
179+
"""
180+
181+
required_arguments = 0
182+
optional_arguments = 1
183+
option_spec = dict(CodeBlock.option_spec)
184+
option_spec.update(
185+
{
186+
"hide-code": directives.flag,
187+
"hide-output": directives.flag,
188+
"code-below": directives.flag,
189+
"stderr": directives.flag,
190+
"raises": directives.unchanged,
191+
}
192+
)
193+
194+
def run(self):
195+
for key in ("hide-code", "hide-output", "code-below", "stderr", "raises"):
196+
self.options.pop(key, None)
197+
if not self.arguments:
198+
self.arguments = ["python3"]
199+
return super().run()
200+
201+
app.add_directive("jupyter-execute", StaticJupyterExecute, override=True)

docs/notebooks/getting_started.ipynb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,9 @@
15711571
}
15721572
],
15731573
"metadata": {
1574+
"nbsphinx": {
1575+
"orphan": true
1576+
},
15741577
"colab": {
15751578
"name": "getting_started.ipynb",
15761579
"provenance": [],

docs/notebooks/getting_started_colab.ipynb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@
813813
}
814814
],
815815
"metadata": {
816+
"nbsphinx": {
817+
"orphan": true
818+
},
816819
"colab": {
817820
"name": "getting_started.ipynb",
818821
"provenance": [],

docs/reference/classes/aero_surfaces/_BaseFin.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
:orphan:
2+
13
_BaseFin Class
24
--------------
35

docs/user/environment/1-atm-models/soundings.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019
2222

2323
Initialize a new Environment instance:
2424

25-
.. jupyter-execute::
25+
.. note::
26+
27+
The example below is shown as static code (it is not executed during the
28+
documentation build) because it requires a live network request to an
29+
external University of Wyoming service. Run it locally to fetch the
30+
sounding and see the resulting atmospheric plots.
31+
32+
.. code-block:: python
2633
2734
from rocketpy import Environment
2835

docs/user/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ RocketPy's User Guide
2424
:caption: Special Case Simulations
2525

2626
Compare Flights Class<compare_flights.rst>
27+
Flight Comparator Class <flight_comparator.rst>
2728
Parachute Triggers (Acceleration-Based) <parachute_triggers.rst>
2829
Deployable Payload <deployable.rst>
2930
Air Brakes Example <airbrakes.rst>

0 commit comments

Comments
 (0)