This is the standing release process for PySnpTools. Short-term release plans
and compatibility investigations belong in specs/; keep this file limited to
the process that should apply to every release.
The maintainer chooses the version and explicitly approves publication. A
release must be built from a clean commit on main, and its vX.Y.Z tag must
point to that exact commit.
PySnpTools must be published before a FaST-LMM release that depends on its new
behavior or compatibility. If a release requires a newer bed-reader, publish
and verify that dependency first.
Final qualification must use published dependencies, not sibling source checkouts, Git dependencies, or prereleases.
- Maintain the tag-triggered
.github/workflows/release.ymlworkflow. - Configure a PyPI Trusted Publisher for the
fastlmm/PySnpToolsrepository, that workflow filename, and a protected GitHubpypienvironment. - Require maintainer approval for the
pypienvironment and restrict it to version tags. - Give the publish job only the permissions it needs, including
id-token: write. Other jobs need onlycontents: read. - Pin third-party GitHub Actions to reviewed full commit SHAs and pin
uvto a reviewed version. - Keep a scheduled CI job that resolves current stable dependencies without rewriting or relying on the committed lockfile. Ordinary CI should continue to use the frozen lockfile for reproducibility.
- Pass the distributions built and tested by CI to the publish job as an artifact. Never rebuild them in the publish job.
The release workflow must publish with PyPI Trusted Publishing and retain the generated attestations. Do not store PyPI passwords or API tokens in the repository or GitHub Actions.
-
Start from a clean release branch based on current
main. -
Review open issues, pull requests, and dependency advisories for anything that affects the release.
-
Set the version in
pyproject.tomland updateCHANGELOG.mdwith the release date, compatibility changes, deprecations, and user-visible fixes. -
Confirm that dependency bounds and Python-version markers describe versions actually tested in CI. When adding support for a new Python feature release, audit every direct dependency against both its declared lower bound and its current stable release. Record whether each bound was retained, raised, or given a Python-version marker, and test the resulting minimum-dependency environment rather than relying only on the locked current environment.
-
Regenerate and commit
uv.lock, then verify it:uv lock --check -
Run the core and optional suites from the locked environment:
uv sync --frozen --all-extras uv run --frozen --no-sync python tests/test.py uv run --frozen --no-sync pytest pysnptools/distreader/test_bgen2.py
-
Execute the tutorial and paper notebooks from start to finish in a clean notebook environment. Inspect numerical results and committed output changes rather than accepting changed output mechanically.
-
Build both the source and published documentation with warnings treated as errors, verify that generated pages are committed, and check external links:
pysnptools_docs_output="$(mktemp -d)" uv run --frozen --no-sync sphinx-build -W --keep-going -E -a -d /tmp/pysnptools-doctrees -b html doc/source "$pysnptools_docs_output" diff -ru --exclude=.nojekyll "$pysnptools_docs_output" docs uv run --frozen --no-sync sphinx-build -W --keep-going -E -a -b linkcheck doc/source doc/build/linkcheck
-
Validate every entry in the package's hashdown manifests, including the packaged tutorial sample files.
-
Build the source distribution and wheel without local source overrides:
uv build --no-sources- Inspect both artifact manifests for required metadata, licenses, hashdown files, and package data. Confirm that development files, caches, notebooks, and generated output are absent unless intentionally distributed.
- Install and smoke-test the exact wheel and source distribution in clean
environments on the oldest and newest supported Python versions, outside
the source checkout and without the repository on
PYTHONPATH. - Wait for all required CI jobs to pass on every supported operating system and Python version. Resolve warnings that indicate a compatibility, packaging, or security problem.
-
Merge the reviewed release change into
mainand confirm thatmainis clean and up to date. -
Confirm that the version is not already present on PyPI and that the tag does not already exist.
-
Create and push an annotated tag at the release commit:
git tag -a vX.Y.Z -m "PySnpTools X.Y.Z" git push origin vX.Y.Z
-
Verify that the tag workflow builds and tests the exact artifacts intended for publication.
-
Review the workflow summary and approve its protected
pypienvironment. -
Confirm that PyPI received both the wheel and source distribution and that their attestations are present.
-
Create the matching GitHub release from the changelog entry.
- Install
pysnptools==X.Y.Zfrom PyPI into a fresh environment with no source checkout onPYTHONPATHand run an import and representative read/write smoke test. - Re-run the clean artifact checks against the published distribution on the oldest and newest supported Python versions.
- Run the relevant InstallTest scenarios.
- Check the rendered documentation and tutorial links.
- Close or update the issues and pull requests resolved by the release, linking to the published version or its CI evidence.
- Continue with dependent releases only after these checks pass.
Do not move or replace a published version tag, and do not upload different files under an existing version. If a published release is unusable, yank it on PyPI with a concise reason and publish a corrected version. Preserve the failed release's tag and evidence for traceability.
TestPyPI may be used through a separate Trusted Publisher when a publishing workflow itself needs qualification. It does not replace clean local artifact tests or final testing against the real PyPI dependency ecosystem.