Skip to main content

anodizer_core/config/
nfpm.rs

1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use super::{FileInfo, StringOrU32};
7
8// ---------------------------------------------------------------------------
9// NfpmConfig
10// ---------------------------------------------------------------------------
11
12#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
13#[serde(default)]
14pub struct NfpmConfig {
15    /// Unique identifier for cross-referencing this nFPM config.
16    pub id: Option<String>,
17    /// Package name (defaults to crate name).
18    pub package_name: Option<String>,
19    /// Package formats to produce: deb, rpm, apk, archlinux (at least one required).
20    pub formats: Vec<String>,
21    /// Package vendor name.
22    pub vendor: Option<String>,
23    /// Project homepage URL.
24    pub homepage: Option<String>,
25    /// Package maintainer in "Name <email>" format.
26    pub maintainer: Option<String>,
27    /// Package description (multiline supported).
28    pub description: Option<String>,
29    /// SPDX license identifier (e.g., "MIT", "Apache-2.0").
30    pub license: Option<String>,
31    /// Installation directory for binaries (default: /usr/bin).
32    pub bindir: Option<String>,
33    /// Files to include in the package beyond the main binary.
34    pub contents: Option<Vec<NfpmContent>>,
35    /// Runtime package dependencies keyed by format (e.g., {"deb": ["libc6"], "rpm": ["glibc"]}).
36    pub dependencies: Option<HashMap<String, Vec<String>>>,
37    /// Per-format setting overrides (e.g., {"deb": {compression: "xz"}}).
38    pub overrides: Option<HashMap<String, serde_json::Value>>,
39    /// Package filename template (supports templates).
40    pub file_name_template: Option<String>,
41    /// Package lifecycle scripts (preinstall, postinstall, preremove, postremove).
42    pub scripts: Option<NfpmScripts>,
43    /// Packages recommended (soft dependency) by this package.
44    pub recommends: Option<Vec<String>>,
45    /// Packages suggested (weaker than recommends) by this package.
46    pub suggests: Option<Vec<String>>,
47    /// Packages this package conflicts with.
48    pub conflicts: Option<Vec<String>>,
49    /// Packages this package replaces (for upgrade paths from old package names).
50    pub replaces: Option<Vec<String>>,
51    /// Virtual packages provided by this package.
52    pub provides: Option<Vec<String>>,
53    /// Build IDs filter: only include artifacts from builds whose `id` is in this list.
54    pub ids: Option<Vec<String>>,
55    /// amd64 microarchitecture variant filter (`["v1"]`, `["v2", "v3"]`, etc.).
56    /// When set, only amd64 binaries with `amd64_variant` matching one of the
57    /// listed values are included. Mirrors GoReleaser nfpm's
58    /// `goamd64: []string` (`pkg/config/config.go:711`, `nfpm.go:147`).
59    /// When unset, all amd64 variants are included (no filtering).
60    pub goamd64: Option<Vec<String>>,
61    /// Package epoch for versioning (integer as string).
62    pub epoch: Option<String>,
63    /// Package release number.
64    pub release: Option<String>,
65    /// Prerelease version suffix.
66    pub prerelease: Option<String>,
67    /// Version metadata (e.g. git commit hash).
68    pub version_metadata: Option<String>,
69    /// Package section (e.g. "utils", "devel").
70    pub section: Option<String>,
71    /// Package priority (e.g. "optional", "required").
72    pub priority: Option<String>,
73    /// Whether this is a meta-package (no files, only dependencies).
74    pub meta: Option<bool>,
75    /// File permission umask. Accepts a YAML int (`18`), an octal-prefixed
76    /// string (`"0o022"`), or a leading-zero octal string (`"022"`).
77    pub umask: Option<StringOrU32>,
78    /// Default modification time for files in the package.
79    pub mtime: Option<String>,
80    /// RPM-specific configuration.
81    pub rpm: Option<NfpmRpmConfig>,
82    /// Deb-specific configuration.
83    pub deb: Option<NfpmDebConfig>,
84    /// APK-specific configuration.
85    pub apk: Option<NfpmApkConfig>,
86    /// Archlinux-specific configuration.
87    pub archlinux: Option<NfpmArchlinuxConfig>,
88    /// IPK-specific configuration (OpenWrt packages).
89    pub ipk: Option<NfpmIpkConfig>,
90    /// CGo library installation directories (header, carchive, cshared).
91    pub libdirs: Option<NfpmLibdirs>,
92    /// Path to a YAML-format changelog file for deb/rpm packages.
93    pub changelog: Option<String>,
94    /// Template-conditional: skip this nfpm config if rendered result is "false" or empty.
95    /// (GoReleaser Pro v2.4+.)
96    #[serde(rename = "if")]
97    pub if_condition: Option<String>,
98    /// Extra file contents whose source files are Tera-rendered before packaging (GoReleaser Pro).
99    /// Each entry mirrors `contents`; the difference is that at stage time the file at `src` is
100    /// read, rendered through the template engine, written to a temp file, and then included
101    /// in the package at `dst` using the temp file as the real source. Useful for shipping
102    /// config files with templated values (version, commit, maintainer, etc.).
103    pub templated_contents: Option<Vec<NfpmContent>>,
104    /// Lifecycle scripts whose script-file bodies are Tera-rendered before packaging
105    /// (GoReleaser Pro). Each path is read, rendered through the template engine, written to
106    /// a temp file, and used as the real script. If a field is set on both `scripts` and
107    /// `templated_scripts`, the templated version wins.
108    pub templated_scripts: Option<NfpmScripts>,
109}
110
111/// Installation directories for CGo library outputs.
112///
113/// Controls where header files, static archives, and shared libraries
114/// are installed in the package.
115#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
116#[serde(default)]
117pub struct NfpmLibdirs {
118    /// Installation directory for C header files.
119    pub header: Option<String>,
120    /// Installation directory for carchive (.a) static libraries.
121    pub carchive: Option<String>,
122    /// Installation directory for cshared (.so / .dylib) shared libraries.
123    pub cshared: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
127#[serde(default)]
128pub struct NfpmScripts {
129    /// Path to script run before package installation.
130    pub preinstall: Option<String>,
131    /// Path to script run after package installation.
132    pub postinstall: Option<String>,
133    /// Path to script run before package removal.
134    pub preremove: Option<String>,
135    /// Path to script run after package removal.
136    pub postremove: Option<String>,
137}
138
139/// Backward-compatible alias — nFPM contents share the same `FileInfo` struct.
140pub type NfpmFileInfo = FileInfo;
141
142/// A single file/directory entry in an nFPM (or SRPM) package's `contents`
143/// list. SCH-8 (WAVE 5.4) merged the formerly-separate `NfpmContentConfig`
144/// (used for SRPM) into this struct — `source` / `destination` / `type` are
145/// accepted as aliases for `src` / `dst` / the renamed `type` so srpm-style
146/// keys still parse.
147///
148/// `Default` is intentionally **not** derived because `src` and `dst` are
149/// required fields with no meaningful defaults — forcing callers to provide
150/// them explicitly prevents accidentally packaging empty paths.
151#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
152pub struct NfpmContent {
153    /// Source path on the build machine (supports glob patterns).
154    pub src: String,
155    /// Destination path inside the package (absolute path).
156    pub dst: String,
157    /// Content entry type: "config", "config|noreplace", "doc", "dir", "symlink", "ghost", or empty for regular file.
158    #[serde(rename = "type")]
159    pub content_type: Option<String>,
160    /// File ownership and permission metadata.
161    pub file_info: Option<NfpmFileInfo>,
162    /// Per-packager filter: only include this content entry for the specified packager
163    /// (e.g. "deb", "rpm", "apk").
164    pub packager: Option<String>,
165    /// When true, expand template variables in the `src` and `dst` paths.
166    pub expand: Option<bool>,
167}
168
169// ---------------------------------------------------------------------------
170// nFPM format-specific configs
171// ---------------------------------------------------------------------------
172
173#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
174#[serde(default)]
175pub struct NfpmRpmConfig {
176    /// One-line package summary (RPM Summary tag).
177    pub summary: Option<String>,
178    /// RPM compression algorithm (e.g. "lzma", "gzip", "xz", "zstd").
179    pub compression: Option<String>,
180    /// RPM group classification (e.g. "System/Tools").
181    pub group: Option<String>,
182    /// RPM packager identity (e.g. "Build Team <build@example.com>").
183    pub packager: Option<String>,
184    /// Relocatable RPM prefix paths (e.g. ["/usr", "/etc"]).
185    pub prefixes: Option<Vec<String>>,
186    /// RPM signing configuration.
187    pub signature: Option<NfpmSignatureConfig>,
188    /// RPM-specific lifecycle scripts (pretrans/posttrans).
189    pub scripts: Option<NfpmRpmScripts>,
190    /// RPM BuildHost tag value.
191    pub build_host: Option<String>,
192}
193
194/// RPM-specific transaction scripts that run outside the normal install/remove lifecycle.
195#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
196#[serde(default)]
197pub struct NfpmRpmScripts {
198    /// Script to run before the RPM transaction begins.
199    pub pretrans: Option<String>,
200    /// Script to run after the RPM transaction completes.
201    pub posttrans: Option<String>,
202}
203
204impl NfpmRpmConfig {
205    /// Returns `true` when every field is `None` — the YAML section would be
206    /// empty and should be omitted.
207    pub fn is_empty(&self) -> bool {
208        self.summary.is_none()
209            && self.compression.is_none()
210            && self.group.is_none()
211            && self.packager.is_none()
212            && self.prefixes.is_none()
213            && self.signature.is_none()
214            && self.scripts.is_none()
215            && self.build_host.is_none()
216    }
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
220#[serde(default)]
221pub struct NfpmDebConfig {
222    /// Deb compression algorithm (e.g. "gzip", "xz", "zstd", "none").
223    pub compression: Option<String>,
224    /// Pre-dependency packages (stronger than Depends).
225    pub predepends: Option<Vec<String>>,
226    /// Deb trigger definitions.
227    pub triggers: Option<NfpmDebTriggers>,
228    /// Packages this package breaks (Breaks relationship).
229    pub breaks: Option<Vec<String>>,
230    /// Lintian overrides to embed in the package.
231    pub lintian_overrides: Option<Vec<String>>,
232    /// Deb signing configuration.
233    pub signature: Option<NfpmSignatureConfig>,
234    /// Additional control fields (e.g. Bugs, Built-Using).
235    pub fields: Option<HashMap<String, String>>,
236    /// Deb-specific maintainer scripts (rules, templates, config).
237    pub scripts: Option<NfpmDebScripts>,
238    /// amd64 microarchitecture variant propagated to nfpm's `deb.arch_variant`
239    /// (`v1`, `v2`, `v3`, `v4`). Auto-derived from artifact metadata's
240    /// `amd64_variant` when unset.
241    pub arch_variant: Option<String>,
242}
243
244/// Deb-specific maintainer scripts for package configuration and rules.
245#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
246#[serde(default)]
247pub struct NfpmDebScripts {
248    /// Path to debian/rules file.
249    pub rules: Option<String>,
250    /// Path to debian/templates file (debconf templates).
251    pub templates: Option<String>,
252    /// Path to debian/config script (debconf configuration).
253    pub config: Option<String>,
254}
255
256impl NfpmDebConfig {
257    /// Returns `true` when every field is `None` — the YAML section would be
258    /// empty and should be omitted.
259    pub fn is_empty(&self) -> bool {
260        self.compression.is_none()
261            && self.predepends.is_none()
262            && self.triggers.is_none()
263            && self.breaks.is_none()
264            && self.lintian_overrides.is_none()
265            && self.signature.is_none()
266            && self.fields.is_none()
267            && self.scripts.is_none()
268    }
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
272#[serde(default)]
273pub struct NfpmDebTriggers {
274    /// Deb interest triggers: package waits for these triggers to complete.
275    pub interest: Option<Vec<String>>,
276    /// Deb interest-await triggers: package waits with synchronous trigger processing.
277    pub interest_await: Option<Vec<String>>,
278    /// Deb interest-noawait triggers: package registers interest without waiting.
279    pub interest_noawait: Option<Vec<String>>,
280    /// Deb activate triggers: package activates these triggers after install.
281    pub activate: Option<Vec<String>>,
282    /// Deb activate-await triggers: activate and wait for synchronous trigger processing.
283    pub activate_await: Option<Vec<String>>,
284    /// Deb activate-noawait triggers: activate without waiting.
285    pub activate_noawait: Option<Vec<String>>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
289#[serde(default)]
290pub struct NfpmApkConfig {
291    /// APK signing configuration.
292    pub signature: Option<NfpmSignatureConfig>,
293    /// APK-specific lifecycle scripts (preupgrade/postupgrade).
294    pub scripts: Option<NfpmApkScripts>,
295}
296
297/// APK-specific upgrade lifecycle scripts.
298#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
299#[serde(default)]
300pub struct NfpmApkScripts {
301    /// Script to run before upgrading an existing package.
302    pub preupgrade: Option<String>,
303    /// Script to run after upgrading an existing package.
304    pub postupgrade: Option<String>,
305}
306
307impl NfpmApkConfig {
308    /// Returns `true` when every field is `None` — the YAML section would be
309    /// empty and should be omitted.
310    pub fn is_empty(&self) -> bool {
311        self.signature.is_none() && self.scripts.is_none()
312    }
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
316#[serde(default)]
317pub struct NfpmArchlinuxConfig {
318    /// Base package name for split packages.
319    pub pkgbase: Option<String>,
320    /// Packager identity (e.g. "Build Team <build@example.com>").
321    pub packager: Option<String>,
322    /// Archlinux-specific lifecycle scripts.
323    pub scripts: Option<NfpmArchlinuxScripts>,
324}
325
326impl NfpmArchlinuxConfig {
327    /// Returns `true` when every field is `None` — the YAML section would be
328    /// empty and should be omitted.
329    pub fn is_empty(&self) -> bool {
330        self.pkgbase.is_none() && self.packager.is_none() && self.scripts.is_none()
331    }
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
335#[serde(default)]
336pub struct NfpmArchlinuxScripts {
337    /// Script to run before upgrading an existing package.
338    pub preupgrade: Option<String>,
339    /// Script to run after upgrading an existing package.
340    pub postupgrade: Option<String>,
341}
342
343/// IPK (OpenWrt) package-specific configuration.
344#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
345#[serde(default)]
346pub struct NfpmIpkConfig {
347    /// ABI version string for the package.
348    pub abi_version: Option<String>,
349    /// Alternative file links managed by the update-alternatives system.
350    pub alternatives: Option<Vec<NfpmIpkAlternative>>,
351    /// Whether the package was automatically installed as a dependency.
352    pub auto_installed: Option<bool>,
353    /// Whether the package is essential for the system.
354    pub essential: Option<bool>,
355    /// Strong pre-dependencies that must be fully installed before this package.
356    pub predepends: Option<Vec<String>>,
357    /// Tags for categorizing the package.
358    pub tags: Option<Vec<String>>,
359    /// Additional control fields as key-value pairs.
360    pub fields: Option<HashMap<String, String>>,
361}
362
363impl NfpmIpkConfig {
364    /// Returns `true` when every field is `None` — the YAML section would be
365    /// empty and should be omitted.
366    pub fn is_empty(&self) -> bool {
367        self.abi_version.is_none()
368            && self.alternatives.is_none()
369            && self.auto_installed.is_none()
370            && self.essential.is_none()
371            && self.predepends.is_none()
372            && self.tags.is_none()
373            && self.fields.is_none()
374    }
375}
376
377/// An alternative file link for IPK's update-alternatives system.
378#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
379#[serde(default)]
380pub struct NfpmIpkAlternative {
381    /// Priority for alternative selection (higher wins).
382    pub priority: Option<i32>,
383    /// Target file path that the alternative points to.
384    pub target: Option<String>,
385    /// Symlink name in the alternatives directory.
386    pub link_name: Option<String>,
387}
388
389/// Unified signature configuration shared by nFPM (deb/rpm/apk) and SRPM
390/// packages — SRPM's surface is a strict subset, so a single struct covers
391/// both. The legacy SRPM `passphrase:` key is accepted as a serde alias
392/// for `key_passphrase:` so both spellings parse.
393///
394/// GR keeps three distinct signature types (`NFPMRPMSignature`,
395/// `NFPMDebSignature`, `NFPMAPKSignature`) with overlapping but slightly
396/// different fields. Anodizer's union here avoids the 3-struct cascade
397/// when 90% of fields overlap.
398#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
399#[serde(default)]
400pub struct NfpmSignatureConfig {
401    /// Path to the signing key file.
402    pub key_file: Option<String>,
403    /// Key ID to use for signing.
404    pub key_id: Option<String>,
405    /// Passphrase for the signing key. Falls back to `NFPM_PASSPHRASE` /
406    /// `SRPM_PASSPHRASE` env vars in their respective stages.
407    pub key_passphrase: Option<String>,
408    /// Public key name for APK signatures (defaults to `<maintainer email>.rsa.pub`).
409    pub key_name: Option<String>,
410    /// Signature type for deb packages: "origin", "maint", or "archive" (default: "origin").
411    #[serde(rename = "type")]
412    pub type_: Option<String>,
413}