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.
Trusted publishing setup
Section titled “Trusted publishing setup”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.
Check the release commit
Section titled “Check the release commit”Choose the new version and update these files:
crates/zirium/Cargo.tomlcrates/zirium-python/Cargo.tomlpyproject.tomlCHANGELOG.md- Version references in
README.mdanddocs/compatibility.md
Keep the three package versions identical and refresh both lockfiles after changing them:
cargo check --workspaceuv lockCommit 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:
cargo publish -p zirium --dry-run --lockedcargo package -p zirium --listInspect 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.
Publish a version
Section titled “Publish a version”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.
git tag -a v0.3.0 -m "Zirium 0.3.0"git push origin v0.3.0Do 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:
cargo info zirium@0.3.0uv 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.
CLI archives
Section titled “CLI archives”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.
Retry after a workflow failure
Section titled “Retry after a workflow failure”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:
gh workflow run release.yml --ref main -f tag=v0.3.0 -f registry=pypiUse 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.