This document describes the release pipeline implemented in
.github/workflows/release.yml. The older release-bundles.yml workflow is
retained as a manual-only legacy workflow and does not trigger on release tags.
| Deliverable | Artifacts | Produced by |
|---|---|---|
| Linux static binaries (musl) | lxmf-rs_<ver>_linux-x86_64.tar.gz, …_linux-aarch64.tar.gz, …_linux-armv7.tar.gz |
build job |
| Raspberry Pi | aarch64 (Pi 4/5, 64-bit OS) and armv7/armhf (32-bit OS) static builds above | build job |
| Debian / RPM packages | lxmf-rs_<ver>_{amd64,arm64,armhf}.{deb,rpm} (nfpm) |
package-linux job |
| Windows | lxmf-rs_<ver>_windows-x86_64.zip + lxmf-rs_<ver>_x64.msi (native signing when credentials are configured) |
package-windows job |
| macOS universal | lxmf-rs_<ver>_macos-universal.tar.gz (lipo universal2, optional codesign; notarization is not wired) |
macos-universal job |
| OCI container | ghcr.io/freetakteam/lxmf-rs:<ver> (and :latest for stable releases only), multi-arch (amd64 + arm64) |
container job |
| SBOM | CycloneDX per app crate (sbom-reticulumd.cdx.json, sbom-lxmf-cli.cdx.json, sbom-rns-tools.cdx.json) + sbom-container.cdx.json |
sbom and container jobs |
| Checksums | SHA256SUMS.txt + SHA256SUMS.txt.cosign.bundle (keyless sigstore signature) |
release job |
| Provenance | GitHub build-provenance attestations for every release file and for the container image | release and container jobs |
| Homebrew | Stable-release formula update when tap credentials are configured | homebrew job |
All Linux targets are built against musl with +crt-static, so the shipped
binaries are fully static (SQLite is bundled via rusqlite's bundled feature,
and the Linux dbus dependency enables vendoring). The Linux packages reuse the
release binaries. The container is built from the same release commit for its
declared architectures; verify its provenance and digest separately.
prepare ─► build (matrix: linux-musl x3, windows, macos x2)
│ ├─► macos-universal (lipo, optional codesign)
│ ├─► package-linux (nfpm → .deb/.rpm x3 arches)
│ ├─► package-windows (sign → ZIP + WiX MSI → sign)
│ ├─► sbom (cargo-sbom per app crate)
│ └─► container (buildx amd64+arm64 → ghcr, cosign sign, attest, syft SBOM)
└─► release (SHA256SUMS → cosign sign-blob → attest-build-provenance → gh release)
└─► homebrew (render formula with sha256 → push to homebrew-tap)
-
Bump
VERSIONand the crate versions (cargo xtaskrelease helpers already exist for crates.io publishing). Align public dependency requirements and include the separately publishedreticulum-rs-zeromqtransport. Keeplicense-file.workspace = trueon publishable first-party packages; the existing license and conditional secondary notice travel together. -
Run
cargo xtask release-checkand the applicable exact-commit hosted gates. This includes the existing license/security checks and Cargo package notice validation. Choose a new version and tag the reviewed commit:release_version="$(cat VERSION)" git tag -a "v${release_version}" -m "LXMF-rs v${release_version}" git push origin "v${release_version}"
Published tags are immutable. v0.14.0 is already published; do not recreate it for the next release.
-
The workflow runs end to end. A manual dry run is available via Actions → Release → Run workflow (set
publish: falseto build and smoke-test everything without publishing). -
Record the exact tag/commit and terminal outcomes for platform/OCI and crates.io publication, independent interoperability, and performance. Verify public downloads, checksums and provenance before describing the release as verified. Update the roadmap, release notes and installation examples without promoting unrelated hardware or production claims.
Pre-release tags containing -rc, -alpha, -beta, or preview are
published as GitHub pre-releases automatically.
| Secret | Purpose | Required? |
|---|---|---|
GITHUB_TOKEN |
Release upload, ghcr push, attestations | Automatic |
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TS_ENDPOINT, AZURE_TS_ACCOUNT_NAME, AZURE_TS_CERT_PROFILE |
Windows/MSI signing via Azure Trusted Signing | Optional — builds are unsigned if absent |
APPLE_CERTIFICATE_BASE64 (p12), APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_APP_PASSWORD, APPLE_TEAM_ID |
macOS codesign (notarization is not currently wired) | Optional — builds are unsigned if absent |
HOMEBREW_TAP_TOKEN |
PAT with write access to FreeTAKTeam/homebrew-tap |
Optional — tap update skipped if absent |
Every optional integration degrades gracefully: the workflow notices in the log and continues, so a fork or a fresh setup still produces a complete unsigned release.
# Example: verify the existing v0.14.0 release, using its downloaded files.
release_version=0.14.0
# 1. Download the artifact set plus checksums and signature bundle, then:
sha256sum -c SHA256SUMS.txt
# 2. Verify the checksum signature (keyless, against the CI identity):
cosign verify-blob \
--bundle SHA256SUMS.txt.cosign.bundle \
--certificate-identity-regexp "https://github.com/FreeTAKTeam/LXMF-rs/.github/workflows/release.yml.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
SHA256SUMS.txt
# 3. Verify build provenance of any file:
gh attestation verify "lxmf-rs_${release_version}_linux-x86_64.tar.gz" --owner FreeTAKTeam
# 4. Verify the container image:
cosign verify "ghcr.io/freetakteam/lxmf-rs:${release_version}" \
--certificate-identity-regexp "https://github.com/FreeTAKTeam/LXMF-rs/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify "oci://ghcr.io/freetakteam/lxmf-rs:${release_version}" --owner FreeTAKTeam# Container (works on Raspberry Pi OS 64-bit too):
# The image's anonymous volume is writable by UID 65532. For a bind mount,
# prepare the host directory for the image's nonroot user first:
mkdir -p data && sudo chown 65532:65532 data
docker run --rm -v $PWD/data:/data ghcr.io/freetakteam/lxmf-rs:latest
# Homebrew:
brew tap freetakteam/tap && brew install lxmf-rs
# Debian/Ubuntu/Raspberry Pi OS:
sudo dpkg -i lxmf-rs_0.14.0_arm64.deb # or amd64 / armhf
# Fedora/RHEL/openSUSE:
sudo rpm -i lxmf-rs_0.14.0_arm64.rpm # or amd64 / armhfThe optional Homebrew step was skipped for v0.14.0; check the tap's available version before using it. Downloaded archive/package names above match that release's assets.
- Why musl static builds? One binary per architecture runs on every Linux
distro regardless of glibc version, and the same binaries can go straight
into a
distroless/staticimage with no libc at all. - Why nfpm instead of cargo-deb/cargo-generate-rpm? nfpm builds both .deb and .rpm from a single config and does not require editing crate manifests.
- Why Azure Trusted Signing? It is the current Microsoft-recommended path
for OSS code signing and needs no hardware token or certificate files in CI.
A classic PFX +
signtoolflow can replace that step if the project prefers. - Why GitHub artifact attestations?
actions/attest-build-provenanceprovides SLSA-style provenance backed by sigstore with no key management;gh attestation verifyis the consumer-side check. The cosign-signed checksums cover consumers outside the GitHub ecosystem. - Raspberry Pi coverage is the stock aarch64 and armv7 musl targets; they run on Pi 3/4/5 and Zero 2 W under both 64-bit and 32-bit Raspberry Pi OS.