anodizer_core/config/mod.rs
1use std::collections::{BTreeMap, HashMap};
2use std::path::PathBuf;
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7// ---------------------------------------------------------------------------
8// Include specification types
9// ---------------------------------------------------------------------------
10
11/// An include specification: either a plain path string or a structured from_file/from_url.
12///
13/// YAML examples:
14/// ```yaml
15/// includes:
16/// - ./defaults.yaml # plain string (backward compat)
17/// - from_file:
18/// path: ./config/release.yaml # structured file path
19/// - from_url:
20/// url: https://example.com/config.yaml # URL fetch
21/// headers:
22/// x-api-token: "${MYCOMPANY_TOKEN}" # env var expansion in headers
23/// ```
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
25#[serde(untagged)]
26pub enum IncludeSpec {
27 /// Plain string path (backward compatible): "path/to/file.yaml"
28 Path(String),
29 /// Structured file include with `from_file.path`.
30 FromFile { from_file: IncludeFilePath },
31 /// Structured URL include with `from_url.url` and optional headers.
32 FromUrl { from_url: IncludeUrlConfig },
33}
34
35/// File path for a structured include.
36#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
37#[serde(deny_unknown_fields)]
38pub struct IncludeFilePath {
39 /// Path to the include file (relative to the config file).
40 pub path: String,
41}
42
43/// URL configuration for a structured include.
44#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
45#[serde(deny_unknown_fields)]
46pub struct IncludeUrlConfig {
47 /// URL to fetch. If it does not start with `http://` or `https://`,
48 /// `https://raw.githubusercontent.com/` is prepended (GitHub shorthand).
49 pub url: String,
50 /// Optional HTTP headers. Values support `${VAR_NAME}` environment variable expansion.
51 pub headers: Option<HashMap<String, String>>,
52}
53
54// ---------------------------------------------------------------------------
55// Top-level config
56// ---------------------------------------------------------------------------
57
58/// `deny_unknown_fields` rejects typos and unknown config
59/// fields at parse time (strict YAML unmarshalling).
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61#[serde(default, deny_unknown_fields)]
62pub struct Config {
63 /// Schema version. Currently supports 1 (implicit default) and 2.
64 pub version: Option<u32>,
65 /// Human-readable project name used in templates and release titles.
66 pub project_name: String,
67 /// Output directory for build artifacts (default: ./dist).
68 #[serde(default = "default_dist")]
69 pub dist: PathBuf,
70 /// Additional config files to merge into this config.
71 /// Supports plain string paths, `from_file:` for structured file paths,
72 /// and `from_url:` for fetching configs from URLs with optional headers.
73 pub includes: Option<Vec<IncludeSpec>>,
74 /// Environment file configuration. Accepts either:
75 /// - A list of `.env` file paths: `[".env", ".release.env"]`
76 /// - A struct with token file paths: `{ github_token: "~/.config/goreleaser/github_token" }`
77 pub env_files: Option<EnvFilesConfig>,
78 /// Default values applied to all crates unless overridden.
79 pub defaults: Option<Defaults>,
80 /// Hooks run before the release pipeline starts.
81 ///
82 /// Use `--skip=before` to bypass — one token covers this block and every
83 /// `crates[].before:` block.
84 pub before: Option<HooksConfig>,
85 /// Hooks run after the release pipeline completes SUCCESSFULLY.
86 ///
87 /// A failed run never reaches them — route failure handling through
88 /// `on_error:`, and teardown that must happen either way through
89 /// `always:`.
90 ///
91 /// Use `--skip=after` to bypass — one token covers this block and every
92 /// `crates[].after:` block.
93 ///
94 /// ```yaml
95 /// after:
96 /// hooks:
97 /// - cmd: ./notify-release-succeeded.sh
98 /// ```
99 pub after: Option<HooksConfig>,
100 /// Hooks run when the release pipeline fails at ANY stage (build,
101 /// sign, publish, ...). The pipeline holds on failure: published state
102 /// is left exactly where the failed run put it, so `{{ .RolledBack }}`
103 /// is always `false`. Recover by re-running the identical command
104 /// (publishers reconcile against what already shipped and skip it), or
105 /// withdraw the release deliberately with `anodizer tag rollback`.
106 ///
107 /// Notification / cleanup hooks: a hook's own failure is logged as a
108 /// warning and never masks the pipeline error. The failure context is
109 /// exposed both as template vars (`{{ .Error }}`, `{{ .RolledBack }}`)
110 /// and as `ANODIZER_*` env vars (`ANODIZER_ERROR`,
111 /// `ANODIZER_ROLLED_BACK`, `ANODIZER_VERSION`, `ANODIZER_TAG`) so
112 /// hooks can consume the error text without shell interpolation.
113 ///
114 /// Use `--skip=on-error` to bypass — one token covers this block and
115 /// every `publish.on_error:` block.
116 ///
117 /// ```yaml
118 /// on_error:
119 /// hooks:
120 /// - cmd: ./notify-release-failed.sh
121 /// ```
122 pub on_error: Option<HooksConfig>,
123 /// Hooks run LAST on every terminal path — the release's `finally`.
124 ///
125 /// Ordering: on success they run after `after:`; on failure they run
126 /// after `on_error:`. They also fire on the one exit neither of those
127 /// reaches — a `before:` hook that failed before the pipeline started.
128 /// Use them for teardown that has to happen either way: removing a
129 /// staging directory, releasing a lock, stopping a sidecar container.
130 ///
131 /// They fire once per `anodizer release` / `anodizer build` invocation,
132 /// pairing 1:1 with `before:` — including on each `--split` shard and on
133 /// `--merge`, which are separate invocations that each run `before:` of
134 /// their own.
135 ///
136 /// Use `--skip=always` to bypass. Skipping the `finally` lane is
137 /// supported on purpose — `--skip=before` already suppresses the lane it
138 /// pairs with — but the consequence is that teardown does not run, so
139 /// anything the run staged stays staged.
140 ///
141 /// The run's outcome is exposed as template vars (`{{ .Success }}`,
142 /// `{{ .Error }}`) and as `ANODIZER_*` env vars (`ANODIZER_SUCCESS`,
143 /// `ANODIZER_ERROR`, `ANODIZER_VERSION`, `ANODIZER_TAG`), so a hook can
144 /// branch on the outcome and read the error text without interpolating
145 /// untrusted text into the shell command. `ANODIZER_ERROR` is empty on
146 /// success.
147 ///
148 /// A failing `always:` hook never masks a release failure: on the
149 /// failure path it is logged as a warning and the original pipeline
150 /// error is still what the run exits with. On the success path there is
151 /// no error to mask, so the hook's own failure fails the run — the same
152 /// contract `after:` has.
153 ///
154 /// ```yaml
155 /// always:
156 /// hooks:
157 /// - cmd: ./teardown-staging.sh
158 /// ```
159 pub always: Option<HooksConfig>,
160 /// Hooks run after build/archive/sign/sbom/checksum complete but
161 /// immediately before the publish phase dispatches any publisher.
162 ///
163 /// Use cases: smoke-test artifacts against the staged dist tree,
164 /// run external validators (antivirus, vulnerability scanners),
165 /// stage external state, or abort the release before any
166 /// publisher writes to a registry.
167 ///
168 /// A non-zero exit code from any hook aborts the release before
169 /// publish runs. Hooks fire in declared order. Use `--skip=before-publish`
170 /// to bypass.
171 pub before_publish: Option<HooksConfig>,
172 /// List of crates in this project.
173 pub crates: Vec<CrateConfig>,
174 /// Changelog generation configuration.
175 pub changelog: Option<ChangelogConfig>,
176 /// Signing configurations for binaries, archives, and checksums.
177 #[serde(default, deserialize_with = "deserialize_signs")]
178 #[schemars(schema_with = "signs_schema")]
179 pub signs: Vec<SignConfig>,
180 /// Binary-specific signing configs (same shape as `signs` but only for
181 /// binary artifacts). The `artifacts` field on each entry is constrained
182 /// at parse time to `binary` / `none` (or omitted) — a broader filter on
183 /// `binary_signs` would silently match nothing because the loop only
184 /// iterates Binary artifacts. Constraint lives in `deserialize_binary_signs`.
185 ///
186 /// The detached signature and certificate produced for each binary upload
187 /// as release assets alongside the archives, named after the archive built
188 /// from that binary: a `tar.gz` archive `app-1.2.3-linux-amd64.tar.gz`
189 /// gives `app-1.2.3-linux-amd64.sig`. The raw binary's own file name is
190 /// the same under every target, so it is not the asset name.
191 #[serde(default, deserialize_with = "deserialize_binary_signs")]
192 #[schemars(schema_with = "signs_schema")]
193 pub binary_signs: Vec<SignConfig>,
194 /// Docker image signing configurations.
195 pub docker_signs: Option<Vec<DockerSignConfig>>,
196 // No `alias` attribute needed: unlike `signs`/`sign`, "upx" is already
197 // both singular and plural, so a separate alias adds no value.
198 /// UPX binary compression configurations.
199 #[serde(default, deserialize_with = "deserialize_upx")]
200 #[schemars(schema_with = "upx_schema")]
201 pub upx: Vec<UpxConfig>,
202 /// Snapshot release configuration (local/non-tag builds).
203 pub snapshot: Option<SnapshotConfig>,
204 /// Nightly release configuration.
205 pub nightly: Option<NightlyConfig>,
206 /// Announcement configuration (Slack, Discord, email, etc.).
207 pub announce: Option<AnnounceConfig>,
208 /// When true, log artifact file sizes after building.
209 pub report_sizes: Option<bool>,
210 /// Environment variables available to all template expressions.
211 ///
212 /// List of `KEY=VALUE` strings:
213 /// `env: ["MY_VAR=hello", "DEPLOY_ENV=staging"]`. Order is preserved so
214 /// chained env applications (sign + sbom + notarize) see entries in
215 /// declared order. Values are rendered through the template engine before
216 /// being set, so expressions like `{{ Tag }}` or `{{ Date }}` are
217 /// expanded.
218 #[serde(default)]
219 pub env: Option<Vec<String>>,
220 /// Custom template variables accessible as `{{ Var.<key> }}` in templates.
221 /// Provides a way to define reusable values, especially useful with config includes.
222 ///
223 /// Stored as a `BTreeMap` so rendering iterates in deterministic
224 /// (sorted) key order — without this guarantee, a value that references
225 /// another variable (`b: "{{ Var.a }}_v2"`) could render before its
226 /// dependency on a different process / host. The current resolver is
227 /// single-pass (one render per value), so cross-variable references
228 /// only resolve when the referenced key sorts earlier.
229 pub variables: Option<BTreeMap<String, String>>,
230 /// Generic artifact publisher configurations.
231 pub publishers: Option<Vec<PublisherConfig>>,
232 /// DockerHub description sync configurations.
233 pub dockerhub: Option<Vec<DockerHubConfig>>,
234 /// Artifactory upload configurations.
235 pub artifactories: Option<Vec<ArtifactoryConfig>>,
236 /// CloudSmith publisher configurations.
237 pub cloudsmiths: Option<Vec<CloudSmithConfig>>,
238 /// Top-level Homebrew Cask configurations.
239 /// `homebrew_casks` is a top-level array with its own
240 /// repository, commit_author, directory, skip_upload, hooks, dependencies,
241 /// conflicts, completions, manpages, structured uninstall/zap, etc.
242 pub homebrew_casks: Option<Vec<HomebrewCaskConfig>>,
243 /// Repo-committed files that embed the release version outside
244 /// `Cargo.toml` (e.g. a Helm `Chart.yaml`, an install doc, a README
245 /// badge), given as repo-root-relative path strings. At `tag` time each
246 /// listed file has its occurrences of the old version rewritten to the new
247 /// version — both the bare (`0.1.0`) and `v`-prefixed (`v0.1.0`) forms,
248 /// word-boundary anchored — and is staged into the same bump commit as
249 /// `Cargo.toml` / `Cargo.lock`, so these files never drift from the manifest.
250 ///
251 /// An entry may instead be a `path` + `match` mapping, scoping the rewrite
252 /// to the occurrences the `match` regex selects (`{version}` stands for the
253 /// version being rewritten) so two crates can share one file.
254 ///
255 /// ```yaml
256 /// version_files:
257 /// - charts/cfgd/Chart.yaml
258 /// - docs/installation.md
259 /// - path: chart/cfgd/values.yaml
260 /// match: 'operator:\s+image:.*:v{version}'
261 /// ```
262 pub version_files: Option<Vec<VersionFileEntry>>,
263 /// Automatic semantic version tagging configuration.
264 pub tag: Option<TagConfig>,
265 /// Git-level tag discovery and sorting settings.
266 pub git: Option<GitConfig>,
267 /// Partial/split build configuration for fan-out CI pipelines.
268 pub partial: Option<PartialConfig>,
269 /// Independent workspace roots in a monorepo.
270 pub workspaces: Option<Vec<WorkspaceConfig>>,
271 /// Source archive configuration.
272 pub source: Option<SourceConfig>,
273 /// Software bill of materials (SBOM) generation configurations.
274 #[serde(default, deserialize_with = "deserialize_sboms")]
275 #[schemars(schema_with = "sboms_schema")]
276 pub sboms: Vec<SbomConfig>,
277 /// SLSA build-provenance / attestation configuration for binaries and
278 /// archives. In the default `subjects` mode, anodizer writes a subjects
279 /// manifest for `actions/attest-build-provenance`; in `emit` mode it
280 /// generates and signs a self-contained in-toto SLSA provenance statement.
281 /// When omitted (or `enabled: false`), the attestation stage is a no-op.
282 pub attestations: Option<AttestationConfig>,
283 /// GitHub release configuration shared by all crates.
284 pub release: Option<ReleaseConfig>,
285 /// Custom GitHub API/upload/download URLs for GitHub Enterprise installations.
286 pub github_urls: Option<GitHubUrlsConfig>,
287 /// Custom GitLab API/download URLs for self-hosted GitLab installations.
288 pub gitlab_urls: Option<GitLabUrlsConfig>,
289 /// Custom Gitea API/download URLs for self-hosted Gitea installations.
290 pub gitea_urls: Option<GiteaUrlsConfig>,
291 /// Force a specific token type for authentication.
292 /// When set, overrides automatic token detection from environment variables.
293 pub force_token: Option<ForceTokenKind>,
294 /// macOS code signing and notarization configuration.
295 pub notarize: Option<NotarizeConfig>,
296 /// Project metadata configuration (applied to metadata.json output files).
297 pub metadata: Option<MetadataConfig>,
298 /// Template files to render and include as release artifacts.
299 /// File contents are processed through the template engine.
300 pub template_files: Option<Vec<TemplateFileConfig>>,
301 /// Monorepo configuration.
302 /// When configured, tag discovery filters by tag_prefix and the working
303 /// directory is scoped to dir.
304 pub monorepo: Option<MonorepoConfig>,
305 /// Makeself self-extracting archive configurations.
306 #[serde(default, deserialize_with = "deserialize_makeselfs")]
307 #[schemars(schema_with = "makeselfs_schema")]
308 pub makeselfs: Vec<MakeselfConfig>,
309 /// `curl | sh` installer-script configurations. Each entry emits a
310 /// deterministic POSIX `install.sh` release asset that detects the host
311 /// OS + arch, downloads and sha256-verifies the matching archive, and
312 /// installs the binary.
313 #[serde(default, deserialize_with = "deserialize_install_scripts")]
314 #[schemars(schema_with = "install_scripts_schema")]
315 pub install_scripts: Vec<InstallScriptConfig>,
316 /// AppImage configurations. Each entry bundles a built Linux binary plus
317 /// its desktop integration into a single self-contained `.AppImage` via
318 /// linuxdeploy.
319 #[serde(default, deserialize_with = "deserialize_appimages")]
320 #[schemars(schema_with = "appimages_schema")]
321 pub appimages: Vec<AppImageConfig>,
322 /// Opt-in post-release verification gate. Runs LAST (after the release is
323 /// created and every publisher has run) and REPORTS post-publish defects —
324 /// missing assets, failed install smoke-tests, glibc-ceiling violations.
325 /// Because it runs after the irreversible publish, a failure exits
326 /// non-zero to flag CI but never undoes the release. Off unless
327 /// `verify_release.enabled: true`.
328 #[serde(default)]
329 pub verify_release: VerifyReleaseConfig,
330 /// Pre-publish preflight tuning. `preflight.strict: true` promotes
331 /// indeterminate probe outcomes (5xx / rate-limit / network failure /
332 /// undeterminable permissions) from warnings to hard blockers. The
333 /// probes themselves always run read-only before any publisher mutates
334 /// a registry; the default (lenient) behavior needs no config.
335 #[serde(default)]
336 pub preflight: PreflightConfig,
337 /// Source RPM configuration. Renamed from `srpm:` (singular) for spelling
338 /// parity with `Defaults.srpms` and the rest of the plural-name packaging
339 /// fields. The `srpm:` spelling is still accepted via serde alias for
340 /// back-compat.
341 #[serde(alias = "srpm")]
342 pub srpms: Option<SrpmConfig>,
343 /// Milestone closing configurations.
344 pub milestones: Option<Vec<MilestoneConfig>>,
345 /// Generic HTTP upload configurations.
346 pub uploads: Option<Vec<UploadConfig>>,
347 /// AUR source package publishing configurations (source-only PKGBUILD, not -bin).
348 pub aur_sources: Option<Vec<AurSourceConfig>>,
349 /// Top-level retry configuration applied to network-bound operations
350 /// (announcers, git providers, HTTP uploads, docker pipes). When omitted,
351 /// `RetryConfig::default()` is used (10 attempts, 10s base, 5m cap —
352 /// the project-level retry policy).
353 pub retry: Option<RetryConfig>,
354 /// MCP (Model Context Protocol) server registry publishing
355 /// configuration. When `name` is empty (the default), the publisher is
356 /// skipped. The `mcp:` publisher block.
357 #[serde(default)]
358 pub mcp: McpConfig,
359 /// SchemaStore publisher. Registers the project's JSON Schema(s) on
360 /// SchemaStore at release time. When `schemas` is empty (the default),
361 /// the publisher is skipped. The `schemastore:` publisher block.
362 #[serde(default)]
363 pub schemastore: crate::config::publishers::SchemastoreConfig,
364 /// NPM package registry publishing configurations. One entry per
365 /// published package. In the default `optional-deps` mode anodizer emits
366 /// npm's native per-platform packages (biome / git-cliff pattern); in
367 /// `postinstall` mode it emits a download shim (the `npms:`
368 /// parity).
369 pub npms: Option<Vec<NpmConfig>>,
370 /// GemFury (fury.io) deb/rpm/apk publishing configurations. Mirrors
371 /// The `gemfury:` block. The legacy spelling
372 /// `furies:` is accepted via serde alias; a one-time deprecation
373 /// warning is emitted by [`warn_on_legacy_furies_alias`].
374 #[serde(alias = "furies")]
375 pub gemfury: Option<Vec<GemFuryConfig>>,
376 /// PyPI publishing configurations. One entry per published project.
377 /// Emits native `py3-none-<platform>` binary wheels from the built
378 /// binaries (plus an optional `maturin sdist`) and uploads them via
379 /// PyPI's legacy (twine-protocol) upload API. The `pypis:` block.
380 pub pypis: Option<Vec<PypiConfig>>,
381 /// homebrew-core formula-bump configurations. One entry per formula.
382 /// Bumps an existing formula in `Homebrew/homebrew-core` (or a formula
383 /// repository override) via the GitHub API and opens a pull request.
384 /// The `homebrew_cores:` block.
385 pub homebrew_cores: Option<Vec<HomebrewCoreConfig>>,
386 /// Per-crate metadata derived from each crate's `Cargo.toml [package]`
387 /// table (description / license / homepage / authors). Populated at
388 /// config-load time by [`Config::populate_derived_metadata`], keyed by
389 /// crate name. NOT a user-facing YAML field — it backs the
390 /// crate-aware `meta_*_for` accessors so a plain Rust project gets its
391 /// publisher metadata without repeating it in a top-level `metadata:`
392 /// block. A hand-written `metadata:` field and per-publisher overrides
393 /// still win.
394 #[serde(skip)]
395 #[schemars(skip)]
396 pub derived_metadata: BTreeMap<String, MetadataConfig>,
397 /// The tag family [`Config::populate_derived_tag_templates`] filled into
398 /// every crate that omitted `tag_template`, when it filled at least one.
399 /// NOT a user-facing YAML field — it lets the config loader report the
400 /// derivation, since a filled crate is indistinguishable from one the
401 /// operator wrote by hand once the fold has run.
402 #[serde(skip)]
403 #[schemars(skip)]
404 pub derived_tag_template: Option<String>,
405}
406
407/// Helper schema function for the signs field (accepts object or array).
408fn signs_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
409 let mut schema = generator.subschema_for::<Vec<SignConfig>>();
410 schema.ensure_object().insert(
411 "description".to_owned(),
412 "Artifact signing configurations (cosign, GPG, etc.). Accepts a single object or array."
413 .into(),
414 );
415 schema
416}
417
418/// Helper schema function for the upx field (accepts object or array).
419fn upx_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
420 let mut schema = generator.subschema_for::<Vec<UpxConfig>>();
421 schema.ensure_object().insert(
422 "description".to_owned(),
423 "UPX binary compression configurations. Accepts a single object or array.".into(),
424 );
425 schema
426}
427
428/// Helper schema function for the sboms field (accepts object or array).
429fn sboms_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
430 let mut schema = generator.subschema_for::<Vec<SbomConfig>>();
431 schema.ensure_object().insert(
432 "description".to_owned(),
433 "SBOM generation configurations. Accepts a single object or array.".into(),
434 );
435 schema
436}
437
438fn default_dist() -> PathBuf {
439 PathBuf::from("./dist")
440}
441
442impl Default for Config {
443 fn default() -> Self {
444 Config {
445 version: None,
446 project_name: String::new(),
447 dist: default_dist(),
448 includes: None,
449 env_files: None,
450 defaults: None,
451 before: None,
452 after: None,
453 on_error: None,
454 always: None,
455 before_publish: None,
456 crates: Vec::new(),
457 changelog: None,
458 signs: Vec::new(),
459 binary_signs: Vec::new(),
460 docker_signs: None,
461 upx: Vec::new(),
462 snapshot: None,
463 nightly: None,
464 announce: None,
465 report_sizes: None,
466 env: None,
467 variables: None,
468 publishers: None,
469 dockerhub: None,
470 artifactories: None,
471 cloudsmiths: None,
472 homebrew_casks: None,
473 version_files: None,
474 tag: None,
475 git: None,
476 partial: None,
477 workspaces: None,
478 source: None,
479 sboms: Vec::new(),
480 attestations: None,
481 release: None,
482 github_urls: None,
483 gitlab_urls: None,
484 gitea_urls: None,
485 force_token: None,
486 notarize: None,
487 metadata: None,
488 template_files: None,
489 monorepo: None,
490 makeselfs: Vec::new(),
491 install_scripts: Vec::new(),
492 appimages: Vec::new(),
493 verify_release: VerifyReleaseConfig::default(),
494 preflight: PreflightConfig::default(),
495 srpms: None,
496 milestones: None,
497 uploads: None,
498 aur_sources: None,
499 retry: None,
500 mcp: McpConfig::default(),
501 schemastore: crate::config::publishers::SchemastoreConfig::default(),
502 npms: None,
503 gemfury: None,
504 pypis: None,
505 homebrew_cores: None,
506 derived_metadata: BTreeMap::new(),
507 derived_tag_template: None,
508 }
509 }
510}
511
512mod accessors;
513pub use accessors::crate_is_selected;
514
515mod schema;
516pub use schema::*;
517
518/// Run a deserialization closure on a worker thread sized large enough that
519/// the `Config` derive (60+ `Option<NestedStruct>` fields) cannot exhaust
520/// the host's main-thread stack.
521///
522/// Background: debug builds of `serde_yaml_ng::from_value::<Config>` and
523/// `toml::from_str::<Config>` consume several MiB of stack because each
524/// generated visitor branch for the giant struct lives in a single
525/// monomorphised frame and debug builds neither inline nor tail-call. The
526/// Windows main-thread default reservation is 1 MiB, so any debug-built
527/// integration test that triggers full-config deserialization overflows
528/// before reaching the visitor's body.
529///
530/// Routing every full-`Config` deserialization through this helper keeps
531/// every entry-point platform-agnostic without resorting to per-platform
532/// linker flags or `RUST_MIN_STACK`.
533pub fn deserialize_on_worker<F, T>(f: F) -> anyhow::Result<T>
534where
535 F: FnOnce() -> anyhow::Result<T> + Send + 'static,
536 T: Send + 'static,
537{
538 use anyhow::Context as _;
539
540 // 8 MiB matches the Linux/macOS process default and comfortably exceeds
541 // the ~2 MiB peak observed for debug `Config` deserialization.
542 const WORKER_STACK_SIZE: usize = 8 * 1024 * 1024;
543
544 let handle = std::thread::Builder::new()
545 .stack_size(WORKER_STACK_SIZE)
546 .name("anodizer-config-deserialize".to_string())
547 .spawn(f)
548 .context("failed to spawn config deserialization worker thread")?;
549 match handle.join() {
550 Ok(result) => result,
551 Err(payload) => std::panic::resume_unwind(payload),
552 }
553}
554
555mod validate;
556pub use validate::*;
557
558mod publish_axis;
559pub(crate) use publish_axis::*;
560
561mod legacy;
562pub use legacy::*;
563
564// ---------------------------------------------------------------------------
565// EnvFilesConfig — accepts list of .env paths OR structured token file paths
566// ---------------------------------------------------------------------------
567
568mod env_files;
569pub use env_files::*;
570
571// ---------------------------------------------------------------------------
572// VersionFileEntry — accepts a bare path OR a path scoped by a `match` anchor
573// ---------------------------------------------------------------------------
574
575mod version_files;
576pub use version_files::*;
577
578// ---------------------------------------------------------------------------
579// Defaults
580// ---------------------------------------------------------------------------
581
582mod defaults;
583pub use defaults::*;
584
585// ---------------------------------------------------------------------------
586// BuildIgnore — exclude specific os/arch combos from builds
587// ---------------------------------------------------------------------------
588
589mod build;
590pub use build::*;
591
592// ---------------------------------------------------------------------------
593// ArchivesConfig — untagged enum: false => Disabled, array => Configs
594// ---------------------------------------------------------------------------
595
596mod archives;
597pub use archives::*;
598
599mod completions;
600pub use completions::*;
601
602// ---------------------------------------------------------------------------
603// ReleaseConfig
604// ---------------------------------------------------------------------------
605
606mod release;
607pub use release::*;
608
609// ---------------------------------------------------------------------------
610// Shared publisher config types: RepositoryConfig, CommitAuthorConfig
611// ---------------------------------------------------------------------------
612
613mod publishers;
614pub use publishers::*;
615
616// ---------------------------------------------------------------------------
617// DockerV2Config
618// ---------------------------------------------------------------------------
619
620mod docker;
621pub use docker::*;
622
623// ---------------------------------------------------------------------------
624// NfpmConfig
625// ---------------------------------------------------------------------------
626
627mod nfpm;
628pub use nfpm::*;
629
630// ---------------------------------------------------------------------------
631// SnapcraftConfig
632// ---------------------------------------------------------------------------
633
634mod snapcraft;
635pub use snapcraft::*;
636// ---------------------------------------------------------------------------
637// DmgConfig / MsiConfig / PkgConfig / NsisConfig / AppBundleConfig / FlatpakConfig
638// ---------------------------------------------------------------------------
639
640mod installers;
641pub use installers::*;
642
643// ---------------------------------------------------------------------------
644// BlobConfig (S3/GCS/Azure cloud storage)
645// ---------------------------------------------------------------------------
646
647mod blob;
648pub use blob::*;
649
650// ---------------------------------------------------------------------------
651// PartialConfig (split/merge CI fan-out)
652// ---------------------------------------------------------------------------
653
654mod partial;
655pub use partial::*;
656
657// ---------------------------------------------------------------------------
658// BinstallConfig
659// ---------------------------------------------------------------------------
660
661mod binstall;
662pub use binstall::*;
663
664// ---------------------------------------------------------------------------
665// NotarizeConfig (macOS code signing and notarization)
666// ---------------------------------------------------------------------------
667
668mod notarize;
669pub use notarize::*;
670// ---------------------------------------------------------------------------
671// SourceConfig
672// ---------------------------------------------------------------------------
673
674mod source;
675pub use source::*;
676
677// ---------------------------------------------------------------------------
678// SbomConfig
679// ---------------------------------------------------------------------------
680
681mod sbom;
682pub use sbom::*;
683
684// ---------------------------------------------------------------------------
685// AttestationConfig
686// ---------------------------------------------------------------------------
687
688mod attestation;
689pub use attestation::*;
690
691// ---------------------------------------------------------------------------
692// VersionSyncConfig
693// ---------------------------------------------------------------------------
694
695mod version_sync;
696pub use version_sync::*;
697
698// ---------------------------------------------------------------------------
699// ChangelogConfig
700// ---------------------------------------------------------------------------
701
702mod changelog;
703pub use changelog::*;
704// ---------------------------------------------------------------------------
705// SignConfig / DockerSignConfig — lifted to `crate::signing`
706// ---------------------------------------------------------------------------
707//
708// see `crate::signing` for the type definitions. The
709// re-exports below preserve the historical
710// `anodizer_core::config::{SignConfig, DockerSignConfig}` import paths
711// used by every stage that consumes a sign config.
712
713pub use crate::signing::{AuthenticodeConfig, DockerSignConfig, SignConfig, SignVerifyConfig};
714
715// ---------------------------------------------------------------------------
716// UpxConfig
717// ---------------------------------------------------------------------------
718
719mod upx;
720pub use upx::*;
721
722// ---------------------------------------------------------------------------
723// SnapshotConfig
724// ---------------------------------------------------------------------------
725
726mod snapshot_nightly;
727pub use snapshot_nightly::*;
728
729mod cargo_metadata;
730pub use cargo_metadata::{
731 derive_metadata_from_cargo_toml, package_version, workspace_package_version,
732};
733
734mod workspace_deps;
735pub use workspace_deps::{
736 derive_depends_on_from_cargo_toml, discover_cargo_workspace_member_names,
737 extract_workspace_deps,
738};
739
740/// Extract the name portion of a `"Name <email>"` maintainer/author string,
741/// dropping any `<…>` email suffix. Returns `None` when the result is empty
742/// (e.g. a bare-email `<ada@example.com>`), so a derived Vendor / OCI `vendor`
743/// value is never emitted blank.
744pub fn maintainer_name_only(maintainer: &str) -> Option<String> {
745 let name = maintainer.split('<').next().unwrap_or(maintainer).trim();
746 (!name.is_empty()).then(|| name.to_string())
747}
748
749// ---------------------------------------------------------------------------
750// TemplateFileConfig
751// ---------------------------------------------------------------------------
752
753mod templatefiles;
754pub use templatefiles::*;
755
756// ---------------------------------------------------------------------------
757// AnnounceConfig
758// ---------------------------------------------------------------------------
759mod announce;
760pub use announce::*;
761// ---------------------------------------------------------------------------
762// DockerHub description sync
763// ---------------------------------------------------------------------------
764
765mod dockerhub;
766pub use dockerhub::*;
767
768// ---------------------------------------------------------------------------
769// Artifactory publisher
770// ---------------------------------------------------------------------------
771
772mod artifactory;
773pub use artifactory::*;
774
775// ---------------------------------------------------------------------------
776// CloudSmith publisher
777// ---------------------------------------------------------------------------
778
779mod cloudsmith;
780pub use cloudsmith::*;
781
782// ---------------------------------------------------------------------------
783// PublisherConfig
784// ---------------------------------------------------------------------------
785
786mod publisher;
787pub use publisher::*;
788
789// ---------------------------------------------------------------------------
790// HooksConfig
791// ---------------------------------------------------------------------------
792
793mod hooks;
794pub use hooks::*;
795
796// ---------------------------------------------------------------------------
797// GitConfig
798// ---------------------------------------------------------------------------
799
800mod git_config;
801pub use git_config::*;
802
803// ---------------------------------------------------------------------------
804// MonorepoConfig
805// ---------------------------------------------------------------------------
806
807mod monorepo;
808pub use monorepo::*;
809
810// ---------------------------------------------------------------------------
811// TagConfig
812// ---------------------------------------------------------------------------
813
814mod tag;
815pub use tag::*;
816
817// ---------------------------------------------------------------------------
818// WorkspaceConfig
819// ---------------------------------------------------------------------------
820
821mod workspace;
822pub use workspace::*;
823
824// ---------------------------------------------------------------------------
825// RetryConfig (top-level `retry:` block — bridges to crate::retry::RetryPolicy)
826// ---------------------------------------------------------------------------
827
828mod retry;
829pub use retry::*;
830
831// ---------------------------------------------------------------------------
832// PostPublishPollConfig (per-publisher post-publish polling)
833// ---------------------------------------------------------------------------
834
835mod post_publish_poll;
836pub use post_publish_poll::*;
837
838// ---------------------------------------------------------------------------
839// VerifyReleaseConfig (top-level `verify_release:` post-publish gate)
840// ---------------------------------------------------------------------------
841
842mod verify_release;
843pub use verify_release::*;
844
845// ---------------------------------------------------------------------------
846// PreflightConfig (top-level `preflight:` pre-publish probe tuning)
847// ---------------------------------------------------------------------------
848
849mod preflight;
850pub use preflight::*;
851
852// ---------------------------------------------------------------------------
853// StringOrBool — accepts bool or template string in YAML
854// ---------------------------------------------------------------------------
855
856mod string_or_bool;
857pub use string_or_bool::*;
858
859// ---------------------------------------------------------------------------
860// MakeselfConfig + SrpmConfig — lifted to `crate::packagers`
861// ---------------------------------------------------------------------------
862//
863// All packaging config types live in their own modules under
864// `crate::packagers`. The re-exports below preserve the historical
865// `anodizer_core::config::{MakeselfConfig, MakeselfFile, SrpmConfig}`
866// import paths used by stages and tests.
867
868pub use crate::packagers::{
869 AppImageConfig, AppImageExtra, InstallScriptConfig, MakeselfConfig, MakeselfFile,
870 RuntimeHarvest, SrpmConfig,
871};
872pub(crate) use crate::packagers::{
873 appimages_schema, deserialize_appimages, deserialize_install_scripts, deserialize_makeselfs,
874 install_scripts_schema, makeselfs_schema,
875};
876
877// ---------------------------------------------------------------------------
878// MilestoneConfig
879// ---------------------------------------------------------------------------
880
881mod milestone;
882pub use milestone::*;
883
884// ---------------------------------------------------------------------------
885// UploadConfig (generic HTTP upload)
886// ---------------------------------------------------------------------------
887
888mod upload;
889pub use upload::*;
890
891// ---------------------------------------------------------------------------
892// AurSourceConfig
893// ---------------------------------------------------------------------------
894
895mod aur_source;
896pub use aur_source::*;
897
898// ---------------------------------------------------------------------------
899// McpConfig (MCP registry publisher)
900// ---------------------------------------------------------------------------
901
902mod mcp;
903pub use mcp::*;
904
905// ---------------------------------------------------------------------------
906// NpmConfig (NPM package registry publisher)
907// ---------------------------------------------------------------------------
908
909mod npm;
910pub use npm::*;
911
912// ---------------------------------------------------------------------------
913// GemFuryConfig (Gemfury / fury.io publisher)
914// ---------------------------------------------------------------------------
915
916mod gemfury;
917pub use gemfury::*;
918
919// ---------------------------------------------------------------------------
920// PypiConfig (PyPI binary-wheel publisher)
921// ---------------------------------------------------------------------------
922
923mod pypi;
924pub use pypi::*;
925
926// ---------------------------------------------------------------------------
927// HomebrewCoreConfig (homebrew-core formula-bump publisher)
928// ---------------------------------------------------------------------------
929
930mod homebrew_core;
931pub use homebrew_core::*;
932
933// ---------------------------------------------------------------------------
934// Well-known config file discovery
935// ---------------------------------------------------------------------------
936
937mod discovery;
938pub use discovery::*;
939
940// ---------------------------------------------------------------------------
941// Tests
942// ---------------------------------------------------------------------------
943
944#[cfg(test)]
945mod tests;