mk_lib/schema/command/
mod.rs1use 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 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}