This document describes how to cut a release of freshdata, publish to PyPI, and maintain the project.
freshdata follows Semantic Versioning. The version
lives in two places that must stay in sync:
pyproject.toml→[project] versionsrc/freshdata/__init__.py→__version__
From 1.0 the public API is stable: bump MAJOR (X.0.0) for breaking changes,
MINOR (1.x.0) for backward-compatible features, and PATCH (1.0.y) for
backward-compatible fixes.
- Green main —
pytest -m "not online and not large",ruff check .,mypy src/freshdata, andmkdocs build --strictall pass. - Bump the version in
pyproject.tomlandsrc/freshdata/__init__.py. - Update
CHANGELOG.md— moveUnreleasednotes under a new## [X.Y.Z] - YYYY-MM-DDheading. - Commit & PR — merge to
main. - Build & validate locally (see below).
- Publish to TestPyPI, smoke-test the install — run the TestPyPI dry run workflow (see TestPyPI dry run).
- Publish to PyPI (push the tag and let CI do it through trusted publishing).
- Tag & GitHub release —
git tag vX.Y.Zand create the release with notes from the changelog. - Verify —
pip install freshdata-cleanerin a clean environment imports (import freshdata as fd) and reports the new version; docs site updated.
python -m pip install --upgrade build twine
rm -rf dist build
python -m build # builds sdist + wheel into dist/
twine check dist/* # validates metadata + long-description renderingPush a version tag; the Release workflow runs a quality gate first
(lint, typecheck, pytest -m "not online and not large", docs strict),
then builds and publishes via PyPI Trusted Publishing (OIDC, no stored token):
git tag vX.Y.Z # must match the version in pyproject.toml / __version__
git push origin vX.Y.ZOne-time setup: add a trusted publisher at
https://pypi.org/manage/project/freshdata-cleaner/settings/publishing/
(workflow release.yml, environment pypi).
Rehearse a release without touching real PyPI and without any stored token:
the manual TestPyPI dry run workflow (.github/workflows/testpypi.yml)
builds the current commit as a unique development version
(X.Y.Z.dev<run><attempt>, because TestPyPI never accepts a re-upload),
publishes it to TestPyPI through Trusted Publishing, then installs it from
TestPyPI in a clean environment, imports it and prints fd.__version__.
gh workflow run testpypi.yml --ref main
gh run watch "$(gh run list --workflow testpypi.yml --limit 1 --json databaseId --jq '.[0].databaseId')"To verify a TestPyPI upload by hand (use the version from the run summary):
python -m venv /tmp/testpypi-smoke
/tmp/testpypi-smoke/bin/pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ "freshdata-cleaner==X.Y.Z.devN"
/tmp/testpypi-smoke/bin/python -c "import freshdata as fd; print(fd.__version__)"One-time setup (a maintainer, once):
- Add a pending trusted publisher at
https://test.pypi.org/manage/account/publishing/ for project
freshdata-cleaner, ownerFreshCode-Org, repositoryfreshdata, workflowtestpypi.yml, environmenttestpypi. - Create a
testpypienvironment under the repository's Settings → Environments (no secrets needed).
# 1. TestPyPI first
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ freshdata-cleaner
python -c "import freshdata as fd; print(fd.__version__)"
# 2. Real PyPI
twine upload dist/*Use a PyPI API token (username __token__) only
for emergency/manual fallback releases. Never commit tokens; prefer ~/.pypirc
or the TWINE_PASSWORD env var.
The PyPI distribution is freshdata-cleaner; the import name is
freshdata (import freshdata as fd). Keep that split — do not rename the
distribution (the bare name freshdata is unavailable on PyPI).
- Branching — work on feature branches; PR into
main. CI must be green. - Dependencies — keep the floor versions in
pyproject.tomlrealistic; test against the lowest supported (pandas>=1.5, Python 3.9). - Coverage — keep the gate at ≥ 93% (
--cov-fail-under). - Docs — update the relevant
docs/page with any user-facing change; the docs site redeploys automatically on push tomain. - Golden snapshots — after intentional engine changes, refresh with
pytest tests/test_golden.py tests/test_online_datasets.py --update-golden. - Security — triage reports per
SECURITY.md; release a patch promptly.