Releases¶
Bloq releases the Rust workspace, Python package, and application binaries from one shared version. Website publication is separate; see Website maintenance.
Release flow¶
flowchart LR changes["Reviewed changes"] --> pr["release-plz PR version + changelog"] pr --> crates["Publish Rust crates"] crates --> tag["Version tag + release"] tag --> python["Python wheels + source archive"] tag --> binaries["CLI + editor archives"]
Important
GitHub Actions publication is disabled by default. Registry and binary publishing
jobs require RELEASE_PUBLISH_ENABLED=true. Keep it unset or false while preparing
a release or publishing manually. This variable does not gate local CLI uploads.
CI checks platform wheels and source installations for relevant PRs. The
PyPI workflow uses the same checks for tag and
manual builds. See website maintenance for site previews
and publication.
Binary release builds use the checked-in Cargo lockfile. Security audits cover
dependency PRs, dependency changes on main, and new advisories each week.
Names and versions¶
Distribution |
Name |
|---|---|
Rust library |
|
Rust CLI package / executable |
|
Desktop editor package / executable |
|
Python distribution / import |
|
Install bloq-py for Python. PyPI’s bloq is an unrelated package; installing
both in one environment can conflict because they share the import name.
All packages inherit workspace.package.version; internal Rust dependencies
use exact versions. Maturin reads that same version for Python. Release-plz
updates the version and internal requirements together. Routine publication
requires a merged release PR; the first publication uses the explicit bootstrap
procedure below. bloq_py, bloq_test, and xtask are not published
to crates.io.
Python-only changes ship with the next workspace release. For an earlier
Python release, prepare a reviewed release-plz- branch that updates the shared
version, exact internal requirements, and lockfile together, and generates its
changelog with release-plz. Do not set a separate Python version or replace
inherited Cargo versions.
Changelog¶
Do not edit CHANGELOG.md manually, including unreleased entries and version
headings. Release-plz owns versions, release PRs, and retained release notes.
Its supported changelog postprocessor invokes git-cliff over repository-wide
history since the previous stable tag. The same notes appear in the file and
release PR. just changelog previews them without writing the file.
The configuration follows git-cliff’s scoped template:
Conventional Commit scopes group entries into Python API (python or py),
Rust API and compiler (rust, graph, compile, ir, stim, vm), editor,
CLI, workflows and packaging (ci, build, workflows, release), documentation
(docs), and dependencies (deps). Each area has Added, Fixed, Changed, or
Performance subsections. Unscoped docs and ci commits use their corresponding
areas; other unscoped changes remain under General. Use meaningful scopes in
commit messages and squash-merge titles rather than inferring intent from files.
Record migration details in BREAKING CHANGE: footers and the relevant user
documentation; breaking entries retain the footer. Review generated notes in the
release PR. Release and development-version housekeeping is omitted. Package
changes still determine release eligibility and versions; Python, documentation,
and workflow changes outside publishable crates join the next eligible release.
Stable release versions¶
The first stable release is 0.1.0, tagged v0.1.0. Prepare subsequent stable
versions in reviewed release PRs.
Surface |
Published release |
|---|---|
Cargo manifests and exact internal dependencies |
|
Git tag and GitHub release |
|
Python wheel and source archive metadata |
|
|
|
Maturin converts SemVer into Python’s version spelling automatically. Keep the shared Cargo version as the source of truth. Review release-plz’s version and changelog PR before merging. Update the shared version, all exact internal requirements, and both Cargo lockfiles together.
Python environments, local artifact builds, and PyPI uploads use uv. Maturin remains the Rust build backend; its CI action supplies the manylinux wheel build.
git_release_type = "auto" creates a stable GitHub release for 0.1.1.
git_release_latest = true makes it GitHub’s Latest release. Version-specific
binary links use /releases/download/v0.1.1/; links to the current stable
binaries can use /releases/latest/download/.
Development versions after a release¶
Between releases, main uses a version such as 0.1.2-dev. Cargo and
bloq.__version__ retain that spelling; Python package metadata normalizes it to
0.1.2.dev0. The suffix stays unchanged throughout the development cycle.
After Rust, Python, and binary publication succeeds, the post-release workflow opens a PR for the next development version. Review its checks and merge it. The PR updates the shared version, exact internal requirements, and both Cargo lockfiles. Release-plz prepares the next stable version in a separate release PR.
For manual publication, run just start-dev on a chore/ branch after verifying
all uploads, then open a PR. To adjust a release candidate, use
just set-version VERSION and regenerate its pending changelog with release-plz.
Release credentials¶
Rust publication uses CARGO_REGISTRY_TOKEN in the crates-io environment and
repository secret RELEASE_PLZ_TOKEN. The GitHub token needs repository Contents
and Pull requests write access. It also triggers downstream workflows when
release-plz creates tags, releases, and development PRs.
Python publication uses a PyPI trusted publisher
for bloq-py, repository inmzhang/bloq, workflow pypi.yml, and environment
pypi. Manual uploads use a project-scoped PyPI token.
RELEASE_PUBLISH_ENABLED=true enables Actions publication. Manual CI and PyPI
workflow dispatches build and test without uploading packages.
Prepare and publish a release¶
The examples below target 0.1.1; replace that version when preparing a later
release.
Choose release-plz or manual Cargo and
uv uploads. For the fully manual route, keep
RELEASE_PUBLISH_ENABLED false throughout; the build-only workflow still works.
Finish the checks below against the reviewed release commit. Dispatch CI on
main to run every suite, build all supported wheels, rebuild the source archive,
and assemble the website without publishing:
gh workflow run ci.yml --repo inmzhang/bloq --ref main
gh run list --repo inmzhang/bloq --workflow ci.yml
Review the wheel, source-archive, and site artifacts from that commit. Confirm license copies, package contents, typing files, dependency resolution, and the release version in artifact metadata.
Release-plz publication¶
Review the release PR’s stable version, changelog, and checks. With publication enabled, merging the PR publishes Rust crates and creates the version tag and GitHub release. The tag triggers Python publication; the GitHub release triggers binary uploads. Verify all uploads before announcing the release.
If publication is interrupted, retry the failed jobs at the same commit and version. Do not move the tag or bump the version to retry an upload.
Manual Cargo publication¶
Authenticate locally with cargo login or privately supply
CARGO_REGISTRY_TOKEN; GitHub secrets are not needed for this route.
For CLI publication, use the following order, which also respects retained
versioned dev dependencies. Before each upload, run
cargo publish -p NAME --dry-run --locked after predecessors are available on
crates.io. Then publish that crate with
--locked, without --allow-dirty or --no-verify:
cargo publish -p bloq_utils --locked
cargo publish -p bloq_circuit --locked
cargo publish -p bloq_graph --locked
cargo publish -p bloq_ir --locked
cargo publish -p bloq_compile --locked
cargo publish -p bloq_stim --locked
cargo publish -p bloq-cli --locked
cargo publish -p bloq_editor --locked
cargo publish -p bloq_vm --locked
cargo publish -p bloq --locked
Wait for registry indexing between dependent uploads. Do not publish bloq_py,
bloq_test, or xtask. After all ten crates and the Python uploads below are
verified, create and push only the v0.1.1 tag, then create the stable GitHub
release. Prepare reviewed notes in a local file and pass them with --notes-file:
mkdir -p target
cp CHANGELOG.md target/release-notes.md
git tag -a v0.1.1 -m "Bloq 0.1.1"
git push origin refs/tags/v0.1.1
gh release create v0.1.1 --repo inmzhang/bloq --verify-tag --latest --title "v0.1.1" --notes-file target/release-notes.md
Choose release-plz or the combined manual Cargo/PyPI route. Keep the tag fixed once published.
Manual PyPI publication¶
Use the package artifacts from the CI or build-only PyPI run prepared above.
Confirm its headSha equals the
reviewed release commit, then download all four named artifacts from that one
run into a new directory. Replace RUN_ID in both the command and directory name:
gh run view RUN_ID --repo inmzhang/bloq --json headSha,status,conclusion
gh run download RUN_ID --repo inmzhang/bloq --name wheels-x86_64-unknown-linux-gnu --dir target/pypi-0.1.1-RUN_ID
gh run download RUN_ID --repo inmzhang/bloq --name wheels-aarch64-apple-darwin --dir target/pypi-0.1.1-RUN_ID
gh run download RUN_ID --repo inmzhang/bloq --name wheels-x86_64-pc-windows-msvc --dir target/pypi-0.1.1-RUN_ID
gh run download RUN_ID --repo inmzhang/bloq --name sdist --dir target/pypi-0.1.1-RUN_ID
Expect three bloq_py-0.1.1-cp310-abi3-*.whl files (manylinux x86_64,
macOS ARM64, Windows x86_64) and bloq_py-0.1.1.tar.gz. Inspect their metadata,
licenses, and contents; retain their SHA-256 hashes. Do not upload a mixed local
target/wheels/ directory or the local linux_x86_64 wheel.
For a new PyPI project, create an account-scoped API token. After the first
upload creates bloq-py, replace it with a project-scoped token. A pending
Trusted Publisher authenticates Actions, not a local terminal. Supply the token
through a password manager or a hidden Bash prompt; do not pass it in arguments:
read -r -s -p 'PyPI API token: ' UV_PUBLISH_TOKEN
printf '\n'
export UV_PUBLISH_TOKEN
uv publish --trusted-publishing never --publish-url https://upload.pypi.org/legacy/ \
--check-url https://pypi.org/simple --dry-run \
'target/pypi-0.1.1-RUN_ID/*.whl' 'target/pypi-0.1.1-RUN_ID/*.tar.gz'
The dry run does not upload files or prove registry authorization. After reviewing it, upload those exact artifacts and clear the token from the shell:
uv publish --trusted-publishing never --publish-url https://upload.pypi.org/legacy/ \
--check-url https://pypi.org/simple \
'target/pypi-0.1.1-RUN_ID/*.whl' 'target/pypi-0.1.1-RUN_ID/*.tar.gz'
unset UV_PUBLISH_TOKEN
Keep the publishing variable false for this route. On a partial upload, retry
with the same files and --check-url; uv skips files already present with matching
hashes. PyPI filenames cannot be replaced, including after deletion. If an uploaded
artifact is wrong, stop and prepare a new version instead of rebuilding that filename.
Check the release candidate¶
Check |
Evidence |
|---|---|
|
Formatting, build variants, documentation, Rust and Python tests |
|
Release-mode coverage including ignored cases |
Manual PyPI workflow |
Installed wheels and source archive tested; no publication |
Website artifact |
Documentation and generated APIs agree with the candidate |
Binary archives |
Executables at archive root; target and package names agree |
just changelog # preview unreleased notes
just py-build # build a local wheel
just py-sdist # build a local source archive
The Python workflow builds abi3-py310 wheels for Linux x86_64 (manylinux),
macOS ARM64, and Windows x86_64. It tests installed wheels on Python 3.10,
including the bloq console script, and rebuilds the source archive separately.
A wheel should work without Cargo on PATH; a source build requires Rust. Local
just py-build wheels may have a native linux_x86_64 tag that PyPI rejects.
Use the workflow’s manylinux wheels for upload, rather than that local smoke-test
artifact.
CLI/editor archives cover Linux x86_64, macOS ARM64, and Windows x86_64. They
support GitHub downloads and cargo-binstall. The CLI metadata maps package
bloq-cli to archive bloq-<target>; keep that URL synchronized with the binary
workflow. Desktop binaries are unsigned, and Linux still needs system display
and audio libraries.
Editor releases also include desktop archives for macOS and Linux. macOS
archives contain Bloq Editor.app; Linux archives include a launcher, icon,
and install.py for user-local installation. Windows executables embed the app
icon. Build with just editor-package (Python 3.11+) and check the package layouts
with just editor-package-check. Bare binary archives remain available for
cargo-binstall.
Inspect package-local Apache-2.0 license copies, Python typing files, gallery data, and command entry points. Include breaking API changes and Python-only changes in the release notes.
Wheels and binary archives include THIRD-PARTY-NOTICES.txt; editor archives
also retain the bundled font licenses. Check the dependency notices before
building release artifacts:
cargo fetch --locked
uv run --no-project python tools/third_party_licenses.py --check
After changing dependencies, run the script without --check and review both
generated copies. It collects license files from the locked Cargo sources,
including vendored native libraries and fonts. Crates that omit those files use
version-specific upstream notices and declared license texts recorded in
tools/third_party_licenses.json; review that file when updating those crates.
Verify uploads and install the release¶
Confirm all ten Rust packages list 0.1.1 under the expected owners,
PyPI lists bloq-py==0.1.1 with all supported wheels and the source archive,
and the GitHub release contains CLI/editor archives and checksums. Verify
installation in a fresh environment, outside the source checkout:
uv venv --python 3.10 target/release-install
uv pip install --python target/release-install/bin/python --only-binary=:all: "bloq-py==0.1.1"
target/release-install/bin/python -c "import bloq; assert bloq.__version__ == '0.1.1'"
target/release-install/bin/bloq --version
target/release-install/bin/bloq compile --gallery cnot -d 3 --quiet -o target/release-install/cnot.stim
cargo install bloq-cli --version 0.1.1 --locked --root target/release-cargo-install
target/release-cargo-install/bin/bloq --version
The venv and executable paths above are for Linux/macOS; on Windows use
Scripts/python.exe and Scripts/bloq.exe. Users can add the Rust facade with
cargo add bloq@=0.1.1, and install the Python CLI with
uv tool install "bloq-py==0.1.1". Explicit versions select the release without
accidentally installing a placeholder or an unrelated Python project.
Website publication is separate. Use release_tag=v0.1.1 and make_stable=true
to build its retained documentation snapshot and stable alias. Review the website
artifact with publish=false before publishing it. Set
RELEASE_PUBLISH_ENABLED=false again if you want a manual hold until the next
release. That stops future Actions uploads; it does not undo
an upload or cancel one already running.
Recover an interrupted release¶
Workspace publication is not atomic. Verify all published crates and downstream workflows before announcing completion.
Failure |
Recovery |
|---|---|
Registry rate limit or interrupted crate publication |
Follow the registry retry guidance and rerun the failed job at the same commit |
Python publication |
Rerun failed jobs for the same tag |
Missing CLI/editor assets |
Run the Binaries workflow manually with the existing release tag |
Future publication must stop |
Set the publishing variable to false; this does not undo uploads or stop an already-running job |
Published versions are immutable. Do not bump versions or move/delete tags just to retry an incomplete run. Keep the website’s retained release snapshots intact.