Releasing geometric-function-atlas¶
This is the maintained release procedure. The first GitHub software release is separate from PyPI publication and from versioned registry-data snapshots.
Free-tier GitHub policy¶
The repository is public. CI uses only standard ubuntu-24.04 and
windows-2025 GitHub-hosted runners, which GitHub documents as free for public
repositories. The workflow does not use larger runners, Actions artifact uploads,
or persistent Actions caches. Release files are attached directly to the GitHub
release; GitHub documents no total release-size or bandwidth limit, subject to a
2 GiB limit per asset.
Do not add larger runners, paid products, or persistent Actions storage without explicit approval from the repository owner.
Software release¶
- Start from a clean branch/worktree at the current
origin/maincommit; keep the published previous-version tag/assets, artifact snapshots, frozen PyPI manifest, and installer URLs unchanged. - Update
src/geometric_function_atlas/version.py,CITATION.cff, andCHANGELOG.mdtogether. Setdate-releasedand the changelog date in the reviewed release commit before tagging; if publication slips to another day, update both and rerun CI before creating the tag. Run the version-contract test red before the bump and green afterward. ReviewREADME.mdanddocs/PROVENANCE.mdfor bounded evidence claims. - Regenerate
uv.lockwhen the version changes (uv lock), then run the local gate:
uv sync --extra test --extra build --extra lab --locked
uv run --frozen --extra test --extra lab python -m ruff check src tests scripts
uv run --frozen --extra test --extra lab python -m mypy src --ignore-missing-imports
uv run --frozen --extra test --extra lab python -W error -m pytest -q
rm -rf dist build
uv run --frozen --extra build python -m build
uv run --frozen --extra build python -m twine check dist/*
uv run --frozen python scripts/check_distribution.py dist
# The build directory must contain exactly one current wheel.
wheel=$(uv run --frozen python -c 'from pathlib import Path; wheels = list(Path("dist").glob("geometric_function_atlas-*.whl")); assert len(wheels) == 1, wheels; print(wheels[0])')
uv run --frozen python scripts/check_clean_install.py "$wheel"
uv run --frozen python scripts/check_uv_tool_install.py "$wheel" --python 3.12
- Obtain an independent review of the exact diff.
- Push a branch, open a pull request, and wait for every Linux and Windows CI job to pass.
- Merge to
mainand verify the merge commit is green. - Build once more from the clean merge commit. Record SHA-256 sums.
- Create tag
vX.Y.Zand a GitHub release with the wheel, sdist, checksums, and notes fromCHANGELOG.md. - Download the published assets and rerun the clean-install check against the downloaded wheel.
- Publish the release to PyPI through the frozen-manifest procedure below.
- Only after both the GitHub assets and PyPI version are live, open a small
follow-up pull request that switches
install.sh,install.ps1, their tests, and public installation examples from the previous release to the new one. - Merge that follow-up after CI, run both package-repository public installer
commands, and verify
gfa --versionplus the new first-use command. The website-hosted/install.shand/install.ps1endpoints are maintained in the website repository: check their live versions separately and update them there before advertising those URLs as current. This order prevents a broken installer URL or a PyPI command that names an unpublished version.
PyPI publication¶
PyPI publication is a separate, explicitly triggered step after a GitHub release
has passed every gate above. The publish-pypi.yml workflow uses PyPI Trusted
Publishing; no PyPI password or API token belongs in this repository.
- Update and independently review
.github/pypi-publish.jsonso it pins the release tag, commit, exact asset names, and independently recorded SHA-256 digests. - Confirm that the PyPI trusted publisher matches this public repository,
.github/workflows/publish-pypi.yml, and thepypiGitHub environment. - Manually dispatch Publish to PyPI. It accepts no release input; the reviewed manifest is the sole publication target.
- The workflow verifies the pinned tag/commit, downloads the exact wheel,
sdist, and
SHA256SUMS, checks both the manifest-pinned digests and the release checksum file, and runs the metadata and distribution-content gates. - A separate minimal OIDC job downloads and reverifies the pinned distribution digests before uploading only those artifacts through short-lived OpenID Connect credentials.
- Verify the new PyPI release through its JSON API and install it by name in a fresh uv-managed Python environment.
The workflow uses a standard public-repository runner and does not upload Actions
artifacts or use persistent Actions caches. Release and CI environments are
resolved from the committed uv.lock with --locked/--frozen; uv, Python
for publishing, and every third-party Action are pinned (Actions by full commit
SHA). Runtime dependency ranges remain compatible library metadata, while the
lockfile is the exact reproducibility record. Do not enable automatic
tag-triggered publishing, mutable Action tags, dependency caches, or long-lived
PyPI credentials.
Maintained scripts¶
The scripts/ directory contains only permanent entry points:
install.shandinstall.ps1: supported end-user installers;check_distribution.py: wheel/sdist content contract;check_clean_install.py: clean wheel API/CLI test;check_uv_tool_install.py: fresh uv-managed-Python installation test.
Do not commit ad hoc migration, profiling, review, or release-upload scripts.
Registry-data releases¶
Registry snapshots are not bundled into the wheel. Each snapshot is versioned independently and needs a consistent SQLite file, schema dump, manifest, checksums, population definitions, and successful SQLite integrity checks.