anodizer_core/config/upload.rs
1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use super::{ExtraFileSpec, StringOrBool, deserialize_string_or_bool_opt};
7
8// ---------------------------------------------------------------------------
9// UploadConfig (generic HTTP upload)
10// ---------------------------------------------------------------------------
11
12#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
13#[serde(default, deny_unknown_fields)]
14pub struct UploadConfig {
15 /// Human-readable name for this upload config.
16 pub name: Option<String>,
17 /// Build IDs filter: only upload artifacts whose `id` is in this list.
18 pub ids: Option<Vec<String>>,
19 /// Glob patterns matched against each artifact's file name; anodizer drops
20 /// any artifact whose name matches at least one glob from THIS upload
21 /// target only. Use it to keep heavy sidecars (checksums, signatures,
22 /// SBOMs) off a given endpoint while archives still upload. Composes with
23 /// `ids:` and `exts:` (all filters apply). `None`/empty keeps everything.
24 ///
25 /// ```yaml
26 /// uploads:
27 /// - name: mirror
28 /// target: "https://mirror.example.com/{{ .ArtifactName }}"
29 /// exclude: ["*.sha256", "*.sig", "*.cdx.json"]
30 /// ```
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub exclude: Option<Vec<String>>,
33 /// File extension filter: only upload artifacts with these extensions.
34 pub exts: Option<Vec<String>>,
35 /// Target URL template (supports template variables like {{ ProjectName }}, {{ Version }}).
36 pub target: String,
37 /// Username for HTTP basic auth.
38 /// Resolution order: rendered `username` template → env `UPLOAD_{NAME}_USERNAME`.
39 /// Set this to a literal value or a `{{ Env.X }}` template.
40 pub username: Option<String>,
41 /// Password for HTTP basic auth.
42 ///
43 /// Strongly prefer `{{ Env.UPLOAD_PASSWORD }}` (or any other env-var
44 /// template) over an in-config literal — plaintext values here are NOT
45 /// redacted from dry-run output and will land in `dist/config.yaml`
46 /// when the pipeline runs with `--dry-run` / `--snapshot`. Resolution
47 /// order: rendered `password` template → env `UPLOAD_{NAME}_SECRET`.
48 /// Password-resolution cascade.
49 pub password: Option<String>,
50 /// HTTP method: PUT or POST (default: PUT).
51 pub method: Option<String>,
52 /// Upload mode: "archive" (default) or "binary".
53 pub mode: Option<String>,
54 /// Header name for the SHA256 checksum of the artifact.
55 pub checksum_header: Option<String>,
56 /// Path to PEM-encoded trusted CA certificates.
57 pub trusted_certificates: Option<String>,
58 /// Path to PEM-encoded client X.509 certificate for mTLS.
59 pub client_x509_cert: Option<String>,
60 /// Path to PEM-encoded client X.509 key for mTLS.
61 pub client_x509_key: Option<String>,
62 /// Include checksums in uploaded artifacts.
63 pub checksum: Option<bool>,
64 /// Include signatures in uploaded artifacts.
65 pub signature: Option<bool>,
66 /// Include `dist/metadata.json` in uploaded artifacts. The sibling
67 /// `dist/artifacts.json` manifest is never uploaded.
68 pub meta: Option<bool>,
69 /// Custom HTTP headers (each value is template-expanded).
70 pub custom_headers: Option<HashMap<String, String>>,
71 /// When true, use the artifact name as-is (don't append to target URL).
72 pub custom_artifact_name: Option<bool>,
73 /// Extra files to include in uploading.
74 pub extra_files: Option<Vec<ExtraFileSpec>>,
75 /// Upload only extra files, skip normal artifacts.
76 pub extra_files_only: Option<bool>,
77 /// Skip condition template (if rendered to "true", skip this upload).
78 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
79 pub skip: Option<StringOrBool>,
80 /// Template-conditional gate: when the rendered result is falsy
81 /// (`"false"` / `"0"` / `"no"` / empty), the upload is skipped.
82 /// Render failure hard-errors. The `uploads[].if:` conditional gate.
83 #[serde(rename = "if")]
84 pub if_condition: Option<String>,
85 /// Re-upload an artifact even when an identical one already exists at the
86 /// target path (default: `false`).
87 ///
88 /// With the default, a re-run that finds the same version's artifact
89 /// already uploaded with a matching SHA-256 records an idempotent SKIP
90 /// rather than re-PUTting it — so re-running a partially-failed release is
91 /// safe. A path that already holds a *different* artifact for the same
92 /// version still hard-errors (immutable-version drift) unless `overwrite`
93 /// is set. With `overwrite: true`, every artifact is PUT unconditionally.
94 pub overwrite: Option<bool>,
95 /// Override whether this upload failing should fail the overall release.
96 ///
97 /// Default: `false` — a failure here is logged but does not abort the
98 /// release. Set to `true` to fail the release on any error.
99 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub required: Option<bool>,
101 /// When `true`, a triggered rollback leaves this upload's artifacts in
102 /// place rather than issuing a server-side DELETE. Default `false`.
103 pub retain_on_rollback: Option<bool>,
104}