Anodizer is a Rust-native release automation tool designed to simplify and streamline the end-to-end release process for Rust projects. It provides a declarative, config-driven approach to building, packaging, signing, publishing, and announcing software releases, enabling developers to define their entire release pipeline in a single configuration file.
Key Features:
Cross-platform builds with support for multiple targets and architectures.
Comprehensive package generation, including binaries, archives, Linux packages (deb/rpm), macOS DMGs, Windows MSI/NSIS installers, and more.
CI/CD integration with GitHub Actions, GitLab, Gitea, and other platforms.
Advanced templating system for release notes, changelogs, and artifact naming.
Reproducible builds with timestamp modification and build metadata tracking.
Audience & Benefit:
Ideal for Rust developers and teams focused on building reliable, consistent, and automated release pipelines. Anodizer saves time by automating repetitive tasks, reducing the risk of human error, and ensuring that releases are consistent across different platforms and environments. It is particularly beneficial for projects with complex release workflows or those requiring cross-platform support.
Anodizer can be installed via winget, making it easy to integrate into your development environment.
README
anodizer
The release pipeline built for Rust — workspace-aware, reproducible, and signed by default.
Anodizer reads a declarative config file and runs your entire release from a single anodizer release command: build, archive, checksum, changelog, sign, release, publish, and announce. It's built around the Rust ecosystem — Cargo workspaces, Cargo.lock-aware version bumps, crates.io, and byte-reproducible artifacts.
See What works (with proof) for a per-feature status — every "live" claim links to a real published artifact you can verify yourself.
Why anodizer?
Your release is a Cargo workspace — not a bag of loose binaries. anodizer is built that way from the ground up.
It speaks Cargo. Per-crate release cadences, per-crate tags, and a tag resolver let a single crate and a thirty-crate monorepo share one config. anodizer tag and anodizer bump rewrite Cargo.tomlandCargo.lock, then commit and tag — locally by default, and (on --push) push the bump commit and tag atomically — no orphaned bump commit, no hand-rolled git push, no lockfile drift.
crates.io, published in the right order. Dependency-aware ordering with sparse-index polling holds each crate until the ones it depends on have propagated — so a workspace publish never races itself into a transient "version not found."
Cross-compiles without the toolchain tax. musl, glibc, Windows, and macOS from one machine via cargo-zigbuild or cross. No rustup target add rituals, no per-target CI shards to babysit.
Reproducible — and it proves it. Deterministic artifacts by default, then anodizer check determinism rebuilds them and byte-compares. "Reproducible" becomes a fact your CI enforces, not a claim in your release notes.
Signing and attestation are first-class. cosign + GPG for binaries, archives, checksums, and images, plus SLSA-style build provenance — wired in a few lines, not bolted on after a CVE scare.
Then the long tail that Rust authors actually hit: generated per-crate READMEs, cargo-binstall metadata derived straight from your config (no hand-maintained pkg-url that 404s), version_files to pin your docs and install scripts to the released version, and post-release install smoke tests that catch a broken artifact before your users do.
Already know GoReleaser? anodizer's {{ .Field }} template syntax will feel right at home. Moving from cargo-dist, release-plz, or cargo-release? The migration guides map your setup straight over.
Features
Build
Cross-platform builds via cargo-zigbuild, cross, or native cargo build
Per-build hooks (pre/post), environment variables, feature flags, and target overrides
UPX binary compression with per-target filtering
Workspace support with per-crate independent release cadences
Package
Archives in tar.gz, tar.xz, tar.zst, zip, gz, or raw binary format with OS-specific overrides
Linux packages (.deb, .rpm, .apk, .archlinux, .ipk) via nFPM with full lifecycle scripts
Snapcraft snaps with prime-dir architecture
macOS DMG disk images and PKG installers
Windows MSI and NSIS installers
Flatpak bundles
AppImage portable Linux applications
Makeself self-extracting archives
curl | sh install-script generation — a per-platform asset table derived from your build/archive config (no hand-maintained URLs to 404), with a checksum-verify gate
Source RPMs (.src.rpm)
Source archives with file filtering
SBOM generation (CycloneDX/SPDX)
Checksums with SHA-256, SHA-512, SHA3, BLAKE2b, BLAKE2s, BLAKE3, CRC32, MD5, and more — combined or split per-artifact sidecars in bare or coreutils (shasum -c) format
Sign
GPG and cosign signing for binaries, archives, checksums, Docker images, and SBOMs
Signed git tags (GPG or SSH) using your existing git signing key
Multiple independent signing configurations
Conditional signing via template expressions
Build provenance attestations (SLSA-style) for binaries and artifacts
Publish
GitHub/GitLab/Gitea Releases with asset uploads, draft/prerelease detection, header/footer templates
crates.io with dependency-aware ordering and index polling
Homebrew formula and cask generation
Homebrew-core formula bump PRs (bump an existing homebrew-core formula)
Scoop manifest generation
Chocolatey package generation
Winget manifest generation
AUR PKGBUILD and .SRCINFO generation
Krew plugin manifest generation
Nix derivation generation
SchemaStore catalog registration for editor autocomplete of your config files
Generic webhooks with custom headers and templates
Advanced
Tera templates (Jinja2-like) with GoReleaser-compatible {{ .Field }} syntax
Nightly builds with date-based versioning
Config includes for shared configuration
Split/merge CI for fan-out parallel builds
Monorepo support with independent workspaces
Auto-tagging from commit message directives
Reproducible builds with mod_timestamp and builds_info
Version-string file syncing (version_files) to keep docs, scripts, and manifests in lockstep at tag
Cross-publisher track promotion (anodizer promote) — flip an already-published artifact to a stable track (snapcraft channels, npm dist-tags, OCI floating tags, GitHub prerelease) with no rebuild
Post-release verification with install smoke tests
JSON Schema for editor autocomplete and validation
Installation
Homebrew (macOS/Linux)
brew install tj-smith47/tap/anodizer
Cargo
cargo install anodizer
From source
git clone https://github.com/tj-smith47/anodizer.git
cd anodizer
cargo install --path crates/cli
Quick Start
# Generate a starter config from your Cargo workspace
anodizer init > .anodizer.yaml
# Validate your config
anodizer check
# Check that required tools are available
anodizer healthcheck
# Build a snapshot (no publishing)
anodizer release --snapshot
# Dry run (full pipeline, no side effects)
anodizer release --dry-run
# Bump crate versions from Conventional Commits — rewrites Cargo.toml + Cargo.lock,
# no tag (tag separately, or let the release workflow tag for you)
anodizer bump --dry-run --output json # preview the per-crate plan as JSON
anodizer bump # infer patch/minor/major per crate since its last tag
anodizer bump minor --package mycrate # force a level for one crate
anodizer bump --workspace --commit --changelog # bump every member, commit, refresh CHANGELOG.md
anodizer bump --pre rc.1 # append a prerelease identifier (0.5.0-rc.1)
# Auto-tag from commit directives
# (Conventional Commits: feat: → minor, fix: → patch, BREAKING CHANGE: → major)
anodizer tag --dry-run # preview what tag would be created
anodizer tag --push --changelog # tag + refresh changelog + push (triggers the release)
anodizer tag --sign --push # signed annotated tag (uses git user.signingkey)
anodizer tag --custom-tag v0.1.0 # force a tag value, bypassing bump derivation
anodizer tag --version 1.2.3 --crate mycrate # pin an exact version for recovery (per-crate mode)
anodizer tag rollback "$GITHUB_SHA" # delete anodize-managed tags at a SHA + revert the bump
# Resume a stalled release from a populated dist/ — no rebuild
anodizer continue # resume the publish-only pipeline after a transient failure
anodizer continue --merge # fan-in split-build workers, then sign/checksum/release/publish
anodizer continue --skip npm,announce # resume but skip named stages/publishers
# Run only the publish stages (release / blob / publish) from a completed dist/
anodizer publish --publishers homebrew,scoop # publish to a subset of configured publishers
anodizer publish --dry-run # preview the publish chain with no side effects
# Promote an already-published artifact to a stable track — no rebuild
anodizer promote --to stable # promote the newest prerelease artifact
anodizer promote --to stable --from candidate --dry-run # preview promoting from a named source track
anodizer promote --to stable --version v1.2.0 \
--publishers snapcraft,npm # promote a pinned version on chosen publishers
For CI-based releases, set GITHUB_TOKEN (or ANODIZER_GITHUB_TOKEN) as a secret — the release pipeline picks it up automatically.
Configuration
Anodizer uses .anodizer.yaml (or .anodizer.toml) in your project root. Add a schema comment for editor autocomplete:
cfgd — declarative, GitOps-style machine configuration management — is anodizer's first real-world adopter and dogfoods every shipped publisher. It's a 4-crate workspace (shared lib + CLI + Kubernetes operator + CSI driver) that ships to crates.io (dependency-aware ordering), GitHub Releases, Homebrew, Scoop, Chocolatey, Winget, the Snap Store, Krew, GHCR, and via cargo binstall — all from one .anodizer.yaml and one tag push.
anodizer release Full release pipeline (--snapshot, --dry-run, --split/--merge, --publish-only)
anodizer tag Auto-tag from commit directives
anodizer tag rollback Delete anodize-managed tags at a SHA and revert the bump commit
anodizer bump Bump crate versions (Conventional Commits → semver level) without tagging
anodizer promote Promote a published artifact to a stable track without rebuilding
anodizer continue Resume a stalled or split release from a populated dist/ (--merge to fan-in)
anodizer publish Run only the publish stages (release, blob, publish) from a completed dist/
anodizer check Validate configuration + run determinism harness
anodizer preflight Verify the environment can run the configured release (tools, secrets, key material)
anodizer init Generate starter .anodizer.yaml
anodizer healthcheck Probe external tools (nfpm, cosign, ...)
Failure handling is in-process: a failed anodizer release executes the
release.on_failure policy itself (rollback by default — delete the tag,
revert the bump — auto-degrading to hold once a one-way-door publisher like
crates.io has landed), so release workflows need no if: failure() recovery
steps. anodizer tag rollback "$GITHUB_SHA" is the manual recovery command
for killed or held runs. See
Release resilience
for the flag matrix and recovery flows.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.