gw_bin/actions/utils/
command.rs

1use duct::{cmd, Expression};
2use duct_sh::sh_dangerous;
3use log::{trace, warn};
4
5pub fn create_command(original_command: &str, runs_in_shell: bool) -> Option<(String, Expression)> {
6    // If we are not in a shell, test if the user might want to be in one (uses variables or pipes)
7    if !runs_in_shell {
8        let contains_variables = original_command
9            .find('$')
10            .and_then(|pos| original_command.chars().nth(pos + 1))
11            .map(|ch| ch.is_ascii_alphabetic() || ch == '{')
12            == Some(true);
13
14        let contains_suspicious = original_command.contains(" | ")
15            || original_command.contains(" && ")
16            || original_command.contains(" || ");
17
18        if contains_variables || contains_suspicious {
19            warn!("The command {original_command:?} contains a variable or other shell-specific character: you might want to run it in a shell (-S or -P).")
20        }
21    }
22
23    // We have to split the command into parts to get the command id
24    let split_args = shlex::split(original_command)?;
25    let (command, args) = split_args.split_first()?;
26
27    // If we are in a shell we can `sh_dangerous`, otherwise avoid bugs around shells
28    let script = if runs_in_shell {
29        sh_dangerous(original_command)
30    } else {
31        cmd(command, args)
32    };
33
34    trace!("Parsed {original_command:?} to {script:?}.");
35
36    Some((command.clone(), script))
37}