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}