How rvt-rs cuts, tags, and publishes a release. Written for maintainers; also useful for downstream packagers who want to understand what every version number means.
rvt-rs follows Semantic Versioning 2.0.
Pre-1.0, the project uses the Cargo convention — breaking changes bump the minor version, not the major. That is:
- MAJOR — reserved.
1.0.0is the first release with a stability commitment. Until then, major stays at0. - MINOR — breaking API changes (pre-1.0) / new backwards-compatible features (post-1.0). Any change that renames a public item, changes a function signature, removes a variant, or alters serialised output shape goes here.
- PATCH — backwards-compatible bug fixes, doc corrections, performance work, and internal refactors that don't change public behaviour.
The current version lives in Cargo.toml under [package].version and
is mirrored in CITATION.cff and .release-please-manifest.json. All
three must agree at tag time.
There is no fixed cadence. Releases happen when material changes stabilise — a batch of decoders lands, an audit-driven correctness repair closes, a security fix ships, the schema layer covers a new Revit year. The project does not promise monthly, quarterly, or "every N weeks" releases.
If downstream packagers need a stable tag, pin to one. If you need a specific fix that hasn't shipped, the hotfix process below applies.
Every vX.Y.Z release:
- Has a signed git tag
vX.Y.Zonmain. - Has a GitHub release page generated from the tag, with changelog
excerpt, a link to the user guide, and pre-built
artefacts from the
publish.ymlworkflow. - Publishes the
rvtcrate to crates.io viacargo publish. - Publishes Python wheels (one per OS: Linux, macOS, Windows) and an
sdist to PyPI via the Trusted
Publisher OIDC flow in
.github/workflows/publish.yml. - Updates
CHANGELOG.mdwith human-readable entries grouped byAdded/Changed/Fixed/Security(Keep a Changelog format). - Updates
CITATION.cff(version:+date-released:) so academic citations resolve to the right archival state.
Run in order. Don't skip steps — the audit-driven culture for this project treats a silent skipped step the same as an untested change.
- CI clean on
main.cargo fmt --check,cargo clippy --all-targets --all-features -- -D warnings, and the full test matrix (ubuntu/macos/windows × stable + MSRV 1.85 on ubuntu) are all green.cargo deny checkandcargo auditare green. - Synthetic-project IFC integration test.
cargo test --release --test ifc_synthetic_project. This is the end-to-end regression gate — a synthesised document is written, re-read, and round-tripped through the IFC4 STEP exporter. - Corpus integration tests. If the phi-ag/rvt corpus is available
locally,
RVT_SAMPLES_DIR=... cargo test --release --test samples --test ifc_roundtrip --test field_type_coverage. CI runs these on every push, but running once locally against the checked-out corpus before tagging catches anything a stale cache might mask. - Bump version. Edit
Cargo.toml[package].version. If the Python wheel is part of this release,pyproject.tomlderives its version fromCargo.toml, so no manual bump is needed there — confirm withgrep -n version pyproject.toml. - Update
CHANGELOG.md. Move items from[Unreleased]into a new[X.Y.Z] — YYYY-MM-DDsection. Group byAdded/Changed/Fixed/Security. Write entries so a user reading just this section can decide whether to upgrade. - Update
CITATION.cff. Setversion:toX.Y.Zanddate-released:to the ISO date. These two fields must agree with the git tag and theCargo.tomlversion. - Update
.release-please-manifest.json. Set"."to the new version. This keeps release-please in sync even though the workflow isn't yet wired up (see Release automation). - Commit on
main. Commit message:release: vX.Y.Z. Include the four touched files:Cargo.toml,CHANGELOG.md,CITATION.cff,.release-please-manifest.json. - Tag.
git tag -s vX.Y.Z -m "rvt-rs X.Y.Z". The-ssigns the tag with the maintainer's GPG key — required for release authenticity. - Push.
git push origin main --tags. The tag push triggerspublish.ymlwhich builds wheels + sdist on all three OSes and uploads to PyPI via OIDC. - Publish the crate.
cargo publish.cargo publishre-runs tests and a clean package build before upload, so a broken tag still can't ship to crates.io. - Draft the GitHub release. On the repo's Releases page, draft a
new release from the
vX.Y.Ztag. Titlervt-rs vX.Y.Z. Body = theCHANGELOG.mdexcerpt for this version, verbatim, followed byUser guide: https://github.com/DrunkOnJava/rvt-rs/blob/vX.Y.Z/docs/user-guide.mdwith the real tag substituted. Publish once thepublish.ymlrun has finished and artefacts are attached. - Announce (optional). If the release is worth calling out,
drafts live under
docs/launch/— currentlyhn-show-hn.mdandreddit-r-rust.md, with further channels (buildingSMART forum, OSArch) added as drafts land. Post from the drafts rather than writing fresh copy; the drafts have been audit-reviewed for overclaiming.
Run these from a clean shell after the GitHub release, crates.io publish, PyPI
publish, and viewer deploy finish. Replace X.Y.Z with the released version.
cargo install rvt --version X.Y.Z --locked
rvt-inspect --version
python -m venv /tmp/rvt-release-smoke
. /tmp/rvt-release-smoke/bin/activate
python -m pip install --upgrade pip
python -m pip install "rvt==X.Y.Z"
python -c "import rvt; print(rvt.__version__)"Open https://drunkonjava.github.io/rvt-rs/ in a fresh browser session and
confirm the viewer loads. If a redistributable sample is available, inspect it
with both rvt-inspect and the viewer diagnostics download and compare the
failure mode.
When a critical issue (security advisory, data-loss regression, broken docs on docs.rs blocking adoption) surfaces between releases:
- Branch from the latest release tag.
git checkout -b hotfix/vX.Y.(Z+1) vX.Y.Z. - Apply the minimal fix. No refactoring, no adjacent cleanup, no new features. Smallest possible diff that closes the issue, plus a regression test that fails without the fix.
- Bump PATCH.
Cargo.toml+CITATION.cff+.release-please-manifest.jsontoX.Y.(Z+1). - Abbreviated checklist. Steps 1–3 above (CI + synthetic-project test + corpus if available). Skip nothing else.
- Tag, push, publish. Same as the full release flow —
git tag -s,git push origin --tags,cargo publish, draft GitHub release. - Backport to
mainif the fix applies there too.git checkout main && git cherry-pick <hotfix-commit>, resolve any drift, push.
- Monitor
docs.rs. The docs build is async and often trails thecargo publishby several minutes. If it fails, the package page shows a red banner; triage the build log and cut aPATCHwith the docs fix. The[package.metadata.docs.rs]block inCargo.tomlalready setsall-features = trueand thedocsrscfg, so feature gating (#[cfg(docsrs)]) in the source is respected. - Watch issues. Anything filed with the
release-blockerlabel after the release goes out is a hotfix candidate. - Confirm wheel availability.
pip install rvt==X.Y.Zshould resolve on Linux, macOS, and Windows within a few minutes of thepublish.ymlworkflow finishing. If PyPI reports only an sdist, re-check the wheel artefact step in the workflow run. - Confirm the crate is queryable.
cargo search rvtshould return the new version.cargo install rvt --version X.Y.Zshould succeed.
The following is the actual state of release infrastructure, not the aspirational state. Update this section when you wire up more of it.
- CI on every push to
mainand on every PR (.github/workflows/ci.yml):cargo fmt --checkcargo clippy --all-targets --all-features -- -D warningscargo testacrossubuntu-latest,macos-latest,windows-latest× stable + MSRV 1.85 on ubuntucargo doc --no-deps --libwithRUSTDOCFLAGS=-D warningscargo deny check(license allowlist, RustSec advisory deny, crates.io-only source, wildcard deny — seedeny.toml)rustsec/audit-check- Python wheel build + pytest integration tests on all three OSes
- PII-shape guard (belt-and-suspenders on top of
src/redact.rs)
- Publish workflow (
.github/workflows/publish.yml):- Triggered on tag pushes matching
v*and onworkflow_dispatch - Builds
abi3-py38wheels on Linux / macOS / Windows viaPyO3/maturin-action@v1+ an sdist - Uploads to PyPI via OIDC Trusted Publisher — no PyPI API token
stored in the repo. One-time PyPI setup instructions are inlined
at the top of
publish.yml. workflow_dispatchwithtest-pypi: truepublishes to TestPyPI instead — use this for dry runs of a new release path before the first real tag.
- Triggered on tag pushes matching
- Dependabot (
.github/dependabot.yml):- Weekly
cargoPRs, limit 5 open, labeldependencies+rust - Weekly
github-actionsPRs, labeldependencies+github-actions
- Weekly
- release-please: configuration is in place
(
release-please-config.json+.release-please-manifest.jsontargetingrustrelease-type for this crate) but is not yet wired to a GitHub Actions workflow. Until that lands, the release checklist above is run manually. - TestPyPI trial run (task
Q-13in the backlog) anddocs.rspreview build validation (taskQ-12) are planned but have not yet been exercised for any real release. The first maintainer to cut a release after those tasks land should update this section andCHANGELOG.mdaccordingly.