Releases
Two complementary tools own the release pipeline:
| Tool | Owns |
|---|---|
release-plz | version bumps, git tags, crates.io publish, per-crate CHANGELOG.md |
cargo-dist | cross-target binary tarballs, curl | sh / PowerShell installers, sha256 sidecars |
They run on the same tag (nexo-rs-v<version>) and stay independent
— no overlapping config. Phase 27 brings both online; Phase 27.2
wires the GitHub Actions workflow that combines them on tag push.
What ships
The nexo binary is the only artifact in release tarballs. Every
other binary in the workspace (driver subsystem, dispatch tools,
companion-tui, mock MCP server) carries
[package.metadata.dist] dist = false so cargo-dist excludes it.
Dev / smoke programs (browser-test, integration-browser-check,
llm_smoke) live as [[example]] entries under examples/ for
the same reason.
Build provenance — nexo version
build.rs injects four stamps captured at compile time:
NEXO_BUILD_GIT_SHA— short git SHA of the build commit (orunknownoutside a git checkout)NEXO_BUILD_TARGET_TRIPLE— full Rust target tripleNEXO_BUILD_CHANNEL— opaque channel marker; defaults tosource. The release workflow overrides viaNEXO_BUILD_CHANNEL=apt-musl(etc.) so support tickets carry install-channel provenance.NEXO_BUILD_TIMESTAMP— UTC ISO8601 timestamp of the build
Operators see them with:
nexo version
# nexo 0.1.1
# git-sha: abc1234
# target: x86_64-unknown-linux-musl
# channel: apt-musl
# built-at: 2026-04-27T12:34:56Z
nexo --version (without --verbose or the subcommand) prints the
short form nexo <version>.
Local validation
make dist-check
Builds the host-target tarball via dist build --target $(rustc -vV | sed -n 's|host: ||p') and runs
scripts/release-check.sh.
The smoke gate verifies every present tarball contains the bin +
LICENSE-* + README.md and that the host-native --version
output matches the workspace version. Targets the local toolchain
can't satisfy emit [release-check] WARN lines instead of failing.
Full setup notes (cargo-dist, cargo-zigbuild, zig, rustup targets):
packaging/README.md.
What's automatic vs manual
| Step | Owner |
|---|---|
| Bump version + open release PR | release-plz (CI on push to main) |
Tag commit + crates.io publish | release-plz (on PR merge) |
| Build 2 musl tarballs (x86_64 + aarch64) | release.yml (Phase 27.2 ✅) — cargo-dist |
Build Termux .deb (aarch64-linux-android) | release.yml (Phase 27.2 ✅) — packaging/termux/build.sh |
| Upload tarballs + Termux deb + sha256 sidecars | release.yml (Phase 27.2 ✅) |
Smoke-test nexo --version + provenance stamps | release.yml (Phase 27.2 ✅) |
| Sign tarballs + Termux deb (cosign keyless) | sign-artifacts.yml (Phase 27.3 ✅) |
| Generate CycloneDX + SPDX SBOMs | sbom.yml (Phase 27.9 🔄) |
Apt repo publish + signed Release file | Phase 27.4 deferred |
| Yum / dnf repo publish | Phase 27.4 deferred |
| Termux pkg index | Phase 27.8 deferred |
| Homebrew bottle auto-PR | Phase 27.6 PARKED (Apple targets dropped) |
nexo self-update | Phase 27.10 deferred |
Adding a new bin to the release
- Declare the
[[bin]]in the appropriate crate'sCargo.toml. - If the crate hosts the bin via
[package.metadata.dist] dist = false, either remove that opt-out or move the bin to a new crate that doesn't carry it. - Re-run
make dist-checkand confirm the new bin shows up under[bin]in the dist plan output. - Update
scripts/release-check.sh's per-archive content check if the new bin should be required.
Adding a new target
- Append the target triple to
targets = […]indist-workspace.toml. - Append the matching tarball name to
EXPECTED_TARBALLSin the smoke gate. - Land the toolchain story in the GH Actions release workflow (Phase 27.2) — without that, the target builds locally only.