{
  "markdown": "<div align=\"center\">\n\n<img src=\"https://raw.githubusercontent.com/tj-smith47/anodizer/master/assets/logo.svg\" width=\"200\" alt=\"anodizer logo\">\n\n# anodizer\n\nThe release pipeline built for Rust — workspace-aware, reproducible, and signed by default.\n\n[![CI](https://github.com/tj-smith47/anodizer/actions/workflows/ci.yml/badge.svg)](https://github.com/tj-smith47/anodizer/actions/workflows/ci.yml)\n[![Release](https://github.com/tj-smith47/anodizer/actions/workflows/release.yml/badge.svg)](https://github.com/tj-smith47/anodizer/actions/workflows/release.yml)\n[![Docs](https://github.com/tj-smith47/anodizer/actions/workflows/docs.yml/badge.svg)](https://github.com/tj-smith47/anodizer/actions/workflows/docs.yml)\n[![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/tj-smith47/anodizer/badges/coverage.json)](https://github.com/tj-smith47/anodizer/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/anodizer.svg)](https://crates.io/crates/anodizer)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)](#license)\n\n</div>\n\nAnodizer 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.\n\nWritten by [Claude](https://claude.ai); maintained by us.\n\nSee [What works (with proof)](https://tj-smith47.github.io/anodizer/dogfooding/) for a per-feature status — every \"live\" claim links to a real published artifact you can verify yourself.\n\n## Why anodizer?\n\nYour release is a Cargo workspace — not a bag of loose binaries. anodizer is built that way from the ground up.\n\n- **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.toml` *and* `Cargo.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.\n- **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.\"\n- **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.\n- **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.\n- **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.\n\nThen 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.\n\nAlready know GoReleaser? anodizer's `{{ .Field }}` template syntax will feel right at home. Moving from cargo-dist, release-plz, or cargo-release? The [migration guides](https://tj-smith47.github.io/anodizer/migration/) map your setup straight over.\n\n## Features\n\n**Build**\n- Cross-platform builds via `cargo-zigbuild`, `cross`, or native `cargo build`\n- Per-build hooks (pre/post), environment variables, feature flags, and target overrides\n- UPX binary compression with per-target filtering\n- Workspace support with per-crate independent release cadences\n\n**Package**\n- Archives in tar.gz, tar.xz, tar.zst, zip, gz, or raw binary format with OS-specific overrides\n- Linux packages (.deb, .rpm, .apk, .archlinux, .ipk) via nFPM with full lifecycle scripts\n- Snapcraft snaps with prime-dir architecture\n- macOS DMG disk images and PKG installers\n- Windows MSI and NSIS installers\n- Flatpak bundles\n- AppImage portable Linux applications\n- Makeself self-extracting archives\n- `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\n- Source RPMs (.src.rpm)\n- Source archives with file filtering\n- SBOM generation (CycloneDX/SPDX)\n- 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\n\n**Sign**\n- GPG and cosign signing for binaries, archives, checksums, Docker images, and SBOMs\n- Signed git tags (GPG or SSH) using your existing git signing key\n- Multiple independent signing configurations\n- Conditional signing via template expressions\n- Build provenance attestations (SLSA-style) for binaries and artifacts\n\n**Publish**\n- GitHub/GitLab/Gitea Releases with asset uploads, draft/prerelease detection, header/footer templates\n- crates.io with dependency-aware ordering and index polling\n- Homebrew formula and cask generation\n- Homebrew-core formula bump PRs (bump an existing `homebrew-core` formula)\n- Scoop manifest generation\n- Chocolatey package generation\n- Winget manifest generation\n- AUR PKGBUILD and .SRCINFO generation\n- Krew plugin manifest generation\n- Nix derivation generation\n- SchemaStore catalog registration for editor autocomplete of your config files\n- MCP registry server-manifest publishing (Model Context Protocol)\n- Docker multi-arch images via `docker buildx`\n- Blob storage uploads (S3, GCS, Azure)\n- NPM per-platform binary packages and PyPI native binary wheels\n- Keyless OIDC Trusted Publishing for crates.io, npm (with provenance), and PyPI — no stored registry tokens\n- Artifactory, Cloudsmith, Fury, Docker Hub\n- Custom publisher commands\n\n**Announce**\n- Discord, Slack, Telegram, Teams, Mattermost\n- Email, Reddit, Twitter/X, Mastodon, Bluesky, LinkedIn\n- OpenCollective, Discourse\n- Generic webhooks with custom headers and templates\n\n**Advanced**\n- Tera templates (Jinja2-like) with GoReleaser-compatible `{{ .Field }}` syntax\n- Nightly builds with date-based versioning\n- Config includes for shared configuration\n- Split/merge CI for fan-out parallel builds\n- Monorepo support with independent workspaces\n- Auto-tagging from commit message directives\n- Reproducible builds with `mod_timestamp` and `builds_info`\n- Version-string file syncing (`version_files`) to keep docs, scripts, and manifests in lockstep at tag\n- 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\n- Post-release verification with install smoke tests\n- JSON Schema for editor autocomplete and validation\n\n## Installation\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew install tj-smith47/tap/anodizer\n```\n\n### Cargo\n\n```bash\ncargo install anodizer\n```\n\n### From source\n\n```bash\ngit clone https://github.com/tj-smith47/anodizer.git\ncd anodizer\ncargo install --path crates/cli\n```\n\n## Quick Start\n\n```bash\n# Generate a starter config from your Cargo workspace\nanodizer init > .anodizer.yaml\n\n# Validate your config\nanodizer check\n\n# Check that required tools are available\nanodizer healthcheck\n\n# Build a snapshot (no publishing)\nanodizer release --snapshot\n\n# Dry run (full pipeline, no side effects)\nanodizer release --dry-run\n\n# Bump crate versions from Conventional Commits — rewrites Cargo.toml + Cargo.lock,\n# no tag (tag separately, or let the release workflow tag for you)\nanodizer bump --dry-run --output json         # preview the per-crate plan as JSON\nanodizer bump                                 # infer patch/minor/major per crate since its last tag\nanodizer bump minor --package mycrate         # force a level for one crate\nanodizer bump --workspace --commit --changelog  # bump every member, commit, refresh CHANGELOG.md\nanodizer bump --pre rc.1                       # append a prerelease identifier (0.5.0-rc.1)\n\n# Auto-tag from commit directives\n# (Conventional Commits: feat: → minor, fix: → patch, BREAKING CHANGE: → major)\nanodizer tag --dry-run                         # preview what tag would be created\nanodizer tag --push --changelog                # tag + refresh changelog + push (triggers the release)\nanodizer tag --sign --push                     # signed annotated tag (uses git user.signingkey)\nanodizer tag --custom-tag v0.1.0               # force a tag value, bypassing bump derivation\nanodizer tag --version 1.2.3 --crate mycrate   # pin an exact version for recovery (per-crate mode)\nanodizer tag rollback \"$GITHUB_SHA\"            # delete anodize-managed tags at a SHA + revert the bump\n\n# Resume a stalled release from a populated dist/ — no rebuild\nanodizer continue                              # resume the publish-only pipeline after a transient failure\nanodizer continue --merge                      # fan-in split-build workers, then sign/checksum/release/publish\nanodizer continue --skip npm,announce          # resume but skip named stages/publishers\n\n# Run only the publish stages (release / blob / publish) from a completed dist/\nanodizer publish --publishers homebrew,scoop   # publish to a subset of configured publishers\nanodizer publish --dry-run                     # preview the publish chain with no side effects\n\n# Promote an already-published artifact to a stable track — no rebuild\nanodizer promote --to stable                                  # promote the newest prerelease artifact\nanodizer promote --to stable --from candidate --dry-run       # preview promoting from a named source track\nanodizer promote --to stable --version v1.2.0 \\\n  --publishers snapcraft,npm                                  # promote a pinned version on chosen publishers\n```\n\nFor CI-based releases, set `GITHUB_TOKEN` (or `ANODIZER_GITHUB_TOKEN`) as a secret — the release pipeline picks it up automatically.\n\n## Configuration\n\nAnodizer uses `.anodizer.yaml` (or `.anodizer.toml`) in your project root. Add a schema comment for editor autocomplete:\n\n```yaml\n# yaml-language-server: $schema=https://tj-smith47.github.io/anodizer/schema.json\n\nproject_name: myapp\n\ndefaults:\n  targets:\n    - x86_64-unknown-linux-gnu\n    - aarch64-unknown-linux-gnu\n    - x86_64-apple-darwin\n    - aarch64-apple-darwin\n    - x86_64-pc-windows-msvc\n  cross: auto\n\ncrates:\n  - name: myapp\n    path: \".\"\n    tag_template: \"v{{ Version }}\"\n    builds:\n      - binary: myapp\n    archives:\n      - name_template: \"{{ ProjectName }}-{{ Version }}-{{ Os }}-{{ Arch }}\"\n        files: [LICENSE, README.md]\n    release:\n      github:\n        owner: myorg\n        name: myapp\n    publish:\n      cargo: {}\n      homebrew:\n        repository:\n          owner: myorg\n          name: homebrew-tap\n```\n\nSee the [full configuration reference](https://tj-smith47.github.io/anodizer/docs/reference/configuration/) and the [template reference](https://tj-smith47.github.io/anodizer/docs/general/templates/) for all available fields, variables, and filters.\n\n## Real-world adoption: cfgd\n\n[`cfgd`](https://github.com/tj-smith47/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.\n\nA condensed slice of [cfgd's `.anodizer.yaml`](https://github.com/tj-smith47/cfgd/blob/master/.anodizer.yaml):\n\n```yaml\nworkspaces:\n  - name: cfgd-core\n    crates:\n      - name: cfgd-core\n        tag_template: \"core-v{{ Version }}\"\n        version_sync: { enabled: true, mode: cargo }\n\n  - name: cfgd\n    crates:\n      - name: cfgd\n        depends_on: [cfgd-core]\n        version_sync: { enabled: true, mode: cargo }\n        universal_binaries:\n          - name_template: \"{{ ProjectName }}\"\n            replace: false\n        binstall:\n          enabled: true   # pkg-url + per-target overrides derived from archive.name_template\n  # ... cfgd-operator, cfgd-csi\n```\n\nEvery cell of [What works (with proof)](https://tj-smith47.github.io/anodizer/dogfooding/) links to a real published cfgd artifact for the feature in question — that's the verification surface.\n\n## GitHub Actions\n\nAnodizer ships a first-party action, [`tj-smith47/anodizer-action`](https://github.com/tj-smith47/anodizer-action), which is what this repo dogfoods in its own `release.yml`:\n\n```yaml\nname: Release\n\non:\n  push:\n    tags: [\"v*\"]\n\npermissions:\n  contents: write\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - name: Release\n        uses: tj-smith47/anodizer-action@v1\n        with:\n          version: latest        # accepts `latest`, `nightly`, or an exact tag (e.g. `v0.5.0`). Pin in production.\n          auto-install: true     # auto-detect nfpm/makeself/snapcraft/cosign/etc from .anodizer.yaml\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n```\n\nFor split/merge fan-out, GPG key import, registry login, and per-platform variants, see [anodizer-action](https://github.com/tj-smith47/anodizer-action) and the live [`.github/workflows/release.yml`](.github/workflows/release.yml) in this repo.\n\n## CLI Reference\n\n```\nanodizer release       Full release pipeline (--snapshot, --dry-run, --split/--merge, --publish-only)\nanodizer tag           Auto-tag from commit directives\nanodizer tag rollback  Delete anodize-managed tags at a SHA and revert the bump commit\nanodizer bump          Bump crate versions (Conventional Commits → semver level) without tagging\nanodizer promote       Promote a published artifact to a stable track without rebuilding\nanodizer continue      Resume a stalled or split release from a populated dist/ (--merge to fan-in)\nanodizer publish       Run only the publish stages (release, blob, publish) from a completed dist/\nanodizer check         Validate configuration + run determinism harness\nanodizer preflight     Verify the environment can run the configured release (tools, secrets, key material)\nanodizer init          Generate starter .anodizer.yaml\nanodizer healthcheck   Probe external tools (nfpm, cosign, ...)\n```\n\nFailure handling is in-process: a failed `anodizer release` executes the\n`release.on_failure` policy itself (`rollback` by default — delete the tag,\nrevert the bump — auto-degrading to `hold` once a one-way-door publisher like\ncrates.io has landed), so release workflows need no `if: failure()` recovery\nsteps. `anodizer tag rollback \"$GITHUB_SHA\"` is the manual recovery command\nfor killed or held runs. See\n[Release resilience](https://tj-smith47.github.io/anodizer/docs/advanced/release-resilience/)\nfor the flag matrix and recovery flows.\n\nFull reference: `anodizer --help` or the [docs site](https://tj-smith47.github.io/anodizer/docs/reference/cli/).\n\n## Documentation\n\nFull documentation is available at **[tj-smith47.github.io/anodizer](https://tj-smith47.github.io/anodizer/)**.\n\nOperator guides:\n\n- [Release resilience guide](https://tj-smith47.github.io/anodizer/docs/advanced/release-resilience/) - three-group publisher dispatch, Submitter gate, rollback, replay-from-run\n- [Determinism guide](https://tj-smith47.github.io/anodizer/docs/advanced/determinism/) - byte-stability contract, `anodizer check determinism` harness, runtime allow-list\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)\n\nat your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n",
  "bytes": 16433,
  "sha": "9f51dc33721692083012eb79cbde3fb518edd76b6a43784ede17fa5fe61b4394",
  "repo_slug": "tj-smith47/anodizer",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_tj_smith47_anodizer_2a960528/readme"
}