4.0 KiB
Releasing
This is the maintainer's guide to versioning and the release automation. End users do not need it; they want the README and the setup guide.
Versioning
Releases are dated, not semantic. The version is YY.MM.PATCH:
YYis the two-digit year. 2026 is26.MMis the month with no leading zero. August is8, not08. The version has to parse as SemVer (Cargo insists), and SemVer rejects leading zeros in a numeric field, so26.08.0is invalid and26.8.0is the form to use.PATCHstarts at0in a new month and counts up for any further releases that month.
So the sequence across a few releases reads 26.8.0, 26.8.1, 26.9.0.
These sort correctly both as dates and as SemVer, because each field is compared as a number.
The number is a date, not a compatibility contract. Any release can change behavior; the CHANGELOG is where that is written down.
This is separate from rust-version in Cargo.toml, which is the oldest Rust that compiles the crate (currently 1.85, the edition-2024 floor).
The pixi manifest pins a specific recent toolchain for reproducible builds. The two numbers answer different questions and are allowed to differ.
Cutting a stable release
The version lives in two files, Cargo.toml and pixi.toml.
bump-my-version keeps them in step and computes the next number from today's date; it is configured in .bumpversion.toml and installed in the release pixi environment.
-
Record what changed: move the
Unreleasednotes inCHANGELOG.mdunder a heading for the new version, and commit that. A clean working tree is required, so this commit comes first. -
Preview the number the bump would produce:
pixi run bump-show -
Cut it:
pixi run bumpThis rewrites the version in both manifests, makes a
Release <version>commit, and tags it. The number follows the calendar: the first release in a month is.0, and a later one that month is.1. To see every edit before it happens, runpixi run --environment release bump-my-version bump patch --dry-run --verbose. -
Push the commit and its tag:
git push --follow-tags
The pushed tag triggers .gitea/workflows/release.yml, which rebuilds from the tagged commit, checks that the tag matches the Cargo.toml version (the bump has just made them agree), and publishes a Gitea release with the packaged binary and its checksum attached.
A v prefix on the tag is tolerated if you ever tag by hand.
The workflows
All three live in .gitea/workflows/ and build through pixi, the same way the site deploy does, so the runner needs pixi rather than a hand-installed Rust toolchain.
Each workflow installs pixi if it is not already on the runner.
ci.ymlruns on every push tomainand every pull request. It runspixi run check: formatting, clippy, the full test suite, and a docs build. This is the gate.nightly.ymlruns on every push tomain, so the nightly build tracks the branch. It also carries an optional dailycron. It builds the binary and publishes it as a single rolling prerelease taggednightly, replacing the previous one so the tag always points at the current tip.release.ymlruns on a version tag. It builds, regenerates the third-party license notices, packages the full bundle, and publishes a normal (non-prerelease) release named after the tag.
Publishing the API docs
.gitea/workflows/docs.yml builds the rustdoc and publishes it in two channels: release/, built from a version tag, and nightly/, built from main.
The site root redirects to the latest release, falling back to nightly until the first release exists.
Each page carries a small switcher to flip between the two channels.
It runs on the pixi-build-rust runner, so the doc build reuses the baked toolchain and the crate cache rather than compiling cold.
That runner builds inside a container, so give its job containers access to the docs directory by adding it to the runner's config.yaml alongside the cache volumes.