Skip to main content

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    #[serde(default, deserialize_with = "deserialize_binary_signs")]
186    #[schemars(schema_with = "signs_schema")]
187    pub binary_signs: Vec<SignConfig>,
188    /// Docker image signing configurations.
189    pub docker_signs: Option<Vec<DockerSignConfig>>,
190    // No `alias` attribute needed: unlike `signs`/`sign`, "upx" is already
191    // both singular and plural, so a separate alias adds no value.
192    /// UPX binary compression configurations.
193    #[serde(default, deserialize_with = "deserialize_upx")]
194    #[schemars(schema_with = "upx_schema")]
195    pub upx: Vec<UpxConfig>,
196    /// Snapshot release configuration (local/non-tag builds).
197    pub snapshot: Option<SnapshotConfig>,
198    /// Nightly release configuration.
199    pub nightly: Option<NightlyConfig>,
200    /// Announcement configuration (Slack, Discord, email, etc.).
201    pub announce: Option<AnnounceConfig>,
202    /// When true, log artifact file sizes after building.
203    pub report_sizes: Option<bool>,
204    /// Environment variables available to all template expressions.
205    ///
206    /// List of `KEY=VALUE` strings:
207    /// `env: ["MY_VAR=hello", "DEPLOY_ENV=staging"]`. Order is preserved so
208    /// chained env applications (sign + sbom + notarize) see entries in
209    /// declared order. Values are rendered through the template engine before
210    /// being set, so expressions like `{{ Tag }}` or `{{ Date }}` are
211    /// expanded.
212    #[serde(default)]
213    pub env: Option<Vec<String>>,
214    /// Custom template variables accessible as `{{ Var.<key> }}` in templates.
215    /// Provides a way to define reusable values, especially useful with config includes.
216    ///
217    /// Stored as a `BTreeMap` so rendering iterates in deterministic
218    /// (sorted) key order — without this guarantee, a value that references
219    /// another variable (`b: "{{ Var.a }}_v2"`) could render before its
220    /// dependency on a different process / host. The current resolver is
221    /// single-pass (one render per value), so cross-variable references
222    /// only resolve when the referenced key sorts earlier.
223    pub variables: Option<BTreeMap<String, String>>,
224    /// Generic artifact publisher configurations.
225    pub publishers: Option<Vec<PublisherConfig>>,
226    /// DockerHub description sync configurations.
227    pub dockerhub: Option<Vec<DockerHubConfig>>,
228    /// Artifactory upload configurations.
229    pub artifactories: Option<Vec<ArtifactoryConfig>>,
230    /// CloudSmith publisher configurations.
231    pub cloudsmiths: Option<Vec<CloudSmithConfig>>,
232    /// Top-level Homebrew Cask configurations.
233    /// `homebrew_casks` is a top-level array with its own
234    /// repository, commit_author, directory, skip_upload, hooks, dependencies,
235    /// conflicts, completions, manpages, structured uninstall/zap, etc.
236    pub homebrew_casks: Option<Vec<HomebrewCaskConfig>>,
237    /// Repo-committed files that embed the release version outside
238    /// `Cargo.toml` (e.g. a Helm `Chart.yaml`, an install doc, a README
239    /// badge), given as repo-root-relative path strings. At `tag` time each
240    /// listed file has its occurrences of the old version rewritten to the new
241    /// version — both the bare (`0.1.0`) and `v`-prefixed (`v0.1.0`) forms,
242    /// word-boundary anchored — and is staged into the same bump commit as
243    /// `Cargo.toml` / `Cargo.lock`, so these files never drift from the tag.
244    ///
245    /// ```yaml
246    /// version_files:
247    ///   - charts/cfgd/Chart.yaml
248    ///   - docs/installation.md
249    /// ```
250    pub version_files: Option<Vec<String>>,
251    /// Automatic semantic version tagging configuration.
252    pub tag: Option<TagConfig>,
253    /// Git-level tag discovery and sorting settings.
254    pub git: Option<GitConfig>,
255    /// Partial/split build configuration for fan-out CI pipelines.
256    pub partial: Option<PartialConfig>,
257    /// Independent workspace roots in a monorepo.
258    pub workspaces: Option<Vec<WorkspaceConfig>>,
259    /// Source archive configuration.
260    pub source: Option<SourceConfig>,
261    /// Software bill of materials (SBOM) generation configurations.
262    #[serde(default, deserialize_with = "deserialize_sboms")]
263    #[schemars(schema_with = "sboms_schema")]
264    pub sboms: Vec<SbomConfig>,
265    /// SLSA build-provenance / attestation configuration for binaries and
266    /// archives. In the default `subjects` mode, anodizer writes a subjects
267    /// manifest for `actions/attest-build-provenance`; in `emit` mode it
268    /// generates and signs a self-contained in-toto SLSA provenance statement.
269    /// When omitted (or `enabled: false`), the attestation stage is a no-op.
270    pub attestations: Option<AttestationConfig>,
271    /// GitHub release configuration shared by all crates.
272    pub release: Option<ReleaseConfig>,
273    /// Custom GitHub API/upload/download URLs for GitHub Enterprise installations.
274    pub github_urls: Option<GitHubUrlsConfig>,
275    /// Custom GitLab API/download URLs for self-hosted GitLab installations.
276    pub gitlab_urls: Option<GitLabUrlsConfig>,
277    /// Custom Gitea API/download URLs for self-hosted Gitea installations.
278    pub gitea_urls: Option<GiteaUrlsConfig>,
279    /// Force a specific token type for authentication.
280    /// When set, overrides automatic token detection from environment variables.
281    pub force_token: Option<ForceTokenKind>,
282    /// macOS code signing and notarization configuration.
283    pub notarize: Option<NotarizeConfig>,
284    /// Project metadata configuration (applied to metadata.json output files).
285    pub metadata: Option<MetadataConfig>,
286    /// Template files to render and include as release artifacts.
287    /// File contents are processed through the template engine.
288    pub template_files: Option<Vec<TemplateFileConfig>>,
289    /// Monorepo configuration.
290    /// When configured, tag discovery filters by tag_prefix and the working
291    /// directory is scoped to dir.
292    pub monorepo: Option<MonorepoConfig>,
293    /// Makeself self-extracting archive configurations.
294    #[serde(default, deserialize_with = "deserialize_makeselfs")]
295    #[schemars(schema_with = "makeselfs_schema")]
296    pub makeselfs: Vec<MakeselfConfig>,
297    /// `curl | sh` installer-script configurations. Each entry emits a
298    /// deterministic POSIX `install.sh` release asset that detects the host
299    /// OS + arch, downloads and sha256-verifies the matching archive, and
300    /// installs the binary.
301    #[serde(default, deserialize_with = "deserialize_install_scripts")]
302    #[schemars(schema_with = "install_scripts_schema")]
303    pub install_scripts: Vec<InstallScriptConfig>,
304    /// AppImage configurations. Each entry bundles a built Linux binary plus
305    /// its desktop integration into a single self-contained `.AppImage` via
306    /// linuxdeploy.
307    #[serde(default, deserialize_with = "deserialize_appimages")]
308    #[schemars(schema_with = "appimages_schema")]
309    pub appimages: Vec<AppImageConfig>,
310    /// Opt-in post-release verification gate. Runs LAST (after the release is
311    /// created and every publisher has run) and REPORTS post-publish defects —
312    /// missing assets, failed install smoke-tests, glibc-ceiling violations.
313    /// Because it runs after the irreversible publish, a failure exits
314    /// non-zero to flag CI but never undoes the release. Off unless
315    /// `verify_release.enabled: true`.
316    #[serde(default)]
317    pub verify_release: VerifyReleaseConfig,
318    /// Pre-publish preflight tuning. `preflight.strict: true` promotes
319    /// indeterminate probe outcomes (5xx / rate-limit / network failure /
320    /// undeterminable permissions) from warnings to hard blockers. The
321    /// probes themselves always run read-only before any publisher mutates
322    /// a registry; the default (lenient) behavior needs no config.
323    #[serde(default)]
324    pub preflight: PreflightConfig,
325    /// Source RPM configuration. Renamed from `srpm:` (singular) for spelling
326    /// parity with `Defaults.srpms` and the rest of the plural-name packaging
327    /// fields. The `srpm:` spelling is still accepted via serde alias for
328    /// back-compat.
329    #[serde(alias = "srpm")]
330    pub srpms: Option<SrpmConfig>,
331    /// Milestone closing configurations.
332    pub milestones: Option<Vec<MilestoneConfig>>,
333    /// Generic HTTP upload configurations.
334    pub uploads: Option<Vec<UploadConfig>>,
335    /// AUR source package publishing configurations (source-only PKGBUILD, not -bin).
336    pub aur_sources: Option<Vec<AurSourceConfig>>,
337    /// Top-level retry configuration applied to network-bound operations
338    /// (announcers, git providers, HTTP uploads, docker pipes). When omitted,
339    /// `RetryConfig::default()` is used (10 attempts, 10s base, 5m cap —
340    /// the project-level retry policy).
341    pub retry: Option<RetryConfig>,
342    /// MCP (Model Context Protocol) server registry publishing
343    /// configuration. When `name` is empty (the default), the publisher is
344    /// skipped. The `mcp:` publisher block.
345    #[serde(default)]
346    pub mcp: McpConfig,
347    /// SchemaStore publisher. Registers the project's JSON Schema(s) on
348    /// SchemaStore at release time. When `schemas` is empty (the default),
349    /// the publisher is skipped. The `schemastore:` publisher block.
350    #[serde(default)]
351    pub schemastore: crate::config::publishers::SchemastoreConfig,
352    /// NPM package registry publishing configurations. One entry per
353    /// published package. In the default `optional-deps` mode anodizer emits
354    /// npm's native per-platform packages (biome / git-cliff pattern); in
355    /// `postinstall` mode it emits a download shim (the `npms:`
356    /// parity).
357    pub npms: Option<Vec<NpmConfig>>,
358    /// GemFury (fury.io) deb/rpm/apk publishing configurations. Mirrors
359    /// The `gemfury:` block. The legacy spelling
360    /// `furies:` is accepted via serde alias; a one-time deprecation
361    /// warning is emitted by [`warn_on_legacy_furies_alias`].
362    #[serde(alias = "furies")]
363    pub gemfury: Option<Vec<GemFuryConfig>>,
364    /// PyPI publishing configurations. One entry per published project.
365    /// Emits native `py3-none-<platform>` binary wheels from the built
366    /// binaries (plus an optional `maturin sdist`) and uploads them via
367    /// PyPI's legacy (twine-protocol) upload API. The `pypis:` block.
368    pub pypis: Option<Vec<PypiConfig>>,
369    /// homebrew-core formula-bump configurations. One entry per formula.
370    /// Bumps an existing formula in `Homebrew/homebrew-core` (or a formula
371    /// repository override) via the GitHub API and opens a pull request.
372    /// The `homebrew_cores:` block.
373    pub homebrew_cores: Option<Vec<HomebrewCoreConfig>>,
374    /// Per-crate metadata derived from each crate's `Cargo.toml [package]`
375    /// table (description / license / homepage / authors). Populated at
376    /// config-load time by [`Config::populate_derived_metadata`], keyed by
377    /// crate name. NOT a user-facing YAML field — it backs the
378    /// crate-aware `meta_*_for` accessors so a plain Rust project gets its
379    /// publisher metadata without repeating it in a top-level `metadata:`
380    /// block. A hand-written `metadata:` field and per-publisher overrides
381    /// still win.
382    #[serde(skip)]
383    #[schemars(skip)]
384    pub derived_metadata: BTreeMap<String, MetadataConfig>,
385}
386
387/// Helper schema function for the signs field (accepts object or array).
388fn signs_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
389    let mut schema = generator.subschema_for::<Vec<SignConfig>>();
390    schema.ensure_object().insert(
391        "description".to_owned(),
392        "Artifact signing configurations (cosign, GPG, etc.). Accepts a single object or array."
393            .into(),
394    );
395    schema
396}
397
398/// Helper schema function for the upx field (accepts object or array).
399fn upx_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
400    let mut schema = generator.subschema_for::<Vec<UpxConfig>>();
401    schema.ensure_object().insert(
402        "description".to_owned(),
403        "UPX binary compression configurations. Accepts a single object or array.".into(),
404    );
405    schema
406}
407
408/// Helper schema function for the sboms field (accepts object or array).
409fn sboms_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
410    let mut schema = generator.subschema_for::<Vec<SbomConfig>>();
411    schema.ensure_object().insert(
412        "description".to_owned(),
413        "SBOM generation configurations. Accepts a single object or array.".into(),
414    );
415    schema
416}
417
418fn default_dist() -> PathBuf {
419    PathBuf::from("./dist")
420}
421
422impl Default for Config {
423    fn default() -> Self {
424        Config {
425            version: None,
426            project_name: String::new(),
427            dist: default_dist(),
428            includes: None,
429            env_files: None,
430            defaults: None,
431            before: None,
432            after: None,
433            on_error: None,
434            always: None,
435            before_publish: None,
436            crates: Vec::new(),
437            changelog: None,
438            signs: Vec::new(),
439            binary_signs: Vec::new(),
440            docker_signs: None,
441            upx: Vec::new(),
442            snapshot: None,
443            nightly: None,
444            announce: None,
445            report_sizes: None,
446            env: None,
447            variables: None,
448            publishers: None,
449            dockerhub: None,
450            artifactories: None,
451            cloudsmiths: None,
452            homebrew_casks: None,
453            version_files: None,
454            tag: None,
455            git: None,
456            partial: None,
457            workspaces: None,
458            source: None,
459            sboms: Vec::new(),
460            attestations: None,
461            release: None,
462            github_urls: None,
463            gitlab_urls: None,
464            gitea_urls: None,
465            force_token: None,
466            notarize: None,
467            metadata: None,
468            template_files: None,
469            monorepo: None,
470            makeselfs: Vec::new(),
471            install_scripts: Vec::new(),
472            appimages: Vec::new(),
473            verify_release: VerifyReleaseConfig::default(),
474            preflight: PreflightConfig::default(),
475            srpms: None,
476            milestones: None,
477            uploads: None,
478            aur_sources: None,
479            retry: None,
480            mcp: McpConfig::default(),
481            schemastore: crate::config::publishers::SchemastoreConfig::default(),
482            npms: None,
483            gemfury: None,
484            pypis: None,
485            homebrew_cores: None,
486            derived_metadata: BTreeMap::new(),
487        }
488    }
489}
490
491mod accessors;
492
493mod schema;
494pub use schema::*;
495
496/// Run a deserialization closure on a worker thread sized large enough that
497/// the `Config` derive (60+ `Option<NestedStruct>` fields) cannot exhaust
498/// the host's main-thread stack.
499///
500/// Background: debug builds of `serde_yaml_ng::from_value::<Config>` and
501/// `toml::from_str::<Config>` consume several MiB of stack because each
502/// generated visitor branch for the giant struct lives in a single
503/// monomorphised frame and debug builds neither inline nor tail-call. The
504/// Windows main-thread default reservation is 1 MiB, so any debug-built
505/// integration test that triggers full-config deserialization overflows
506/// before reaching the visitor's body.
507///
508/// Routing every full-`Config` deserialization through this helper keeps
509/// every entry-point platform-agnostic without resorting to per-platform
510/// linker flags or `RUST_MIN_STACK`.
511pub fn deserialize_on_worker<F, T>(f: F) -> anyhow::Result<T>
512where
513    F: FnOnce() -> anyhow::Result<T> + Send + 'static,
514    T: Send + 'static,
515{
516    use anyhow::Context as _;
517
518    // 8 MiB matches the Linux/macOS process default and comfortably exceeds
519    // the ~2 MiB peak observed for debug `Config` deserialization.
520    const WORKER_STACK_SIZE: usize = 8 * 1024 * 1024;
521
522    let handle = std::thread::Builder::new()
523        .stack_size(WORKER_STACK_SIZE)
524        .name("anodizer-config-deserialize".to_string())
525        .spawn(f)
526        .context("failed to spawn config deserialization worker thread")?;
527    match handle.join() {
528        Ok(result) => result,
529        Err(payload) => std::panic::resume_unwind(payload),
530    }
531}
532
533mod validate;
534pub use validate::*;
535
536mod publish_axis;
537pub(crate) use publish_axis::*;
538
539mod legacy;
540pub use legacy::*;
541
542// ---------------------------------------------------------------------------
543// EnvFilesConfig — accepts list of .env paths OR structured token file paths
544// ---------------------------------------------------------------------------
545
546mod env_files;
547pub use env_files::*;
548
549// ---------------------------------------------------------------------------
550// Defaults
551// ---------------------------------------------------------------------------
552
553mod defaults;
554pub use defaults::*;
555
556// ---------------------------------------------------------------------------
557// BuildIgnore — exclude specific os/arch combos from builds
558// ---------------------------------------------------------------------------
559
560mod build;
561pub use build::*;
562
563// ---------------------------------------------------------------------------
564// ArchivesConfig — untagged enum: false => Disabled, array => Configs
565// ---------------------------------------------------------------------------
566
567mod archives;
568pub use archives::*;
569
570mod completions;
571pub use completions::*;
572
573// ---------------------------------------------------------------------------
574// ReleaseConfig
575// ---------------------------------------------------------------------------
576
577mod release;
578pub use release::*;
579
580// ---------------------------------------------------------------------------
581// Shared publisher config types: RepositoryConfig, CommitAuthorConfig
582// ---------------------------------------------------------------------------
583
584mod publishers;
585pub use publishers::*;
586
587// ---------------------------------------------------------------------------
588// DockerV2Config
589// ---------------------------------------------------------------------------
590
591mod docker;
592pub use docker::*;
593
594// ---------------------------------------------------------------------------
595// NfpmConfig
596// ---------------------------------------------------------------------------
597
598mod nfpm;
599pub use nfpm::*;
600
601// ---------------------------------------------------------------------------
602// SnapcraftConfig
603// ---------------------------------------------------------------------------
604
605mod snapcraft;
606pub use snapcraft::*;
607// ---------------------------------------------------------------------------
608// DmgConfig / MsiConfig / PkgConfig / NsisConfig / AppBundleConfig / FlatpakConfig
609// ---------------------------------------------------------------------------
610
611mod installers;
612pub use installers::*;
613
614// ---------------------------------------------------------------------------
615// BlobConfig (S3/GCS/Azure cloud storage)
616// ---------------------------------------------------------------------------
617
618mod blob;
619pub use blob::*;
620
621// ---------------------------------------------------------------------------
622// PartialConfig (split/merge CI fan-out)
623// ---------------------------------------------------------------------------
624
625mod partial;
626pub use partial::*;
627
628// ---------------------------------------------------------------------------
629// BinstallConfig
630// ---------------------------------------------------------------------------
631
632mod binstall;
633pub use binstall::*;
634
635// ---------------------------------------------------------------------------
636// NotarizeConfig (macOS code signing and notarization)
637// ---------------------------------------------------------------------------
638
639mod notarize;
640pub use notarize::*;
641// ---------------------------------------------------------------------------
642// SourceConfig
643// ---------------------------------------------------------------------------
644
645mod source;
646pub use source::*;
647
648// ---------------------------------------------------------------------------
649// SbomConfig
650// ---------------------------------------------------------------------------
651
652mod sbom;
653pub use sbom::*;
654
655// ---------------------------------------------------------------------------
656// AttestationConfig
657// ---------------------------------------------------------------------------
658
659mod attestation;
660pub use attestation::*;
661
662// ---------------------------------------------------------------------------
663// VersionSyncConfig
664// ---------------------------------------------------------------------------
665
666mod version_sync;
667pub use version_sync::*;
668
669// ---------------------------------------------------------------------------
670// ChangelogConfig
671// ---------------------------------------------------------------------------
672
673mod changelog;
674pub use changelog::*;
675// ---------------------------------------------------------------------------
676// SignConfig / DockerSignConfig — lifted to `crate::signing`
677// ---------------------------------------------------------------------------
678//
679// see `crate::signing` for the type definitions. The
680// re-exports below preserve the historical
681// `anodizer_core::config::{SignConfig, DockerSignConfig}` import paths
682// used by every stage that consumes a sign config.
683
684pub use crate::signing::{AuthenticodeConfig, DockerSignConfig, SignConfig, SignVerifyConfig};
685
686// ---------------------------------------------------------------------------
687// UpxConfig
688// ---------------------------------------------------------------------------
689
690mod upx;
691pub use upx::*;
692
693// ---------------------------------------------------------------------------
694// SnapshotConfig
695// ---------------------------------------------------------------------------
696
697mod snapshot_nightly;
698pub use snapshot_nightly::*;
699
700mod cargo_metadata;
701pub use cargo_metadata::derive_metadata_from_cargo_toml;
702
703mod workspace_deps;
704pub use workspace_deps::{
705    derive_depends_on_from_cargo_toml, discover_cargo_workspace_member_names,
706    extract_workspace_deps,
707};
708
709/// Extract the name portion of a `"Name <email>"` maintainer/author string,
710/// dropping any `<…>` email suffix. Returns `None` when the result is empty
711/// (e.g. a bare-email `<ada@example.com>`), so a derived Vendor / OCI `vendor`
712/// value is never emitted blank.
713pub fn maintainer_name_only(maintainer: &str) -> Option<String> {
714    let name = maintainer.split('<').next().unwrap_or(maintainer).trim();
715    (!name.is_empty()).then(|| name.to_string())
716}
717
718// ---------------------------------------------------------------------------
719// TemplateFileConfig
720// ---------------------------------------------------------------------------
721
722mod templatefiles;
723pub use templatefiles::*;
724
725// ---------------------------------------------------------------------------
726// AnnounceConfig
727// ---------------------------------------------------------------------------
728mod announce;
729pub use announce::*;
730// ---------------------------------------------------------------------------
731// DockerHub description sync
732// ---------------------------------------------------------------------------
733
734mod dockerhub;
735pub use dockerhub::*;
736
737// ---------------------------------------------------------------------------
738// Artifactory publisher
739// ---------------------------------------------------------------------------
740
741mod artifactory;
742pub use artifactory::*;
743
744// ---------------------------------------------------------------------------
745// CloudSmith publisher
746// ---------------------------------------------------------------------------
747
748mod cloudsmith;
749pub use cloudsmith::*;
750
751// ---------------------------------------------------------------------------
752// PublisherConfig
753// ---------------------------------------------------------------------------
754
755mod publisher;
756pub use publisher::*;
757
758// ---------------------------------------------------------------------------
759// HooksConfig
760// ---------------------------------------------------------------------------
761
762mod hooks;
763pub use hooks::*;
764
765// ---------------------------------------------------------------------------
766// GitConfig
767// ---------------------------------------------------------------------------
768
769mod git_config;
770pub use git_config::*;
771
772// ---------------------------------------------------------------------------
773// MonorepoConfig
774// ---------------------------------------------------------------------------
775
776mod monorepo;
777pub use monorepo::*;
778
779// ---------------------------------------------------------------------------
780// TagConfig
781// ---------------------------------------------------------------------------
782
783mod tag;
784pub use tag::*;
785
786// ---------------------------------------------------------------------------
787// WorkspaceConfig
788// ---------------------------------------------------------------------------
789
790mod workspace;
791pub use workspace::*;
792
793// ---------------------------------------------------------------------------
794// RetryConfig (top-level `retry:` block — bridges to crate::retry::RetryPolicy)
795// ---------------------------------------------------------------------------
796
797mod retry;
798pub use retry::*;
799
800// ---------------------------------------------------------------------------
801// PostPublishPollConfig (per-publisher post-publish polling)
802// ---------------------------------------------------------------------------
803
804mod post_publish_poll;
805pub use post_publish_poll::*;
806
807// ---------------------------------------------------------------------------
808// VerifyReleaseConfig (top-level `verify_release:` post-publish gate)
809// ---------------------------------------------------------------------------
810
811mod verify_release;
812pub use verify_release::*;
813
814// ---------------------------------------------------------------------------
815// PreflightConfig (top-level `preflight:` pre-publish probe tuning)
816// ---------------------------------------------------------------------------
817
818mod preflight;
819pub use preflight::*;
820
821// ---------------------------------------------------------------------------
822// StringOrBool — accepts bool or template string in YAML
823// ---------------------------------------------------------------------------
824
825mod string_or_bool;
826pub use string_or_bool::*;
827
828// ---------------------------------------------------------------------------
829// MakeselfConfig + SrpmConfig — lifted to `crate::packagers`
830// ---------------------------------------------------------------------------
831//
832// All packaging config types live in their own modules under
833// `crate::packagers`. The re-exports below preserve the historical
834// `anodizer_core::config::{MakeselfConfig, MakeselfFile, SrpmConfig}`
835// import paths used by stages and tests.
836
837pub use crate::packagers::{
838    AppImageConfig, AppImageExtra, InstallScriptConfig, MakeselfConfig, MakeselfFile,
839    RuntimeHarvest, SrpmConfig,
840};
841pub(crate) use crate::packagers::{
842    appimages_schema, deserialize_appimages, deserialize_install_scripts, deserialize_makeselfs,
843    install_scripts_schema, makeselfs_schema,
844};
845
846// ---------------------------------------------------------------------------
847// MilestoneConfig
848// ---------------------------------------------------------------------------
849
850mod milestone;
851pub use milestone::*;
852
853// ---------------------------------------------------------------------------
854// UploadConfig (generic HTTP upload)
855// ---------------------------------------------------------------------------
856
857mod upload;
858pub use upload::*;
859
860// ---------------------------------------------------------------------------
861// AurSourceConfig
862// ---------------------------------------------------------------------------
863
864mod aur_source;
865pub use aur_source::*;
866
867// ---------------------------------------------------------------------------
868// McpConfig (MCP registry publisher)
869// ---------------------------------------------------------------------------
870
871mod mcp;
872pub use mcp::*;
873
874// ---------------------------------------------------------------------------
875// NpmConfig (NPM package registry publisher)
876// ---------------------------------------------------------------------------
877
878mod npm;
879pub use npm::*;
880
881// ---------------------------------------------------------------------------
882// GemFuryConfig (Gemfury / fury.io publisher)
883// ---------------------------------------------------------------------------
884
885mod gemfury;
886pub use gemfury::*;
887
888// ---------------------------------------------------------------------------
889// PypiConfig (PyPI binary-wheel publisher)
890// ---------------------------------------------------------------------------
891
892mod pypi;
893pub use pypi::*;
894
895// ---------------------------------------------------------------------------
896// HomebrewCoreConfig (homebrew-core formula-bump publisher)
897// ---------------------------------------------------------------------------
898
899mod homebrew_core;
900pub use homebrew_core::*;
901
902// ---------------------------------------------------------------------------
903// Well-known config file discovery
904// ---------------------------------------------------------------------------
905
906mod discovery;
907pub use discovery::*;
908
909// ---------------------------------------------------------------------------
910// Tests
911// ---------------------------------------------------------------------------
912
913#[cfg(test)]
914mod tests;