rust-build-package-release-action is an opinionated GitHub Action that automates release workflows for Rust projects,
built as a single Rust binary crate with clap subcommands (edition 2024, rust-version 1.85).
This is a conventions-based, opinionated release process extracted from:
Run all tests:
cargo testRun clippy:
cargo clippy --all-targets -- -D warningsSee CONTRIBUTING.md as well.
For verifying YAML file syntax, use yq, Ruby or Python YAML modules (whichever is available).
action.yml: GitHub Action definitionCargo.toml: crate manifestsrc/main.rs: clap CLI and command dispatcher (routes subcommands to modules)src/lib.rs: shared utilities (env_or,parse_comma_list) and module re-exportssrc/error.rs: error type andResultaliassrc/output.rs: GitHub Actions output helpers (output,output_multiline,print_hr)src/platform.rs: target triple parsing and platform detectionsrc/version.rs: version validation,get-version,get-release-versionsrc/cargo_info.rs: reads package metadata fromCargo.tomlsrc/changelog.rs: extracts and validates changelog entriessrc/build.rs: cargo build orchestrationsrc/archive.rs: archive file listing, doc copying, include handlingsrc/checksum.rs: SHA-256, SHA-512, BLAKE2 checksum generation and verificationsrc/collect_artifacts.rs: collects artifacts, computes checksums, outputs structured data for Homebrew/Wingetsrc/release.rs: unified release command that auto-selects platform from target triplesrc/nfpm.rs: nfpm config generation for .deb/.rpm/.apk packagessrc/homebrew.rs: Homebrew formula generatorsrc/aur.rs: Arch Linux PKGBUILD generatorsrc/winget.rs: Winget manifest generatorsrc/format_release.rs: GitHub Release body formattersrc/sbom.rs: SPDX and CycloneDX SBOM generation via cargo-sbomsrc/sign.rs: Sigstore/cosign artifact signingsrc/download.rs: downloads artifacts from GitHub releasessrc/testing.rs: tests Debian/RPM packages and Windows binariessrc/tools.rs: external tool installation (cross, cargo-zigbuild, nfpm, etc.)tests/test_helpers.rs: shared test utilities (create_temp_file,create_temp_text_file)tests/*_tests.rs: unit tests for each moduletests/*_proptests.rs: property-based tests (proptest) for version, changelog, platformexamples/*.yml: workflow examples for common scenarios
Building macOS binaries requires a native runner:
- Intel Mac (
x86_64-apple-darwin): usemacos-15-intelormacos-26-intelrunners (macos-13was removed in December 2025) - Apple Silicon (
aarch64-apple-darwin): usemacos-14or newer runners for native compilation - Building for Intel on Apple Silicon runners is technically possible but not recommended (cross-compilation adds complexity)
- Each architecture needs a separate build job with its corresponding runner
- The action generates Homebrew formulas that auto-select the correct binary per architecture
- Platform detection in
src/platform.rsalready handles both:macos-x64andmacos-arm64
For workflows, use a matrix strategy to build both in parallel. See examples/macos-multi-arch.yml for a complete example.
- Use
usestatements at the top module level, never in function scope - Avoid fully qualified type paths (e.g.
std::fmt::Display) unless needed for disambiguation - All tests go in
tests/as integration tests, not inline#[cfg(test)]modules - Use
LazyLock<Mutex<()>>to serialise tests that modify process-wide state (env vars, CWD) - Mark
env::set_varandenv::remove_varcalls asunsafewith a safety comment - Format with
cargo fmt --all, lint withcargo clippy --all-features --all
- Do not commit changes automatically without an explicit permission to do so
- Never add yourself to commit co-authors list
- Never mention yourself in commit messages
- If a
ghoperation fails with an authentication failure, unsetGITHUB_TOKENand retry once, asghmay be configured to fetch the token differently
Only add very important comments to the tests and the implementation.
Write like an engineer who values clarity and simplicity. This applies to all prose: design docs, analyses, notes, and commit messages.
- Plain and factual: state the why in one line, never narrate the what
- Literal mechanism over metaphor: name the actual thing, not an image of it
- Prefer the plainest word. No coined verbs, no jargon for its own sake
- No flourish, no editorializing, no imagery. Real domain terms are fine
- If a sentence needs a second clause to justify itself, it is probably too clever
- Plain full sentences over compressed clever noun phrases: "a helper
crate", not "a
tower-shaped convenience" - State guarantees and behavior explicitly; do not leave them implied by jargon
- Name tools and platforms precisely:
rustc1.92, edition 2024, crates.io, WebAssembly - No bold for emphasis; bold is for structural labels only, and sparingly
- No "term — explanation" em-dash glosses: use ": " or parentheses
- These vocabulary rules apply to identifiers too: test function names, helper modules, and fixture names use the same plain words as prose
- Never add full stops to Markdown list items
- Use "X and Y" in prose, not "X / Y" slash-shorthand. Exceptions: unit
fractions (
bytes/sec), single-concept abbreviations (I/O), and paths or code (tests/unit/,src/lib.rs) - Wrap code identifiers in backticks in prose: types like
Vec<T>, traits likeDisplay, functions likeIterator::next, modules, file names, and paths - Avoid robotic labels such as
**Thing / other:**; write a plain sentence or a simple label - Match the existing conventions of the file and subdirectory you are editing: bullet character, heading depth, ID schemes, and table shape vary by project, and the local choice wins
GitHub Actions use major version tags (@v1, @v2, @v3) as their public API.
Consumers pin to @vN and automatically receive all non-breaking updates.
Suppose the current development version in Cargo.toml is N.0.0 and CHANGELOG.md has
a ## vN.0.0 (in development) section at the top.
To produce a new major release:
- Update the changelog: replace
(in development)with today's date, e.g.(Mar 22, 2026). Make sure all notable changes since the previous release are listed - Commit with the message
N.0.0(just the version number, nothing else) - Tag the commit:
git tag vN.0.0 - Move the floating major tag to the same commit:
git tag -f vN - Bump the dev version: set
Cargo.tomlversion to(N+1).0.0 - Add a new
## v(N+1).0.0 (in development)section toCHANGELOG.mdwithNo changes yet.underneath - Commit with the message
Bump dev version - Push:
git push && git push --tags --force
The --force on the tag push is required because the floating vN tag already exists
on the remote and must be moved forward.
- This crate is
publish = false: there is no crates.io publishing step - The floating major tag (e.g.
v2) is what consumers reference in their workflows - The precise tag (e.g.
v2.0.0) is for changelog traceability
Review the changes very carefully and holistically for correctness and safety, opportunities to meaningfully simplify the implementation without losing fidelity and effectiveness, the use of Rust idioms, the rich type system patterns, meaningful test coverage, API usability and whether the changes are worth adopting to begin with.
Look hard for ways to meaningfully improve both the tests and the implementation.
Perform 5 such iterations (holistic analysis runs).