1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use anyhow::Result;
use which::which;
use xshell::{cmd, Cmd, Shell};

use crate::Config;

pub fn actionlint_cmd<'a>(config: &Config, sh: &'a Shell) -> Option<Cmd<'a>> {
    create_cmd(
        "actionlint",
        "https://github.com/rhysd/actionlint",
        config,
        sh,
    )
}

pub fn cargo_cmd<'a>(config: &Config, sh: &'a Shell) -> Option<Cmd<'a>> {
    create_cmd(
        "cargo",
        "https://www.rust-lang.org/learn/get-started",
        config,
        sh,
    )
}

pub fn prettier_cmd<'a>(config: &Config, sh: &'a Shell) -> Option<Cmd<'a>> {
    create_cmd("prettier", "https://prettier.io", config, sh)
}

pub fn typos_cmd<'a>(config: &Config, sh: &'a Shell) -> Option<Cmd<'a>> {
    create_cmd("typos", "https://github.com/crate-ci/typos", config, sh)
}

fn create_cmd<'a>(
    cmd_name: &str,
    install_url: &str,
    config: &Config,
    sh: &'a Shell,
) -> Option<Cmd<'a>> {
    if check_command(cmd_name, install_url, config).is_err() {
        return None;
    }

    let cmd = cmd!(sh, "{cmd_name}");

    Some(cmd)
}

fn check_command(cmd_name: &str, install_url: &str, config: &Config) -> Result<()> {
    // only ignore missing commands when not running in a CI environment
    let is_local = !is_ci::cached();
    match which(cmd_name) {
        Ok(_) => {
            // the command exists, we're good
            Ok(())
        }
        Err(_) if config.ignore_missing_commands && is_local => {
            println!("Warning: command not found `{cmd_name}`");
            println!("Install: {install_url}");
            Err(anyhow::Error::msg("command is missing, but ignored"))
        }
        Err(_) if is_ci::cached() => {
            println!("Error: command not found `{cmd_name}`");
            println!("Install: {install_url}");
            std::process::exit(1);
        }
        Err(_) => {
            println!("Error: command not found `{cmd_name}`; skip this task with --ignore-missing");
            println!("Install: {install_url}");
            std::process::exit(1);
        }
    }
}