1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::collections::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::{StringOrBool, deserialize_string_or_bool_opt};
// ---------------------------------------------------------------------------
// CloudSmith publisher
// ---------------------------------------------------------------------------
/// Per-format distribution value. Accepts either a single distribution string
/// (`deb: "ubuntu/focal"`) or an array of distribution slugs
/// (`deb: ["ubuntu/focal", "ubuntu/jammy"]`) — the array form causes the
/// publisher to issue one upload per distribution slug.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum CloudSmithDistributions {
/// Single distribution slug (`"ubuntu/focal"`).
Single(String),
/// Multiple distribution slugs; the publisher uploads once per entry.
Multiple(Vec<String>),
}
impl CloudSmithDistributions {
/// Materialize as a `Vec<&str>` regardless of which YAML form the user
/// wrote. A `Single` value yields a one-element vec so the caller can
/// always iterate.
pub fn to_str_vec(&self) -> Vec<&str> {
match self {
Self::Single(s) => vec![s.as_str()],
Self::Multiple(v) => v.iter().map(String::as_str).collect(),
}
}
}
/// CloudSmith publisher configuration.
/// Pushes packages to CloudSmith repositories.
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
#[serde(default, deny_unknown_fields)]
pub struct CloudSmithConfig {
/// CloudSmith organization slug.
pub organization: Option<String>,
/// CloudSmith repository slug.
pub repository: Option<String>,
/// Build IDs filter: only publish artifacts from builds whose `id` is in this list.
pub ids: Option<Vec<String>>,
/// Glob patterns matched against each artifact's file name; anodizer drops
/// any artifact whose name matches at least one glob from THIS CloudSmith
/// target only. Use it to keep heavy sidecars off a given repository while
/// packages still upload. Composes with `ids:` and `formats:` (all filters
/// apply). `None`/empty keeps everything.
///
/// ```yaml
/// cloudsmiths:
/// - organization: my-org
/// repository: my-repo
/// exclude: ["*.sha256", "*.sig", "*.cdx.json"]
/// ```
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exclude: Option<Vec<String>>,
/// Package format filter: only publish artifacts matching these formats.
pub formats: Option<Vec<String>>,
/// Distribution mapping per format. Each entry accepts either a single
/// slug (`deb: "ubuntu/focal"`) or an array of slugs
/// (`deb: ["ubuntu/focal", "ubuntu/jammy"]`); the array form issues one
/// upload per entry.
pub distributions: Option<HashMap<String, CloudSmithDistributions>>,
/// Debian component name (e.g. "main").
pub component: Option<String>,
/// Environment variable name containing the CloudSmith API key.
pub secret_name: Option<String>,
/// Template-conditional skip: if rendered result is `"true"`, skip this publisher.
#[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
pub skip: Option<StringOrBool>,
/// When true, allow republishing over existing package versions.
#[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
pub republish: Option<StringOrBool>,
/// Override whether this publisher failing should fail the overall release.
///
/// Default: `false` — a failure here is logged but does not abort the release.
/// Set to `true` to fail the release on any error.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
/// Template-conditional gate: when the rendered result is falsy
/// (`"false"` / `"0"` / `"no"` / empty), the CloudSmith publisher is
/// skipped. Render failure hard-errors. Config key: `cloudsmiths[].if:`.
#[serde(rename = "if")]
pub if_condition: Option<String>,
/// When `true`, a triggered rollback leaves this publisher's work in
/// place rather than attempting to undo it. Default `false`.
pub retain_on_rollback: Option<bool>,
/// Retain only the `N` most-recent release versions of each published
/// package, pruning older ones from the CloudSmith repository after a
/// successful upload.
///
/// This is **opt-in** and **destructive**: leaving it unset (the default)
/// prunes nothing. When set, after the just-uploaded artifacts are
/// confirmed present the publisher lists every version of *this* package
/// in the repository, ranks the distinct release versions by SemVer
/// (newest first), keeps the top `N` — which always includes the version
/// just published — and issues `DELETE` for every artifact (all formats
/// and architectures) belonging to versions ranked beyond `N`. Other
/// packages sharing the repository are never touched.
///
/// All package formats of one release are treated as the same version:
/// the deb/rpm epoch (`1:0.9.1-1`) and apk revision (`0.9.1-r1`) suffixes
/// are normalized to the base SemVer (`0.9.1`) before ranking, so
/// keeping `2` versions keeps every `.deb`/`.rpm`/`.apk` of the two newest
/// releases.
///
/// Pruning is **best-effort**: it runs only after the upload (the real
/// work) has already succeeded, is skipped entirely in dry-run and
/// snapshot mode, and a list/delete failure emits a prominent warning and
/// continues rather than failing the release or rolling anything back.
/// `keep_versions: 0` is rejected — anodizer never prunes every version.
///
/// Primarily a remedy for storage-capped repositories (e.g. the
/// CloudSmith free plan's 500 MB limit, which offers no server-side
/// retention policy).
///
/// ```yaml
/// cloudsmiths:
/// - organization: acme
/// repository: tools
/// keep_versions: 3 # keep the 3 newest releases, prune older ones
/// ```
#[serde(default, skip_serializing_if = "Option::is_none")]
pub keep_versions: Option<u32>,
}