Skip to main content

Quick Start

Get your first release running in 5 minutes

1. Install anodizer

cargo install anodizer

2. Generate a config

In your project root (where Cargo.toml lives):

anodizer init > .anodizer.yaml

This reads your Cargo.toml and generates a starter config with sensible defaults. init discovers your project_name, every [[bin]] target across the workspace, the GitHub owner/name from your origin remote, and a default archive name_template — so the generated config builds and releases without further edits. Anything it can derive is left out of the file as an implicit default; you only write config to override a default or to add a publisher (a tap, a registry) it can't infer.

3. Validate the config

anodizer check config

This validates your .anodizer.yaml against the schema — checks for missing fields, invalid target triples, dependency cycles, and more.

Anodizer rejects unknown config keys, so a typo like dockrs_v2: or fromats: fails fast at load time instead of being silently ignored. Migrating from GoReleaser? Paste your config in as-is — anodizer accepts the GoReleaser field names disable, docker_v2, layouts, and name_template (snapshot) as back-compat aliases for their canonical anodizer spellings (skip, dockers_v2, layout, version_template). disable and name_template are deprecation-warned at load so you can migrate at your own pace; docker_v2 and layouts are accepted silently.

4. Do a dry run

anodizer release --dry-run

This runs the full pipeline without any side effects — no GitHub release created, no packages published, no images pushed. --dry-run plans and renders every stage (templates, manifest contents, the publisher PRs it would open) but skips the real build and every network write, so it's fast and offline. Use it to confirm your config resolves before spending a real build.

5. Do a snapshot build

anodizer release --snapshot

Unlike --dry-run, a snapshot performs the real build, archive, checksum, and signing stages — it just skips every stage that writes to a remote (GitHub release, crates.io, taps, registries, images). It also derives a snapshot version (the default appends -SNAPSHOT; the template can fold in the commit hash) instead of requiring a release tag, so you can snapshot an untagged working tree. Use it to test that your binaries compile for every target and that archive formats and checksums come out right. Both --dry-run and --snapshot run the emission cross-check — each publisher's would-be output (a binstall download URL, a Nix asset map) is rendered in-memory and compared against the run's registered artifacts, catching a class of "release succeeds but is silently wrong" bugs locally. The check is only meaningful under --snapshot, where real built archives back the comparison; under --dry-run the artifacts are placeholder registrations, so it confirms the emission renders but not that it points at real bytes.

6. Release for real

export GITHUB_TOKEN="ghp_..."
anodizer release --crate myapp

This runs the full pipeline: build, archive, checksum, changelog, GitHub release with asset uploads, and any configured publishers. Each publisher reads its own credential from the environment — GITHUB_TOKEN (needs contents:write for the release, plus pull_request:write only if a Homebrew cask or other tap is set to the PR workflow), CARGO_REGISTRY_TOKEN for crates.io, and so on. A homebrew_casks entry commits the cask directly to its tap repo on every release; set pull_request.enabled under the entry's repository to open a PR per release instead, so review and merge happen on your terms.

Tap repos must already exist. If you configure homebrew_casks (or a Scoop bucket, AUR repo, etc.), create the target repo — e.g. an empty <owner>/homebrew-tapbefore your first release; anodizer writes into it but never creates it. The release token needs contents:write for the default direct push, plus pull_request:write only if the tap uses the PR workflow (pull_request.enabled).

Preflight checks run before any publishing. --strict promotes preflight warnings — an under-scoped token, or a feature you configured that would silently skip — into hard errors, so a misconfigured CI run fails at the top instead of half-publishing. --strict-preflight is a back-compat alias for --strict; both also block when a publisher's remote state can't be determined (Unknown).

What next?