The package version lives in exactly one place:
src/vfbquery/_version.py -> __version__ = "X.Y.Z"
Everything else derives from it, so the fields can never drift apart:
setup.pyreads_version.pyat build time (viaexec, without importing the package), so the wheel/sdist metadata matches.vfbquery/__init__.pydoesfrom ._version import __version__, sovfbquery.__version__(andha_api.py's version reporting) matches.- The SOLR result cache stamps entries with this version (major.minor) and uses it for invalidation — see CACHING.md.
Do not hard-code the version anywhere else.
- Create a GitHub Release with a tag of the form
vX.Y.Z(e.g.v1.21.0).
That's it — the Publish 🐍 📦 to PyPI workflow
(.github/workflows/publish_to_pypi.yaml) does the rest:
- Checks out the tag, extracts
X.Y.Zfromrefs/tags/vX.Y.Z, and writes it into_version.py(sed). - Builds the sdist/wheel (version comes from
_version.py) and verifies the metadata matches the tag. - Publishes to PyPI via trusted publishing.
- Commits the bump back to
main— switches from the detached tag checkout to livemain, re-appliesX.Y.Zto_version.py, and pushesBump version to X.Y.Z [skip ci].
So after a release, main reflects the released version too — you don't have
to bump it by hand.
A minor/major bump invalidates the previous version's cache entries (see CACHING.md), so they're refilled with the new version's output. That happens two ways, with no dedicated release-triggered step:
- Lazily, by the deployed production service as it serves traffic (the primary path — each query refreshes on first read).
- By the
performance-testworkflow onmain— its perf steps are writable on push-to-mainand scheduled (daily) runs (read-only only on PRs), so they recompute and re-cache the perf-test query set under the currentmainversion. The daily schedule guarantees the new version's entries are warmed within a day of a release, so later PR runs read a warm cache.
- The commit-back step runs only after a successful publish and only for
refs/tags/v*(if: success() && startsWith(github.ref, 'refs/tags/v')). - It's a no-op if
mainis already at that version (guarded bygit diff --staged --quiet), so you can also bump_version.pyin a PR before tagging and the workflow won't create an empty commit. - The push needs
contents: write, which is declared in the workflow's jobpermissionsalongside theid-token: writeused for PyPI. [skip ci]keeps the housekeeping commit from retriggering the test/perf workflows.
Because the cache namespace is keyed on major.minor (see CACHING.md):
- Bump the patch for changes that don't alter query output — cached results stay valid (no invalidation).
- Bump minor/major when query output changes — older cache entries are then invalidated on read, so users get refreshed results.