Releasing¶
This checklist keeps PyPI, GitHub, documentation, citation metadata, and Zenodo aligned for each public release.
Before Tagging¶
Five files carry the release identity. The version appears in
pyproject.toml, CITATION.cff, CHANGELOG.md, docs/about/citation.md and
README.md; the date appears in CITATION.cff, .zenodo.json and
CHANGELOG.md. Nothing writes them together, so update all of them:
- Update the package version in
pyproject.toml. - Update
CITATION.cffwith the same version and release date. - Roll
CHANGELOG.md: turn## [Unreleased]into a dated## [<version>] - <date>section, open a fresh empty## [Unreleased]above it, add the[<version>]link reference, and repoint the[Unreleased]comparison atv<version>. - Update the citation guidance in
docs/about/citation.mdandREADME.md. Both name the version in running text and in the BibTeX, APA, IEEE, MLA and Chicago entries. - Update
CITATION.cffwhen the title, authors, affiliations, keywords, references, license, or release date change. - Update
.zenodo.jsononly when Zenodo-specific fields change. The validator checks that shared citation fields stay aligned withCITATION.cff. - Confirm every file agrees and the tag is still free:
Do not tag until this passes. It reports every problem at once, so there is no need to fix one and run it again.
This step exists because skipping it is expensive. The same couplings are
enforced by the CI test job, and publish waits on five jobs -- test,
coverage, lint-and-type-check, security and build -- so a release
commit that misses one of these files still tags cleanly, still cuts a
GitHub release, and simply never reaches PyPI. Nothing fails loudly at the
moment anyone is watching. Since PyPI will not accept a re-upload of a version,
recovering costs a whole patch release. That is how 0.6.0 was lost: it
missed steps 3 and 4, which this checklist did not previously mention.
- Dispatch the coverage job and wait for it to pass:
coverage runs on a release or a manual dispatch and on nothing else --
not on pushes, not on pull requests. But publish waits on it, so on an
ordinary release the first time it ever runs against the code being shipped
is after the tag is public. If it fails there, for any reason including a
tooling change rather than a real coverage drop, the tag and the GitHub
release are already spent and the recovery is another version number. That
is the same shape of failure as 0.6.0. Dispatching it here moves the one
unexercised gate in front of the irreversible step.
- Run the regular quality checks:
poetry run ruff check .
poetry run ruff format --check .
poetry run mypy heavytails/ scripts/
poetry run pytest
poetry run mkdocs build --strict
Zenodo Setup¶
Zenodo archives GitHub releases after the repository is connected in Zenodo's
GitHub integration. The repository-level .zenodo.json file overrides the
metadata Zenodo would otherwise infer from GitHub, while CITATION.cff remains
the source of truth for shared citation fields.
For the first archived release:
- Sign in to Zenodo with the maintainer account.
- Enable GitHub integration for
DiogoRibeiro7/heavytails. - Create and publish a GitHub release from an annotated version tag.
- Wait for Zenodo to archive the release.
- Copy the concept DOI and version DOI from Zenodo.
- Replace DOI placeholders in citation documentation with the real DOI values.
- Add a Zenodo DOI badge to
README.mdonce the concept DOI exists.
Do not add a fake DOI before Zenodo has minted one.
Release Notes¶
Each GitHub release should include:
- Version number and date.
- New distributions, estimators, or diagnostics.
- Bug fixes and numerical accuracy changes.
- Backward-incompatible changes.
- Documentation and citation metadata changes.
- The Zenodo DOI once the archive is available.
After Release¶
- Confirm the GitHub release triggered the PyPI publish workflow, and that
it ran rather than being skipped.
publishwaits ontest,coverage,lint-and-type-check,securityandbuild; if any of them failed, the release is tagged and public but not on PyPI. - Confirm Zenodo created a new archived version.
- Confirm the Zenodo record uses the
.zenodo.jsontitle, creator ORCID, license, references, keywords, and related identifiers. - Copy the new version DOI into
CITATION.cffanddocs/about/citation.md. Zenodo mints it only on archiving, so it cannot be filled in before the release, andmake release-checkfails while the two files disagree. The "Citing release X exactly" block must carry the version DOI: the concept DOI resolves to whatever is newest, so quoting it there pins nothing. - Update badges with the minted DOI if this was the first archived release.