Publishing rcrpy

The CI workflow builds distributions automatically on every push; this file covers the manual steps for actually uploading to TestPyPI / PyPI.

One-time setup

  1. Create accounts on both:

    • TestPyPI — for trial releases

    • PyPI — for real releases

  2. Enable 2FA on both accounts (PyPI requires this for new uploads).

  3. Create API tokens — one per account. Save them somewhere safe.

    • Tip: You can set the UV_PUBLISH_TOKEN environment variable (e.g., in your .zshrc) to avoid passing the --token flag every time. uv will use this token automatically for uploads.

Per-release checklist

From the rcrpy directory:

# 1. Bump the version in TWO places.
#    - pyproject.toml:  [project] version = "x.y.z"
#    - src/rcrpy/__init__.py:  __version__ = "x.y.z"
#    (These must match.)

# 2. Run the full test suite.
uv run pytest

# 3. Build the wheel + sdist.
uv build
ls dist/   # should show rcrpy-x.y.z-py3-none-any.whl and rcrpy-x.y.z.tar.gz

# 4. Install the BUILT wheel in a CLEAN venv and smoke-test it.
uv venv /tmp/rcrpy-clean
uv pip install dist/rcrpy-x.y.z-py3-none-any.whl --python /tmp/rcrpy-clean
/tmp/rcrpy-clean/bin/python scripts/smoke_test_full_rcr.py
# (On Windows: c:\path\rcrpy-clean\Scripts\python.exe — adjust accordingly.)

# 5. Upload to TestPyPI FIRST.
# If UV_PUBLISH_TOKEN is set, you can omit the --token flag.
uv publish --publish-url https://test.pypi.org/legacy/ --token "pypi-your-testpypi-token" dist/*

# 6. Install FROM TestPyPI in another clean venv to confirm the upload.
uv venv /tmp/rcrpy-from-testpypi
uv pip install \
    --index-url https://test.pypi.org/simple/ \
    --extra-index-url https://pypi.org/simple/ \
    rcrpy==x.y.z \
    --python /tmp/rcrpy-from-testpypi
# scipy and numpy come from real PyPI (--extra-index-url), rcrpy from
# TestPyPI (--index-url).

# 7. If TestPyPI looked good: upload to real PyPI.
# If UV_PUBLISH_TOKEN is set, you can omit the --token flag.
uv publish --token "pypi-your-real-pypi-token" dist/*

# 8. Tag the release in git.
git tag -a vx.y.z -m "rcrpy vx.y.z"
git push origin vx.y.z

# 9. Create a GitHub Release attached to that tag.

Documentation (Sphinx + Read the Docs)

The docs live in rcrpy/docs/ (Sphinx + MyST Markdown, furo theme) and are built and hosted automatically by Read the Docs. The build config is .readthedocs.yaml at the repository root; the Sphinx config is rcrpy/docs/conf.py. The docs extra in pyproject.toml pins the build dependencies (sphinx, furo, myst-parser).

Build & preview locally

From the rcrpy directory:

# Build the HTML site (installs the docs extra into the env on first run).
uv run --extra docs sphinx-build -b html docs docs/_build/html

# Open it.
open docs/_build/html/index.html        # macOS
# xdg-open docs/_build/html/index.html   # Linux

# Stricter pre-release check: fail on any warning (use once warnings are clean).
uv run --extra docs sphinx-build -b html -W --keep-going docs docs/_build/html

(From the repo root instead, add --project rcrpy and use the full paths: uv run --project rcrpy --extra docs sphinx-build -b html rcrpy/docs rcrpy/docs/_build/html.)

The docs/_build/ and docs/generated/ output directories are git-ignored.

How it deploys

You don’t upload docs by hand — Read the Docs rebuilds them on its own:

  • Every push to main rebuilds the latest docs (via the RTD webhook).

  • Every release tag (vX.Y.Z, step 8 above) builds a versioned copy, as long as the “build on new tag” Automation Rule is enabled in the RTD admin.

So once you push the release tag, the matching docs version publishes automatically. Watch the Builds tab of the Read the Docs project to confirm it succeeded.

Adding a page

Drop a new .md (or .rst) file in rcrpy/docs/ and add its name (without the extension) to a toctree in rcrpy/docs/index.md. The API reference in rcrpy/docs/api.md is generated from the package’s public docstrings, so new public classes/functions show up there automatically.

Version-bump conventions

rcrpy follows semver:

Bump

When

0.1.0 → 0.1.1

Bug fix, no API change

0.1.0 → 0.2.0

New feature OR breaking API change (allowed in 0.x)

0.x → 1.0.0

“Stable API; we’re committing to backwards compat”

We’re at 0.1.0 — early beta. Stay in 0.x until 1-2 real users have exercised the package and reported back without surfacing critical issues.

If something goes wrong

  • Upload rejected with “already exists”: PyPI / TestPyPI does not allow re-uploading the same version. Bump the version and try again.

  • Test install fails with “no matching distribution”: TestPyPI doesn’t have scipy/numpy. Use --extra-index-url https://pypi.org/simple/ to pull dependencies from real PyPI.

  • Wheel is missing a file: check MANIFEST.in and the [tool.hatch.build.targets.wheel] section of pyproject.toml. By default hatchling includes files in src/rcrpy/, the LICENSE, and the README.