anodizer_core/config/publishers/homebrew.rs
1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use super::super::{StringOrBool, deserialize_string_or_bool_opt};
7use super::{CommitAuthorConfig, RepositoryConfig};
8
9// ---------------------------------------------------------------------------
10// HomebrewConfig / ScoopConfig / TapConfig / BucketConfig
11// ---------------------------------------------------------------------------
12
13#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
14#[serde(default, deny_unknown_fields)]
15pub struct HomebrewConfig {
16 /// Unified repository config with branch, token, PR, git SSH support.
17 /// (Replaces the legacy `tap: TapConfig` owner/name-only form.)
18 pub repository: Option<RepositoryConfig>,
19 /// Commit author with optional signing.
20 pub commit_author: Option<CommitAuthorConfig>,
21 /// Formula directory in the tap (e.g. "Formula").
22 pub directory: Option<String>,
23 /// Override the formula name (default: crate name).
24 pub name: Option<String>,
25 /// Short description of the formula (shown in `brew info`).
26 pub description: Option<String>,
27 /// SPDX license identifier (e.g., "MIT", "Apache-2.0").
28 pub license: Option<String>,
29 /// Ruby `install` block content for the formula.
30 pub install: Option<String>,
31 /// Additional install commands appended after the main install block.
32 pub extra_install: Option<String>,
33 /// Post-install commands (separate `def post_install` block in formula).
34 pub post_install: Option<String>,
35 /// Ruby `test` block content for the formula (run by `brew test`).
36 pub test: Option<String>,
37 /// Project homepage URL. Falls back to the GitHub release URL when unset.
38 pub homepage: Option<String>,
39 /// Package dependencies (e.g. `openssl`, `libgit2`).
40 pub dependencies: Option<Vec<HomebrewDependency>>,
41 /// Conflicting formula names with optional reason.
42 pub conflicts: Option<Vec<HomebrewConflict>>,
43 /// Post-install user-facing notes shown by `brew info`.
44 pub caveats: Option<String>,
45 /// Skip publishing the formula. `"true"` always skips; `"auto"` skips
46 /// for prerelease versions. Accepts bool or template string.
47 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
48 pub skip_upload: Option<StringOrBool>,
49 /// Custom commit message template. Rendered via Tera with the standard
50 /// release template variables (`ProjectName`, `Tag`, `Version`, etc.).
51 /// Default: `"Brew formula update for {{ ProjectName }} version {{ Tag }}"`
52 /// (set in `crates/stage-publish/src/homebrew.rs::default_commit_msg_template`).
53 pub commit_msg_template: Option<String>,
54 // Legacy flat `commit_author_name` / `commit_author_email` fields are
55 // gone; use the structured `commit_author: { name, email, signing }`.
56 /// Build IDs filter: only include artifacts whose `id` is in this list.
57 pub ids: Option<Vec<String>>,
58 /// Custom URL template for download URLs (overrides release URL).
59 pub url_template: Option<String>,
60 /// HTTP headers to include in download requests (e.g. for private repos).
61 pub url_headers: Option<Vec<String>>,
62 /// Custom download strategy class name (e.g. `:using => GitHubPrivateRepositoryReleaseDownloadStrategy`).
63 pub download_strategy: Option<String>,
64 /// Ruby `require` statement for custom download strategies.
65 pub custom_require: Option<String>,
66 /// Custom Ruby code block inserted into the formula class body.
67 pub custom_block: Option<String>,
68 /// Launchd plist content for `brew services`.
69 pub plist: Option<String>,
70 /// Homebrew service block content (alternative to plist).
71 pub service: Option<String>,
72 /// Manpage file paths to install into the formula's `man1` (e.g.
73 /// `["mytool.1"]`). Each entry renders a `man1.install "<path>"` line in
74 /// the install block, mirroring real Rust-CLI formulae (ripgrep, fd, bat).
75 /// A path ending in `.N` (where N is 1–8) routes to the matching `manN`
76 /// section; anything else defaults to `man1`.
77 pub manpages: Option<Vec<String>>,
78 /// Prebuilt shell-completion file paths to install. When set, the formula
79 /// emits `bash_completion.install "<path>"` / `zsh_completion.install` /
80 /// `fish_completion.install` in its install block — the form used when the
81 /// archive ships ready-made completion files.
82 pub completions: Option<HomebrewCaskCompletions>,
83 /// Generate completions by running the installed binary at install time.
84 /// Renders the modern homebrew-core idiom
85 /// `generate_completions_from_executable(bin/"<exe>", ...)` in the install
86 /// block. Preferred over `completions` when the binary can emit its
87 /// own completions; the two are independent and may both be set.
88 pub generate_completions_from_executable: Option<HomebrewCaskGeneratedCompletions>,
89 /// `livecheck` stanza configuration for the formula. When unset, a binary
90 /// tap formula emits `livecheck { skip "Auto-generated on release." }` to
91 /// match the cask (the archive URL/sha are rewritten on every release, so
92 /// `brew livecheck` cannot meaningfully poll). Set `strategy:` /
93 /// `regex:`/`url:` to opt into active version detection instead.
94 pub livecheck: Option<HomebrewLivecheck>,
95 /// Homebrew Cask configuration (macOS .app bundles).
96 pub cask: Option<HomebrewCaskConfig>,
97 /// amd64 microarchitecture variant filter (e.g. "v1", "v2", "v3", "v4").
98 /// Only artifacts matching this variant are included. Default: "v1".
99 pub amd64_variant: Option<String>,
100 /// ARM version filter (e.g. "6", "7"). Only artifacts matching this
101 /// variant are included.
102 pub arm_variant: Option<String>,
103 /// Override whether this publisher failing should fail the overall release.
104 ///
105 /// Default: `false` — a failure here is logged but does not abort the release.
106 /// Set to `true` to fail the release on any error.
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub required: Option<bool>,
109 /// Template-conditional gate: when the rendered result is falsy
110 /// (`"false"` / `"0"` / `"no"` / empty), the Homebrew publisher is
111 /// skipped. Render failure hard-errors. Config key: `brews[].if:`.
112 #[serde(rename = "if")]
113 pub if_condition: Option<String>,
114 /// When `true`, a triggered rollback leaves this publisher's work in
115 /// place rather than attempting to undo it. Default `false`.
116 pub retain_on_rollback: Option<bool>,
117}
118
119#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
120#[serde(default, deny_unknown_fields)]
121pub struct HomebrewDependency {
122 /// Homebrew formula name of the dependency.
123 pub name: String,
124 /// Restrict to a specific OS: `"mac"` or `"linux"`.
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub os: Option<String>,
127 /// Dependency type, e.g. `"optional"`.
128 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
129 pub dep_type: Option<String>,
130 /// Version constraint for the dependency (e.g. `">= 1.1"`).
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub version: Option<String>,
133}
134
135/// A Homebrew conflict entry, supporting both a bare name string and a
136/// structured object with an optional `because` reason.
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
138#[serde(untagged)]
139pub enum HomebrewConflict {
140 /// Just the formula name (e.g. `"other-tool"`).
141 Name(String),
142 /// Name with reason (e.g. `{name: "other-tool", because: "both install a bin/foo binary"}`).
143 WithReason {
144 name: String,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 because: Option<String>,
147 },
148}
149
150impl HomebrewConflict {
151 pub fn name(&self) -> &str {
152 match self {
153 Self::Name(n) => n,
154 Self::WithReason { name, .. } => name,
155 }
156 }
157 pub fn because(&self) -> Option<&str> {
158 match self {
159 Self::Name(_) => None,
160 Self::WithReason { because, .. } => because.as_deref(),
161 }
162 }
163}
164
165/// `livecheck` stanza configuration for a Homebrew formula.
166///
167/// Default (the struct absent from config): the formula emits a
168/// `livecheck { skip "Auto-generated on release." }` block — correct for a
169/// binary tap whose archive URL/sha256 are rewritten every release. To opt
170/// into active version polling, set `skip: false` and a `strategy:` (and
171/// optionally `url:` / `regex:`):
172///
173/// ```yaml
174/// livecheck:
175/// strategy: github_latest
176/// ```
177///
178/// renders:
179///
180/// ```ruby
181/// livecheck do
182/// url :stable
183/// strategy :github_latest
184/// end
185/// ```
186#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
187#[serde(default, deny_unknown_fields)]
188pub struct HomebrewLivecheck {
189 /// Force the `skip "<reason>"` form even when a strategy is set, or set to
190 /// `false` to opt out of the default skip and emit an active livecheck.
191 /// When `None` (the default), the formula skips iff no `strategy`/`url`
192 /// is configured.
193 pub skip: Option<bool>,
194 /// Reason text for the `skip "<reason>"` form.
195 /// Default: `"Auto-generated on release."`.
196 pub skip_reason: Option<String>,
197 /// `livecheck` strategy symbol (e.g. `github_latest`, `git`, `page_match`).
198 /// Rendered as a Ruby symbol: `strategy :github_latest`.
199 pub strategy: Option<String>,
200 /// `url` for the livecheck. Accepts a Ruby symbol shorthand
201 /// (`stable` / `head` / `homepage` → `url :stable`) or a literal URL
202 /// string (`url "https://..."`). Defaults to `:stable` when a strategy
203 /// is set without an explicit url.
204 pub url: Option<String>,
205 /// `regex(...)` argument for `page_match`-style strategies. Emitted
206 /// verbatim inside `regex(...)`, so it is raw Ruby (e.g. `%r{v(\d+\.\d+)}i`).
207 pub regex: Option<String>,
208}
209
210/// Unified Homebrew Cask configuration.
211///
212/// Used at both call-sites:
213/// - `homebrew_casks:` — top-level array; carries `repository`,
214/// `commit_author`, `directory`, `ids`, `url`, structured `uninstall`/`zap`, etc.
215/// - `crates[].publish.homebrew_cask:` — per-crate override; same shape, with
216/// `url_template` as the simpler URL alternative.
217///
218/// Fields from both original types are present; any field may be `None` at either
219/// call-site. The union avoids a two-type bifurcation while keeping both axes.
220#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
221#[serde(default, deny_unknown_fields)]
222pub struct HomebrewCaskConfig {
223 // ----- Identity -----
224 /// Cask name (default: crate / project name).
225 pub name: Option<String>,
226 /// Alternative cask names (aliases).
227 pub alternative_names: Option<Vec<String>>,
228
229 // ----- Tap repository (top-level axis) -----
230 /// Unified repository config for the Homebrew tap.
231 pub repository: Option<RepositoryConfig>,
232 /// Commit author with optional signing.
233 pub commit_author: Option<CommitAuthorConfig>,
234 /// Custom commit message template.
235 /// Default: "Brew cask update for {{ ProjectName }} version {{ Tag }}"
236 pub commit_msg_template: Option<String>,
237 /// Subdirectory in the tap repo for cask placement (default: "Casks").
238 pub directory: Option<String>,
239
240 // ----- Artifact selection -----
241 /// Build IDs filter: only include artifacts from builds whose `id` is in this list.
242 pub ids: Option<Vec<String>>,
243
244 // ----- Download URL -----
245 /// Simple URL template for the .dmg/.zip download (per-crate shorthand).
246 ///
247 /// Cannot be combined with `url.template:` — set one or the other.
248 /// If both are present, config validation rejects the config at parse time.
249 /// Use `url:` for the structured form (verified domain, custom headers, etc.)
250 /// or `url_template:` for a bare string shorthand — never both simultaneously.
251 pub url_template: Option<String>,
252 /// Structured download URL configuration (top-level axis).
253 pub url: Option<HomebrewCaskURL>,
254
255 // ----- macOS bundle -----
256 /// macOS .app bundle name (e.g. "MyApp.app").
257 pub app: Option<String>,
258 /// Binary stubs to create in /usr/local/bin.
259 ///
260 /// Each entry is either a bare string (`"my-cli"` → emits
261 /// `binary "my-cli"`) or a structured `{ name, target }` object
262 /// (`{ name: "my-cli", target: "mycli" }` → emits
263 /// `binary "my-cli", target: "mycli"`). The `target:` form mirrors
264 /// the Homebrew Ruby cask DSL for binary renames — without it, a
265 /// wrapped binary installs at the wrong path.
266 /// Cask binary entry.
267 pub binaries: Option<Vec<HomebrewCaskBinary>>,
268 /// Deprecated singular spelling of [`Self::binaries`]. The upstream
269 /// replaced `binary: foo` with `binaries: [foo]`; this field captures the
270 /// legacy spelling so imported configs keep parsing.
271 /// [`apply_homebrew_cask_legacy_singulars`](super::super::apply_homebrew_cask_legacy_singulars)
272 /// folds the value into [`Self::binaries`] at config-load time and emits
273 /// a one-time deprecation warning per occurrence. The field is excluded
274 /// from serialization so a round-tripped config emits only the canonical
275 /// plural form.
276 #[serde(default, rename = "binary", skip_serializing)]
277 pub legacy_binary: Option<String>,
278
279 // ----- Metadata -----
280 /// Cask description.
281 pub description: Option<String>,
282 /// Project homepage URL.
283 pub homepage: Option<String>,
284 /// License identifier (SPDX).
285 pub license: Option<String>,
286 /// Custom caveats shown after install.
287 pub caveats: Option<String>,
288
289 // ----- Ruby block -----
290 /// Arbitrary Ruby code inserted into the cask block.
291 pub custom_block: Option<String>,
292 /// Homebrew service definition.
293 pub service: Option<String>,
294
295 // ----- Livecheck -----
296 /// `livecheck` stanza configuration for the cask. When unset, the cask
297 /// emits `livecheck do\n skip "Auto-generated on release."\nend` (a
298 /// binary cask's download URL/sha256 are rewritten on every release, so
299 /// `brew livecheck` has nothing stable to poll). Set `strategy:` /
300 /// `url:` / `regex:` (with `skip: false`) to opt into active version
301 /// detection — the same shape a Homebrew cask `livecheck do … end`
302 /// block accepts. Reuses the formula `livecheck` config type.
303 pub livecheck: Option<HomebrewLivecheck>,
304
305 // ----- Completions / manpages -----
306 /// Manual page references to install.
307 pub manpages: Option<Vec<String>>,
308 /// Deprecated singular spelling of [`Self::manpages`]. The upstream replaced
309 /// `manpage: foo.1` with `manpages: [foo.1]`; this field captures the
310 /// legacy spelling so imported configs keep parsing.
311 /// [`apply_homebrew_cask_legacy_singulars`](super::super::apply_homebrew_cask_legacy_singulars)
312 /// folds the value into [`Self::manpages`] at config-load time and emits
313 /// a one-time deprecation warning per occurrence. The field is excluded
314 /// from serialization so a round-tripped config emits only the canonical
315 /// plural form.
316 #[serde(default, rename = "manpage", skip_serializing)]
317 pub legacy_manpage: Option<String>,
318 /// Shell completion definitions.
319 pub completions: Option<HomebrewCaskCompletions>,
320 /// Auto-generate shell completions from an executable.
321 pub generate_completions_from_executable: Option<HomebrewCaskGeneratedCompletions>,
322
323 // ----- Dependencies / conflicts -----
324 /// Cask dependencies (other casks or formulae).
325 pub dependencies: Option<Vec<HomebrewCaskDependencyEntry>>,
326 /// Conflicting casks or formulae.
327 pub conflicts: Option<Vec<HomebrewCaskConflictEntry>>,
328
329 // ----- Lifecycle hooks -----
330 /// Pre/post install/uninstall hooks.
331 pub hooks: Option<HomebrewCaskHooks>,
332
333 // ----- Uninstall / zap -----
334 /// Structured uninstall stanza configuration.
335 pub uninstall: Option<HomebrewCaskUninstall>,
336 /// Deep uninstall (zap) stanza configuration.
337 pub zap: Option<HomebrewCaskUninstall>,
338
339 // ----- Publishing control -----
340 /// Skip publishing the cask. `"true"` always skips; `"auto"` skips
341 /// for prerelease versions. Accepts bool or template string.
342 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
343 pub skip_upload: Option<StringOrBool>,
344 /// When true, force-push the updated cask file to the existing PR branch
345 /// when a PR for the same head branch already exists. The PR content is
346 /// updated in place rather than creating a duplicate. When false (default),
347 /// the push is skipped and a warning is emitted so the operator sees that
348 /// the publisher did not update the PR.
349 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
350 pub update_existing_pr: Option<StringOrBool>,
351 /// Override whether this publisher failing should fail the overall release.
352 ///
353 /// Default: `false` — a failure here is logged but does not abort the release.
354 /// Set to `true` to fail the release on any error.
355 #[serde(default, skip_serializing_if = "Option::is_none")]
356 pub required: Option<bool>,
357 /// Template-conditional gate: when the rendered result is falsy
358 /// (`"false"` / `"0"` / `"no"` / empty), the Homebrew Cask config is
359 /// skipped. Render failure hard-errors. Config key: `homebrew_casks[].if:`.
360 #[serde(rename = "if")]
361 pub if_condition: Option<String>,
362 /// When `true`, a triggered rollback leaves this publisher's work in
363 /// place rather than attempting to undo it. Default `false`.
364 pub retain_on_rollback: Option<bool>,
365}
366
367/// Structured URL configuration for Homebrew Cask downloads.
368#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
369#[serde(default, deny_unknown_fields)]
370pub struct HomebrewCaskURL {
371 /// URL template for the download.
372 pub template: Option<String>,
373 /// Verification string (domain shown to user).
374 pub verified: Option<String>,
375 /// Custom downloader (e.g. `:homebrew_curl`, `:post`).
376 pub using: Option<String>,
377 /// HTTP cookies for the download.
378 pub cookies: Option<HashMap<String, String>>,
379 /// Referer header for the download.
380 pub referer: Option<String>,
381 /// Custom HTTP headers.
382 pub headers: Option<Vec<String>>,
383 /// Custom user agent string.
384 pub user_agent: Option<String>,
385 /// POST data for form submissions.
386 pub data: Option<HashMap<String, String>>,
387}
388
389/// Structured uninstall/zap configuration for Homebrew Cask.
390/// Used for both `uninstall` and `zap` stanzas.
391#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
392#[serde(default, deny_unknown_fields)]
393pub struct HomebrewCaskUninstall {
394 /// Launch daemon/agent identifiers to stop.
395 pub launchctl: Option<Vec<String>>,
396 /// Application bundle IDs to quit.
397 pub quit: Option<Vec<String>>,
398 /// Login item names to remove.
399 pub login_item: Option<Vec<String>>,
400 /// File paths to delete.
401 pub delete: Option<Vec<String>>,
402 /// File paths to trash (preserves app state).
403 pub trash: Option<Vec<String>>,
404}
405
406/// Pre/post install/uninstall hooks for Homebrew Cask.
407#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
408#[serde(default, deny_unknown_fields)]
409pub struct HomebrewCaskHooks {
410 /// Pre-install/uninstall hooks.
411 pub pre: Option<HomebrewCaskHook>,
412 /// Post-install/uninstall hooks.
413 pub post: Option<HomebrewCaskHook>,
414}
415
416/// Individual hook for install/uninstall phases.
417#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
418#[serde(default, deny_unknown_fields)]
419pub struct HomebrewCaskHook {
420 /// Ruby code for preflight/postflight during install.
421 pub install: Option<String>,
422 /// Ruby code for uninstall_preflight/uninstall_postflight.
423 pub uninstall: Option<String>,
424}
425
426/// Shell completion file paths for Homebrew Cask.
427#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
428#[serde(default, deny_unknown_fields)]
429pub struct HomebrewCaskCompletions {
430 /// Path to bash completion file.
431 pub bash: Option<String>,
432 /// Path to zsh completion file.
433 pub zsh: Option<String>,
434 /// Path to fish completion file.
435 pub fish: Option<String>,
436}
437
438/// Cask `binary` stanza entry.
439///
440/// Two shapes accepted in YAML:
441/// - bare string — `"my-cli"` → renders `binary "my-cli"`.
442/// - `{ name, target }` object — `{ name: "my-cli", target: "mycli" }`
443/// → renders `binary "my-cli", target: "mycli"`. The `target:` form is
444/// the Homebrew Ruby cask DSL rename: install the symlink at
445/// `/usr/local/bin/<target>` instead of `/usr/local/bin/<name>`.
446#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
447#[serde(untagged)]
448pub enum HomebrewCaskBinary {
449 /// Bare binary name. Equivalent to `{ name: "<n>", target: None }`.
450 Name(String),
451 /// Structured `{ name, target }` rename form.
452 WithTarget {
453 /// Path inside the .app bundle (e.g. `"my-cli"`).
454 name: String,
455 /// Optional rename target — the symlink name in `/usr/local/bin`.
456 /// When `None`, the symlink uses `name`.
457 #[serde(skip_serializing_if = "Option::is_none")]
458 target: Option<String>,
459 },
460}
461
462impl HomebrewCaskBinary {
463 /// The binary name (the path inside the .app bundle).
464 pub fn name(&self) -> &str {
465 match self {
466 Self::Name(n) => n,
467 Self::WithTarget { name, .. } => name,
468 }
469 }
470 /// The optional rename target. `None` for bare-string entries and for
471 /// `{ name, target }` objects without `target` set.
472 pub fn target(&self) -> Option<&str> {
473 match self {
474 Self::Name(_) => None,
475 Self::WithTarget { target, .. } => target.as_deref(),
476 }
477 }
478}
479
480/// Cask dependency (on another cask or formula).
481#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
482#[serde(default, deny_unknown_fields)]
483pub struct HomebrewCaskDependencyEntry {
484 /// Dependent cask name.
485 pub cask: Option<String>,
486 /// Dependent formula name.
487 pub formula: Option<String>,
488}
489
490/// Cask conflict (with another cask or formula).
491#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
492#[serde(default, deny_unknown_fields)]
493pub struct HomebrewCaskConflictEntry {
494 /// Conflicting cask name.
495 pub cask: Option<String>,
496 /// Conflicting formula name (deprecated by Homebrew).
497 pub formula: Option<String>,
498}
499
500/// Auto-generate shell completions from an executable.
501#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
502#[serde(default, deny_unknown_fields)]
503pub struct HomebrewCaskGeneratedCompletions {
504 /// Binary to generate completions from.
505 pub executable: Option<String>,
506 /// Arguments to pass to the executable.
507 pub args: Option<Vec<String>>,
508 /// Base name for completion files.
509 pub base_name: Option<String>,
510 /// Shell completion framework type (arg, clap, click, cobra, flag, none, typer).
511 pub shell_parameter_format: Option<String>,
512 /// Target shells (bash, zsh, fish, pwsh).
513 pub shells: Option<Vec<String>>,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
517#[serde(default, deny_unknown_fields)]
518pub struct ScoopConfig {
519 /// Unified repository config with branch, token, PR, git SSH support.
520 /// (Replaces the legacy `bucket: BucketConfig` owner/name-only form.)
521 pub repository: Option<RepositoryConfig>,
522 /// Commit author with optional signing.
523 pub commit_author: Option<CommitAuthorConfig>,
524 /// Override the manifest name (default: crate name).
525 pub name: Option<String>,
526 /// Subdirectory in the bucket repo for manifest placement.
527 pub directory: Option<String>,
528 /// Short description of the package (shown in `scoop info`).
529 pub description: Option<String>,
530 /// SPDX license identifier (e.g., "MIT", "Apache-2.0").
531 pub license: Option<String>,
532 /// Project homepage URL. Falls back to the GitHub-derived URL when unset.
533 pub homepage: Option<String>,
534 /// Data paths persisted between Scoop updates.
535 pub persist: Option<Vec<String>>,
536 /// Application dependencies (other Scoop packages).
537 pub depends: Option<Vec<String>>,
538 /// Commands to run before installation.
539 pub pre_install: Option<Vec<String>>,
540 /// Commands to run after installation.
541 pub post_install: Option<Vec<String>>,
542 /// Start menu shortcuts as `[executable, label]` pairs.
543 pub shortcuts: Option<Vec<Vec<String>>>,
544 /// Scoop `checkver` strategy used by bucket maintainers to detect new
545 /// releases. Defaults to `"github"` (derived from the configured GitHub
546 /// repo) — `ScoopInstaller/Main` requires checkver for automated-update
547 /// PRs. Override with a homepage regex when GitHub release detection is
548 /// not appropriate.
549 ///
550 /// Example: `checkver: "github"` or `checkver: "v([\\d.]+)"`.
551 pub checkver: Option<String>,
552 /// Skip publishing the manifest. `"true"` always skips; `"auto"` skips
553 /// for prerelease versions. Accepts bool or template string.
554 #[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
555 pub skip_upload: Option<StringOrBool>,
556 /// Custom commit message template.
557 pub commit_msg_template: Option<String>,
558 // Use the structured `commit_author: { name, email, signing }` form for
559 // commit author identity (legacy flat `commit_author_name` /
560 // `commit_author_email` fields are not accepted).
561 /// Build IDs filter: only include artifacts whose `id` is in this list.
562 pub ids: Option<Vec<String>>,
563 /// Custom URL template for download URLs (overrides release URL).
564 pub url_template: Option<String>,
565 /// Artifact selection: "archive" (default), "msi", or "nsis".
566 #[serde(rename = "use")]
567 pub use_artifact: Option<String>,
568 /// amd64 microarchitecture variant filter (e.g. "v1", "v2", "v3", "v4").
569 /// Only artifacts matching this variant are included. Default: "v1".
570 pub amd64_variant: Option<String>,
571 /// Override whether this publisher failing should fail the overall release.
572 ///
573 /// Default: `false` — a failure here is logged but does not abort the release.
574 /// Set to `true` to fail the release on any error.
575 #[serde(default, skip_serializing_if = "Option::is_none")]
576 pub required: Option<bool>,
577 /// Template-conditional gate: when the rendered result is falsy
578 /// (`"false"` / `"0"` / `"no"` / empty), the Scoop publisher is
579 /// skipped. Render failure hard-errors. Config key: `scoop[].if:`.
580 #[serde(rename = "if")]
581 pub if_condition: Option<String>,
582 /// When `true`, a triggered rollback leaves this publisher's work in
583 /// place rather than attempting to undo it. Default `false`.
584 pub retain_on_rollback: Option<bool>,
585}
586
587// `TapConfig` / `BucketConfig` (legacy {owner, name}-only repo types) live
588// nowhere — every publisher now carries `repository: RepositoryConfig`
589// with the broader feature set (token / branch / git SSH / pull_request).