An opinionated, conventions-based GitHub Action that automates release workflows for Rust projects using Nu shell scripts.
- Build release binaries for Linux, macOS, and Windows from a single workflow
- Auto-enable
mimallocfor musl builds for projects that support it. This significantly improves allocator performance of the MUSL builds - Cross-compile with
cargo-zigbuildfor targets likeaarch64-unknown-linux-musl - Package as
.deb,.rpm,.apk(Linux),.dmg(macOS), or.msi(Windows) - Generate Homebrew formulae, AUR PKGBUILDs, and Winget manifests
- Sign artifacts with Sigstore/cosign
- Produce SBOMs in SPDX and CycloneDX formats
- Extract release notes from your changelog
- Compute SHA256, SHA512, and BLAKE2 checksums
This action expects:
- A certain changelog format: versions as
## v{version} ({date})headers - Tag format: tags prefixed with
v(e.g.,v1.2.3,v1.0.0-beta.1) - Versioning:
MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]format - Version variable: a repository variable
NEXT_RELEASE_VERSIONcontaining the expected next version (used byvalidate-version)
# Build a release binary (auto-selects platform from target)
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release
target: x86_64-unknown-linux-gnu
archive: 'true'Or use platform-specific commands:
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: x86_64-unknown-linux-gnu
locked: 'true'Copy this to .github/workflows/release.yml to get started:
name: Release
on:
push:
tags: ['v*']
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install stable --profile minimal
- uses: michaelklishin/rust-build-package-release-action@v1
id: build
with:
command: release
target: x86_64-unknown-linux-gnu
archive: 'true'
- uses: softprops/action-gh-release@v2
with:
files: target/x86_64-unknown-linux-gnu/release/*.tar.gzThis builds a Linux binary, creates a .tar.gz archive with checksums, and uploads it to a GitHub Release. Add more targets using a matrix strategy.
Universal options used across most commands.
| Input | Description | Default |
|---|---|---|
command |
Required. Command to run (see Commands) | — |
version |
Version string without v prefix |
— |
target |
Rust target triple | Platform default |
binary-name |
Binary name | Package name from Cargo.toml |
package |
Cargo package name (for workspaces) | — |
manifest |
Path to Cargo.toml | Cargo.toml |
working-directory |
Working directory for commands | . |
Standard Cargo build flags. These map directly to familiar cargo build options.
| Input | Description | Default |
|---|---|---|
pre-build |
Shell command to run before cargo build (e.g., frontend build step) |
— |
skip-build |
Skip cargo build and use existing binary | false |
binary-path |
Path to existing binary when skip-build is true |
— |
features |
Cargo features to enable | — |
profile |
Cargo build profile | release |
locked |
Build with --locked for reproducible builds |
false |
no-default-features |
Build with --no-default-features |
false |
rustflags |
Extra RUSTFLAGS for the build | — |
use-zigbuild |
Use cargo-zigbuild for cross-compilation |
false |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: aarch64-unknown-linux-musl
use-zigbuild: 'true'
archive: 'true'- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: x86_64-unknown-linux-musl
no-default-features: 'true'
features: 'rustls-tls'
locked: 'true'Tip: For musl builds, consider using mimalloc as the global allocator for improved performance.
For projects that require a frontend build (WASM, npm, etc.) before cargo build:
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: x86_64-unknown-linux-gnu
pre-build: 'cd frontend && npm install && npm run build'
archive: 'true'For Alpine/container workflows where the build happens in a separate step:
# Build in Alpine container
- name: Build in Alpine
run: |
cargo build --release --target x86_64-unknown-linux-musl
# Package the pre-built binary
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: x86_64-unknown-linux-musl
skip-build: 'true'
binary-path: 'target/x86_64-unknown-linux-musl/release/myapp'
archive: 'true'Control artifact generation and checksums.
| Input | Description | Default |
|---|---|---|
archive |
Create archive (.tar.gz on Linux/macOS, .zip on Windows) |
false |
checksum |
Checksum algorithms: sha256, sha512, b2 (comma-separated) |
sha256 |
include |
Extra files to include in archive (glob patterns, comma-separated) | — |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux
target: x86_64-unknown-linux-gnu
archive: 'true'
checksum: 'sha256,sha512'
include: 'config/*.toml,docs/*.md'For extract-changelog and validate-changelog commands.
| Input | Description | Default |
|---|---|---|
version |
Version to extract/validate | Auto-detected from git tag |
changelog |
Path to CHANGELOG.md | CHANGELOG.md |
notes-output |
Output file for extracted release notes | release_notes.md |
Note: When version is not provided, it's auto-detected from GITHUB_REF_NAME (strips v prefix from tags like v1.2.3).
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: extract-changelog
# version auto-detected from git tag (e.g., v1.2.3 -> 1.2.3)For validate-version command.
| Input | Description | Default |
|---|---|---|
tag |
Git tag to validate | GITHUB_REF_NAME |
expected-version |
Expected version to match (required) | — |
validate-cargo-toml |
Also verify Cargo.toml version matches tag | false |
Set a repository variable NEXT_RELEASE_VERSION (e.g., 1.2.3) in your repo settings, then:
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: validate-version
expected-version: ${{ vars.NEXT_RELEASE_VERSION }}This fails the build if the git tag doesn't match the expected version, catching accidental releases.
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: validate-version
expected-version: ${{ vars.NEXT_RELEASE_VERSION }}
validate-cargo-toml: 'true'For validate-changelog command. Fails fast if no changelog entry exists for the release version.
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: validate-changelog
version: '1.2.3' # Or omit to auto-detect from git tagFor collect-artifacts command. Scans a directory, computes checksums, and outputs structured data for Homebrew/Winget generation.
| Input | Description | Default |
|---|---|---|
artifacts-dir |
Directory containing artifacts | artifacts |
base-url |
Base URL for download links | — |
- uses: actions/download-artifact@v4
with:
path: artifacts
- uses: michaelklishin/rust-build-package-release-action@v1
id: collect
with:
command: collect-artifacts
artifacts-dir: artifacts
base-url: 'https://github.com/${{ github.repository }}/releases/download/v${{ needs.validate.outputs.version }}'
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: generate-homebrew
version: ${{ needs.validate.outputs.version }}
brew-macos-arm64-sha256: ${{ steps.collect.outputs.macos_arm64_sha256 }}
brew-linux-x64-sha256: ${{ steps.collect.outputs.linux_x64_sha256 }}
# ... URLs constructed from base-url + artifact namesOutputs: collection (JSON), checksums_file, macos_arm64_sha256, macos_x64_sha256, linux_arm64_sha256, linux_x64_sha256, windows_x64_sha256, windows_arm64_sha256
Shared metadata for Linux packages (deb/rpm/apk), Homebrew, AUR, and Winget.
| Input | Description | Default |
|---|---|---|
pkg-description |
Package description | — |
pkg-maintainer |
Maintainer (Name <email>) |
— |
pkg-homepage |
Project homepage URL | — |
pkg-license |
License identifier (e.g., MIT, Apache-2.0) |
— |
pkg-vendor |
Vendor/organization name | — |
pkg-depends |
Runtime dependencies (comma-separated) | — |
pkg-recommends |
Recommended packages (comma-separated) | — |
pkg-suggests |
Suggested packages (comma-separated) | — |
pkg-conflicts |
Conflicting packages (comma-separated) | — |
pkg-replaces |
Packages this replaces (comma-separated) | — |
pkg-provides |
Virtual packages provided (comma-separated) | — |
pkg-contents |
Extra files (src:dst,src:dst) |
— |
pkg-section |
Debian section | utils |
pkg-priority |
Debian priority | optional |
pkg-group |
RPM group | Applications/System |
pkg-release |
Package release/revision number | — |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: release-linux-deb
target: x86_64-unknown-linux-gnu
pkg-maintainer: 'Your Name <[email protected]>'
pkg-description: 'A CLI tool for doing things'
pkg-homepage: 'https://github.com/you/project'
pkg-license: 'MIT'
pkg-depends: 'libc6,libssl3'For generate-sbom command.
| Input | Description | Default |
|---|---|---|
sbom-format |
Formats: spdx, cyclonedx, or both |
spdx,cyclonedx |
sbom-dir |
Output directory | target/sbom |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: generate-sbom
sbom-format: 'spdx,cyclonedx'For generate-homebrew command. SHA256 values come from build step outputs.
| Input | Description | Default |
|---|---|---|
brew-class |
Ruby class name for formula | Auto-generated |
brew-macos-arm64-url |
macOS ARM64 artifact URL | — |
brew-macos-arm64-sha256 |
macOS ARM64 SHA256 | — |
brew-macos-x64-url |
macOS x64 artifact URL | — |
brew-macos-x64-sha256 |
macOS x64 SHA256 | — |
brew-linux-arm64-url |
Linux ARM64 artifact URL | — |
brew-linux-arm64-sha256 |
Linux ARM64 SHA256 | — |
brew-linux-x64-url |
Linux x64 artifact URL | — |
brew-linux-x64-sha256 |
Linux x64 SHA256 | — |
brew-dir |
Output directory | target/homebrew |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: generate-homebrew
version: ${{ needs.build.outputs.version }}
brew-macos-arm64-url: 'https://github.com/you/proj/releases/download/v1.0.0/proj-1.0.0-aarch64-apple-darwin.tar.gz'
brew-macos-arm64-sha256: ${{ needs.build-macos-arm64.outputs.sha256 }}
brew-linux-x64-url: 'https://github.com/you/proj/releases/download/v1.0.0/proj-1.0.0-x86_64-unknown-linux-gnu.tar.gz'
brew-linux-x64-sha256: ${{ needs.build-linux-x64.outputs.sha256 }}For sign-artifact command.
| Input | Description | Default |
|---|---|---|
artifact |
Path to artifact for Sigstore signing | — |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: sign-artifact
artifact: 'target/release/myapp-1.0.0.tar.gz'For format-release command.
| Input | Description | Default |
|---|---|---|
artifacts-dir |
Directory containing release artifacts | release |
notes-file |
Release notes file to include | release_notes.md |
include-checksums |
Include checksums section | true |
include-signatures |
Include signatures section | true |
homebrew-tap |
Homebrew tap for installation instructions | — |
aur-package |
AUR package name for installation instructions | — |
winget-id |
Winget package ID for installation instructions | — |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: format-release
version: '1.0.0'
artifacts-dir: 'release'
homebrew-tap: 'you/tap/mytool'
aur-package: 'mytool'
winget-id: 'You.MyTool'For generate-aur command.
| Input | Description | Default |
|---|---|---|
aur-name |
AUR package name | Binary name |
aur-maintainer |
Maintainer (Name <email>) |
— |
aur-source-url |
Source tarball URL | — |
aur-source-sha256 |
Source SHA256 | — |
aur-makedepends |
Build dependencies (comma-separated) | cargo |
aur-optdepends |
Optional dependencies | — |
aur-dir |
Output directory | target/aur |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: generate-aur
version: '1.0.0'
aur-maintainer: 'Your Name <[email protected]>'
aur-source-url: 'https://github.com/you/project/archive/refs/tags/v1.0.0.tar.gz'
aur-source-sha256: ${{ steps.source.outputs.sha256 }}
pkg-description: 'A CLI tool'
pkg-license: 'MIT'For generate-winget command.
| Input | Description | Default |
|---|---|---|
winget-publisher |
Publisher display name | — |
winget-publisher-id |
Publisher ID | Publisher without spaces |
winget-package-id |
Package ID | Binary name |
winget-license-url |
License URL | — |
winget-copyright |
Copyright notice | — |
winget-tags |
Package tags (comma-separated) | — |
winget-x64-url |
Windows x64 artifact URL | — |
winget-x64-sha256 |
Windows x64 SHA256 | — |
winget-arm64-url |
Windows ARM64 artifact URL | — |
winget-arm64-sha256 |
Windows ARM64 SHA256 | — |
winget-dir |
Output directory | target/winget |
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: generate-winget
version: '1.0.0'
winget-publisher: 'Your Name'
winget-x64-url: 'https://github.com/you/project/releases/download/v1.0.0/project-1.0.0-x86_64-pc-windows-msvc.zip'
winget-x64-sha256: ${{ needs.build-windows.outputs.sha256 }}
pkg-description: 'A CLI tool'
pkg-license: 'MIT'The test-deb, test-rpm, and test-windows commands install packages, verify the binary runs and reports the expected version, then uninstall. Useful for smoke-testing releases across distributions.
| Input | Description | Default |
|---|---|---|
download-from-release |
Fetch artifacts from GitHub release automatically | false |
arch |
Architecture (amd64, arm64, x86_64, aarch64) | — |
artifact |
Path to local artifact (when not downloading) | — |
checksum-file |
Path to checksum file for verification | — |
msi-path |
Path to MSI installer (test-windows only) | — |
msi-checksum-file |
Path to checksum file for MSI (test-windows only) | — |
With download-from-release: true, the action fetches artifacts from the current repository's releases using standard naming conventions and verifies checksums automatically (supports .sha256, .sha512, and .b2 files). For private repos, set GITHUB_TOKEN or GH_TOKEN in the environment.
- uses: michaelklishin/rust-build-package-release-action@v1
id: version
with:
command: get-release-version
version: ${{ inputs.version }}If version is provided, it's used directly. Otherwise, the latest release tag is fetched from the GitHub API and the v prefix stripped. No authentication is required for public repos. For private repos, set GITHUB_TOKEN or GH_TOKEN in the environment:
- uses: michaelklishin/rust-build-package-release-action@v1
id: version
with:
command: get-release-version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: test-deb
download-from-release: true
binary-name: myapp
version: ${{ steps.version.outputs.version }}
arch: ${{ matrix.arch }}See examples/verify-artifacts.yml for a complete multi-distribution testing workflow.
| Command | Description |
|---|---|
release |
Unified build command (auto-selects platform from target triple) |
extract-changelog |
Extract release notes from CHANGELOG.md |
validate-changelog |
Validate changelog has entry for version |
validate-version |
Validate git tag matches expected version (optionally checks Cargo.toml) |
get-version |
Get version from Cargo.toml |
collect-artifacts |
Collect artifacts, compute checksums, generate SHA256SUMS |
generate-sbom |
Generate SPDX and CycloneDX SBOMs |
generate-homebrew |
Generate Homebrew formula |
generate-aur |
Generate AUR PKGBUILD and .SRCINFO |
generate-winget |
Generate Winget manifest files |
sign-artifact |
Sign artifact with Sigstore/cosign |
format-release |
Format GitHub Release body |
release-linux |
Build Linux binary or tarball |
release-linux-deb |
Build Debian package |
release-linux-rpm |
Build RPM package |
release-linux-apk |
Build Alpine APK package |
release-macos |
Build macOS binary or tarball |
release-macos-dmg |
Build macOS DMG installer |
release-windows |
Build Windows binary or zip |
release-windows-msi |
Build Windows MSI installer |
test-deb |
Test Debian package (install, verify version, uninstall) |
test-rpm |
Test RPM package (install, verify version, uninstall) |
test-windows |
Test Windows binary and MSI installer |
get-release-version |
Get latest release version from GitHub (strips v prefix) |
Note: The unified release command auto-detects the platform from the target input:
- Targets containing
linuxuserelease-linux - Targets containing
darwinorappleuserelease-macos - Targets containing
windowsuserelease-windows
This simplifies matrix builds by eliminating the need for per-platform command mapping.
| Output | Description |
|---|---|
version |
Version from get-version, validate-version, or release commands |
release_notes_file |
Path to release notes file |
release_notes |
Release notes content |
artifact |
Artifact filename (archive when archive=true, bare binary otherwise) |
artifact_path |
Full path to artifact (archive when archive=true, bare binary otherwise) |
bare_artifact |
Bare binary filename (always produced) |
bare_artifact_path |
Full path to bare binary (always produced) |
binary_name |
Binary name that was built |
binary_path |
Path to raw binary (before archiving) |
target |
Target triple used for the build |
sha256 |
SHA256 checksum |
sha512 |
SHA512 checksum |
b2 |
BLAKE2 checksum |
summary |
JSON build summary |
sbom_spdx |
Path to SPDX SBOM file |
sbom_cyclonedx |
Path to CycloneDX SBOM file |
formula_file |
Path to Homebrew formula |
formula_class |
Homebrew formula class name |
formula |
Homebrew formula content |
signature_path |
Path to signature file |
certificate_path |
Path to signing certificate |
bundle_path |
Path to Sigstore bundle |
body |
Formatted release body |
pkgbuild_path |
Path to AUR PKGBUILD |
srcinfo_path |
Path to AUR .SRCINFO |
pkgbuild |
PKGBUILD content |
manifest_dir |
Winget manifest directory |
manifest_id |
Winget manifest ID |
checksum_file |
Path to checksum file (.sha256) |
result |
Test result (success/failure) for test-* commands |
name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
permissions:
contents: write
jobs:
validate:
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.validate.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: michaelklishin/rust-build-package-release-action@v1
id: validate
with:
command: validate-version
build:
needs: validate
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
command: release-linux
- target: aarch64-apple-darwin
os: macos-14
command: release-macos
- target: x86_64-pc-windows-msvc
os: windows-2022
command: release-windows
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install stable --profile minimal
- uses: michaelklishin/rust-build-package-release-action@v1
id: build
with:
command: ${{ matrix.command }}
target: ${{ matrix.target }}
locked: 'true'
archive: 'true'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}
path: target/${{ matrix.target }}/release/*
release:
needs: [validate, build]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: michaelklishin/rust-build-package-release-action@v1
with:
command: extract-changelog
version: ${{ needs.validate.outputs.version }}
- uses: actions/download-artifact@v4
with:
path: artifacts
- run: |
mkdir -p release
find artifacts -type f -name '*${{ needs.validate.outputs.version }}*' -exec cp {} release/ \;
- uses: softprops/action-gh-release@v2
with:
body_path: release_notes.md
files: release/*The examples/ directory contains ready-to-use workflow templates:
| Workflow | Description |
|---|---|
| basic-release.yml | Single-platform Linux release |
| multi-platform.yml | Linux, macOS, and Windows builds |
| linux-packages.yml | Debian, RPM, and Alpine packages |
| installers.yml | macOS DMG and Windows MSI |
| package-managers.yml | Homebrew, AUR, and Winget manifests |
| supply_chain_security.yml | SBOM generation and Sigstore signing |
| verify-artifacts.yml | Test packages across distributions |
| complete.yml | Full workflow with all features |
This Action is dual-licensed under:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
2025-2026 (c) Michael S. Klishin and Contributors.