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::{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}