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