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: osTop-level config
| Key | Status | Notes |
|---|---|---|
project_name | ✅ Verified | anodizer .anodizer.yaml (project_name: anodizer) |
dist | ✅ Verified | anodizer .anodizer.yaml (dist: ./dist) |
env | ✅ Verified | anodizer .anodizer.yaml (env: - RELEASE_TYPE=stable) |
env_files | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (variables.repo_url + .description). cfgd carried the same block through v0.4.0 (dropped on master 2026-06-11) |
template_files[] | ✅ Verified | install.sh (rendered + attached on every cfgd release) |
includes[].from_file | ✅ Verified | cfgd .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 wanted | No live config pulls a remote include |
before | ✅ Verified | cfgd .anodizer.yaml (before.hooks runs cargo fmt --check, clippy, test) |
after | ✅ Verified | cfgd .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 | ✅ Verified | cfgd .anodizer.yaml (archive hooks.before) |
build.hooks.post | ✅ Verified | cfgd .anodizer.yaml (archive hooks.after) |
snapshot.name_template | ✅ Verified | anodizer .anodizer.yaml (snapshot.version_template) |
--auto-snapshot | ✅ Verified | anodizer ci.yml (snapshot build on every master push) |
nightly.* | ✅ Verified | Both 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 | ✅ Verified | cfgd .anodizer.yaml (metadata.homepage: https://github.com/tj-smith47/cfgd) |
metadata.license | ✅ Verified | cfgd .anodizer.yaml (metadata.license: MIT) |
metadata.description | ✅ Verified | cfgd .anodizer.yaml (metadata.description) |
metadata.maintainers | ✅ Verified | cfgd .anodizer.yaml (metadata.maintainers) |
metadata.mod_timestamp | ✅ Verified | anodizer .anodizer.yaml (metadata.mod_timestamp: "{{ CommitTimestamp }}"; applied as mtime of dist/metadata.json and dist/artifacts.json) |
report_sizes | ✅ Verified | anodizer .anodizer.yaml (report_sizes: true; prints per-artifact and total sizes in the release summary) |
retry (attempts / delay / max_delay / max_elapsed) | ✅ Verified | anodizer .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.
| Helper | Status | Notes |
|---|---|---|
{{ .Field }} | ✅ Verified | crates/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 }} | ✅ Verified | crates/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 }} | ✅ Verified | crates/core/src/template/vars.rs (Metadata binding) |
{{ .IsMerging }} | ✅ Verified | crates/core/src/template/vars.rs (IsMerging binding) |
{{ .IsRelease }} | ✅ Verified | crates/core/src/template/vars.rs (IsRelease binding) |
| String / path / version / env / filter helpers | ✅ Verified | crates/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 | ✅ Verified | crates/core/src/template/base_tera/hash.rs (register_hash_fn! macro) |
readFile, mustReadFile | ✅ Verified | crates/core/src/template/base_tera/env_file.rs (readFile / mustReadFile registrations) |
time, .Now.Format | ✅ Verified | crates/core/src/template/base_tera/datetime.rs (time function + Now binding) |
mdv2escape | ✅ Verified | crates/core/src/template/base_tera/text.rs (mdv2escape filter) |
urlPathEscape | ✅ Verified | crates/core/src/template/base_tera/path.rs (urlPathEscape filter) |
in | ✅ Verified | crates/core/src/template/base_tera/collection.rs (in filter) |
reReplaceAll | ✅ Verified | crates/core/src/template/base_tera/text.rs (reReplaceAll filter) |
Monorepo
| Key | Status | Notes |
|---|---|---|
monorepo.tag_prefix | ✅ Verified | Per-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 | ✅ Verified | Per-crate. cfgd .anodizer.yaml (path: crates/cfgd-crd, crates/cfgd-core, crates/cfgd, crates/cfgd-operator, crates/cfgd-csi) |
cargo_workspace detection | ✅ Verified | Per-crate. cfgd .anodizer.yaml (5 workspaces: entries: cfgd-crd, cfgd-core, cfgd, cfgd-operator, cfgd-csi) |
depends_on | ✅ Verified | Per-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
| Key | Status | Notes |
|---|---|---|
publish.on_error | ✅ Verified | anodizer .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 | ⏳ Pending | anodizer .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 | ✅ Verified | anodizer .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
| Key | Status | Notes |
|---|---|---|
tag.default_bump | ✅ Verified | anodizer .anodizer.yaml (default_bump: none — chore/docs/ci-only ranges produce no release) |
tag.bump_minor_pre_major | ✅ Verified | anodizer .anodizer.yaml (bump_minor_pre_major: true — breaking changes stay in 0.x until 1.0 is deliberate) |
tag.tag_prefix | ✅ Verified | cfgd .anodizer.yaml (tag_prefix: "v") |
tag.release_branches | ✅ Verified | anodizer .anodizer.yaml (release_branches: [main, master]) |
tag.initial_version | ✅ Verified | Per-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 | ✅ Verified | anodizer .anodizer.yaml (tag_sort: smartsemver) |
git.ignore_tag_prefixes | ✅ Verified | anodizer .anodizer.yaml (ignore_tag_prefixes: ["draft-"]) |
git.prerelease_suffix | ✅ Verified | anodizer .anodizer.yaml + cfgd .anodizer.yaml (prerelease_suffix: "-" — strips trailing pre-release suffixes from version strings) |
git.ignore_tags | ✅ Verified | anodizer .anodizer.yaml + cfgd .anodizer.yaml (ignore_tags: ["nightly"] — excludes transient tags from version resolution) |
version_files | ✅ Verified | Per-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
| Key | Status | Notes |
|---|---|---|
defaults.targets | ✅ Verified | anodizer .anodizer.yaml (6 targets: linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64/aarch64) |
defaults.cross | ✅ Verified | anodizer .anodizer.yaml (cross: auto) |
defaults.builds.flags | ✅ Verified | anodizer .anodizer.yaml (flags: [--release]) |
defaults.archives.formats | ✅ Verified | anodizer .anodizer.yaml (formats: [tar.gz], format_overrides: windows→zip) |
defaults.archives.hooks | ✅ Verified | anodizer .anodizer.yaml (hooks.before + hooks.after with Tera vars) |
defaults.checksum.algorithm | ✅ Verified | Lockstep. anodizer .anodizer.yaml (algorithm: sha256) → the combined anodizer_0.23.0_checksums.txt on every release |
defaults.publish.cargo | ✅ Verified | anodizer .anodizer.yaml (cargo: {} — presence opts every crate into crates.io) |
defaults.publish.on_error | ⏳ Pending | anodizer .anodizer.yaml (on_error: [{cmd: "echo ..."}]) |
partial.by | ✅ Verified | anodizer .anodizer.yaml + cfgd .anodizer.yaml (partial.by: os — shards the CI matrix by OS; enables the determinism fan-out build strategy) |
Changelog
| Key | Status | Notes |
|---|---|---|
changelog.use | ✅ Verified | anodizer .anodizer.yaml (use: git) |
changelog.title | ✅ Verified | anodizer .anodizer.yaml (title: "Changelog") |
changelog.header | ✅ Verified | anodizer .anodizer.yaml (header: "# Changelog for {{ ProjectName }}") |
changelog.footer | ✅ Verified | anodizer .anodizer.yaml (footer: "_Generated by anodizer._") |
changelog.sort | ✅ Verified | anodizer .anodizer.yaml (sort: asc) |
changelog.abbrev | ✅ Verified | anodizer .anodizer.yaml (abbrev: 12) |
changelog.format | ✅ Verified | anodizer .anodizer.yaml (format: "* {{ .SHA }} {{ .Message }} ({{ .AuthorUsername }})") |
changelog.divider | ✅ Verified | anodizer .anodizer.yaml (divider: "---") |
changelog.filters.exclude | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (Features/Bug Fixes/Performance/Others groups with regexp + order) |
changelog.paths | ✅ Verified | brontes .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 | ✅ Verified | cfgd .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
| Key | Status | Notes |
|---|---|---|
source.enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true) |
source.format | ✅ Verified | anodizer .anodizer.yaml (format: tar.gz) |
source.name_template | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}-source") |
source.prefix_template | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}/") |
source.files | ✅ Verified | anodizer .anodizer.yaml (globs: crates/**/*.rs, Cargo.toml, Cargo.lock, LICENSE-MIT, LICENSE-APACHE, README.md) |
sboms[].id | ✅ Verified | anodizer .anodizer.yaml (id: default) |
sboms[].documents | ✅ Verified | anodizer .anodizer.yaml ("{{ .ArtifactName }}.cdx.json") |
sboms[].artifacts | ✅ Verified | anodizer .anodizer.yaml (artifacts: archive) |
upx[].enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true) |
upx[].binary | ✅ Verified | anodizer .anodizer.yaml (binary: upx) |
upx[].args | ✅ Verified | anodizer .anodizer.yaml (["--best", "--lzma"]) |
upx[].compress | ✅ Verified | anodizer .anodizer.yaml (compress: "9") |
upx[].lzma | ✅ Verified | anodizer .anodizer.yaml (lzma: true) |
upx[].targets | ✅ Verified | anodizer .anodizer.yaml (4 targets; excludes macOS ARM + Windows ARM — UPX unsupported there) |
binstall.enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true — per-target pkg_url overrides auto-derived from archive name_template) |
checksum.name_template | ✅ Verified | Per-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 | ✅ Verified | Per-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
| Key | Status | Notes |
|---|---|---|
signs[].id | ✅ Verified | anodizer .anodizer.yaml (id: default) |
signs[].artifacts | ✅ Verified | anodizer .anodizer.yaml (artifacts: checksum — GPG signs each .sha256 sidecar) |
signs[].cmd | ✅ Verified | anodizer .anodizer.yaml (cmd: gpg) |
signs[].args | ✅ Verified | anodizer .anodizer.yaml (--batch --local-user {{ Env.GPG_FINGERPRINT }} --output {{ Signature }} --detach-sig {{ Artifact }}) |
signs[].if | ✅ Verified | anodizer .anodizer.yaml (skips in snapshot mode; runs in harness mode for determinism proof) |
binary_signs[].artifacts | ✅ Verified | anodizer .anodizer.yaml (artifacts: binary — cosign signs each binary blob) |
binary_signs[].cmd | ✅ Verified | anodizer .anodizer.yaml (cmd: cosign sign-blob --key=env://COSIGN_KEY --bundle={{ Signature }} --yes {{ Artifact }}) |
binary_signs[].if | ✅ Verified | anodizer .anodizer.yaml (same snapshot/harness guard as signs) |
docker_signs[].artifacts | ✅ Verified | anodizer .anodizer.yaml (artifacts: manifests — cosign signs OCI manifests) |
docker_signs[].cmd | ✅ Verified | anodizer .anodizer.yaml (cmd: cosign sign --key=env://COSIGN_KEY --yes {{ Artifact }}) |
docker_signs[].if | ✅ Verified | anodizer .anodizer.yaml (same snapshot/harness guard) |
Packaging
| Key | Status | Notes |
|---|---|---|
nfpm[].id | ✅ Verified | anodizer .anodizer.yaml (id: default) |
nfpm[].formats | ✅ Verified | anodizer .anodizer.yaml (formats: [deb, rpm, apk]) |
nfpm[].vendor/maintainer/homepage | ✅ Verified | anodizer .anodizer.yaml (metadata fields propagated to all three formats) |
nfpm[].bindir | ✅ Verified | anodizer .anodizer.yaml (bindir: /usr/bin) |
nfpm[].section/priority/epoch/release/umask | ✅ Verified | anodizer .anodizer.yaml (Debian/RPM packaging metadata) |
nfpm[].mtime | ✅ Verified | anodizer .anodizer.yaml (mtime: "{{ CommitDate }}" — reproducible package mtime) |
nfpm[].recommends/suggests | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }}_{{ RawVersion }}_{{ Os }}_{{ Arch }}") |
nfpm[].contents | ✅ Verified | anodizer .anodizer.yaml (LICENSE + README.md installed to /usr/share/doc/anodizer/) |
nfpm[].deb.signature.key_file | ✅ Verified | anodizer .anodizer.yaml ("{{ .Env.GPG_KEY_PATH }}", type: origin) |
nfpm[].rpm.signature.key_file | ✅ Verified | anodizer .anodizer.yaml (same GPG key; also sets group + packager) |
nfpm[].apk.signature.key_file | ✅ Verified | anodizer .anodizer.yaml ("{{ .Env.APK_PRIVATE_KEY_PATH }}" — RSA-PSS, not OpenPGP) |
snapcrafts[].name/title/summary/description | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (base: core24; dogfooded through the v0.9.1 store revision) |
snapcrafts[].grade/confinement | ✅ Verified | anodizer .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 | ⏳ Pending | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (["latest/beta"] — the Store only permits devmode uploads on beta/edge) |
snapcrafts[].apps | ✅ Verified | anodizer .anodizer.yaml (app with home, network, network-bind plugs; dogfooded through the v0.9.1 store revision) |
appimages[].desktop | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (assets/logo.png); embedded in the shipped v0.15.5 AppImages |
appimages[].filename | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (id: default) |
makeselfs[].name | ✅ Verified | anodizer .anodizer.yaml ("anodizer Installer") |
makeselfs[].script | ✅ Verified | anodizer .anodizer.yaml (scripts/makeself-install.sh) |
makeselfs[].filename | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }}-{{ Version }}-{{ Os }}-{{ Arch }}-installer.run") |
srpm.enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true) |
srpm.package_name | ✅ Verified | anodizer .anodizer.yaml (package_name: anodizer) |
srpm.spec_file | ✅ Verified | anodizer .anodizer.yaml (spec_file: anodizer.spec) |
srpm.file_name_template | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }}-{{ RawVersion }}-1.src.rpm") |
Release
| Key | Status | Notes |
|---|---|---|
release.github.owner/name | ✅ Verified | anodizer .anodizer.yaml (owner: tj-smith47, name: anodizer) |
release.draft | ✅ Verified | anodizer .anodizer.yaml (draft: false) |
release.prerelease | ✅ Verified | anodizer .anodizer.yaml (prerelease: auto) |
release.make_latest | ✅ Verified | anodizer .anodizer.yaml (make_latest: auto) |
release.mode | ✅ Verified | anodizer .anodizer.yaml (mode: keep-existing) |
release.target_commitish | ✅ Verified | anodizer .anodizer.yaml ("{{ .Commit }}") |
release.discussion_category_name | ✅ Verified | anodizer .anodizer.yaml ("Announcements") |
release.replace_existing_draft | ✅ Verified | anodizer .anodizer.yaml (replace_existing_draft: true) |
release.replace_existing_artifacts | ✅ Verified | anodizer .anodizer.yaml (replace_existing_artifacts: true) |
release.name_template | ✅ Verified | anodizer .anodizer.yaml ("{{ ProjectName }} {{ Tag }}") |
release.header | ✅ Verified | anodizer .anodizer.yaml ("## What's new in {{ .Tag }}") |
release.footer | ✅ Verified | anodizer .anodizer.yaml (credits anodizer with 🦀) |
release.include_meta | ✅ Verified | anodizer .anodizer.yaml (include_meta: true) |
release.extra_files | ✅ Verified | anodizer .anodizer.yaml (APK signing public key + man page, both with allow_empty: true) |
Package managers
| Key | Status | Notes |
|---|---|---|
publish.cargo (via defaults) | ✅ Verified | anodizer crates.io (all workspace crates published in dependency order) |
publish.cargo.wait_for_workspace_deps | ✅ Verified | anodizer .anodizer.yaml (inherited from defaults.publish.cargo: {} — waits for sparse index propagation) |
publish.cargo.retain_on_rollback | ⏳ Pending | anodizer .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 | ✅ Verified | cfgd .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 | ✅ Verified | brontes .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 | ✅ Verified | anodizer .anodizer.yaml (name: anodizer) → pypi.org/project/anodizer at 0.23.0, pip install anodizer |
pypis[].auth: oidc | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (>=3.8, Documentation + Repository links, three classifiers) — all four render on the live project page |
| per-platform wheel set | ✅ Verified | One 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 | ✅ Verified | Per-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 wanted | Implemented, 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[] | ✅ Verified | Lockstep. 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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (ssh://aur@aur.archlinux.org/anodizer-bin.git) |
publish.aur.name/description/license/depends | ✅ Verified | anodizer .anodizer.yaml |
publish.aur.private_key | ✅ Verified | anodizer release.yml (AUR_SSH_KEY secret) |
publish.nix.repository | ✅ Verified | anodizer .anodizer.yaml (tj-smith47/nix-pkgs) |
publish.nix.formatter | ✅ Verified | anodizer .anodizer.yaml (formatter: alejandra) |
publish.nix.extra_install/post_install | ✅ Verified | anodizer .anodizer.yaml (installs man page; echoes setup hint) |
publish.scoop.repository | ✅ Verified | anodizer .anodizer.yaml (tj-smith47/scoop-bucket) |
publish.scoop.depends/shortcuts | ✅ Verified | anodizer .anodizer.yaml (depends: git; shortcuts: anodizer.exe) |
publish.winget.package_identifier | ✅ Verified | anodizer .anodizer.yaml (TJSmith.Anodizer) |
publish.winget.dependencies | ✅ Verified | anodizer .anodizer.yaml (Microsoft.VCRedist.2015+.x64 — required for MSVC binaries) |
publish.winget.update_existing_pr | ✅ Verified | anodizer .anodizer.yaml (update_existing_pr: true) |
publish.chocolatey.title/summary/authors/owners | ✅ Verified | anodizer .anodizer.yaml |
publish.chocolatey.republish_in_moderation | ✅ Verified | anodizer .anodizer.yaml (republish_in_moderation: true) |
homebrew_casks[].repository/directory | ✅ Verified | anodizer .anodizer.yaml (tj-smith47/homebrew-tap, Casks/) |
homebrew_casks[].binaries | ✅ Verified | anodizer .anodizer.yaml (binaries: [anodizer]) |
homebrew_casks[].generate_completions_from_executable | ✅ Verified | anodizer .anodizer.yaml (calls anodizer completion <shell> at cask install for bash/zsh/fish) |
homebrew_casks[].manpages | ✅ Verified | anodizer .anodizer.yaml (anodizer.1 downloaded from release extra_files) |
homebrew_casks[].caveats | ✅ Verified | anodizer .anodizer.yaml ("Run anodizer init...") |
homebrew_casks[].dependencies | ✅ Verified | anodizer .anodizer.yaml (formula: git) |
Distributions
| Key | Status | Notes |
|---|---|---|
blobs[].provider | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (anodizer-releases); the live run summary records objects at s3://anodizer-releases/v0.15.5/… (run 28882554907) |
blobs[].endpoint | ✅ Verified | anodizer .anodizer.yaml ("{{ Env.MINIO_ENDPOINT }}" → http://minio.jarvispro.svc.cluster.local:9003); the live 47-object upload rides this endpoint (run 28882554907) |
blobs[].region | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (directory: "{{ Tag }}"); rendered live as the …/v0.15.5/ prefix on every uploaded object (run 28882554907) |
cloudsmiths[].organization | ✅ Verified | anodizer .anodizer.yaml (organization: jarvispro) |
cloudsmiths[].repository | ✅ Verified | anodizer .anodizer.yaml (repository: anodizer) |
cloudsmiths[].formats | ✅ Verified | anodizer .anodizer.yaml ([deb, rpm, alpine]) |
cloudsmiths[].distributions | ✅ Verified | anodizer .anodizer.yaml (deb/rpm: any-distro/any-version; alpine: alpine/any-version) |
cloudsmiths[].republish | ✅ Verified | anodizer .anodizer.yaml (republish: true — prevents MD5 conflict on re-cut) |
dockerhub[].username | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 wanted | anodizer .anodizer.yaml (skip: true — no Artifactory instance) |
artifactories[].method | 🤝 Help wanted | anodizer .anodizer.yaml (method: PUT, disabled) |
npms[].scope | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml |
mcp.repository | ✅ Verified | anodizer .anodizer.yaml (source: github) |
mcp.packages | ✅ Verified | anodizer .anodizer.yaml (registry_type: oci, identifier: ghcr.io/tj-smith47/anodizer, transport: stdio) |
mcp.auth | ✅ Verified | anodizer .anodizer.yaml (auth.type: github-oidc; requires id-token: write on release job) |
mcp.retain_on_rollback | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (tj-smith47/schemastore) |
schemastore.schemas | ✅ Verified | anodizer .anodizer.yaml (matches .anodizer.yaml + .anodizer.yml; URL: tj-smith47.github.io/anodizer/schema.json) |
schemastore.retain_on_rollback | ✅ Verified | anodizer .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
| Key | Status | Notes |
|---|---|---|
verify_release.enabled | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (enabled: true); the v0.23.0 release ships the produced attestation-subjects.json asset |
attestations.mode | ✅ Verified | Both 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 | ✅ Verified | brontes .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 | ✅ Verified | anodizer .anodizer.yaml (tj-smith47/anodizer) |
milestones[].close | 🤝 Help wanted | anodizer .anodizer.yaml (close: false — wired but disabled; no milestones configured) |
milestones[].fail_on_error | ⏳ Pending | anodizer .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 | ✅ Verified | anodizer .anodizer.yaml (name_template: "{{ Tag }}") |
Announce
| Key | Status | Notes |
|---|---|---|
announce.gate_on | ✅ Verified | anodizer .anodizer.yaml (gate_on: required_publishers — announce only after all required publishers succeed) |
announce.webhook.enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true — posts JSON to tj.jarvispro.io/webhooks/anodizer) |
announce.webhook.endpoint_url | ✅ Verified | anodizer .anodizer.yaml |
announce.webhook.content_type | ✅ Verified | anodizer .anodizer.yaml (application/json) |
announce.webhook.message_template | ✅ Verified | anodizer .anodizer.yaml (JSON payload with project, tag, url) |
announce.webhook.headers | ✅ Verified | anodizer .anodizer.yaml (X-Anodizer-Source: release) |
announce.webhook.skip_tls_verify | ✅ Verified | anodizer .anodizer.yaml (skip_tls_verify: false) |
announce.webhook.expected_status_codes | ✅ Verified | anodizer .anodizer.yaml ([200, 202]) |
announce.email.enabled | ✅ Verified | anodizer .anodizer.yaml (enabled: true) |
announce.email.host/port/username/from/to | ✅ Verified | anodizer .anodizer.yaml (Gmail SMTP, port 587, STARTTLS) |
announce.email.subject_template | ✅ Verified | anodizer .anodizer.yaml |
announce.email.encryption | ✅ Verified | anodizer .anodizer.yaml (encryption: starttls) |
announce.discord | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.slack | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.telegram | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.teams | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.mattermost | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.reddit | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.twitter | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.mastodon | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.bluesky | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.linkedin | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.discourse | 🤝 Help wanted | anodizer .anodizer.yaml (enabled: false) |
announce.opencollective | 🤝 Help wanted | anodizer .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).
| Key | Status | Notes |
|---|---|---|
flatpaks[] | ✅ Verified | anodizer .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[] | ✅ Verified | anodizer .anodizer.yaml (unsigned .app assembly, shipped inside the live .dmg) |
dmgs[] | ✅ Verified | anodizer .anodizer.yaml; live anodizer_amd64.dmg |
pkgs[] | ✅ Verified | anodizer .anodizer.yaml; live anodizer_amd64.pkg |
msis[] | ✅ Verified | anodizer .anodizer.yaml; live anodizer_amd64.msi |
nsis[] | ✅ Verified | anodizer .anodizer.yaml; live anodizer_x64-setup.exe |
notarize | 🤝 Help wanted | Implemented (rcodesign, cross-platform) but not configured — signing + notarization need an Apple Developer Program membership we don't have. Bundles ship unsigned |