From ca16254e87853c34d74cbd66b1f91d9286f70a47 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Fri, 12 Jun 2026 06:40:25 -0700 Subject: [PATCH 1/4] Add docs-build CI job for PRs touching docs (#3249) --- .github/workflows/docs.yml | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..c13b66dde --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,42 @@ +name: docs +on: + pull_request: + paths: + - 'docs/**' + - 'README.md' + - 'setup.cfg' + - '.readthedocs.yml' + - '.github/workflows/docs.yml' + workflow_dispatch: + +jobs: + # Job id/name kept distinct from the pytest workflow's `run` job: + # duplicate check names across workflows get deduplicated in the PR + # checks UI and can hide failures. + docs-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + # Match the Read the Docs build (.readthedocs.yml). + python-version: '3.12' + - name: Install pandoc + # nbsphinx needs the pandoc binary; the `pandoc` pip package in + # the doc extra does not ship it. + run: sudo apt-get update && sudo apt-get install -y pandoc + - name: Install dependencies + # Same extras as the Read the Docs pre_build step, so this job + # and RTD cannot drift apart. + run: | + python -m pip install --upgrade pip + pip install -e '.[doc,tests]' + - name: Build HTML + # No -W: the build carries a handful of pre-existing warnings in + # notebooks and reference pages. Gate on errors only. + run: sphinx-build -b html docs/source docs/build/html + - name: Link check + # External links flake; report but never fail the job. + continue-on-error: true + run: sphinx-build -b linkcheck docs/source docs/build/linkcheck From 35ce0913454b2fa8d316e7358bb42a7ae96ab257 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Fri, 12 Jun 2026 06:41:45 -0700 Subject: [PATCH 2/4] Address review: cap docs job at 30 minutes (#3249) --- .github/workflows/docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c13b66dde..612290983 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,6 +15,10 @@ jobs: # checks UI and can hide failures. docs-build: runs-on: ubuntu-latest + # Bound a hung linkcheck: continue-on-error keeps it from gating, + # but without a cap a stalled external link holds the runner for + # the default 6-hour job timeout. + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - name: Setup Python From 5f6077ec146c1ff43986805931896cb635490822 Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Fri, 12 Jun 2026 07:17:54 -0700 Subject: [PATCH 3/4] Raise docs job timeout to 45 minutes; runs take ~26 (#3249) --- .github/workflows/docs.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 612290983..0a82ec76a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,8 +17,10 @@ jobs: runs-on: ubuntu-latest # Bound a hung linkcheck: continue-on-error keeps it from gating, # but without a cap a stalled external link holds the runner for - # the default 6-hour job timeout. - timeout-minutes: 30 + # the default 6-hour job timeout. Normal runs take ~26 minutes + # (HTML build ~11, linkcheck ~14), so 45 leaves headroom without + # letting a wedged run camp on a runner. + timeout-minutes: 45 steps: - uses: actions/checkout@v4 - name: Setup Python From b4f153995c179bb694263504405a023ec08e614c Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Fri, 12 Jun 2026 09:37:29 -0700 Subject: [PATCH 4/4] Cut docs-build PR time: parallel sphinx read, linkcheck moved to weekly cron (#3249) The PR job spent ~10 of its 18 minutes on linkcheck, whose failures were masked by continue-on-error, and ~8 on a serial HTML build whose read phase is dominated by plot-directive examples (sample data load plus numba compile per reference page). - build with -j auto; every extension in conf.py declares parallel_read_safe - run linkcheck on a weekly cron and workflow_dispatch instead of on PRs, without continue-on-error so broken links surface - drop the job timeout from 45 to 15 minutes to match --- .github/workflows/docs.yml | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0a82ec76a..adb7f0939 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,19 +8,24 @@ on: - '.readthedocs.yml' - '.github/workflows/docs.yml' workflow_dispatch: + schedule: + # Weekly linkcheck (Monday 06:00 UTC). External links rot on their + # own schedule, not the PR schedule; checking them per-PR cost ~10 + # minutes of runner time and its failures were masked by + # continue-on-error anyway. + - cron: '0 6 * * 1' jobs: # Job id/name kept distinct from the pytest workflow's `run` job: # duplicate check names across workflows get deduplicated in the PR # checks UI and can hide failures. docs-build: + if: github.event_name != 'schedule' runs-on: ubuntu-latest - # Bound a hung linkcheck: continue-on-error keeps it from gating, - # but without a cap a stalled external link holds the runner for - # the default 6-hour job timeout. Normal runs take ~26 minutes - # (HTML build ~11, linkcheck ~14), so 45 leaves headroom without - # letting a wedged run camp on a runner. - timeout-minutes: 45 + # Normal runs take ~4 minutes (install ~1, HTML build ~3 with + # parallel read), so 15 leaves headroom without letting a wedged + # run camp on a runner. + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Setup Python @@ -41,8 +46,32 @@ jobs: - name: Build HTML # No -W: the build carries a handful of pre-existing warnings in # notebooks and reference pages. Gate on errors only. - run: sphinx-build -b html docs/source docs/build/html + # + # -j auto: the read phase is dominated by plot-directive examples + # in the reference docstrings (sample data load + numba compile + # per page, 30-60s each); all extensions in conf.py declare + # parallel_read_safe, so this fans them out across the runner's + # cores. + run: sphinx-build -b html -j auto docs/source docs/build/html + + # Linkcheck runs on the weekly schedule (or manual dispatch), not on + # PRs: it took ~10 minutes per PR and never gated anything. Here it + # is allowed to fail loudly so broken links actually surface. + linkcheck: + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install pandoc + run: sudo apt-get update && sudo apt-get install -y pandoc + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e '.[doc,tests]' - name: Link check - # External links flake; report but never fail the job. - continue-on-error: true run: sphinx-build -b linkcheck docs/source docs/build/linkcheck