Releasing#

A QUARTIC2D release has one source artifact, the annotated Git tag, and three publication surfaces: PyPI, a GitHub Release, and a Zenodo software archive. The release automation publishes them in that order so an archived GitHub release is not created before the Python distributions have passed validation and reached PyPI.

1. Prepare the release commit#

Update

  • src/quartic2d/_version.py

  • CITATION.cff

  • the matching version heading in CHANGELOG.md

For a release commit, CHANGELOG.md must use a date in YYYY-MM-DD form and CITATION.cff must contain the same date-released value.

Check the metadata before tagging:

python tools/check_release_metadata.py

For the current unreleased 0.1.0 state, the checker prints

package version : 0.1.0
citation version: 0.1.0
changelog state : Unreleased
release metadata: consistent

Install the maintainer dependencies:

python -m pip install -e ".[test,docs,dev,release,ogata]"

2. Run the local release checks#

python -m pytest
python -m ruff check src tests examples benchmarks docs/scripts tools
python -m benchmarks.gaussian_validation --quick
python docs/scripts/generate_figures.py examples
python docs/scripts/generate_example_artifacts.py
python docs/scripts/check_artifacts.py
python -m sphinx -W --keep-going -b html docs/source docs/_build/html
python docs/scripts/check_rendered_docs.py docs/_build/html

A clean source state gives the following stable end conditions:

pytest: all tests passed
ruff: no diagnostics
gaussian validation: PASS
documentation artifact manifest: PASS
Sphinx: build succeeded.

Run the executable examples:

MPLBACKEND=Agg bash -c '
for example in examples/*.py; do
    echo "==> $example"
    python "$example"
done
'

If publication benchmark evidence accompanies the release, run it from the exact release commit:

python -m benchmarks.run_suite publication 2>&1 | \
    tee ~/Downloads/quartic2d_publication_run.log

The log path is deliberately outside the repository. Benchmark provenance samples the Git working tree during execution, so a tracked output log would make the run dirty even when no computational input changed.

A successful run ends with

BENCHMARK SUITE COMPLETE
Status  : PASS
Results : <repository>/benchmarks/results

Check the generated manifest before archiving the benchmark dataset. The commit must match the release commit and the recorded source state must be clean.

3. Build and inspect the distributions#

rm -rf build dist src/*.egg-info
python -m build
python -m twine check dist/*

The final lines should report successful wheel/sdist creation followed by PASSED from twine check for both distributions.

Inspect the archives:

python -m zipfile -l dist/*.whl
tar -tf dist/*.tar.gz

Both commands print archive member lists. Confirm that quartic2d/, package metadata, license, README, examples, documentation sources, tests, and benchmark sources are present according to MANIFEST.in, while generated result/build directories are absent.

The source distribution includes the package metadata, license, documentation sources, examples, tests, benchmark sources, and maintainer/release documentation. Generated benchmark results, documentation builds, caches, and local environments are excluded.

4. Test the wheel outside the checkout#

An editable installation can hide packaging mistakes. Test the built wheel in a fresh environment:

python -m venv /tmp/quartic2d-release-check
source /tmp/quartic2d-release-check/bin/activate
python -m pip install --upgrade pip
python -m pip install dist/*.whl
cd /tmp
python - <<'PY'
import quartic2d
print(quartic2d.__version__)
print(quartic2d.__all__)
PY
deactivate

For version 0.1.0 the import block prints

0.1.0
['HankelTransform', 'HarmonicConvergenceResult', 'HarmonicTransform', 'Interaction', 'InteractionConvergenceResult', '__version__']

5. Enable GitHub Pages#

Before the first public release, enable GitHub Pages for the repository from Settings → Pages → Build and deployment → Source → GitHub Actions. The Documentation workflow builds and validates the Sphinx site on every push to main and deploys that verified artifact through the github-pages environment.

actions/configure-pages cannot enable Pages with the default GITHUB_TOKEN, so this repository setting must be enabled once by a maintainer. After enabling it, rerun the Documentation workflow and require both its build and deployment jobs to pass before tagging a release.

6. Configure PyPI Trusted Publishing#

The release workflow uses the GitHub environment pypi and OIDC Trusted Publishing. Configure the PyPI project or pending publisher with

  • owner: QuantumArtificer

  • repository: quartic2d

  • workflow: release.yml

  • environment: pypi

No long-lived PyPI token is stored in the repository.

Official guidance: Publishing package distribution releases and PyPI Trusted Publishing.

7. Enable Zenodo before the first tag#

Connect the GitHub account to Zenodo, synchronize repositories, and enable QuantumArtificer/quartic2d before publishing the first release tag.

QUARTIC2D uses CITATION.cff as the release metadata source. Add .zenodo.json only if Zenodo-specific metadata are needed. When .zenodo.json is present, Zenodo uses it instead of CITATION.cff for GitHub-release metadata.

Zenodo guidance: GitHub integration and software metadata.

8. Create and push the release tag#

Commit the fully checked release state, then create an annotated tag:

git status --short
python tools/check_release_metadata.py --tag v0.1.0
git tag -a v0.1.0 -m "QUARTIC2D v0.1.0"
git push origin main
git push origin v0.1.0

For a clean tree, git status --short prints nothing. check_release_metadata.py --tag ... must end with release metadata: consistent before the tag is created.

The tag push starts .github/workflows/release.yml.

The workflow performs the following sequence:

  1. checks the tag, package version, CITATION.cff, release date, and changelog

  2. runs tests, Ruff, analytic validation, documentation, and executable examples

  3. builds the wheel and source distribution once and checks their metadata

  4. installs the built wheel and imports it outside the repository checkout

  5. publishes the verified distributions to PyPI through Trusted Publishing

  6. creates the GitHub Release from the same tag and attaches the distributions plus SHA256SUMS

  7. allows the enabled Zenodo GitHub integration to archive that GitHub Release

A failure before step 5 leaves no PyPI or GitHub release. A PyPI failure prevents creation of the GitHub Release and therefore prevents the Zenodo archive from being triggered by this release path.

9. Verify the published release#

After the workflow succeeds:

  1. install quartic2d==X.Y.Z from PyPI in a clean environment

  2. confirm the GitHub Release contains the wheel, source distribution, and SHA256SUMS

  3. confirm the GitHub Release points to the intended annotated tag

  4. confirm the Zenodo record contains the correct title, author, version, license, and repository link

  5. record the version DOI after Zenodo creates it

The DOI does not exist before the first archive is created. Add it to the next source commit and to subsequent citation metadata. Do not rewrite the already published tag solely to insert an identifier that did not exist when that source state was created.

The release lint gate uses the repository-local Ruff policy in pyproject.toml through the dev extra. User-level Ruff configuration is not part of the release contract.