Preparing a polars-bio release
Why this exists
polars-bio's version lives in three sources of truth that must always match, and a release is easy to get half-right (bump two of three files, forget to cut the changelog, skip the compile check). This skill encodes the exact, verified sequence so a release is complete and consistent every time.
The version lives in 3 places (+ lockfile)
| File | Line shape |
|---|---|
Cargo.toml |
version = "X.Y.Z" under [package] |
pyproject.toml |
version = "X.Y.Z" under [project] |
polars_bio/__init__.py |
__version__ = "X.Y.Z" |
Cargo.lock |
regenerate via cargo check; stage it |
uv.lock |
regenerate via uv lock; stage it |
⚠️ Both lockfiles pin the project version. CI's
linux_testsjob runsuv lock --checkand fails the build ifuv.lockstill shows the old version. Always runuv lockafter bumpingpyproject.toml. Likewisecargo checkupdatesCargo.lock. Forgetting either is the #1 way a release PR goes red.
Workflow
Track these as todos so none is skipped.
1. Establish the version and what's shipping
- Current version:
git describe --tags --abbrev=0andgrep version Cargo.toml. - Commits since last tag:
git log --oneline <last-tag>..HEAD. - Decide the new version with the user if not given (semver: breaking → minor while pre-1.0, features → minor, fixes → patch). polars-bio has been bumping the minor for feature releases (0.30 → 0.31 → 0.32).
- Cross-check
git logagainst the[Unreleased]section ofCHANGELOG.md— the most recent merged PRs are often missing. Add anything absent (check the latest commit/PR especially, e.g. a last-minute fix).
2. Create the release branch
git checkout -b release/X.Y.Z
Never bump version on master directly — it goes through a PR.
3. Bump the version in all 3 files
Edit each of Cargo.toml, pyproject.toml, polars_bio/__init__.py. Match on
the surrounding line (e.g. name = "polars_bio" above the Cargo version) so the
edit is unambiguous.
4. Cut the changelog
CHANGELOG.md follows Keep a Changelog. Convert the open [Unreleased] heading
into a dated release heading, leaving a fresh empty [Unreleased] above it:
## [Unreleased]
## [X.Y.Z] - YYYY-MM-DD
### Added
...
Use today's date (it's in the session context — do not invent one). Keep the
### Added / Changed / Fixed subsections that were already accumulated.
5. Release-readiness checks (do not skip)
- Version consistency — confirm zero stragglers and all three at the new version:
grep -rn "<OLD>" Cargo.toml pyproject.toml polars_bio/__init__.py # expect: no matches grep -n "<NEW>" Cargo.toml pyproject.toml polars_bio/__init__.py CHANGELOG.md - Compile —
cargo check(run in background; the workspace is large). Must exit 0. - Lockfiles — regenerate and stage BOTH:
cargo checkupdatesCargo.lock; confirmpolars_bioshows the new version.uv lockupdatesuv.lock; thenuv lock --checkmust pass (CI runs this).
- Report the readiness results plainly (pass/fail with evidence), per verification-before-completion.
6. Commit, push, open the PR
git add CHANGELOG.md Cargo.toml Cargo.lock pyproject.toml uv.lock polars_bio/__init__.py
git commit # message: "chore: release X.Y.Z" (pre-commit hooks run black/isort)
git push -u origin release/X.Y.Z
gh pr create --title "chore: release X.Y.Z" --body "..."
End the commit message with the standard Co-Authored-By: trailer and the PR
body with the standard Claude Code generation line.
The PR body should list the highlights since the last release (Added / Changed / Fixed, with PR numbers) and the release-readiness results.
7. Draft the LinkedIn announcement
See references/linkedin-template.md. Emoji-led, scannable, one emoji per
highlight, ends with pip install polars-bio==X.Y.Z, a GitHub star CTA, and
genomics/Python/Rust hashtags. Show it to the user — they post it manually.
What this skill does NOT do
- It does not tag or publish to PyPI/crates.io — that's the maintainer's CI on merge. Stop at the PR + announcement draft unless the user asks for more.
- It does not edit upstream
datafusion-bio-formatsversions.