Skip to main content

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}