Releasing noodlelab

A release starts when you push a version tag. Everything after that is automated in .github/workflows/release.yml:

git tag v0.3.0 → checks → build → smoke → GitHub Release → PyPI (→ MCP Registry) + Docker images + docs → verify

Step

What it does

checks

Runs all of CI against the tagged commit: ruff, pytest on Python 3.11–3.13, the editor’s typecheck and build, and every installation tier on its own (only its own packs load, and its examples run).

build

Builds the editor into the package and runs uv build. It checks that the wheel’s version equals the tag, that the editor is inside the wheel, and runs twine check.

smoke

Installs the wheel with uv tool install on Linux, macOS and Windows (the basic tier, plus the full tier on Linux). It checks noodlelab --version and noodlelab tiers, then starts the server and calls /api/health.

github-release

Creates the GitHub Release for the tag with generated notes and attaches the wheel and sdist.

publish-pypi

Final versions: uploads to PyPI with trusted publishing, so no API token is stored anywhere.

publish-testpypi

Pre-releases (a, b, rc): uploads to TestPyPI instead.

publish-mcp-registry

Final versions: stamps the version into server.json and publishes it to the MCP Registry as io.github.nkalis/noodlelab, logging in with GitHub OIDC. The registry confirms the PyPI package is ours from the mcp-name: comment at the top of the README, so keep that line.

docker

Builds one image per tier for amd64 and arm64 and pushes it to ghcr.io/<owner>/noodlelab.

docs

Final versions: builds the documentation at the tag and publishes it to GitHub Pages as /<X.Y>/ and /stable/ (see the documentation site).

verify

Runs uvx "noodlelab[maths]==<version>" from the package index, retrying while the index catches up, and runs the pushed images.

If a step fails, nothing after it runs. For example, a failed smoke test means nothing is published. Re-running a failed workflow is safe:

  • the GitHub Release is updated rather than created again;

  • uv publish --check-url skips files that are already on the index;

  • the images are simply pushed again.

Versions come from the tag

pyproject.toml has no version number. hatch-vcs reads it from git:

  • tag v0.3.0 builds 0.3.0;

  • the commits after it build 0.3.1.devN+g<sha>;

  • the installed package reports its version through noodlelab --version, /api/health and the admin System tab.

Tags must look like vX.Y.Z or vX.Y.Z followed by aN, bN or rcN. The build step rejects anything else.

Cutting a release

git switch main && git pull
git tag v0.3.0rc1 && git push origin v0.3.0rc1   # optional: rehearse on TestPyPI
git tag v0.3.0 && git push origin v0.3.0          # the release

The live demo on Render builds without git history, so it cannot read the tag. Its version is pinned as NOODLELAB_VERSION in render.yaml: set it to the new version in a commit on main, and the next deploy reports it.

Watch it under Actions → Release. When it finishes, users can install it:

uvx "noodlelab[full]" .                    # or any tier: maths, science, engineering, geo
uv tool install "noodlelab[engineering]"
pip install "noodlelab[science]"
docker run -p 8000:8000 ghcr.io/<owner>/noodlelab:0.3.0

Install a pre-release from TestPyPI (its dependencies still come from PyPI):

uvx --index https://test.pypi.org/simple/ --index-strategy unsafe-best-match \
    --from "noodlelab[maths]==0.3.0rc1" noodlelab

The documentation site

The site is built with Sphinx from docs/ (the guides), the README (the home page) and the code (the Python API, every node and the command line), and served by GitHub Pages from the gh-pages branch, one folder per version:

Folder

What

Published by

/dev/

main

docs.yml, after every green CI run on main

/0.3/

The latest 0.3.x release

the release workflow’s docs step

/stable/

The latest final release; the site’s root redirects here

the release workflow’s docs step

Pre-releases are not published. CI builds the site on every pull request with warnings as errors (a broken link, a docstring autodoc can’t read) and keeps the HTML as the docs-html artifact. To build it locally:

uv sync --all-extras --group docs
make docs                          # docs/_build/html/index.html

To republish main by hand, run Actions → Docs → Run workflow.

Docker image tags

Tag

Image

0.3.0, 0.3.0-full

The full tier of this release.

0.3.0-<tier>

One tier of this release: basic, maths, science, engineering or geo.

0.3, 0.3-<tier>

The latest patch release of 0.3. Final releases only.

latest, <tier>

The latest final release.

One-time setup

  1. PyPI trusted publisher.

    • On PyPI, go to Your account → Publishing and add a pending publisher (or go to Manage project → Publishing once the project exists).

    • Settings: owner nkalis, repository noodlelab, workflow release.yml, environment pypi.

  2. TestPyPI trusted publisher. Do the same on test.pypi.org, with environment testpypi.

  3. GitHub environments.

    • Under Settings → Environments, create pypi and testpypi.

    • Add required reviewers to pypi if you want to approve each upload by hand. The workflow waits at that step.

  4. Container images.

    • The workflow pushes with the built-in GITHUB_TOKEN, so nothing needs configuring.

    • After the first release, open the package under Your profile → Packages → noodlelab → Package settings.

    • Make it public so docker pull works without logging in, and link it to the repository.

  5. GitHub Pages.

    • Merge to main once so that the Docs workflow creates the gh-pages branch.

    • Under Settings → Pages → Build and deployment, choose Deploy from a branch, then gh-pages and / (root).

    • The site is then at https://nkalis.github.io/noodlelab/. Pages is free for public repositories; a private one needs a paid GitHub plan.