From 50c7833222cdd14ca800c354452ed5ba65527ccf Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 30 Jul 2026 14:31:45 +1000 Subject: [PATCH 1/2] CI: gate notebook execution errors on the sphinx-tojupyter step The "Build Download Notebooks (sphinx-tojupyter)" step is the one that actually executes the notebooks, and in this repo it could not fail: - it carried no `-n -W`, so a CellExecutionError was a non-fatal warning - its exit code was `cp`'s, not `jb build`'s The second point is the less obvious one. The step runs three commands under `shell: bash -l {0}`. GitHub only injects `-eo pipefail` for the bare `shell: bash` shorthand; an explicit custom shell spec gets neither `-e` nor `-o pipefail`. So a failing `jb build` was followed by `mkdir` and `cp`, and the step exited with `cp`'s status. `--keep-going` makes that worse rather than better, since it forces Sphinx to emit output despite the errors, guaranteeing the .ipynb files exist for `cp` to succeed on. Adding the flags alone would therefore not have gated anything. Both changes are needed together. This matches the step already merged in lecture-python.zh-cn. Checked before making the change: the repo has no `raises-exception` tags and no intentionally-erroring cells, and all 47 published lecture pages render without error output, so this should not turn CI red on existing content. Refs QuantEcon/meta#340 Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7f5b27a..97be8470 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,13 @@ jobs: - name: Build Download Notebooks (sphinx-tojupyter) shell: bash -l {0} run: | - jb build lectures --path-output ./ --builder=custom --custom-builder=jupyter + # `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 this, 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 -n -W --keep-going --path-output ./ --builder=custom --custom-builder=jupyter mkdir -p _build/html/_notebooks cp -u _build/jupyter/*.ipynb _build/html/_notebooks # Build HTML (Website) From a7d98f308ac5b634cf8a245489d0c82538d0d7c2 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 30 Jul 2026 14:38:46 +1000 Subject: [PATCH 2/2] CI: move the gate to the first jb build, where execution happens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrects the previous commit, which gated the wrong step. `lectures/_config.yml` sets `execute_notebooks: "cache"`, so only the FIRST `jb build` in the workflow executes notebooks — every later builder reads the cache. In this repo the first build is "Build PDF from LaTeX", not the sphinx-tojupyter step. Gating tojupyter therefore caught nothing that matters: by the time it runs, execution has already happened. The LaTeX step was the real hole. It had `-n --keep-going` with no `-W`, so a CellExecutionError was a non-fatal warning, and it carries the same trailing-cp exit-code masking. Both are fixed here; the tojupyter step is reverted to its original form, so there is exactly one gate and it sits on the step that executes. Note for anyone porting this: the equivalent change in lecture-python.zh-cn sits on the tojupyter step, and that is correct there — its LaTeX step is commented out, making tojupyter the first jb build. The rule is "gate the first jb build", not "gate a particular step name". Refs QuantEcon/meta#340 Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97be8470..466ee0cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,18 @@ jobs: - name: Build PDF from LaTeX shell: bash -l {0} run: | - jb build lectures --builder pdflatex --path-output ./ -n --keep-going + # This is the FIRST `jb build` in the workflow, and `execute_notebooks: "cache"` + # means only the first build actually executes notebooks — the later + # tojupyter and HTML builds read 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 PDF exists for `cp` to succeed on. + set -eo pipefail + jb build lectures --builder pdflatex --path-output ./ -n -W --keep-going mkdir -p _build/html/_pdf cp -u _build/latex/*.pdf _build/html/_pdf - name: Upload Execution Reports (LaTeX) @@ -62,13 +73,7 @@ jobs: - name: Build Download Notebooks (sphinx-tojupyter) shell: bash -l {0} run: | - # `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 this, 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 -n -W --keep-going --path-output ./ --builder=custom --custom-builder=jupyter + jb build lectures --path-output ./ --builder=custom --custom-builder=jupyter mkdir -p _build/html/_notebooks cp -u _build/jupyter/*.ipynb _build/html/_notebooks # Build HTML (Website)