1use std::collections::HashMap;
2use std::process::Command;
3
4use crate::error::ReleaseError;
5
6#[derive(Debug, Clone)]
8pub struct HookCommand {
9 pub command: String,
10}
11
12#[derive(Debug, Clone, Default)]
14pub struct HookContext {
15 pub env: HashMap<String, String>,
16}
17
18impl HookContext {
19 pub fn set(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
21 self.env.insert(key.into(), value.into());
22 self
23 }
24}
25
26pub trait HookRunner: Send + Sync {
28 fn run(&self, hooks: &[HookCommand], ctx: &HookContext) -> Result<(), ReleaseError>;
29}
30
31pub struct ShellHookRunner;
33
34impl HookRunner for ShellHookRunner {
35 fn run(&self, hooks: &[HookCommand], ctx: &HookContext) -> Result<(), ReleaseError> {
36 for hook in hooks {
37 let status = Command::new("sh")
38 .arg("-c")
39 .arg(&hook.command)
40 .envs(&ctx.env)
41 .status()
42 .map_err(|e| ReleaseError::Hook {
43 command: format!("{}: {e}", hook.command),
44 })?;
45
46 if !status.success() {
47 return Err(ReleaseError::Hook {
48 command: hook.command.clone(),
49 });
50 }
51 }
52 Ok(())
53 }
54}