Files
alexm abc0bdf621
Sync README to GitHub / sync (push) Successful in 12s
CI / check (push) Successful in 8m31s
Deploy docs / deploy (push) Successful in 6m44s
Nightly / nightly (push) Successful in 10m33s
Dev (#1)
Reviewed-on: #1
2026-08-07 15:48:11 -04:00

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:

  • YY is the two-digit year. 2026 is 26.
  • MM is the month with no leading zero. August is 8, not 08. The version has to parse as SemVer (Cargo insists), and SemVer rejects leading zeros in a numeric field, so 26.08.0 is invalid and 26.8.0 is the form to use.
  • PATCH starts at 0 in 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.

  1. Record what changed: move the Unreleased notes in CHANGELOG.md under a heading for the new version, and commit that. A clean working tree is required, so this commit comes first.

  2. Preview the number the bump would produce:

    pixi run bump-show
    
  3. Cut it:

    pixi run bump
    

    This 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, run pixi run --environment release bump-my-version bump patch --dry-run --verbose.

  4. 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.yml runs on every push to main and every pull request. It runs pixi run check: formatting, clippy, the full test suite, and a docs build. This is the gate.
  • nightly.yml runs on every push to main, so the nightly build tracks the branch. It also carries an optional daily cron. It builds the binary and publishes it as a single rolling prerelease tagged nightly, replacing the previous one so the tag always points at the current tip.
  • release.yml runs 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.