Releasing RustiQ
RustiQ uses dist to build and
publish binary archives to GitHub Releases. crates.io publication runs in a
separate workflow. Registry lookup or dry-run failures block promotion; once
promoted, a registry publishing failure does not block the binary workflow.
Neither workflow creates a release from a GitHub Release event; both are
explicitly dispatched by promotion after a merge into main, and crates.io publication can also be
started manually for an existing tag.
Binary releases
The repository-root dist-workspace.toml pins dist 0.34.0, selects only the
rustiq-chem CLI package, and lists the six supported targets. Pull requests
run dist plan; this checks the release plan without building or publishing
artifacts. The generated workflow also runs on non-main pull requests because
dist 0.34.0 does not expose a pull-request base-branch filter. A pull request
cannot enter the publishing jobs.
All six target builds use native GitHub-hosted runners: Linux x64 on
ubuntu-22.04, Linux ARM64 on ubuntu-24.04-arm, macOS Intel on
macos-15-intel, Apple Silicon on macos-latest, Windows x64 on windows-2025,
and Windows ARM64 on windows-11-arm. The configuration explicitly selects
the ARM runners and preserves the native Windows runners.
The
rustiq-chem-core library remains a build dependency and is not packaged as an
application. The generated workflow is .github/workflows/release.yml. It pins
actions/upload-artifact to an exact version. Because cargo-dist emits a major
version reference for that action, dist-workspace.toml sets
allow-dirty = ["ci"] so dist plan does not reject the exact pin as an
out-of-date generated workflow. Keep the pin synchronized with the intended
upload-artifact release when updating the workflow or cargo-dist. Preserve the
custom build-manual job, the local-build dependency on it, and the download
into manual/html/ when regenerating the release workflow; generated defaults do
not contain this offline-manual integration. Preserve the static local-build job
name, Build platform artifacts, so pull requests that skip artifact builds
display a readable name before the target matrix is expanded.
Release rehearsal
The Release artifact smoke test workflow runs automatically on pull requests
targeting main, testing the prospective merge commit. To rehearse another
revision, run Actions → Release artifact smoke test → Run workflow and select
the commit, branch, or tag. New runs cancel older rehearsals for the same pull
request or selected workflow ref. The workflow builds the configured target archive with the pinned dist version on each target’s native
GitHub-hosted runner. It extracts that archive, verifies its dist-generated
SHA-256 checksum, starts the packaged rustiq executable, checks --help,
and runs the checked-in H2/STO-3G regression with a copied local basis fixture
and JSON output. Per-target results appear in the job summary; archives, dist
manifests, and command logs are uploaded as workflow artifacts. Successful targets also
publish a separate rustiq-packages-<target> artifact containing the distributable
archive and checksums. Follow the download link in the run summary, then unzip
the GitHub Actions artifact to obtain the original release archive. Downloads
require a GitHub login and remain available until the workflow artifacts expire.
After all six native builds pass, a global job runs dist build --artifacts=global,
just as publication does. This builds the corresponding source archive and combined
checksums. Download rustiq-release-packages from its summary link to obtain
all native archives and global assets together. Only after this upload do the
six native smoke jobs download that same artifact and test their target archive
against its combined sha256.sum. Thus the downloadable final packages are
the packages exercised by the smoke tests. The source archive requires a
clean committed checkout; commit local changes before rehearsing them in CI.
The Apple Silicon smoke job uses the native macos-latest runner.
This checks archive integrity and basic executable operation for the six
configured targets. The H2 energy comparison reuses the regression reference
and is a packaging smoke test, not broad scientific validation, independent
platform validation, or proof of compatibility with every supported operating
system. The runs use GitHub-hosted runners, which are not pristine operating
system installations. The rehearsal requires the combined sha256.sum generated by the global
dist build and verifies each tested archive against it. A checksum made during a rehearsal does not independently validate
a published release.
The rehearsal has read-only repository permissions and supports pull_request
events targeting main and manual workflow_dispatch runs. It does not run dist’s host command, upload
release assets, create a GitHub Release, create tags, or publish crates. It
complements .github/workflows/release.yml, whose dispatched release path builds
and publishes the release assets. Pull requests targeting develop continue
to use the generated plan-only check without the six-target rehearsal.
Each executable archive includes rustiq (named rustiq.exe on Windows),
the README, LICENSE-MIT, LICENSE-APACHE, COPYING, DISTRIBUTION.md, and
THIRD-PARTY-NOTICES.txt, and the complete offline manual in manual/html/.
The release workflow builds the validated Nix book once and downloads that
artifact into every native archive build. Archive rehearsals check the manual
after relocating the extracted tree. See documentation checks
for local archive preparation and the offline build process. RustiQ-authored source retains MIT OR Apache-2.0;
Malachite and ansi_colours retain their LGPL terms. COPYING supplies the GPLv3
text referenced by LGPLv3, rather than imposing GPLv3 on all RustiQ code. Unix archives use .tar.gz
and Windows archives use .zip. Dist adds a .sha256 file for each archive
and a sha256.sum manifest. Installers are disabled. Archives do not include
calculation samples or basis data.
Each release also includes rustiq-corresponding-source.tar.gz, generated by
the dist extra-artifact command python3 tools/distribution.py source before
publication. This supplies the committed repository tree and all locked
dependency sources. The generated Cargo configuration allows registry-free
rebuilding. Dist’s repository-only source tarball is disabled because it would
omit the dependency sources.
Dist treats this file as an extra artifact; it does not emit a separate
checksum for it in the release plan. Before publishing, verify that the source
archive is actually present in the release assets and corresponds to the tagged
commit. Its checksum can be calculated separately when verifying distribution
integrity.
See licenses and redistribution for rebuild instructions and
the requirements for package maintainers. The Nix package installs the same
notice documents into share/doc/rustiq/manual/html; its metadata describes RustiQ’s
MIT/Apache terms and the bundled LGPL/MPL components.
To verify the assets listed in sha256.sum on Linux, download it and all listed
assets into one directory, then run:
sha256sum --check sha256.sum
On macOS, use the built-in shasum command:
shasum -a 256 --check sha256.sum
On Windows PowerShell, use the explicit manifest comparison in the installation guide, replacing its example archive filename with the archive for your target.
The archive target identifies the operating system and CPU architecture; it does not guarantee compatibility with every Linux distribution or older system library. The Linux GNU archives require a compatible glibc runtime.
Maintaining the changelog
Maintain the repository-root CHANGELOG.md by hand using Keep a Changelog categories and concise, user-facing entries.
- Add notable implemented changes under
Unreleasedduring development. - Review and edit the entries before each release, checking capabilities and limitations against the current code, tests, and documentation.
- During release preparation, move the relevant entries into a versioned
section, such as
## [0.1.0-beta.1] - YYYY-MM-DD. - Replace
YYYY-MM-DDwith the actual release date and update the changelog’s version and comparison links. - Leave an empty
## [Unreleased]section for future changes, omitting empty categories. - After updating package versions as described below, run
dist plan --tag v0.1.0-beta.1 --output-format=json(substitute the intended tag). Check thatannouncement_github_bodycontains the intended version’s changelog entries and limitations, rather than only artifact download notes. - Finalize and commit the changelog with the release preparation changes before creating the release tag.
The first public beta should document the capabilities available at launch. For subsequent beta releases, describe incremental changes instead of repeating the complete feature inventory.
Before the first release, preserve the existing Unreleased link to the develop
branch’s commit history: no beta release tag exists yet. When preparing
v0.1.0-beta.1, finalize the entries under a dated version heading and leave
Unreleased ready for future changes:
## [Unreleased]
## [0.1.0-beta.1] - YYYY-MM-DD
Replace YYYY-MM-DD with the actual publication date. As part of that release
preparation, set the reference links to the following convention, which applies
after the first release:
[Unreleased]: https://github.com/mveril/RustiQ/compare/v0.1.0-beta.1...HEAD
[0.1.0-beta.1]: https://github.com/mveril/RustiQ/releases/tag/v0.1.0-beta.1
For each subsequent release, add its version link and update the Unreleased
comparison to start from the latest published version. Until release preparation
begins, do not finalize the beta section or add links to an unpublished release.
Dist 0.34.0 has built-in changelog support.
It discovers the root changelog and selects the matching version section for
release notes. It can also use Unreleased for prereleases, but release
preparation should still finalize a dated version section. The existing
workflow passes dist’s announcement_github_body to gh release create;
no separate release-note generator or workflow change is needed.
Preparing a release
Start from develop on a task branch:
git switch -c chore/prepare-version develop
From the repository root, update the version in both package manifests and
update the CLI’s rustiq-chem-core dependency requirement to that same
version:
Cargo.toml(rustiq-chem)crates/rustiq-core/Cargo.toml(rustiq-chem-core)Cargo.toml’srustiq-coredependency
After editing the manifests, regenerate Cargo.lock and the corresponding
notices. The distribution script needs Python with tarfile extraction filters:
Python 3.12 or later, or the backported filters in Python 3.10.12+/3.11.4+.
This is independent of the optional PySCF environment. Enter the Nix rust or
full shell for the pinned notice generator, or install cargo-about 0.9.2
outside Nix, then run:
cargo check --workspace
cargo install cargo-about --version 0.9.2 --locked
python3 tools/distribution.py notices
python3 tools/distribution.py check
cargo deny --locked --workspace --all-features check
git add CHANGELOG.md Cargo.toml crates/rustiq-core/Cargo.toml Cargo.lock THIRD-PARTY-NOTICES.txt THIRD-PARTY-NOTICES.sha256
git commit -m "Prepare release X.Y.Z"
The publishing workflow checks both package versions and the CLI dependency requirement against the tag using Cargo metadata. After committing the version change, build the workspace:
cargo check --workspace --locked
Promotion requires registry availability and a successful workspace publication dry run.
There is currently no local Nickel patch or corresponding crates.io
publication block. Before publishing crates, review the acknowledged
upstream advisories described in crates.io publication
and run cargo publish --dry-run --workspace --locked.
Regenerate and commit notices after changing dependencies, license configuration, the notice template or generator. Review supplemental upstream notices when updating crates whose published packages omit their licenses. CI checks their package inventory, input hashes and generated-file integrity. This catches stale or accidentally edited output; it does not independently verify every license clause or certify legal compliance. Review generated notices and supplemental attribution when changing dependencies. To rehearse the complete source delivery from a clean committed checkout:
python3 tools/distribution.py source
The command rejects source changes and untracked files, except the dist workflow’s generated JSON manifests. It verifies offline Cargo resolution from the archive staging tree. Before a release, also extract the source artifact and run the reconstruction command documented in licensing.
Review the dist release plan and regenerate the workflow after changing the
dist configuration or updating dist. The allow-dirty = ["ci"] setting means
the generator does not check whether the checked-in release workflow matches
its current template. Preserve the exact upload-artifact pin if you regenerate
the workflow:
dist plan
dist generate --mode ci
dist generate --mode ci --check
The Rust development shell includes the Nixpkgs cargo-dist package. The
generated workflow installs the exact dist version recorded in
dist-workspace.toml. Before regenerating locally, install that same version
if the Nixpkgs package differs:
cargo install cargo-dist --version 0.34.0 --locked
Development takes place on the develop branch. Use a version ending
in .dev, keeping both crate versions and the internal dependency synchronized.
Prepare a release on a chore/* task branch from develop: remove the development
identifier, finalize the dated changelog, and update Cargo.lock.
Push the task branch and open a pull request targeting main:
git push -u origin chore/prepare-version
gh pr create --base main --head chore/prepare-version
The required Release eligibility check rejects development versions, mismatched package versions, and versions already present as a tag, GitHub Release, crates.io package, or published manual. Lookup failures block release eligibility. The check also runs:
cargo publish --workspace --locked --dry-run
Cargo packages and compiles both crates, including the unpublished core dependency.
Keep package verification enabled. A failure blocks the merge. Update the branch
with main before merging so checks apply to the prospective merged result.
Every merge into main, including documentation changes, requires a fresh version.
Alpha and beta versions are allowed; a complete dev prerelease identifier is not.
After merging, Promote release repeats validation and the publication dry run,
creates vVERSION at the merged commit, and explicitly dispatches the binary,
crates.io, and documentation workflows at that tag. Do not create or push tags
manually. Tag creation with the Actions token does not itself start push workflows.
The three publications run independently; inspect each workflow for completion.
The registry publication still requires CARGO_REGISTRY_TOKEN.
Resume development with the next intended version ending in .dev on develop.
No release/* branches are used. Check the matching /RustiQ/vVERSION/ manual
and versions.json after publication. Versioned manuals remain immutable.
The documentation homepage opens the latest stable version, otherwise the latest
prerelease, otherwise /dev/. Pushes to develop update only /dev/.
crates.io publication
Nickel 0.19’s published dependency graph currently includes
imbl-sized-chunks 0.1.3, affected by the unfixed security advisory
RUSTSEC-2026-0292, and the unmaintained bitmaps 3.2.1
(RUSTSEC-2026-0247). RustiQ intentionally uses Nickel’s published releases:
it does not fork Nickel or backport its transitive dependencies.
deny.toml records explicit exceptions for these advisories. Passing
cargo deny check does not indicate that the vulnerability is fixed.
Review these exceptions and upstream fixes before each release; see
dependency review.
The crates.io workflow is not blocked by these exceptions. A merge into main
can trigger publication of both crates while the upstream vulnerability remains
present. Decide whether to publish that version with the documented risk or
hold the release pull request until Nickel publishes a compatible fix.
.github/workflows/publish-crates.yml publishes both workspace crates when a
release is promoted from main. Cargo publishes workspace members in dependency
order, so
rustiq-chem-core is available before rustiq-chem. Before publishing, the
workflow uses the crates.io API to exclude package versions that already
exist. If a run publishes the core crate and then fails while publishing the
CLI, rerunning the same tag skips the existing core version and retries the
CLI. For a retry, open Actions → Publish crates.io packages → Run
workflow, enter the existing v… tag, and start the workflow.
This workflow remains separate from the dist binary release workflow and can
also be run manually for an existing tag.
The workflow currently authenticates with the Actions secret
CARGO_REGISTRY_TOKEN. Create it from a crates.io API token with only package
publishing permissions, then add it under the repository’s Actions secrets
before the first release. Keep the token out of repository files and do not
grant permissions to yank crates.
Crates.io Trusted Publishing uses GitHub OIDC and avoids a long-lived token,
but crates.io requires each crate to have an initial token-authenticated
publication before a Trusted Publisher can be configured. For RustiQ’s first
publication, use CARGO_REGISTRY_TOKEN to publish both packages through the
tag workflow or its manual execution for an existing tag. After both crates
exist, configure a Trusted Publisher for each crate in crates.io to trust this
repository and the publishing workflow. A follow-up change can then replace
the token authentication with OIDC; the binary release workflow does not use
or depend on the crates.io token.
Cargo may time out while waiting for a published version to appear in the registry index even when the upload succeeded. Check the version on crates.io before retrying; the workflow’s existence check will skip a version already published.
All package formats derive their online documentation channel from the Cargo
version. Build a tagged release with nix build github:mveril/RustiQ/vX.Y.Z;
no package selector or release flag is required.
If tag creation succeeds but a dispatch fails, run the missing publication workflow manually at that existing tag. Do not rerun promotion to create the tag again. Rerun failed publication jobs for the same tag; never move the tag. Cargo skips already published crates, and the documentation publisher checks immutable content. For binaries, inspect an existing GitHub Release and its assets before retrying.
Resume development through a task pull request to develop. Port any release-only
fixes or changelog changes, then synchronize both crate versions, the internal
dependency, and Cargo.lock with the next version ending in .dev. Do not merge
release versions back without restoring the development suffix.
For an existing release tag, explicitly select that tag when retrying:
gh workflow run release.yml --ref vVERSION
gh workflow run docs-publish.yml --ref vVERSION
gh workflow run publish-crates.yml --ref vVERSION -f tag=vVERSION
Dispatch only the missing publication. A default-branch dispatch does not select the release revision. Rerun failed jobs in the original binary workflow when some assets already exist; do not create a competing GitHub Release.