Releases are automated. Pushing a v* tag runs
.github/workflows/release.yml, which runs the
full test suite, builds the sdist + wheel, checks them with twine, and publishes
to PyPI via Trusted Publishing (OIDC) — no API tokens.
Trusted publishing needs a publisher registered on (Test)PyPI and a matching GitHub environment. Do this once per index.
- Create the GitHub environments: repo → Settings → Environments → add
pypiandtestpypi. (Optionally require a reviewer so a publish needs approval.) - Register the PyPI trusted publisher: https://pypi.org/manage/account/publishing/
→ Add a pending publisher:
- PyPI Project Name:
moderndive - Owner:
moderndive - Repository name:
moderndive-python - Workflow name:
release.yml - Environment name:
pypi
- PyPI Project Name:
- Register the TestPyPI publisher the same way at
https://test.pypi.org/manage/account/publishing/ with environment
testpypi.
- GitHub → Actions → Release → Run workflow (this is
workflow_dispatch). It builds and publishes to TestPyPI (thepublish-testpypijob; the real PyPI job only runs on a tag). - Confirm it installs and imports from TestPyPI:
(The
pip install -i https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ moderndive python -c "import moderndive as md; print(md.__version__); md.load_bowl()"--extra-index-urllets the real dependencies resolve from PyPI.)
You can also build and check locally without uploading:
make build # uv build
uvx twine check dist/*
unzip -l dist/*.whl | grep -c parquet # sanity: bundled datasets are present- Pick the version (PyPI versions are immutable — you can't re-upload one).
Any change that can alter existing users' results is breaking: it needs a
dedicated
### ⚠️ Breaking changessection inCHANGELOG.md(what changed, how to restore the old behavior, why) and a minor/major bump — never a patch. Prefer adding an opt-in parameter with the old default to avoid breaking. - Bump
versioninpyproject.toml. - Update
CHANGELOG.md: rename the## Unreleasedsection to## <version> (YYYY-MM-DD)and start a fresh empty## Unreleasedabove it. - Regenerate the README if
README.qmdchanged:make readme. - Commit, then tag and push:
The tag triggers the release workflow → tests → build → publish to PyPI.
git tag v0.1.0 git push origin main --tags
- Create a GitHub Release for the tag (paste the CHANGELOG section) if you want release notes on GitHub.
CHANGELOG.mdis the single source of truth for release notes (there is no separateNEWS.md).- The wheel bundles
moderndive/data/*.parquet;README.md(generated fromREADME.qmd) is the PyPI long description and its plot uses an absolute URL so it renders on PyPI. - Docs (Read the Docs) build separately from source on every push to
main; they are not part of the PyPI release.