Skip to main content

anodizer_core/config/
hooks.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Deserializer, Serialize};
3
4// ---------------------------------------------------------------------------
5// HooksConfig
6// ---------------------------------------------------------------------------
7
8/// Top-level lifecycle hooks for `before` and `after` blocks.
9/// Each block has `pre` and `post` lists of hook commands that run around the
10/// entire pipeline (not individual stages).
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
12#[serde(default)]
13pub struct HooksConfig {
14    /// Commands to run before the pipeline or stage starts. Matches GoReleaser
15    /// `before.hooks` canonically.
16    pub hooks: Option<Vec<HookEntry>>,
17    /// Commands to run after the pipeline or stage completes. Anodizer extension
18    /// (GoReleaser has no top-level `after:` block).
19    pub post: Option<Vec<HookEntry>>,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
23#[serde(default)]
24pub struct StructuredHook {
25    /// Command to run (passed through the shell).
26    pub cmd: String,
27    /// Working directory for the command (defaults to project root).
28    pub dir: Option<String>,
29    /// Environment variables for the command.
30    #[serde(default)]
31    pub env: Option<Vec<String>>,
32    /// When true, capture and log stdout/stderr of the command.
33    pub output: Option<bool>,
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
37#[serde(untagged)]
38pub enum HookEntry {
39    Simple(String),
40    Structured(StructuredHook),
41}
42
43impl PartialEq<&str> for HookEntry {
44    fn eq(&self, other: &&str) -> bool {
45        match self {
46            HookEntry::Simple(s) => s.as_str() == *other,
47            HookEntry::Structured(h) => h.cmd.as_str() == *other,
48        }
49    }
50}
51
52impl<'de> Deserialize<'de> for HookEntry {
53    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
54    where
55        D: Deserializer<'de>,
56    {
57        let value = serde_json::Value::deserialize(deserializer)?;
58        match &value {
59            serde_json::Value::String(s) => Ok(HookEntry::Simple(s.clone())),
60            serde_json::Value::Object(_) => {
61                let hook: StructuredHook =
62                    serde_json::from_value(value).map_err(serde::de::Error::custom)?;
63                Ok(HookEntry::Structured(hook))
64            }
65            _ => Err(serde::de::Error::custom(
66                "hook entry must be a string or an object with cmd/dir/env/output",
67            )),
68        }
69    }
70}