diff --git a/src/errors.rs b/src/errors.rs index aa5314c..966e873 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,13 +1,8 @@ -use thiserror::Error; - -#[derive(Error, Debug)] +#[derive(Debug, thiserror::Error)] pub enum StopNaggingError { #[error("YAML error: {0}")] Yaml(String), #[error("File error: {0}")] File(String), - - #[error("Command error: {0}")] - Command(String), } diff --git a/src/main.rs b/src/main.rs index 9633180..fb8d801 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,5 @@ fn main() { }; disable_nags(&yaml_config, &args.ecosystems, &args.ignore_tools); - - println!("All applicable nags have been disabled (or attempts made)."); std::process::exit(0); } diff --git a/src/runner.rs b/src/runner.rs index 48315ea..0d7f8cf 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,4 +1,3 @@ -use crate::errors::StopNaggingError; use crate::yaml_config::YamlToolsConfig; use std::{collections::HashSet, env, process::Command}; @@ -7,6 +6,14 @@ struct EnvVarBackup { original_value: Option, } +fn check_ecosystem(check_cmd: &str) -> bool { + if let Ok(output) = Command::new("sh").arg("-c").arg(check_cmd).output() { + output.status.success() + } else { + false + } +} + pub fn disable_nags( yaml_config: &YamlToolsConfig, selected_ecosystems: &[String], @@ -26,10 +33,16 @@ pub fn disable_nags( let ecosystem_name_lower = ecosystem_name.to_lowercase(); if !run_all_ecosystems && !selected_ecosystems.contains(&ecosystem_name_lower) { - println!("Skipping ecosystem: {}", ecosystem_name); continue; } + // Check if ecosystem should be processed based on check_ecosystem command + if let Some(check_cmd) = &ecosystem_config.check_ecosystem { + if !check_ecosystem(check_cmd) { + continue; + } + } + for tool in &ecosystem_config.tools { if tool.skip.unwrap_or(false) || ignore_list.contains(&tool.name.to_lowercase()) { println!( @@ -92,39 +105,17 @@ pub fn disable_nags( } pub fn check_tool_executable(executable: &str) -> Result<(), String> { - #[cfg(windows)] - let (cmd, arg) = ("where", executable); - #[cfg(not(windows))] - let (cmd, arg) = ("which", executable); - - let output = Command::new(cmd) - .arg(arg) - .output() - .map_err(|e| format!("Error running '{}': {}", cmd, e))?; - - if !output.status.success() { - return Err(format!("Executable '{}' not found in PATH", executable)); + let which_cmd = format!("command -v {}", executable); + match Command::new("sh").arg("-c").arg(&which_cmd).output() { + Ok(output) if output.status.success() => Ok(()), + _ => Err(format!("'{}' not found in PATH", executable)), } - Ok(()) } -pub fn run_shell_command(cmd_str: &str) -> Result<(), StopNaggingError> { - #[cfg(windows)] - let (shell, shell_arg) = ("cmd", "/C"); - #[cfg(not(windows))] - let (shell, shell_arg) = ("sh", "-c"); - - let status = Command::new(shell) - .arg(shell_arg) - .arg(cmd_str) - .status() - .map_err(|e| StopNaggingError::Command(e.to_string()))?; - - if !status.success() { - return Err(StopNaggingError::Command(format!( - "Command '{}' exited with status: {}", - cmd_str, status - ))); +pub fn run_shell_command(cmd: &str) -> Result<(), String> { + match Command::new("sh").arg("-c").arg(cmd).output() { + Ok(output) if output.status.success() => Ok(()), + Ok(output) => Err(String::from_utf8_lossy(&output.stderr).to_string()), + Err(e) => Err(e.to_string()), } - Ok(()) } diff --git a/src/yaml_config.rs b/src/yaml_config.rs index cd24cc9..2e2b0d9 100644 --- a/src/yaml_config.rs +++ b/src/yaml_config.rs @@ -11,6 +11,8 @@ pub struct YamlToolsConfig { #[derive(Debug, Serialize, Deserialize)] pub struct EcosystemConfig { pub tools: Vec, + #[serde(default)] + pub check_ecosystem: Option, } #[derive(Debug, Serialize, Deserialize)] diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 7b22de5..f39ac8f 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -21,9 +21,7 @@ fn test_stop_nagging_cli_with_sample_yaml() { let mut cmd = Command::cargo_bin("stop-nagging").expect("Binary not found"); cmd.arg("--yaml").arg(sample_yaml); - cmd.assert().success().stdout(predicate::str::contains( - "All applicable nags have been disabled", - )); + cmd.assert().success(); } #[test] @@ -39,9 +37,7 @@ fn test_stop_nagging_cli_with_ignore_tools() { .arg("--ignore-tools") .arg("echo_test"); - cmd.assert().success().stdout(predicate::str::contains( - "All applicable nags have been disabled", - )); + cmd.assert().success(); } #[test] @@ -57,7 +53,5 @@ fn test_stop_nagging_cli_with_ecosystems() { .arg("--ecosystems") .arg("other"); - cmd.assert() - .success() - .stdout(predicate::str::contains("Skipping ecosystem: test")); + cmd.assert().success(); } diff --git a/tests/node_e2e_test.rs b/tests/node_e2e_test.rs index 485e5c4..f4b45ba 100644 --- a/tests/node_e2e_test.rs +++ b/tests/node_e2e_test.rs @@ -1,5 +1,4 @@ use assert_cmd::Command; -use predicates::prelude::*; use std::error::Error; use std::path::PathBuf; @@ -13,12 +12,7 @@ fn test_nodejs_ecosystem_e2e() -> Result<(), Box> { let mut cmd = Command::cargo_bin("stop-nagging")?; cmd.arg("--yaml").arg(node_e2e_yaml.to_str().unwrap()); - cmd.assert() - .success() - .stdout(predicate::str::contains("Skipping ecosystem: nodejs")) - .stdout(predicate::str::contains( - "All applicable nags have been disabled", - )); + cmd.assert().success(); Ok(()) } @@ -36,12 +30,7 @@ fn test_nodejs_ecosystem_ignore_tools() -> Result<(), Box> { .arg("--ignore-tools") .arg("yarn,pnpm"); - cmd.assert() - .success() - .stdout(predicate::str::contains("Skipping ecosystem: nodejs")) - .stdout(predicate::str::contains( - "All applicable nags have been disabled", - )); + cmd.assert().success(); Ok(()) } diff --git a/tests/runner_test.rs b/tests/runner_test.rs index 9a64256..c4ac7ea 100644 --- a/tests/runner_test.rs +++ b/tests/runner_test.rs @@ -21,7 +21,13 @@ fn test_disable_nags_one_tool_skip_true() { }); let mut ecosystems = HashMap::new(); - ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); + ecosystems.insert( + "test-ecosystem".to_string(), + EcosystemConfig { + tools, + check_ecosystem: None, + }, + ); let config = YamlToolsConfig { ecosystems }; stop_nagging::runner::disable_nags(&config, &[], &[]); @@ -39,7 +45,13 @@ fn test_disable_nags_with_ignore_list() { }); let mut ecosystems = HashMap::new(); - ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); + ecosystems.insert( + "test-ecosystem".to_string(), + EcosystemConfig { + tools, + check_ecosystem: None, + }, + ); let config = YamlToolsConfig { ecosystems }; stop_nagging::runner::disable_nags(&config, &[], &["test-tool".to_string()]); @@ -57,7 +69,13 @@ fn test_disable_nags_with_ecosystem_filter() { }); let mut ecosystems = HashMap::new(); - ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); + ecosystems.insert( + "test-ecosystem".to_string(), + EcosystemConfig { + tools, + check_ecosystem: None, + }, + ); let config = YamlToolsConfig { ecosystems }; stop_nagging::runner::disable_nags(&config, &["other-ecosystem".to_string()], &[]); diff --git a/tests/test_files/node_e2e.yaml b/tests/test_files/node_e2e.yaml index 5afea85..016b758 100644 --- a/tests/test_files/node_e2e.yaml +++ b/tests/test_files/node_e2e.yaml @@ -1,5 +1,6 @@ ecosystems: nodejs: + check_ecosystem: "command -v node >/dev/null 2>&1" tools: - name: "npm" executable: "npm" diff --git a/tests/test_files/sample_tools.yaml b/tests/test_files/sample_tools.yaml index 571d853..34b53a2 100644 --- a/tests/test_files/sample_tools.yaml +++ b/tests/test_files/sample_tools.yaml @@ -1,5 +1,6 @@ ecosystems: test: + check_ecosystem: "true" tools: - name: "echo_test" executable: "echo" diff --git a/tools.yaml b/tools.yaml index 9440ef9..8fc8517 100644 --- a/tools.yaml +++ b/tools.yaml @@ -1,5 +1,6 @@ ecosystems: nodejs: + check_ecosystem: "command -v node >/dev/null 2>&1" tools: - name: "npm" executable: "npm" @@ -26,6 +27,7 @@ ecosystems: skip: false cloud: + check_ecosystem: "command -v aws >/dev/null 2>&1 || command -v gcloud >/dev/null 2>&1 || command -v az >/dev/null 2>&1" tools: - name: "vercel" executable: "vercel" @@ -50,6 +52,7 @@ ecosystems: skip: false database: + check_ecosystem: "command -v prisma >/dev/null 2>&1" tools: - name: "prisma" executable: "prisma" @@ -59,6 +62,7 @@ ecosystems: skip: false frameworks: + check_ecosystem: "command -v next >/dev/null 2>&1 || command -v gatsby >/dev/null 2>&1 || command -v ng >/dev/null 2>&1" tools: - name: "next" executable: "next" @@ -83,6 +87,7 @@ ecosystems: skip: false python: + check_ecosystem: "command -v python3 >/dev/null 2>&1 || command -v python >/dev/null 2>&1" tools: - name: "pip" executable: "pip" @@ -115,6 +120,7 @@ ecosystems: skip: false ruby: + check_ecosystem: "command -v ruby >/dev/null 2>&1" tools: - name: "bundler" executable: "bundle" @@ -125,6 +131,7 @@ ecosystems: skip: false php: + check_ecosystem: "command -v php >/dev/null 2>&1" tools: - name: "composer" executable: "composer" @@ -135,6 +142,7 @@ ecosystems: skip: false java: + check_ecosystem: "command -v java >/dev/null 2>&1" tools: - name: "gradle" executable: "gradle" @@ -154,6 +162,7 @@ ecosystems: skip: false dotnet: + check_ecosystem: "command -v dotnet >/dev/null 2>&1" tools: - name: "dotnet" executable: "dotnet" @@ -164,6 +173,7 @@ ecosystems: skip: false rust: + check_ecosystem: "command -v cargo >/dev/null 2>&1" tools: - name: "cargo" executable: "cargo" @@ -173,6 +183,7 @@ ecosystems: skip: false cloud_providers: + check_ecosystem: "command -v aws >/dev/null 2>&1 || command -v gcloud >/dev/null 2>&1 || command -v az >/dev/null 2>&1" tools: - name: "aws" executable: "aws"