Skip to main content

anodizer_core/config/
docker.rs

1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use super::build::BuildHooksConfig;
7use super::release::{SkipPushConfig, skip_push_schema};
8use super::{StringOrBool, deserialize_string_or_bool_opt};
9
10// Use `DockerV2Config` (canonical) for docker image builds.
11
12/// Per-pipe retry configuration for `docker.retry` / `docker_manifest.retry`.
13///
14/// **Deprecated**: prefer the top-level `retry:` block ([`super::RetryConfig`])
15/// which applies to docker pipes (and every other network-bound stage) via
16/// `Project.Retry`. When a per-pipe block is present alongside the top-level
17/// block, the per-pipe values win for back-compat, but
18/// `stage-docker::resolve_retry_params` emits a one-shot deprecation warning.
19/// New configs should leave this field unset.
20//
21// Note: `#[deprecated]` on the type cascades through derive-generated impls
22// (Default, Serialize, JsonSchema, ...) and is hard to silence cleanly, so the
23// deprecation lives in (a) this rustdoc prose, (b) the runtime `tracing::warn!`
24// fired once per process by `stage-docker::resolve_retry_params`, and (c) the
25// schemars-generated JSON-schema description carries the same prose for
26// editor / IDE consumers.
27#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
28#[serde(default)]
29pub struct DockerRetryConfig {
30    /// Number of retry attempts for failed docker push operations
31    /// (default: 10, set in `crates/stage-docker/src/lib.rs::resolve_retry_settings`).
32    pub attempts: Option<u32>,
33    /// Duration string for the initial retry delay (default: `"10s"`).
34    /// Examples: `"1s"`, `"500ms"`.
35    pub delay: Option<String>,
36    /// Maximum delay between retries (default: `"5m"`). Caps the exponential
37    /// backoff so attempt-9 with a 10s base does not stretch to ~42 min.
38    /// Example: `"30s"`.
39    pub max_delay: Option<String>,
40}
41
42// ---------------------------------------------------------------------------
43// DockerV2Config
44// ---------------------------------------------------------------------------
45
46/// Docker V2 configuration — the canonical Docker build API.
47///
48/// Notable surface:
49/// - `images` + `tags` (cleaner separation than a single `image_templates` list)
50/// - `annotations` map for OCI annotations (`--annotation`)
51/// - `build_args` map for build-time variables
52/// - `skip` as a [`StringOrBool`] template for conditional opt-out
53/// - `sbom` as a [`StringOrBool`] — when truthy, adds `--sbom=true` to buildx
54/// - `flags` for arbitrary extra `docker build` flags
55/// - `platforms` is the only target selector — no per-arch field overrides
56#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
57#[serde(default, deny_unknown_fields)]
58pub struct DockerV2Config {
59    /// Unique identifier for this Docker V2 config.
60    pub id: Option<String>,
61    /// Build IDs filter: only include binary artifacts whose metadata `id` is in this list.
62    pub ids: Option<Vec<String>>,
63    /// Path to the Dockerfile relative to the project root.
64    pub dockerfile: String,
65    /// Base image names (e.g., ["ghcr.io/owner/app"]). Combined with `tags` to form full references.
66    pub images: Vec<String>,
67    /// Tag suffixes (e.g., ["latest", "{{ .Version }}"]). Each image is tagged with each tag.
68    pub tags: Vec<String>,
69    /// OCI labels to apply to the image via `--label key=value` flags.
70    pub labels: Option<HashMap<String, String>>,
71    /// OCI annotations to apply via `--annotation key=value` flags.
72    pub annotations: Option<HashMap<String, String>>,
73    /// Extra files to copy into the Docker build context.
74    pub extra_files: Option<Vec<String>>,
75    /// Target platforms for multi-arch builds (e.g., ["linux/amd64", "linux/arm64"]).
76    pub platforms: Option<Vec<String>>,
77    /// Build arguments passed as `--build-arg KEY=VALUE`.
78    ///
79    /// Each value is template-expanded and forwarded verbatim to buildx
80    /// (one argv token per pair, no shell tokenization). Prefer
81    /// `{{ .Env.VAR }}` over raw user-config strings for secrets — buildx
82    /// records build-args in image history by default, so plaintext values
83    /// here propagate into the image metadata.
84    pub build_args: Option<HashMap<String, String>>,
85    /// Retry configuration for docker push operations.
86    pub retry: Option<DockerRetryConfig>,
87    /// Arbitrary extra flags passed to the docker build command.
88    pub flags: Option<Vec<String>>,
89    /// When truthy, skip this docker build entirely. Supports templates.
90    /// Accepts the legacy `disable:` spelling via serde alias for back-compat
91    /// with imported GoReleaser configs (GR's docker config field is
92    /// `pkg/config/config.go:1149` `Disable string`).
93    #[serde(
94        default,
95        alias = "disable",
96        deserialize_with = "deserialize_string_or_bool_opt"
97    )]
98    pub skip: Option<StringOrBool>,
99    /// When truthy, adds `--sbom=true` to buildx. Supports templates.
100    #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
101    pub sbom: Option<StringOrBool>,
102    /// Pre/post hooks for this docker_v2 config. Each hook accepts the same
103    /// `cmd`/`dir`/`env`/`output` shape as build/archive hooks. `pre` hooks
104    /// run after the staging directory is prepared but before `docker buildx
105    /// build`; `post` hooks run after the image digest is captured. Hook
106    /// commands, working directories, and env values are template-expanded;
107    /// in addition to the standard template surface, hooks see:
108    ///
109    /// - `{{ .Images }}` — list of `image:tag` references for this build.
110    ///   Iterate via `{% for img in Images %}{{ img }}{% endfor %}` to mirror
111    ///   GR's `[]string` exposure of the same field; `{{ .Images | join(sep=",") }}`
112    ///   reproduces a flat comma-separated string for legacy templates.
113    /// - `{{ .Dockerfile }}` — path to the rendered Dockerfile
114    /// - `{{ .ContextDir }}` — path to the buildx context staging directory
115    /// - `{{ .Digest }}` — image manifest digest (post hooks only)
116    /// - `{{ .BaseImage }}` / `{{ .BaseImageDigest }}` — final-stage base image
117    ///   (matches the `BaseImage` / `BaseImageDigest` overlay GR adds in
118    ///   `internal/pipe/docker/v2/docker.go`)
119    pub hooks: Option<BuildHooksConfig>,
120    // No `skip_push` field — use the canonical `skip:` to suppress
121    // the publish step (matches every other publisher / pipe in anodizer).
122}
123
124// ---------------------------------------------------------------------------
125// DockerDigestConfig
126// ---------------------------------------------------------------------------
127
128/// Controls docker image digest file creation.
129///
130/// After each docker image push, a digest file (containing the sha256 digest)
131/// is written to the dist directory. This config controls whether that happens
132/// and how the files are named.
133#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
134#[serde(default)]
135pub struct DockerDigestConfig {
136    /// When truthy, disable docker digest artifact creation.
137    #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
138    pub skip: Option<StringOrBool>,
139    /// Template for the digest artifact filename.
140    /// Default: tag-based naming (e.g., "ghcr.io_owner_app_v1.0.0.digest").
141    pub name_template: Option<String>,
142}
143
144// ---------------------------------------------------------------------------
145// DockerManifestConfig
146// ---------------------------------------------------------------------------
147
148/// Deprecated: prefer `docker_v2` (which produces multi-arch manifests via
149/// the `platforms:` field automatically). `DockerManifestConfig` is retained
150/// for back-compat with imported GoReleaser configs and for the niche case
151/// of stitching together manifest lists from images that were not built by
152/// `docker_v2` in the same run.
153///
154/// Mirrors GoReleaser commit e09e23a, which marked the v1 docker / docker
155/// manifest pipes deprecated in favour of the v2 buildx flow. The rustdoc
156/// here is the load-bearing surface for the deprecation: it flows into the
157/// schemars-generated JSON Schema (consumed by IDEs / editor tooling) and
158/// rustdoc HTML, both of which are how downstream config authors discover
159/// that the v2 pipe is the preferred entry point.
160#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
161#[serde(default)]
162pub struct DockerManifestConfig {
163    /// Template for the manifest name, e.g. "ghcr.io/owner/app:{{ .Version }}".
164    pub name_template: String,
165    /// Image references to include in the manifest.
166    pub image_templates: Vec<String>,
167    /// Extra flags for `docker manifest create`.
168    pub create_flags: Option<Vec<String>>,
169    /// Extra flags for `docker manifest push`.
170    pub push_flags: Option<Vec<String>>,
171    /// Skip push: true, false, or "auto" (skip for prereleases).
172    #[schemars(schema_with = "skip_push_schema")]
173    pub skip_push: Option<SkipPushConfig>,
174    /// Unique identifier for this manifest config.
175    pub id: Option<String>,
176    /// Docker backend for manifest commands: "docker" (default) or "podman".
177    #[serde(rename = "use")]
178    pub use_backend: Option<String>,
179    /// Retry configuration for manifest push (handles transient registry errors).
180    pub retry: Option<DockerRetryConfig>,
181}