anodizer_core/config/installers.rs
1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use super::archives::{ArchiveFileSpec, ExtraFileSpec, TemplatedExtraFile};
5use super::build::BuildHooksConfig;
6use super::{StringOrBool, deserialize_string_or_bool_opt};
7
8// ---------------------------------------------------------------------------
9// DmgConfig
10// ---------------------------------------------------------------------------
11
12#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
13#[serde(default, deny_unknown_fields)]
14pub struct DmgConfig {
15 /// Unique identifier for this DMG config.
16 pub id: Option<String>,
17 /// Build IDs to include. Empty means all builds.
18 pub ids: Option<Vec<String>>,
19 /// Output DMG filename (supports templates).
20 pub name: Option<String>,
21 /// Additional files to include in the DMG (glob or {glob, name_template}).
22 pub extra_files: Option<Vec<ExtraFileSpec>>,
23 /// Extra files whose contents are rendered through the template engine before inclusion.
24 /// Unlike `extra_files` which copy as-is, template variables like `{{ Tag }}` are expanded.
25 /// Conditional-skip gate.
26 pub templated_extra_files: Option<Vec<TemplatedExtraFile>>,
27 /// Remove source archives from artifacts, keeping only DMG.
28 pub replace: Option<bool>,
29 /// Output timestamp for reproducible builds.
30 pub mod_timestamp: Option<String>,
31 /// Skip this DMG config. Accepts bool or template string.
32 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
33 pub skip: Option<StringOrBool>,
34 /// Which artifact type to package: "binary" (default) or "appbundle".
35 #[serde(rename = "use")]
36 pub use_: Option<String>,
37 /// amd64 microarchitecture variant filter (`v1` / `v2` / `v3` / `v4`),
38 /// set via the `amd64_variant:` key. When set, only artifacts with the
39 /// matching `amd64_variant` metadata are included. The legacy `goamd64:`
40 /// spelling is accepted via serde alias for back-compat with imported
41 /// configs. When unset, all amd64 variants are included (no filtering).
42 #[serde(alias = "goamd64")]
43 pub amd64_variant: Option<String>,
44 /// Template-conditional: skip this DMG config if rendered result is "false"
45 /// or empty. Render failure hard-errors (not silent-skip).
46 #[serde(rename = "if")]
47 pub if_condition: Option<String>,
48 /// Volume label shown in Finder when the image is mounted.
49 ///
50 /// Supports template variables. Defaults to the project name.
51 pub volume_name: Option<String>,
52}
53
54// ---------------------------------------------------------------------------
55// MsiConfig
56// ---------------------------------------------------------------------------
57
58#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
59#[serde(default, deny_unknown_fields)]
60pub struct MsiConfig {
61 /// Unique identifier for this MSI config.
62 pub id: Option<String>,
63 /// Build IDs to include. Empty means all builds.
64 pub ids: Option<Vec<String>>,
65 /// Path to the WiX source file (.wxs). Goes through template engine. Required.
66 pub wxs: Option<String>,
67 /// Output MSI filename (supports templates).
68 pub name: Option<String>,
69 /// WiX schema version: v3 or v4 (auto-detected from .wxs if omitted).
70 pub version: Option<String>,
71 /// Remove source archives from artifacts, keeping only MSI.
72 pub replace: Option<bool>,
73 /// Output timestamp for reproducible builds.
74 pub mod_timestamp: Option<String>,
75 /// Skip this MSI config. Accepts bool or template string.
76 /// Accepts the legacy `disable:` spelling via serde alias for back-compat
77 /// with imported configs.
78 #[serde(
79 default,
80 alias = "disable",
81 deserialize_with = "deserialize_string_or_bool_opt"
82 )]
83 pub skip: Option<StringOrBool>,
84 /// amd64 microarchitecture variant filter (`v1` / `v2` / `v3` / `v4`),
85 /// set via the `amd64_variant:` key. When set, only artifacts with the
86 /// matching `amd64_variant` metadata are included. The legacy `goamd64:`
87 /// spelling is accepted via serde alias for back-compat with imported
88 /// configs.
89 #[serde(alias = "goamd64")]
90 pub amd64_variant: Option<String>,
91 /// Additional files available in the WiX build context (simple filenames).
92 pub extra_files: Option<Vec<String>>,
93 /// WiX extensions to enable (e.g., "WixUIExtension"). Templates allowed.
94 pub extensions: Option<Vec<String>>,
95 /// Template-conditional: skip this MSI config if rendered result is "false"
96 /// or empty. Render failure hard-errors (not silent-skip).
97 #[serde(rename = "if")]
98 pub if_condition: Option<String>,
99 /// Pre/post MSI-build hooks. Accepts `pre`/`post`
100 /// or `before`/`after` via BuildHooksConfig's serde aliases. Runs before
101 /// / after candle+light for each matched artifact.
102 pub hooks: Option<BuildHooksConfig>,
103}
104
105// ---------------------------------------------------------------------------
106// PkgConfig (macOS .pkg installer)
107// ---------------------------------------------------------------------------
108
109#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
110#[serde(default, deny_unknown_fields)]
111pub struct PkgConfig {
112 /// Unique identifier for this PKG config.
113 pub id: Option<String>,
114 /// Build IDs to include. Empty means all builds.
115 pub ids: Option<Vec<String>>,
116 /// Package identifier in reverse-domain notation (e.g. com.example.myapp). Required.
117 /// Templates allowed (e.g. `com.example.{{ ProjectName }}`).
118 pub identifier: Option<String>,
119 /// Output PKG filename (supports templates).
120 /// Default: `{{ ProjectName }}_{{ Arch }}` (no extension enforced; user controls it).
121 pub name: Option<String>,
122 /// Installation path. Default: /usr/local/bin. Templates allowed.
123 pub install_location: Option<String>,
124 /// Path to scripts directory containing preinstall/postinstall scripts. Templates allowed.
125 pub scripts: Option<String>,
126 /// Additional files to include in the package (glob or {glob, name_template}).
127 /// Anodizer-additive.
128 pub extra_files: Option<Vec<ExtraFileSpec>>,
129 /// Extra files whose contents are rendered through the template engine before inclusion.
130 /// Unlike `extra_files` which copy as-is, template variables like `{{ Tag }}` are expanded.
131 /// Anodizer-additive.
132 pub templated_extra_files: Option<Vec<TemplatedExtraFile>>,
133 /// Remove source archives from artifacts, keeping only PKG.
134 pub replace: Option<bool>,
135 /// Output timestamp for reproducible builds. Templates allowed (e.g. `{{ CommitTimestamp }}`).
136 pub mod_timestamp: Option<String>,
137 /// Which artifact type to package: "binary" (default) or "appbundle".
138 #[serde(rename = "use")]
139 pub use_: Option<String>,
140 /// Minimum macOS version (e.g. "10.13"). Forwarded to `pkgbuild --min-os-version`.
141 pub min_os_version: Option<String>,
142 /// Skip this PKG config. Accepts bool or template string.
143 /// Accepts the legacy `disable:` spelling via serde alias for back-compat
144 /// with imported configs.
145 #[serde(
146 default,
147 alias = "disable",
148 deserialize_with = "deserialize_string_or_bool_opt"
149 )]
150 pub skip: Option<StringOrBool>,
151 /// Template-conditional: skip this PKG config if rendered result is "false"
152 /// or empty. Render failure hard-errors (not silent-skip).
153 #[serde(rename = "if")]
154 pub if_condition: Option<String>,
155}
156
157// ---------------------------------------------------------------------------
158// NsisConfig (Windows NSIS installer)
159// ---------------------------------------------------------------------------
160
161#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
162#[serde(default, deny_unknown_fields)]
163pub struct NsisConfig {
164 /// Unique identifier for this NSIS config.
165 pub id: Option<String>,
166 /// Build IDs to include. Empty means all builds.
167 pub ids: Option<Vec<String>>,
168 /// Output installer filename (supports templates).
169 pub name: Option<String>,
170 /// Path to the NSIS script template (.nsi). Goes through template engine.
171 pub script: Option<String>,
172 /// Additional files to include alongside the installer (glob or {glob, name_template}).
173 pub extra_files: Option<Vec<ExtraFileSpec>>,
174 /// Extra files whose contents are rendered through the template engine before inclusion.
175 /// Unlike `extra_files` which copy as-is, template variables like `{{ Tag }}` are expanded.
176 /// Conditional-skip gate.
177 pub templated_extra_files: Option<Vec<TemplatedExtraFile>>,
178 /// Skip this NSIS config. Accepts bool or template string.
179 /// Accepts the legacy `disable:` spelling via serde alias for back-compat
180 /// with imported configs.
181 #[serde(
182 default,
183 alias = "disable",
184 deserialize_with = "deserialize_string_or_bool_opt"
185 )]
186 pub skip: Option<StringOrBool>,
187 /// amd64 microarchitecture variant filter (`v1` / `v2` / `v3` / `v4`),
188 /// set via the `amd64_variant:` key. When set, only artifacts with the
189 /// matching `amd64_variant` metadata are included. The legacy `goamd64:`
190 /// spelling is accepted via serde alias for back-compat with imported
191 /// configs.
192 #[serde(alias = "goamd64")]
193 pub amd64_variant: Option<String>,
194 /// Remove source archives from artifacts, keeping only the installer.
195 pub replace: Option<bool>,
196 /// Output timestamp for reproducible builds.
197 pub mod_timestamp: Option<String>,
198 /// Template-conditional: skip this NSIS config if rendered result is "false"
199 /// or empty. Render failure hard-errors (not silent-skip).
200 #[serde(rename = "if")]
201 pub if_condition: Option<String>,
202}
203
204// ---------------------------------------------------------------------------
205// AppBundleConfig (macOS .app bundle)
206// ---------------------------------------------------------------------------
207
208#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
209#[serde(default, deny_unknown_fields)]
210pub struct AppBundleConfig {
211 /// Unique identifier for this app bundle config.
212 pub id: Option<String>,
213 /// Build IDs to include. Empty means all builds.
214 pub ids: Option<Vec<String>>,
215 /// Output .app bundle name (supports templates).
216 pub name: Option<String>,
217 /// Path to .icns icon file for the app bundle (supports templates).
218 pub icon: Option<String>,
219 /// Bundle identifier in reverse-DNS notation (e.g. `com.example.myapp`). Required.
220 ///
221 /// Must be set explicitly; there is no default. A missing value is caught at
222 /// validation time with an actionable error message.
223 pub bundle: Option<String>,
224 /// Additional files to include in the bundle (src/dst/info objects or glob strings).
225 pub extra_files: Option<Vec<ArchiveFileSpec>>,
226 /// Extra files whose contents are rendered through the template engine before inclusion.
227 /// Unlike `extra_files` which copy as-is, template variables like `{{ Tag }}` are expanded.
228 /// Conditional-skip gate.
229 pub templated_extra_files: Option<Vec<TemplatedExtraFile>>,
230 /// Output timestamp for reproducible builds.
231 pub mod_timestamp: Option<String>,
232 /// Remove source archives from artifacts, keeping only the app bundle.
233 ///
234 /// Anodizer-additive: `replace:` on `app_bundles`.
235 pub replace: Option<bool>,
236 /// Minimum macOS version written to `LSMinimumSystemVersion` in `Info.plist`.
237 ///
238 /// Defaults to `"10.13"` when unset. Symmetry with `PkgConfig.min_os_version`.
239 pub min_os_version: Option<String>,
240 /// Skip this app bundle config. Accepts bool or template string.
241 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
242 pub skip: Option<StringOrBool>,
243 /// Template-conditional: skip this app bundle config if rendered result is
244 /// "false" or empty. Render failure hard-errors (not silent-skip).
245 #[serde(rename = "if")]
246 pub if_condition: Option<String>,
247}
248
249// ---------------------------------------------------------------------------
250// FlatpakConfig (Linux Flatpak bundle)
251// ---------------------------------------------------------------------------
252
253#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
254#[serde(default, deny_unknown_fields)]
255pub struct FlatpakConfig {
256 /// Unique identifier for this Flatpak config.
257 pub id: Option<String>,
258 /// Build IDs to include. Empty means all builds.
259 pub ids: Option<Vec<String>>,
260 /// Output .flatpak filename (supports templates).
261 pub name_template: Option<String>,
262 /// Flatpak application ID in reverse-DNS notation (e.g. org.example.MyApp). Required.
263 pub app_id: Option<String>,
264 /// Flatpak runtime (e.g. org.freedesktop.Platform). Required.
265 pub runtime: Option<String>,
266 /// Flatpak runtime version (e.g. "24.08"). Required.
267 pub runtime_version: Option<String>,
268 /// Flatpak SDK (e.g. org.freedesktop.Sdk). Required.
269 pub sdk: Option<String>,
270 /// Command to run inside the Flatpak sandbox. Defaults to first binary name.
271 pub command: Option<String>,
272 /// Sandbox permissions (e.g. --share=network, --socket=x11).
273 pub finish_args: Option<Vec<String>>,
274 /// Additional files to include alongside the binary (glob or {glob, name_template}).
275 pub extra_files: Option<Vec<ExtraFileSpec>>,
276 /// Remove source archives from artifacts, keeping only the Flatpak bundle.
277 pub replace: Option<bool>,
278 /// Output timestamp for reproducible builds.
279 pub mod_timestamp: Option<String>,
280 /// Skip this Flatpak config. Accepts bool or template string.
281 /// Accepts the legacy `disable:` spelling via serde alias for back-compat.
282 #[serde(
283 alias = "disable",
284 deserialize_with = "deserialize_string_or_bool_opt",
285 default
286 )]
287 pub skip: Option<StringOrBool>,
288}
289
290#[cfg(test)]
291mod goamd64_alias_tests {
292 use super::*;
293
294 #[test]
295 fn dmg_goamd64_alias_parses_into_amd64_variant() {
296 let dmg: DmgConfig = serde_yaml_ng::from_str("goamd64: v3").unwrap();
297 assert_eq!(dmg.amd64_variant.as_deref(), Some("v3"));
298 }
299
300 #[test]
301 fn dmg_canonical_amd64_variant_still_parses() {
302 let dmg: DmgConfig = serde_yaml_ng::from_str("amd64_variant: v2").unwrap();
303 assert_eq!(dmg.amd64_variant.as_deref(), Some("v2"));
304 }
305
306 #[test]
307 fn msi_goamd64_alias_parses_into_amd64_variant() {
308 let msi: MsiConfig = serde_yaml_ng::from_str("goamd64: v4").unwrap();
309 assert_eq!(msi.amd64_variant.as_deref(), Some("v4"));
310 }
311
312 #[test]
313 fn nsis_goamd64_alias_parses_into_amd64_variant() {
314 let nsis: NsisConfig = serde_yaml_ng::from_str("goamd64: v1").unwrap();
315 assert_eq!(nsis.amd64_variant.as_deref(), Some("v1"));
316 }
317}