Skip to main content

anodizer.yml config

Top-level anodizer.yml keys, Tera template helpers, lifecycle hooks, and monorepo configuration.

anodizer.yml config

Top-level configuration keys and the Tera helpers available inside any template string. Tera syntax is GoReleaser-compatible.

Live configuration

Top of cfgd/.anodizer.yaml (snapshot 2026-05-24) — every top-level / monorepo / git key in the tables below is wired here.

version: 2
project_name: cfgd
dist: ./dist
report_sizes: true

env:
  - REGISTRY=ghcr.io
  - RELEASE_TYPE=stable

variables:
  repo_url: "https://github.com/tj-smith47/cfgd"
  description: "Declarative, GitOps-style machine configuration management"

git:
  tag_sort: "-version:refname"
  ignore_tags: ["nightly"]
  ignore_tag_prefixes: ["draft-"]
  prerelease_suffix: "-"

tag:
  default_bump: none
  branch_history: full
  tag_prefix: "v"
  release_branches: [master]
  initial_version: "0.5.0"

metadata:
  description: "Declarative, GitOps-style machine configuration management"
  homepage: "https://github.com/tj-smith47/cfgd"
  license: MIT
  maintainers: ["TJ Smith"]
  mod_timestamp: "{{ CommitTimestamp }}"
  full_description: { from_file: README.md }
  commit_author: { name: TJ Smith, email: tj@jarvispro.io }

# 5 workspace entries — independent release cadences, dep-aware ordering.
workspaces:
  - { name: cfgd-crd,      crates: [{ name: cfgd-crd,      tag_template: "crd-v{{ Version }}",      ... }] }
  - { name: cfgd-core,     crates: [{ name: cfgd-core,     tag_template: "core-v{{ Version }}",     ... }] }
  - { name: cfgd,          crates: [{ name: cfgd,          tag_template: "v{{ Version }}",          depends_on: [cfgd-core], ... }] }
  - { name: cfgd-operator, crates: [{ name: cfgd-operator, tag_template: "operator-v{{ Version }}", depends_on: [cfgd-core], ... }] }
  - { name: cfgd-csi,      crates: [{ name: cfgd-csi,      tag_template: "csi-v{{ Version }}",      depends_on: [cfgd-core], ... }] }

partial:
  by: os

Top-level config

KeyStatusNotes
project_name✅ Verifiedanodizer .anodizer.yaml (project_name: anodizer)
dist✅ Verifiedanodizer .anodizer.yaml (dist: ./dist)
env✅ Verifiedanodizer .anodizer.yaml (env: - RELEASE_TYPE=stable)
env_files✅ Verifiedanodizer .anodizer.yaml (env_files: [.env.release] — non-secret release config loaded from a tracked file, overridable per environment); parser at crates/core/src/config/mod.rs
variables✅ Verifiedanodizer .anodizer.yaml (variables.repo_url + .description). cfgd carried the same block through v0.4.0 (dropped on master 2026-06-11)
template_files[]✅ Verifiedinstall.sh (rendered + attached on every cfgd release)
includes[].from_file✅ Verifiedcfgd .anodizer.yaml (includes: - from_file: path: .anodizer/announce.yaml — the announce matrix lives in .anodizer/announce.yaml and merges as defaults beneath the root config; live since v0.5.0). Parser: crates/core/src/config/mod.rs (IncludeSpec)
includes[].from_url🤝 Help wantedNo live config pulls a remote include
before✅ Verifiedcfgd .anodizer.yaml (before.hooks runs cargo fmt --check, clippy, test)
after✅ Verifiedcfgd .anodizer.yaml (after.hooks echo)
always✅ Verified (tests)crates/cli/tests/always_hooks.rs — the release's finally: fires last on success (after after:), last on failure (after on_error:), and on a before: hook failure, which is the exit neither of the others reaches. Fires once per invocation, so each --split shard and the --merge leg get their own. Neither dogfood config wires it yet
build.hooks.pre✅ Verifiedcfgd .anodizer.yaml (archive hooks.before)
build.hooks.post✅ Verifiedcfgd .anodizer.yaml (archive hooks.after)
snapshot.name_template✅ Verifiedanodizer .anodizer.yaml (snapshot.version_template)
--auto-snapshot✅ Verifiedanodizer ci.yml (snapshot build on every master push)
nightly.*✅ VerifiedBoth shapes. anodizer .anodizer.yaml (tag_name: nightly, publish_release: true, keep_single_release: true — one rolling release rather than an accumulating pile) driven by nightly.yml on a 0 4 * * * cron; cfgd .anodizer.yaml runs the per-crate equivalent
metadata.homepage✅ Verifiedcfgd .anodizer.yaml (metadata.homepage: https://github.com/tj-smith47/cfgd)
metadata.license✅ Verifiedcfgd .anodizer.yaml (metadata.license: MIT)
metadata.description✅ Verifiedcfgd .anodizer.yaml (metadata.description)
metadata.maintainers✅ Verifiedcfgd .anodizer.yaml (metadata.maintainers)
metadata.mod_timestamp✅ Verifiedanodizer .anodizer.yaml (metadata.mod_timestamp: "{{ CommitTimestamp }}"; applied as mtime of dist/metadata.json and dist/artifacts.json)
report_sizes✅ Verifiedanodizer .anodizer.yaml (report_sizes: true; prints per-artifact and total sizes in the release summary)
retry (attempts / delay / max_delay / max_elapsed)✅ Verifiedanodizer .anodizer.yaml (retry.max_elapsed: 15m — run-wide wall-clock retry budget bounding every publisher's ladder). See crates/core/src/config/retry.rs

Templates

Tera engine, GoReleaser-compatible syntax. Every template string in the config is rendered.

HelperStatusNotes
{{ .Field }}✅ Verifiedcrates/core/src/template/vars.rs (every {{ .Project }} / .Version / .Tag / .Os / .Arch binding)
{{ .Var.* }}✅ Verified (tests)crates/core/src/template/vars.rs (Var.* binding from the variables: block). No live config interpolates {{ Var.* }} in a template string — anodizer declares the variables: block but references it only from docs
{{ .PrefixedTag }}✅ Verifiedcrates/core/src/context/populate.rs (PrefixedTag binding — the full tag including a monorepo or tag_prefix prefix, while {{ .Tag }} is the stripped form)
{{ .Artifacts }}✅ Verified (tests)crates/core/src/context/populate.rs (the produced-artifact list, iterable as {% for a in Artifacts %}), covered in crates/core/src/context/tests.rs. No dogfood config interpolates it — the docker_manifests[].image_templates use case it exists for is bypassed because dockers_v2 pushes a multi-arch index directly
{{ .Metadata }}✅ Verifiedcrates/core/src/template/vars.rs (Metadata binding)
{{ .IsMerging }}✅ Verifiedcrates/core/src/template/vars.rs (IsMerging binding)
{{ .IsRelease }}✅ Verifiedcrates/core/src/template/vars.rs (IsRelease binding)
String / path / version / env / filter helpers✅ Verifiedcrates/core/src/template/base_tera/text.rs (tolower, toupper, etc.) and crates/core/src/template/base_tera/path.rs (dir, base, abs)
sha*, blake2*, blake3, crc32, md5✅ Verifiedcrates/core/src/template/base_tera/hash.rs (register_hash_fn! macro)
readFile, mustReadFile✅ Verifiedcrates/core/src/template/base_tera/env_file.rs (readFile / mustReadFile registrations)
time, .Now.Format✅ Verifiedcrates/core/src/template/base_tera/datetime.rs (time function + Now binding)
mdv2escape✅ Verifiedcrates/core/src/template/base_tera/text.rs (mdv2escape filter)
urlPathEscape✅ Verifiedcrates/core/src/template/base_tera/path.rs (urlPathEscape filter)
in✅ Verifiedcrates/core/src/template/base_tera/collection.rs (in filter)
reReplaceAll✅ Verifiedcrates/core/src/template/base_tera/text.rs (reReplaceAll filter)

Monorepo

KeyStatusNotes
monorepo.tag_prefix✅ VerifiedPer-crate only — a lockstep workspace cuts one v{{ Version }} tag for every crate, so the prefix has nothing to distinguish. cfgd cuts five distinct tag families (cfgd .anodizer.yaml: crd-v / core-v / v / operator-v / csi-v), visible as cfgd's tag list
monorepo.dir✅ VerifiedPer-crate. cfgd .anodizer.yaml (path: crates/cfgd-crd, crates/cfgd-core, crates/cfgd, crates/cfgd-operator, crates/cfgd-csi)
cargo_workspace detection✅ VerifiedPer-crate. cfgd .anodizer.yaml (5 workspaces: entries: cfgd-crd, cfgd-core, cfgd, cfgd-operator, cfgd-csi)
depends_on✅ VerifiedPer-crate only. Independent workspaces need an explicit edge to order their publishes; a lockstep workspace derives the order from each Cargo.toml instead, which is why anodizer .anodizer.yaml declares none. cfgd .anodizer.yaml carries depends_on: [cfgd-core] on its downstream crates

Publisher resilience

KeyStatusNotes
publish.on_error✅ Verifiedanodizer .anodizer.yaml (defaults.publish.on_error runs a cmd per failed publisher before rollback; failure context arrives as ANODIZER_PUBLISHER/ANODIZER_ERROR/ANODIZER_VERSION/ANODIZER_TAG/ANODIZER_GROUP/ANODIZER_REQUIRED/ANODIZER_ROLLED_BACK env vars on the hook process — read these instead of interpolating untrusted error text into the shell string — with matching template vars for trusted values). Workspace-wide; per-crate entries append before defaults. Fired live in the failed v0.15.1 publish (run 28809062839): ran on-error hook: anodizer notify --raw "anodizer: publisher $ANODIZER_PUBLISHER failed …" after the gemfury failure
publish.on_rollback✅ Verified (tests)crates/core/src/config/publishers/mod.rs (on_rollback: hook list) + crates/stage-publish/src/failure_hooks.rs — runs a cmd per publisher whose landed publish was rolled back (independent of on_error), with the rollback trigger reason exposed to the hook env. Neither dogfood config wires it yet
defaults.publish.cargo.retain_on_rollback⏳ Pendinganodizer .anodizer.yaml (retain_on_rollback: true under defaults.publish.cargo — crates.io publishes are permanent; retain even if a downstream publisher rolls back). Not yet consulted live: in the one live rollback (run 28809062839) cargo was Submitter-gated before publishing, so there was nothing to retain
schemastore.retain_on_rollback / mcp.retain_on_rollback✅ Verifiedanodizer .anodizer.yaml (retain_on_rollback: true on the top-level schemastore and mcp keys). Fired live in the v0.15.1 rollback (run 28809062839): skipped rollback for 'mcp' — retain_on_rollback is set and skipped rollback for 'schemastore' — retain_on_rollback is set

Tag configuration

KeyStatusNotes
tag.default_bump✅ Verifiedanodizer .anodizer.yaml (default_bump: none — chore/docs/ci-only ranges produce no release)
tag.bump_minor_pre_major✅ Verifiedanodizer .anodizer.yaml (bump_minor_pre_major: true — breaking changes stay in 0.x until 1.0 is deliberate)
tag.tag_prefix✅ Verifiedcfgd .anodizer.yaml (tag_prefix: "v")
tag.release_branches✅ Verifiedanodizer .anodizer.yaml (release_branches: [main, master])
tag.initial_version✅ VerifiedPer-crate. cfgd .anodizer.yaml (initial_version: "0.5.0" — the floor a newly added workspace starts from instead of 0.1.0)
git.tag_sort✅ Verifiedanodizer .anodizer.yaml (tag_sort: smartsemver)
git.ignore_tag_prefixes✅ Verifiedanodizer .anodizer.yaml (ignore_tag_prefixes: ["draft-"])
git.prerelease_suffix✅ Verifiedanodizer .anodizer.yaml + cfgd .anodizer.yaml (prerelease_suffix: "-" — strips trailing pre-release suffixes from version strings)
git.ignore_tags✅ Verifiedanodizer .anodizer.yaml + cfgd .anodizer.yaml (ignore_tags: ["nightly"] — excludes transient tags from version resolution)
version_files✅ VerifiedPer-crate — the list is declared on the crate whose version it tracks, so each workspace rewrites its own files at its own cadence. cfgd .anodizer.yaml enrols docs/installation.md, docs/bootstrap.md, docs/skill.md, chart/cfgd/Chart.yaml; cfgd v0.6.1 docs/installation.md carries the tag-time-rewritten 0.6.1 download URLs, committed atomically with the version bump

Defaults

KeyStatusNotes
defaults.targets✅ Verifiedanodizer .anodizer.yaml (6 targets: linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64/aarch64)
defaults.cross✅ Verifiedanodizer .anodizer.yaml (cross: auto)
defaults.builds.flags✅ Verifiedanodizer .anodizer.yaml (flags: [--release])
defaults.archives.formats✅ Verifiedanodizer .anodizer.yaml (formats: [tar.gz], format_overrides: windows→zip)
defaults.archives.hooks✅ Verifiedanodizer .anodizer.yaml (hooks.before + hooks.after with Tera vars)
defaults.checksum.algorithm✅ VerifiedLockstep. anodizer .anodizer.yaml (algorithm: sha256) → the combined anodizer_0.23.0_checksums.txt on every release
defaults.publish.cargo✅ Verifiedanodizer .anodizer.yaml (cargo: {} — presence opts every crate into crates.io)
defaults.publish.on_error⏳ Pendinganodizer .anodizer.yaml (on_error: [{cmd: "echo ..."}])
partial.by✅ Verifiedanodizer .anodizer.yaml + cfgd .anodizer.yaml (partial.by: os — shards the CI matrix by OS; enables the determinism fan-out build strategy)

Changelog

KeyStatusNotes
changelog.use✅ Verifiedanodizer .anodizer.yaml (use: git)
changelog.title✅ Verifiedanodizer .anodizer.yaml (title: "Changelog")
changelog.header✅ Verifiedanodizer .anodizer.yaml (header: "# Changelog for {{ ProjectName }}")
changelog.footer✅ Verifiedanodizer .anodizer.yaml (footer: "_Generated by anodizer._")
changelog.sort✅ Verifiedanodizer .anodizer.yaml (sort: asc)
changelog.abbrev✅ Verifiedanodizer .anodizer.yaml (abbrev: 12)
changelog.format✅ Verifiedanodizer .anodizer.yaml (format: "* {{ .SHA }} {{ .Message }} ({{ .AuthorUsername }})")
changelog.divider✅ Verifiedanodizer .anodizer.yaml (divider: "---")
changelog.filters.exclude✅ Verifiedanodizer .anodizer.yaml (excludes docs, ci, chore, style, build, test, revert and merge commits; each pattern carries the optional-scope group, e.g. ^docs(\(.*\))?!?:, so scoped subjects are excluded too)
changelog.groups✅ Verifiedanodizer .anodizer.yaml (Features/Bug Fixes/Performance/Others groups with regexp + order)
changelog.paths✅ Verifiedbrontes .anodizer.yaml (paths: ["src/**", "tests/**", "examples/**", "Cargo.toml", "Cargo.lock"] — only commits touching those paths enter the changelog; live on every brontes release)
changelog.files.per_crate✅ Verifiedcfgd .anodizer.yaml (per_crate: true); live output: per-crate changelogs like crates/cfgd/CHANGELOG.md and crates/cfgd-operator/CHANGELOG.md, written by the v0.5.0 release-bump commit (chore(release): bump cfgd-crd→0.3.5, cfgd-core→0.5.0, …)

Build artifacts

KeyStatusNotes
source.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true)
source.format✅ Verifiedanodizer .anodizer.yaml (format: tar.gz)
source.name_template✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}-source")
source.prefix_template✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}/")
source.files✅ Verifiedanodizer .anodizer.yaml (globs: crates/**/*.rs, Cargo.toml, Cargo.lock, LICENSE-MIT, LICENSE-APACHE, README.md)
sboms[].id✅ Verifiedanodizer .anodizer.yaml (id: default)
sboms[].documents✅ Verifiedanodizer .anodizer.yaml ("{{ .ArtifactName }}.cdx.json")
sboms[].artifacts✅ Verifiedanodizer .anodizer.yaml (artifacts: archive)
upx[].enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true)
upx[].binary✅ Verifiedanodizer .anodizer.yaml (binary: upx)
upx[].args✅ Verifiedanodizer .anodizer.yaml (["--best", "--lzma"])
upx[].compress✅ Verifiedanodizer .anodizer.yaml (compress: "9")
upx[].lzma✅ Verifiedanodizer .anodizer.yaml (lzma: true)
upx[].targets✅ Verifiedanodizer .anodizer.yaml (4 targets; excludes macOS ARM + Windows ARM — UPX unsupported there)
binstall.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true — per-target pkg_url overrides auto-derived from archive name_template)
checksum.name_template✅ VerifiedPer-crate. cfgd .anodizer.yaml (checksum.name_template: "{{ ArtifactName }}.sha256" on the cfgd workspace). anodizer leaves it unset and takes the combined-file default
checksum.split✅ VerifiedPer-crate. cfgd .anodizer.yaml (split: true on the cfgd workspace — one .sha256 sidecar per artifact instead of a combined file); live as the per-asset .sha256 files on cfgd v0.6.1. anodizer runs the combined mode, so its releases carry anodizer_<ver>_checksums.txt and no sidecars

Signing

KeyStatusNotes
signs[].id✅ Verifiedanodizer .anodizer.yaml (id: default)
signs[].artifacts✅ Verifiedanodizer .anodizer.yaml (artifacts: checksum — GPG signs each .sha256 sidecar)
signs[].cmd✅ Verifiedanodizer .anodizer.yaml (cmd: gpg)
signs[].args✅ Verifiedanodizer .anodizer.yaml (--batch --local-user {{ Env.GPG_FINGERPRINT }} --output {{ Signature }} --detach-sig {{ Artifact }})
signs[].if✅ Verifiedanodizer .anodizer.yaml (skips in snapshot mode; runs in harness mode for determinism proof)
binary_signs[].artifacts✅ Verifiedanodizer .anodizer.yaml (artifacts: binary — cosign signs each binary blob)
binary_signs[].cmd✅ Verifiedanodizer .anodizer.yaml (cmd: cosign sign-blob --key=env://COSIGN_KEY --bundle={{ Signature }} --yes {{ Artifact }})
binary_signs[].if✅ Verifiedanodizer .anodizer.yaml (same snapshot/harness guard as signs)
docker_signs[].artifacts✅ Verifiedanodizer .anodizer.yaml (artifacts: manifests — cosign signs OCI manifests)
docker_signs[].cmd✅ Verifiedanodizer .anodizer.yaml (cmd: cosign sign --key=env://COSIGN_KEY --yes {{ Artifact }})
docker_signs[].if✅ Verifiedanodizer .anodizer.yaml (same snapshot/harness guard)

Packaging

KeyStatusNotes
nfpm[].id✅ Verifiedanodizer .anodizer.yaml (id: default)
nfpm[].formats✅ Verifiedanodizer .anodizer.yaml (formats: [deb, rpm, apk])
nfpm[].vendor/maintainer/homepage✅ Verifiedanodizer .anodizer.yaml (metadata fields propagated to all three formats)
nfpm[].bindir✅ Verifiedanodizer .anodizer.yaml (bindir: /usr/bin)
nfpm[].section/priority/epoch/release/umask✅ Verifiedanodizer .anodizer.yaml (Debian/RPM packaging metadata)
nfpm[].mtime✅ Verifiedanodizer .anodizer.yaml (mtime: "{{ CommitDate }}" — reproducible package mtime)
nfpm[].recommends/suggests✅ Verifiedanodizer .anodizer.yaml (recommends git, suggests upx; an earlier provides: [anodizer] self-provide was removed — apk rejects a package that provides its own name)
nfpm[].file_name_template✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}_{{ RawVersion }}_{{ Os }}_{{ Arch }}")
nfpm[].contents✅ Verifiedanodizer .anodizer.yaml (LICENSE + README.md installed to /usr/share/doc/anodizer/)
nfpm[].deb.signature.key_file✅ Verifiedanodizer .anodizer.yaml ("{{ .Env.GPG_KEY_PATH }}", type: origin)
nfpm[].rpm.signature.key_file✅ Verifiedanodizer .anodizer.yaml (same GPG key; also sets group + packager)
nfpm[].apk.signature.key_file✅ Verifiedanodizer .anodizer.yaml ("{{ .Env.APK_PRIVATE_KEY_PATH }}" — RSA-PSS, not OpenPGP)
snapcrafts[].name/title/summary/description✅ Verifiedanodizer .anodizer.yaml. Dogfooded through v0.9.1: snapcraft.io/anodizer still serves the 0.9.1 revision uploaded 2026-06-13, the newest that ever landed
snapcrafts[].base✅ Verifiedanodizer .anodizer.yaml (base: core24; dogfooded through the v0.9.1 store revision)
snapcrafts[].grade/confinement✅ Verifiedanodizer .anodizer.yaml (grade: stable, confinement: devmode). anodizer needs classic — it execs the host's cargo/docker/gpg, which strict sandboxes — and the Snap Store has not granted the classic request, so the snap is parked on devmode
snapcrafts[].publish⏳ Pendinganodizer .anodizer.yaml sets publish: false, so no current release uploads. The upload path itself is proven by the 0.9.1 store revision; re-enabling waits on the classic-confinement grant
snapcrafts[].channel_templates✅ Verifiedanodizer .anodizer.yaml (["latest/beta"] — the Store only permits devmode uploads on beta/edge)
snapcrafts[].apps✅ Verifiedanodizer .anodizer.yaml (app with home, network, network-bind plugs; dogfooded through the v0.9.1 store revision)
appimages[].desktop✅ Verifiedanodizer .anodizer.yaml (assets/anodizer.desktop); AppImages ship on every release — see the v0.15.5 assets (anodizer-0.15.5-{amd64,arm64}.AppImage) built by run 28882554907
appimages[].icon✅ Verifiedanodizer .anodizer.yaml (assets/logo.png); embedded in the shipped v0.15.5 AppImages
appimages[].filename✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}-{{ Arch }}.AppImage") — the shipped asset names anodizer-0.15.5-amd64.AppImage / -arm64.AppImage on the v0.15.5 release are that template rendered
appimages[].update_information✅ Verifiedanodizer .anodizer.yaml (gh-releases-zsync|...|*.AppImage.zsync); the resulting .AppImage.zsync delta-update files ship as release assets — v0.15.5 includes anodizer-0.15.5-{amd64,arm64}.AppImage.zsync
makeselfs[].id✅ Verifiedanodizer .anodizer.yaml (id: default)
makeselfs[].name✅ Verifiedanodizer .anodizer.yaml ("anodizer Installer")
makeselfs[].script✅ Verifiedanodizer .anodizer.yaml (scripts/makeself-install.sh)
makeselfs[].filename✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}-{{ Os }}-{{ Arch }}-installer.run")
srpm.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true)
srpm.package_name✅ Verifiedanodizer .anodizer.yaml (package_name: anodizer)
srpm.spec_file✅ Verifiedanodizer .anodizer.yaml (spec_file: anodizer.spec)
srpm.file_name_template✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }}-{{ RawVersion }}-1.src.rpm")

Release

KeyStatusNotes
release.github.owner/name✅ Verifiedanodizer .anodizer.yaml (owner: tj-smith47, name: anodizer)
release.draft✅ Verifiedanodizer .anodizer.yaml (draft: false)
release.prerelease✅ Verifiedanodizer .anodizer.yaml (prerelease: auto)
release.make_latest✅ Verifiedanodizer .anodizer.yaml (make_latest: auto)
release.mode✅ Verifiedanodizer .anodizer.yaml (mode: keep-existing)
release.target_commitish✅ Verifiedanodizer .anodizer.yaml ("{{ .Commit }}")
release.discussion_category_name✅ Verifiedanodizer .anodizer.yaml ("Announcements")
release.replace_existing_draft✅ Verifiedanodizer .anodizer.yaml (replace_existing_draft: true)
release.replace_existing_artifacts✅ Verifiedanodizer .anodizer.yaml (replace_existing_artifacts: true)
release.name_template✅ Verifiedanodizer .anodizer.yaml ("{{ ProjectName }} {{ Tag }}")
release.header✅ Verifiedanodizer .anodizer.yaml ("## What's new in {{ .Tag }}")
release.footer✅ Verifiedanodizer .anodizer.yaml (credits anodizer with 🦀)
release.include_meta✅ Verifiedanodizer .anodizer.yaml (include_meta: true)
release.extra_files✅ Verifiedanodizer .anodizer.yaml (APK signing public key + man page, both with allow_empty: true)

Package managers

KeyStatusNotes
publish.cargo (via defaults)✅ Verifiedanodizer crates.io (all workspace crates published in dependency order)
publish.cargo.wait_for_workspace_deps✅ Verifiedanodizer .anodizer.yaml (inherited from defaults.publish.cargo: {} — waits for sparse index propagation)
publish.cargo.retain_on_rollback⏳ Pendinganodizer .anodizer.yaml (retain_on_rollback: true in defaults.publish.cargo); also wired in brontes .anodizer.yaml. A live rollback has happened (run 28809062839) but cargo was Submitter-gated before publishing, so this key was never consulted
publish.cargo.index_timeout✅ Verifiedcfgd .anodizer.yaml (defaults.publish.cargo.index_timeout: 600 — inherited by all 4 crates) · brontes .anodizer.yaml (same 600s setting; absorbs sporadic crates.io 503s)
publish.cargo.locked / all_features✅ Verifiedbrontes .anodizer.yaml (publish.cargo: {locked: true, all_features: true} — pins the CI-resolved Cargo.lock for the verify build and matches the docs.rs all-features render); live on crates.io/crates/brontes 0.3.0
pypis[].name✅ Verifiedanodizer .anodizer.yaml (name: anodizer) → pypi.org/project/anodizer at 0.23.0, pip install anodizer
pypis[].auth: oidc✅ Verifiedanodizer .anodizer.yaml (auth: oidc — PyPI Trusted Publishing; the workflow's OIDC id-token is exchanged for a short-lived upload token, so no API token is stored). Runs from the github-hosted publish-oidc.yml, the workflow PyPI's Trusted Publisher config names
pypis[].requires_python / project_urls / classifiers✅ Verifiedanodizer .anodizer.yaml (>=3.8, Documentation + Repository links, three classifiers) — all four render on the live project page
per-platform wheel set✅ VerifiedOne release produces 9 wheels from the same binaries the archives ship — macOS x86_64 / arm64 / universal2, manylinux + musllinux for x86_64 and aarch64, Windows amd64 / arm64 — all listed under 0.23.0's downloads. The platform tags are derived from the configured targets, not restated in config
publish.krew✅ VerifiedPer-crate. cfgd's CLI crate declares publish.krew in cfgd .anodizer.yaml, describing the kubectl-cfgd plugin rather than the CLI; the upstream index serves plugins/cfgd.yaml at v0.6.1
homebrew_cores[]🤝 Help wantedImplemented, but homebrew-core's notability and versioning rules exclude all three of our projects, so no dogfood config declares it. See homebrew_core: docs
aur_sources[]✅ VerifiedLockstep. anodizer .anodizer.yaml (aur_sources: renders a cargo build-from-source PKGBUILD, distinct from the prebuilt-binary aur: package) → aur.archlinux.org/packages/anodizer at 0.23.0-1, published alongside anodizer-bin from the same release. See aur_sources: docs
install_scripts✅ Verifiedanodizer .anodizer.yaml supplies only the banner metadata and binaries: [anodizer]; the os/arch→asset table, the uname detection arms, the supported-platform list, the checksums filename, the tag prefix, and the repo all derive from the configured targets. Live as the install.sh asset on every release
before_publish.hooks✅ Verifiedanodizer .anodizer.yaml — two run_once gates run after build/sign/checksum and before any one-way-door publisher dispatches: every checksum file must exist and be non-empty, and every release archive in each configured format must be a non-empty file. A non-zero exit aborts before crates.io, PyPI, or GitHub receive anything. See before_publish: docs
publish.aur.git_url✅ Verifiedanodizer .anodizer.yaml (ssh://aur@aur.archlinux.org/anodizer-bin.git)
publish.aur.name/description/license/depends✅ Verifiedanodizer .anodizer.yaml
publish.aur.private_key✅ Verifiedanodizer release.yml (AUR_SSH_KEY secret)
publish.nix.repository✅ Verifiedanodizer .anodizer.yaml (tj-smith47/nix-pkgs)
publish.nix.formatter✅ Verifiedanodizer .anodizer.yaml (formatter: alejandra)
publish.nix.extra_install/post_install✅ Verifiedanodizer .anodizer.yaml (installs man page; echoes setup hint)
publish.scoop.repository✅ Verifiedanodizer .anodizer.yaml (tj-smith47/scoop-bucket)
publish.scoop.depends/shortcuts✅ Verifiedanodizer .anodizer.yaml (depends: git; shortcuts: anodizer.exe)
publish.winget.package_identifier✅ Verifiedanodizer .anodizer.yaml (TJSmith.Anodizer)
publish.winget.dependencies✅ Verifiedanodizer .anodizer.yaml (Microsoft.VCRedist.2015+.x64 — required for MSVC binaries)
publish.winget.update_existing_pr✅ Verifiedanodizer .anodizer.yaml (update_existing_pr: true)
publish.chocolatey.title/summary/authors/owners✅ Verifiedanodizer .anodizer.yaml
publish.chocolatey.republish_in_moderation✅ Verifiedanodizer .anodizer.yaml (republish_in_moderation: true)
homebrew_casks[].repository/directory✅ Verifiedanodizer .anodizer.yaml (tj-smith47/homebrew-tap, Casks/)
homebrew_casks[].binaries✅ Verifiedanodizer .anodizer.yaml (binaries: [anodizer])
homebrew_casks[].generate_completions_from_executable✅ Verifiedanodizer .anodizer.yaml (calls anodizer completion <shell> at cask install for bash/zsh/fish)
homebrew_casks[].manpages✅ Verifiedanodizer .anodizer.yaml (anodizer.1 downloaded from release extra_files)
homebrew_casks[].caveats✅ Verifiedanodizer .anodizer.yaml ("Run anodizer init...")
homebrew_casks[].dependencies✅ Verifiedanodizer .anodizer.yaml (formula: git)

Distributions

KeyStatusNotes
blobs[].provider✅ Verifiedanodizer .anodizer.yaml (provider: s3); live every release — v0.15.5 (run 28882554907) logged uploaded 47 object(s), skipped 0 (identical) → s3://…/v0.15.5 against the cluster-internal MinIO
blobs[].bucket✅ Verifiedanodizer .anodizer.yaml (anodizer-releases); the live run summary records objects at s3://anodizer-releases/v0.15.5/… (run 28882554907)
blobs[].endpoint✅ Verifiedanodizer .anodizer.yaml ("{{ Env.MINIO_ENDPOINT }}"http://minio.jarvispro.svc.cluster.local:9003); the live 47-object upload rides this endpoint (run 28882554907)
blobs[].region✅ Verifiedanodizer .anodizer.yaml (region: us-east-1 — compatibility placeholder; MinIO ignores region); part of the live-exercised blobs entry (run 28882554907)
blobs[].s3_force_path_style✅ Verifiedanodizer .anodizer.yaml (s3_force_path_style: true — required for MinIO path-style addressing; without it the live upload cannot address the bucket at all). Live at run 28882554907
blobs[].disable_ssl✅ Verifiedanodizer .anodizer.yaml (disable_ssl: true — the in-cluster MinIO endpoint is plain http; without it the S3 client rejects the non-https endpoint). Live at run 28882554907
blobs[].directory✅ Verifiedanodizer .anodizer.yaml (directory: "{{ Tag }}"); rendered live as the …/v0.15.5/ prefix on every uploaded object (run 28882554907)
cloudsmiths[].organization✅ Verifiedanodizer .anodizer.yaml (organization: jarvispro)
cloudsmiths[].repository✅ Verifiedanodizer .anodizer.yaml (repository: anodizer)
cloudsmiths[].formats✅ Verifiedanodizer .anodizer.yaml ([deb, rpm, alpine])
cloudsmiths[].distributions✅ Verifiedanodizer .anodizer.yaml (deb/rpm: any-distro/any-version; alpine: alpine/any-version)
cloudsmiths[].republish✅ Verifiedanodizer .anodizer.yaml (republish: true — prevents MD5 conflict on re-cut)
dockerhub[].username✅ Verifiedanodizer .anodizer.yaml (username: tsmthtj); the description-sync publisher runs live against hub.docker.com/r/tsmthtj/anodizer — summary dockerhub Assets optional succeeded at v0.15.5 (run 28882554907)
dockerhub[].description✅ Verifiedanodizer .anodizer.yaml (description: + full_description.from_file: README.md); at v0.15.5 the publisher read the remote repo and logged skipped dockerhub PATCH … description / full_description match remote — the synced text is live on hub.docker.com/r/tsmthtj/anodizer
artifactories[].target🤝 Help wantedanodizer .anodizer.yaml (skip: true — no Artifactory instance)
artifactories[].method🤝 Help wantedanodizer .anodizer.yaml (method: PUT, disabled)
npms[].scope✅ Verifiedanodizer .anodizer.yaml (scope: "@tj-smith47"); the per-platform binary packages publish live under that scope (@tj-smith47/anodizer-{darwin,linux,win32}-* on npm)
npms[].metapackage/bin/mode✅ Verifiedanodizer .anodizer.yaml (metapackage: anodizer, bin: anodizer, mode: optional-deps — biome/git-cliff pattern); the live anodizer metapackage resolves the matching binary via 8 optionalDependencies (npm view anodizer optionalDependencies)
mcp.name/title/description/homepage✅ Verifiedanodizer .anodizer.yaml
mcp.repository✅ Verifiedanodizer .anodizer.yaml (source: github)
mcp.packages✅ Verifiedanodizer .anodizer.yaml (registry_type: oci, identifier: ghcr.io/tj-smith47/anodizer, transport: stdio)
mcp.auth✅ Verifiedanodizer .anodizer.yaml (auth.type: github-oidc; requires id-token: write on release job)
mcp.retain_on_rollback✅ Verifiedanodizer .anodizer.yaml (retain_on_rollback: true); fired live in the v0.15.1 rollback (run 28809062839): skipped rollback for 'mcp' — retain_on_rollback is set
schemastore.repository✅ Verifiedanodizer .anodizer.yaml (tj-smith47/schemastore)
schemastore.schemas✅ Verifiedanodizer .anodizer.yaml (matches .anodizer.yaml + .anodizer.yml; URL: tj-smith47.github.io/anodizer/schema.json)
schemastore.retain_on_rollback✅ Verifiedanodizer .anodizer.yaml (retain_on_rollback: true); fired live in the v0.15.1 rollback (run 28809062839): skipped rollback for 'schemastore' — retain_on_rollback is set

Post-release

KeyStatusNotes
verify_release.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true); the stage ran live at v0.15.5 (run 28882554907): Verifying release … all post-publish checks passed, summary verify-release passed, and verify_release.passed: true in the run's summary.json
verify_release.assert_assets✅ Verifiedanodizer .anodizer.yaml (assert_assets: true — every produced artifact must appear as an uploaded release asset); part of the live-passing verify at v0.15.5 (run 28882554907, all post-publish checks passed)
verify_release.install_smoke✅ Verifiedanodizer .anodizer.yaml (install_smoke: {} — the empty block is Some, which enables the check: installs each nfpm package in a container and runs <bin> --version). Ran live at v0.15.5 (run 28882554907): Docker was available on the publish runner and no skipped install smoke-test warning fired, so the smoke jobs ran within the passing verify. Also configured in cfgd .anodizer.yaml
verify_release.glibc_ceiling✅ Verifiedanodizer .anodizer.yaml (glibc_ceiling: "2.36" — fails if any .deb requires glibc > 2.36); part of the live-passing verify at v0.15.5 (run 28882554907)
attestations.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true); the v0.23.0 release ships the produced attestation-subjects.json asset
attestations.mode✅ VerifiedBoth modes live. mode: subjects: anodizer .anodizer.yaml (writes dist/attestation-subjects.json; anodizer-action feeds it to actions/attest-build-provenance) — the 19 KB attestation-subjects.json attached to v0.23.0. mode: emit: brontes .anodizer.yaml (self-contained in-toto SLSA-provenance v1 statement) — the live attestation.intoto.jsonl attached to brontes v0.3.0
attestations.artifacts✅ Verifiedbrontes .anodizer.yaml (artifacts: [source, sbom] — the v0.3.0 attestation.intoto.jsonl covers the source tarball and its SBOM). anodizer leaves it unset (defaults to all artifact kinds under mode: subjects)
milestones[].repo✅ Verifiedanodizer .anodizer.yaml (tj-smith47/anodizer)
milestones[].close🤝 Help wantedanodizer .anodizer.yaml (close: false — wired but disabled; no milestones configured)
milestones[].fail_on_error⏳ Pendinganodizer .anodizer.yaml (fail_on_error: true — milestone errors fail the release instead of vanishing; the flip from false awaits a live release, and close: false means the publisher currently does nothing)
milestones[].name_template✅ Verifiedanodizer .anodizer.yaml (name_template: "{{ Tag }}")

Announce

KeyStatusNotes
announce.gate_on✅ Verifiedanodizer .anodizer.yaml (gate_on: required_publishers — announce only after all required publishers succeed)
announce.webhook.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true — posts JSON to tj.jarvispro.io/webhooks/anodizer)
announce.webhook.endpoint_url✅ Verifiedanodizer .anodizer.yaml
announce.webhook.content_type✅ Verifiedanodizer .anodizer.yaml (application/json)
announce.webhook.message_template✅ Verifiedanodizer .anodizer.yaml (JSON payload with project, tag, url)
announce.webhook.headers✅ Verifiedanodizer .anodizer.yaml (X-Anodizer-Source: release)
announce.webhook.skip_tls_verify✅ Verifiedanodizer .anodizer.yaml (skip_tls_verify: false)
announce.webhook.expected_status_codes✅ Verifiedanodizer .anodizer.yaml ([200, 202])
announce.email.enabled✅ Verifiedanodizer .anodizer.yaml (enabled: true)
announce.email.host/port/username/from/to✅ Verifiedanodizer .anodizer.yaml (Gmail SMTP, port 587, STARTTLS)
announce.email.subject_template✅ Verifiedanodizer .anodizer.yaml
announce.email.encryption✅ Verifiedanodizer .anodizer.yaml (encryption: starttls)
announce.discord🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.slack🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.telegram🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.teams🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.mattermost🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.reddit🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.twitter🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.mastodon🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.bluesky🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.linkedin🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.discourse🤝 Help wantedanodizer .anodizer.yaml (enabled: false)
announce.opencollective🤝 Help wantedanodizer .anodizer.yaml (enabled: false)

Platform-specific installers (now live)

These blocks were originally skip: true; every one except notarization has since been enabled and ships live release assets (see What anodizer builds for the per-format asset links).

KeyStatusNotes
flatpaks[]✅ Verifiedanodizer .anodizer.yaml (app_id: io.github.tj_smith47.Anodizer, runtime org.freedesktop.Platform/24.08, host-surface finish_args); live anodizer_0.23.0_linux_amd64.flatpak
app_bundles[]✅ Verifiedanodizer .anodizer.yaml (unsigned .app assembly, shipped inside the live .dmg)
dmgs[]✅ Verifiedanodizer .anodizer.yaml; live anodizer_amd64.dmg
pkgs[]✅ Verifiedanodizer .anodizer.yaml; live anodizer_amd64.pkg
msis[]✅ Verifiedanodizer .anodizer.yaml; live anodizer_amd64.msi
nsis[]✅ Verifiedanodizer .anodizer.yaml; live anodizer_x64-setup.exe
notarize🤝 Help wantedImplemented (rcodesign, cross-platform) but not configured — signing + notarization need an Apple Developer Program membership we don't have. Bundles ship unsigned