• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaspar030 / laze / 13502291184

24 Feb 2025 04:00PM UTC coverage: 82.274% (-0.05%) from 82.327%
13502291184

push

github

web-flow
feat(task): Run with cmd /C on windows (#653)

6 of 10 new or added lines in 1 file covered. (60.0%)

3574 of 4344 relevant lines covered (82.27%)

104.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.41
/src/model/task.rs
1
use std::path::Path;
2

3
use anyhow::{Error, Result};
4
use thiserror::Error;
5

6
use crate::nested_env;
7
use crate::serde_bool_helpers::{default_as_false, default_as_true};
8
use crate::EXIT_ON_SIGINT;
9

10
use super::shared::VarExportSpec;
11

12
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
13
#[serde(deny_unknown_fields)]
14
pub struct Task {
15
    pub cmd: Vec<String>,
16
    pub help: Option<String>,
17
    pub required_vars: Option<Vec<String>>,
18
    pub required_modules: Option<Vec<String>>,
19
    pub export: Option<Vec<VarExportSpec>>,
20
    #[serde(default = "default_as_true")]
21
    pub build: bool,
22
    #[serde(default = "default_as_false")]
23
    pub ignore_ctrl_c: bool,
24
    pub working_directory: Option<String>,
25
}
26

27
#[derive(Error, Debug, Serialize, Deserialize)]
28
pub enum TaskError {
29
    #[error("required variable `{var}` not set")]
30
    RequiredVarMissing { var: String },
31
    #[error("required module `{module}` not selected")]
32
    RequiredModuleMissing { module: String },
33
}
34

35
impl Task {
36
    pub fn build_app(&self) -> bool {
5✔
37
        self.build
5✔
38
    }
5✔
39

40
    pub fn execute(
5✔
41
        &self,
5✔
42
        start_dir: &Path,
5✔
43
        args: Option<&Vec<&str>>,
5✔
44
        verbose: u8,
5✔
45
    ) -> Result<(), Error> {
5✔
46
        for cmd in &self.cmd {
13✔
47
            use shell_words::join;
5✔
48
            use std::process::Command;
5✔
49

50
            let mut command = if cfg!(target_family = "windows") {
8✔
NEW
51
                let mut cmd = Command::new("cmd");
×
NEW
52
                cmd.arg("/C");
×
NEW
53
                cmd
×
54
            } else {
55
                let mut sh = Command::new("sh");
8✔
56
                if verbose > 0 {
8✔
NEW
57
                    sh.arg("-x");
×
58
                }
8✔
59
                sh.arg("-c");
8✔
60
                sh
8✔
61
            };
62

63
            let cmd = cmd.replace("$$", "$");
8✔
64

65
            if let Some(working_directory) = &self.working_directory {
8✔
66
                // This includes support for absolute working directories through .join
×
67
                command.current_dir(start_dir.join(working_directory));
×
68
            } else {
8✔
69
                command.current_dir(start_dir);
8✔
70
            }
8✔
71

72
            // handle "export:" (export laze variables to task shell environment)
73
            if let Some(export) = &self.export {
8✔
74
                for entry in export {
15✔
75
                    let VarExportSpec { variable, content } = entry;
12✔
76
                    if let Some(val) = content {
12✔
77
                        command.env(variable, val);
12✔
78
                    }
12✔
79
                }
80
            }
5✔
81

82
            if let Some(args) = args {
8✔
83
                command.arg(cmd.clone() + " " + &join(args).to_owned());
8✔
84
            } else {
8✔
85
                command.arg(cmd);
×
86
            }
×
87

88
            if self.ignore_ctrl_c {
8✔
89
                EXIT_ON_SIGINT
×
90
                    .get()
×
91
                    .unwrap()
×
92
                    .clone()
×
93
                    .store(false, std::sync::atomic::Ordering::SeqCst);
×
94
            }
8✔
95

96
            // run command, wait for status
97
            let status = command.status().expect("executing command");
8✔
98

8✔
99
            if self.ignore_ctrl_c {
8✔
100
                EXIT_ON_SIGINT
×
101
                    .get()
×
102
                    .unwrap()
×
103
                    .clone()
×
104
                    .store(true, std::sync::atomic::Ordering::SeqCst);
×
105
            }
8✔
106

107
            if !status.success() {
8✔
108
                return Err(anyhow!("task failed"));
×
109
            }
8✔
110
        }
111
        Ok(())
5✔
112
    }
5✔
113

114
    fn _with_env(&self, env: &im::HashMap<&String, String>, do_eval: bool) -> Result<Task, Error> {
20✔
115
        let expand = |s| {
30✔
116
            if do_eval {
30✔
117
                nested_env::expand_eval(s, env, nested_env::IfMissing::Empty)
15✔
118
            } else {
119
                nested_env::expand(s, env, nested_env::IfMissing::Ignore)
15✔
120
            }
121
        };
30✔
122

123
        Ok(Task {
124
            cmd: self
20✔
125
                .cmd
20✔
126
                .iter()
20✔
127
                .map(expand)
20✔
128
                .collect::<Result<Vec<String>, _>>()?,
20✔
129
            export: if do_eval {
20✔
130
                self.expand_export(env)
10✔
131
            } else {
132
                self.export.clone()
10✔
133
            },
134
            working_directory: self.working_directory.as_ref().map(expand).transpose()?,
20✔
135
            ..(*self).clone()
20✔
136
        })
137
    }
20✔
138

139
    /// This is called early when loading the yaml files.
140
    /// It will not evaluate expressions, and pass-through variables that are not
141
    /// found in `env`.
142
    pub fn with_env(&self, env: &im::HashMap<&String, String>) -> Result<Task, Error> {
10✔
143
        self._with_env(env, false)
10✔
144
    }
10✔
145

146
    /// This is called to generate the final task.
147
    /// It will evaluate expressions, and variables that are not
148
    /// found in `env` will be replaced with the empty string.
149
    pub fn with_env_eval(&self, env: &im::HashMap<&String, String>) -> Result<Task, Error> {
10✔
150
        self._with_env(env, true)
10✔
151
    }
10✔
152

153
    fn expand_export(&self, env: &im::HashMap<&String, String>) -> Option<Vec<VarExportSpec>> {
10✔
154
        VarExportSpec::expand(self.export.as_ref(), env)
10✔
155
    }
10✔
156
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc