Skip to content

Releasing Zirium

Zirium publishes one Rust crate, one Python package, and standalone CLI archives. The internal zirium-python crate is only a build component and must remain marked with publish = false.

Pushing a version tag starts Release, which runs Quality on the tagged commit. After the checks pass, approve the crates-io and pypi deployments to publish the registry packages. PyPI receives the checked artifacts. CLI archives publish automatically to GitHub Releases after Quality and both binary checks pass, independently of the registry approvals.

Both registries use trusted publishing for the zirium package. Each trusts GitHub owner zayenz, repository zirium, and workflow release.yml, with a separate environment:

Registry GitHub environment
crates.io crates-io
PyPI pypi

Both environments require manual approval by zayenz. Publishing jobs receive short-lived credentials; no registry token needs to be stored in GitHub.

Choose the new version and update these files:

  • crates/zirium/Cargo.toml
  • crates/zirium-python/Cargo.toml
  • pyproject.toml
  • CHANGELOG.md
  • Version references in README.md and docs/compatibility.md

Keep the three package versions identical and refresh both lockfiles after changing them:

Terminal window
cargo check --workspace
uv lock

Commit Cargo.lock and uv.lock with the version changes. The editable Python package’s version is recorded in uv.lock; leaving it unchanged makes the release’s locked environment check fail.

Run the local quality checks described in the compatibility guide, then inspect the package contents:

Terminal window
cargo publish -p zirium --dry-run --locked
cargo package -p zirium --list

Inspect the crate under target/package/, the source distribution, and a local version-specific wheel. Commit the release changes and wait for Quality to pass on that commit. The release maintainer then tags it and approves publication.

Tag the checked commit and push only that tag. The commands below use 0.3.0 as an example; substitute the version being released throughout.

Terminal window
git tag -a v0.3.0 -m "Zirium 0.3.0"
git push origin v0.3.0

Do not use git push --tags. This repository may contain local tags that are not part of the public release history.

Release verifies that the tag names a commit on main and matches all package versions. Quality checks the Rust package, builds and installs a source distribution, and builds version-specific wheels for conventional CPython 3.11 through 3.14 on Linux x86_64 (manylinux) and macOS arm64. PyPI publishes those artifacts directly; no stable-ABI wheels are produced.

When these checks pass, open the workflow run, select Review deployments, select both environments, and approve. Each publishing job uploads its package independently. The internal zirium-python crate is not published to crates.io.

After approving the deployment, query the Rust package and install the Python package from their registries:

Terminal window
cargo info zirium@0.3.0
uv run --no-project --isolated --with zirium==0.3.0 python -c \
'import zirium; assert zirium.parse_text("\"test\"() : () -> ()")'

Check the crates.io, docs.rs, and PyPI pages before announcing the release.

Release builds the CLI with Rust 1.88.0 and the committed lockfile for x86_64-unknown-linux-musl and aarch64-apple-darwin. Linux builds use ubuntu-22.04 with musl tools and reject dynamically linked executables. macOS builds use macos-15 with MACOSX_DEPLOYMENT_TARGET=11.0. The platform baselines are Linux kernel 3.2 and macOS 11; CI runs on the listed runners, not the minimum OS versions.

Each archive contains zirium, LICENSE-MIT, and LICENSE-APACHE under zirium-VERSION-TARGET/, with a separate .tar.gz.sha256 checksum file. The build job extracts the archive outside the checkout, checks the version, and runs a strict StableHLO query to check the embedded registry.

The publishing job downloads these checked archives, verifies their checksums, and uploads them to a draft GitHub Release before publishing it. Only this job receives contents: write; it uses the workflow’s GitHub token and needs no additional registry credentials. Release notes are generated by GitHub. Check both platform downloads before announcing the release.

The three distribution channels publish independently. Rerun only the failed publishing job; do not rerun a successful upload.

Do not move or replace a published release tag. If the workflow itself needs a fix, commit it to main, then run the updated workflow against the existing tag. Select only the distribution whose upload has not succeeded:

Terminal window
gh workflow run release.yml --ref main -f tag=v0.3.0 -f registry=pypi

Use registry=crates-io for a Rust-only retry, registry=pypi for Python, or registry=binaries for CLI archives. registry=both retains its original meaning of crates.io and PyPI only; registry=all selects all three channels and is the default for manual runs. Tag pushes select all three.

Check the destination first if an upload’s outcome is uncertain. Binary retries can replace assets in a draft release left by a failed upload. They refuse to replace assets in an already published release. The workflow builds the tagged source, not the current main checkout. Registry publishing still requires approval through the existing environments.