Skip to content

ci(run-notebook): replace whole-cell bash partition with nbclient#573

Merged
jhamon merged 2 commits into
mainfrom
replace-ci-runner-with-nbclient
May 13, 2026
Merged

ci(run-notebook): replace whole-cell bash partition with nbclient#573
jhamon merged 2 commits into
mainfrom
replace-ci-runner-with-nbclient

Conversation

@jhamon
Copy link
Copy Markdown
Collaborator

@jhamon jhamon commented May 12, 2026

Purpose

The current run-notebook action partitions each code cell whole-cell: cells containing !pip go entirely to a bash script, the rest go to a Python script. A cell that mixes !pip install with Python imports — a standard Jupyter pattern — fails with import: command not found because bash tries to execute Python as a shell command. See PR #572 / run 25743389812 for a live example.

Solution

Drive notebooks through nbclient, the same tool Project Jupyter ships behind jupyter nbconvert --execute. nbclient launches a real ipykernel and processes cells line-by-line: !pip and other shell magics route through the kernel's magic handlers (subshell), Python runs as Python. Mixed cells work exactly as they do in Colab and Jupyter Lab.

Changes

  • New run-notebook.py: 50-line nbclient driver. Takes the notebook path as its only arg. Exits 0 on success, 1 on any cell error, 2 on usage error.
  • Updated action.yml: installs nbformat nbclient ipykernel into the runner Python, then invokes the new script. No tempdir, no nested venv, no bash conversion.
  • Removed convert-notebook.py (~120 lines).

Net diff: 66 insertions, 137 deletions.

Verification

Tested locally against the notebook from PR #572 (docs/quick-tour/hello-pinecone.ipynb, with !pip install and Python imports in the same cell — the case that breaks today's runner):

$ python .github/actions/run-notebook/run-notebook.py docs/quick-tour/hello-pinecone.ipynb
Executing docs/quick-tour/hello-pinecone.ipynb
PASS — docs/quick-tour/hello-pinecone.ipynb

Notes

  • check-structure follow-up. The lint rule "imports must be in the first code cell" was designed around today's runner: under the new runner, a single cell holding !pip install + imports is once again the correct pattern. Either relax the rule to skip pip-only cells when locating "the first code cell," or remove the rule entirely. Separate PR.
  • Notebook dep isolation. Notebook deps now install into the runner's Python (the kernel shares its interpreter), not a nested venv. Acceptable for CI (ephemeral runner) and simpler than the previous tempdir/venv dance.
  • Per-cell timeout. 600s by default, overridable via NOTEBOOK_CELL_TIMEOUT.
  • Soak. Worth running this against a known-passing notebook before merging to confirm no regressions on the existing fleet — let me know if you want me to dual-run on a sample.

Note

Medium Risk
Changes the CI notebook execution mechanism from a custom conversion/venv+bash flow to nbclient driving a real Jupyter kernel, which may alter dependency/runtime behavior and failure modes across the notebook suite.

Overview
Updates the run-notebook composite action to execute notebooks end-to-end using nbclient/ipykernel instead of converting cells into separate bash/python scripts.

This removes convert-notebook.py, installs nbformat nbclient ipykernel on the runner, and adds run-notebook.py which runs the notebook with a real kernel, returning clearer per-cell errors and supporting mixed !pip + Python cells (with an optional NOTEBOOK_CELL_TIMEOUT).

Reviewed by Cursor Bugbot for commit f87187a. Bugbot is set up for automated code reviews on this repo. Configure here.

## Purpose

The previous run-notebook action partitioned each code cell whole-cell:
cells containing !pip went to a bash script, the rest to a Python script.
Cells that mixed !pip install with Python imports — a standard Jupyter
pattern — failed with `import: command not found` because bash tried
to execute Python as a shell command (see PR #572 for an example).

## Solution

Drive notebooks through nbclient, the same tool Project Jupyter ships
behind `jupyter nbconvert --execute`. nbclient launches a real ipykernel
and processes cells line-by-line: !pip and other shell magics route
through the kernel's magic handlers (subshell), Python runs as Python.
Mixed cells work exactly as they do in Colab and Jupyter Lab.

## Notes

- Notebook deps install into the runner's Python (kernel shares the
  runner's interpreter), not a nested venv. Fine for CI (ephemeral
  runner) and simpler than the previous tempdir-venv dance.
- Per-cell timeout is 600s, overridable via NOTEBOOK_CELL_TIMEOUT.
- check-structure's "imports must be in the first code cell" rule may
  now warrant relaxation — under the new runner, a single cell holding
  !pip install + imports is the correct pattern again. Follow-up.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6f37ce1. Configure here.

Comment thread .github/actions/run-notebook/run-notebook.py
`CellTimeoutError` (sibling of `CellExecutionError`, inherits from
`TimeoutError`) and `DeadKernelError` (inherits from `RuntimeError`)
escape an `except CellExecutionError`. A cell hitting NOTEBOOK_CELL_TIMEOUT
or a kernel that OOMs/segfaults would surface a raw traceback instead
of the clean failure path.

Catch both alongside `CellExecutionError`, with distinct messages so the
failure mode is obvious in CI logs. Each path still exits 1.

Reported by Cursor Bugbot on PR #573.
@jhamon jhamon merged commit dab29fc into main May 13, 2026
12 checks passed
@jhamon jhamon deleted the replace-ci-runner-with-nbclient branch May 13, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant