diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f6875c1..1a70378 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -7,6 +7,10 @@ name: Python Package # Publishing uses PyPI trusted publishing (no token): the PyPI project must # list this repository, this workflow file and the `pypi` environment as a # trusted publisher. +# +# The `colab` job guards the README's Open-in-Colab badge (see below); the +# weekly run catches it breaking without a push here (a new najaeda or +# anywidget, naja's tutorial benchmarks moving). on: push: @@ -14,6 +18,8 @@ on: tags: [ "v*" ] pull_request: branches: [ main ] + schedule: + - cron: "0 6 * * 1" workflow_dispatch: jobs: @@ -122,3 +128,54 @@ jobs: path: dist/ - uses: pypa/gh-action-pypi-publish@release/v1 + + colab: + # What the Open-in-Colab badge gives a user: the notebook on this branch, + # run as Colab runs it -- nothing preinstalled, its own + # `%pip install -U naja-schematic` fetching the release from PyPI. On a + # tag it runs after publishing and must get the version just released; + # otherwise it uses the current release, so a notebook edit that needs + # an unreleased API fails here rather than in Colab. + needs: publish + if: >- + !cancelled() && (needs.publish.result == 'success' || + (needs.publish.result == 'skipped' && !startsWith(github.ref, 'refs/tags/v'))) + runs-on: ubuntu-latest + env: + PIP_NO_CACHE_DIR: "1" + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - uses: actions/setup-python@v5 + with: + # Colab's runtime Python. + python-version: "3.12" + + - name: Install the notebook runner only + run: python -m pip install pytest nbformat nbclient ipykernel + + - name: Pick the release to expect + shell: bash + run: | + if [[ "$GITHUB_REF" == refs/tags/v* ]]; then + version=${GITHUB_REF_NAME#v} + # A fresh upload takes a moment to show up on the index. + for i in $(seq 30); do + pip download -q --no-deps -d "$RUNNER_TEMP/dl" \ + "naja-schematic==$version" && break + [ "$i" = 30 ] && { echo "naja-schematic $version not on PyPI"; exit 1; } + sleep 20 + done + else + version=latest + fi + echo "NAJA_SCHEMATIC_COLAB_CHECK=$version" >> "$GITHUB_ENV" + + - name: Run the Colab notebook against PyPI + # From outside python/ and without conftest.py (which imports + # najaeda): the notebook installs everything itself. + working-directory: ${{ runner.temp }} + run: >- + python -m pytest -v --noconftest + ${{ github.workspace }}/python/tests/test_notebook.py diff --git a/CLAUDE.md b/CLAUDE.md index a1958f3..17e908a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -252,7 +252,11 @@ widget against the released PyPI package. `tests/test_notebook.py` runs it in a real kernel (nbclient) and splices in a cell that plays the viewer against its `view`, so a `Schematic`/protocol change that breaks it fails CI; its install cell skips `pip install` when `naja_schematic` is already -importable, so CI tests the fresh wheel, not PyPI's. +importable, so CI tests the fresh wheel, not PyPI's. The workflow's +`colab` job runs the same test the other way (`NAJA_SCHEMATIC_COLAB_CHECK`): +the branch's notebook installing from PyPI as Colab does -- the current +release on PRs/main/weekly, the just-published one after a tag -- so a +notebook edit needing an unreleased API, or a release that breaks it, fails CI. ## Architecture diff --git a/README.md b/README.md index dd23b84..e4415a8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # naja-schematic -[![Native macOS Build](https://github.com/najaeda/naja-schematic/actions/workflows/native-macos.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/native-macos.yml) -[![Native Linux Build](https://github.com/najaeda/naja-schematic/actions/workflows/native-linux.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/native-linux.yml) -[![Emscripten Build](https://github.com/najaeda/naja-schematic/actions/workflows/emscripten.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/emscripten.yml) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/najaeda/naja-schematic/blob/main/python/notebooks/colab_test.ipynb) +[![Native macOS Build](https://github.com/keplertech/naja-schematic/actions/workflows/native-macos.yml/badge.svg?branch=main)](https://github.com/keplertech/naja-schematic/actions/workflows/native-macos.yml) +[![Native Linux Build](https://github.com/keplertech/naja-schematic/actions/workflows/native-linux.yml/badge.svg?branch=main)](https://github.com/keplertech/naja-schematic/actions/workflows/native-linux.yml) +[![Emscripten Build](https://github.com/keplertech/naja-schematic/actions/workflows/emscripten.yml/badge.svg?branch=main)](https://github.com/keplertech/naja-schematic/actions/workflows/emscripten.yml) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/keplertech/naja-schematic/blob/main/python/notebooks/colab_test.ipynb) > **Note:** This is a proof of concept in beta. Expect rough edges, breaking @@ -32,7 +32,7 @@ native desktop app and a WASM app that runs in a browser or a VSCode webview. Clone with submodules: ```bash -git clone --recursive https://github.com/najaeda/naja-schematic.git +git clone --recursive https://github.com/keplertech/naja-schematic.git # or, if already cloned without them: git submodule update --init --recursive ``` @@ -134,7 +134,7 @@ RTL-diagnosis workflow. Interfaces (in particular the wire protocol and the ## Reporting Issues This project is beta — if something breaks, crashes, or looks wrong, please -[file a GitHub issue](https://github.com/najaeda/naja-schematic/issues/new). +[file a GitHub issue](https://github.com/keplertech/naja-schematic/issues/new). Before reporting, check for error output in the relevant places: - **Native build**: the terminal you launched `naja-schematic-standalone` diff --git a/REUSE.toml b/REUSE.toml index 5122082..982d676 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -1,7 +1,7 @@ version = 1 SPDX-PackageName = "naja-schematic" SPDX-PackageSupplier = "najaeda" -SPDX-PackageDownloadLocation = "https://github.com/najaeda/naja-schematic" +SPDX-PackageDownloadLocation = "https://github.com/keplertech/naja-schematic" # Project's own sources, build config, docs, and scripts. # Listed explicitly (rather than "**") so the three exceptions below diff --git a/python/README.md b/python/README.md index 240926e..3e84ae6 100644 --- a/python/README.md +++ b/python/README.md @@ -1,11 +1,11 @@ # naja-schematic (Python package) Interactive schematic viewer for [najaeda](https://pypi.org/project/najaeda/) -netlists: the [naja-schematic](https://github.com/najaeda/naja-schematic) +netlists: the [naja-schematic](https://github.com/keplertech/naja-schematic) WASM viewer, packaged with the Python backend that answers it from a live najaeda netlist. -![naja-schematic demo: tracing an output port back to its drivers, then extending the schematic pin by pin](https://raw.githubusercontent.com/najaeda/naja-schematic/main/docs/demo.gif) +![naja-schematic demo: tracing an output port back to its drivers, then extending the schematic pin by pin](https://raw.githubusercontent.com/keplertech/naja-schematic/main/docs/demo.gif) ```bash pip install naja-schematic @@ -13,7 +13,7 @@ pip install naja-schematic ## In a notebook (Jupyter, Google Colab, VSCode) -[Try it in Colab](https://colab.research.google.com/github/najaeda/naja-schematic/blob/main/python/notebooks/colab_test.ipynb): +[Try it in Colab](https://colab.research.google.com/github/keplertech/naja-schematic/blob/main/python/notebooks/colab_test.ipynb): a walkthrough of every widget feature below, runnable as is. ```python diff --git a/python/pyproject.toml b/python/pyproject.toml index 012bb31..1361810 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -29,7 +29,7 @@ test = ["pytest", "nbformat", "nbclient", "ipykernel"] naja-schematic = "naja_schematic.server:main" [project.urls] -Homepage = "https://github.com/najaeda/naja-schematic" +Homepage = "https://github.com/keplertech/naja-schematic" [tool.hatch.version] path = "naja_schematic/__init__.py" diff --git a/python/tests/test_notebook.py b/python/tests/test_notebook.py index 9a6b0b7..a8fbc51 100644 --- a/python/tests/test_notebook.py +++ b/python/tests/test_notebook.py @@ -4,18 +4,32 @@ The kernel side is all that can run headless: there's no browser to render the widget, so a cell spliced in mid-notebook plays the viewer against the notebook's own `view` (requests in over the comm, replies captured). + +By default it runs against the installed package (the wheel under test): the +notebook's install cell is skipped. With NAJA_SCHEMATIC_COLAB_CHECK set it +runs as Colab does instead: nothing preinstalled, the install cell fetches +the release from PyPI, which must be the version given (or any, for +"latest"). CI's `colab` job runs it that way, from outside python/ and with +--noconftest, since neither najaeda nor naja_schematic is installed yet. """ import json +import os from pathlib import Path import pytest -nbformat = pytest.importorskip("nbformat") -nbclient = pytest.importorskip("nbclient") -pytest.importorskip("ipykernel") -pytest.importorskip("anywidget") +COLAB_VERSION = os.environ.get("NAJA_SCHEMATIC_COLAB_CHECK") -from naja_schematic._bundle import bundle_path # noqa: E402 +if COLAB_VERSION: + # Nothing may skip here: a skipped check would pass the job. + import nbclient + import nbformat + import ipykernel # noqa: F401 +else: + nbformat = pytest.importorskip("nbformat") + nbclient = pytest.importorskip("nbclient") + pytest.importorskip("ipykernel") + pytest.importorskip("anywidget") NOTEBOOK = Path(__file__).parents[1] / "notebooks" / "colab_test.ipynb" WIDGET_MIME = "application/vnd.jupyter.widget-view+json" @@ -49,8 +63,24 @@ def _ask(request): ''' +# Run right after the install cell when checking the PyPI release: what +# Colab got must be the expected version and carry the real viewer bundle. +RELEASE_CHECK = r''' +from naja_schematic._bundle import bundle_path +_expected = {expected!r} +assert _expected == "latest" or naja_schematic.__version__ == _expected, ( + naja_schematic.__version__, _expected) +assert bundle_path() is not None and bundle_path().stat().st_size > 100_000, bundle_path() +''' + + @pytest.fixture def notebook_env(tmp_path, monkeypatch): + if COLAB_VERSION: + monkeypatch.delenv("NAJA_SCHEMATIC_NOTEBOOK_TEST", raising=False) + monkeypatch.delenv("NAJA_SCHEMATIC_BUNDLE", raising=False) + return tmp_path + from naja_schematic._bundle import bundle_path # Keeps the install cell from upgrading over the wheel under test. monkeypatch.setenv("NAJA_SCHEMATIC_NOTEBOOK_TEST", "1") # Without a built bundle (a source checkout), give the widget a stub: @@ -72,6 +102,11 @@ def splice_viewer_check(nb): def test_colab_notebook_runs(notebook_env): nb = nbformat.read(NOTEBOOK, as_version=4) splice_viewer_check(nb) + if COLAB_VERSION: + install = next(i for i, cell in enumerate(nb.cells) + if cell.cell_type == "code") + nb.cells.insert(install + 1, nbformat.v4.new_code_cell( + RELEASE_CHECK.format(expected=COLAB_VERSION))) client = nbclient.NotebookClient( nb, kernel_name="python3", timeout=600, resources={"metadata": {"path": str(notebook_env)}}) @@ -79,9 +114,11 @@ def test_colab_notebook_runs(notebook_env): client.execute() code_cells = [c for c in nb.cells if c.cell_type == "code"] - # The install cell must not have reinstalled over the package under test. first = "".join(o.get("text", "") for o in code_cells[0].outputs) - assert "naja-schematic" in first and "Successfully installed" not in first + assert "naja-schematic" in first + if not COLAB_VERSION: + # The install cell must not have reinstalled over the package under test. + assert "Successfully installed" not in first views = [c for c in code_cells if any(WIDGET_MIME in o.get("data", {}) for o in c.outputs)] diff --git a/src/AppLogic.cpp b/src/AppLogic.cpp index e6934b7..b421453 100644 --- a/src/AppLogic.cpp +++ b/src/AppLogic.cpp @@ -818,7 +818,7 @@ bool appFrame(AppState& state) { ImGui::Text("Version: %s", NAJA_SCHEMATIC_VERSION_STRING); ImGui::Text("Commit: %s", NAJA_SCHEMATIC_GIT_HASH); ImGui::Text("naja: %s", NAJA_VERSION_STRING); - ImGui::Text("Project: github.com/najaeda/naja-schematic"); + ImGui::Text("Project: github.com/keplertech/naja-schematic"); ImGui::Spacing(); ImGui::SeparatorText("License");