mk_lib/schema/command/
mod.rs

1use std::io::{
2  BufRead as _,
3  BufReader,
4};
5use std::thread;
6
7use crate::handle_output;
8use crate::schema::get_output_handler;
9use anyhow::Context;
10
11use super::TaskContext;
12use serde::Deserialize;
13
14mod container_build;
15mod container_run;
16mod local_run;
17mod task_run;
18
19pub use local_run::LocalRun;
20
21#[derive(Debug, Deserialize, Clone)]
22#[serde(untagged)]
23pub enum CommandRunner {
24  ContainerBuild(container_build::ContainerBuild),
25  ContainerRun(container_run::ContainerRun),
26  LocalRun(local_run::LocalRun),
27  TaskRun(task_run::TaskRun),
28  CommandRun(String),
29}
30
31impl CommandRunner {
32  pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {
33    match self {
34      CommandRunner::ContainerBuild(container_build) => container_build.execute(context),
35      CommandRunner::ContainerRun(container_run) => container_run.execute(context),
36      CommandRunner::LocalRun(local_run) => local_run.execute(context),
37      CommandRunner::TaskRun(task_run) => task_run.execute(context),
38      CommandRunner::CommandRun(command) => self.execute_command(context, command),
39    }
40  }
41
42  fn execute_command(&self, context: &TaskContext, command: &str) -> anyhow::Result<()> {
43    assert!(!command.is_empty());
44
45    let ignore_errors = context.ignore_errors();
46    let verbose = context.verbose();
47    let shell = context.shell();
48
49    let stdout = get_output_handler(verbose);
50    let stderr = get_output_handler(verbose);
51
52    let mut cmd = shell.proc();
53    cmd.arg(command).stdout(stdout).stderr(stderr);
54
55    // Inject environment variables
56    for (key, value) in context.env_vars.iter() {
57      cmd.env(key, value);
58    }
59
60    let mut cmd = cmd.spawn()?;
61    if verbose {
62      handle_output!(cmd.stdout, context);
63      handle_output!(cmd.stderr, context);
64    }
65
66    let status = cmd.wait()?;
67    if !status.success() && !ignore_errors {
68      anyhow::bail!("Command failed - {}", command);
69    }
70
71    Ok(())
72  }
73}
74
75#[cfg(test)]
76mod test {
77  use super::*;
78
79  #[test]
80  fn test_command_1() -> anyhow::Result<()> {
81    {
82      let yaml = "
83        command: 'echo \"Hello, World!\"'
84        ignore_errors: false
85        verbose: false
86      ";
87      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
88
89      if let CommandRunner::LocalRun(local_run) = command {
90        assert_eq!(local_run.command, "echo \"Hello, World!\"");
91        assert_eq!(local_run.work_dir, None);
92        assert_eq!(local_run.ignore_errors, Some(false));
93        assert_eq!(local_run.verbose, Some(false));
94      } else {
95        panic!("Expected CommandRunner::LocalRun");
96      }
97
98      Ok(())
99    }
100  }
101
102  #[test]
103  fn test_command_2() -> anyhow::Result<()> {
104    {
105      let yaml = "
106        command: 'echo \"Hello, World!\"'
107      ";
108      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
109
110      if let CommandRunner::LocalRun(local_run) = command {
111        assert_eq!(local_run.command, "echo \"Hello, World!\"");
112        assert_eq!(local_run.work_dir, None);
113        assert_eq!(local_run.ignore_errors, None);
114        assert_eq!(local_run.verbose, None);
115      } else {
116        panic!("Expected CommandRunner::LocalRun");
117      }
118
119      Ok(())
120    }
121  }
122
123  #[test]
124  fn test_command_3() -> anyhow::Result<()> {
125    {
126      let yaml = "
127        command: 'echo \"Hello, World!\"'
128        ignore_errors: true
129      ";
130      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
131      if let CommandRunner::LocalRun(local_run) = command {
132        assert_eq!(local_run.command, "echo \"Hello, World!\"");
133        assert_eq!(local_run.work_dir, None);
134        assert_eq!(local_run.ignore_errors, Some(true));
135        assert_eq!(local_run.verbose, None);
136      } else {
137        panic!("Expected CommandRunner::LocalRun");
138      }
139
140      Ok(())
141    }
142  }
143
144  #[test]
145  fn test_command_4() -> anyhow::Result<()> {
146    {
147      let yaml = "
148        command: 'echo \"Hello, World!\"'
149        verbose: false
150      ";
151      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
152      if let CommandRunner::LocalRun(local_run) = command {
153        assert_eq!(local_run.command, "echo \"Hello, World!\"");
154        assert_eq!(local_run.work_dir, None);
155        assert_eq!(local_run.ignore_errors, None);
156        assert_eq!(local_run.verbose, Some(false));
157      } else {
158        panic!("Expected CommandRunner::LocalRun");
159      }
160
161      Ok(())
162    }
163  }
164
165  #[test]
166  fn test_command_5() -> anyhow::Result<()> {
167    {
168      let yaml = "
169        command: 'echo \"Hello, World!\"'
170        work_dir: /tmp
171      ";
172      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
173      if let CommandRunner::LocalRun(local_run) = command {
174        assert_eq!(local_run.command, "echo \"Hello, World!\"");
175        assert_eq!(local_run.work_dir, Some("/tmp".into()));
176        assert_eq!(local_run.ignore_errors, None);
177        assert_eq!(local_run.verbose, None);
178      } else {
179        panic!("Expected CommandRunner::LocalRun");
180      }
181
182      Ok(())
183    }
184  }
185
186  #[test]
187  fn test_command_6() -> anyhow::Result<()> {
188    {
189      let yaml = "
190        echo 'Hello, World!'
191      ";
192      let command = serde_yaml::from_str::<CommandRunner>(yaml)?;
193      if let CommandRunner::CommandRun(command) = command {
194        assert_eq!(command, "echo 'Hello, World!'");
195      } else {
196        panic!("Expected CommandRunner::CommandRun");
197      }
198
199      Ok(())
200    }
201  }
202}