diff --git a/README.md b/README.md index 9c2e225..e5237f1 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,22 @@ stop-nagging [options] 3. If more complex logic is needed, you can add or modify Rust code in `src/runner.rs` 4. Submit a Pull Request +## Behavior: Non-Failing + +`stop-nagging` **never** exits with a nonzero code, even if it fails to disable certain nags. This ensures your CI/CD pipeline won't break due to a missing or optional tool. Instead, it prints **warnings** for: + +1. Missing executables (not found in `PATH`). +2. Commands that fail. +3. Already-set environment variables (which we won't override). + +You'll see these warnings in the console logs, but your process will exit **0** regardless. + +## Skipping Already-Set Environment Variables + +If an environment variable is **already set**, `stop-nagging` **does not override** it. This avoids unintentional conflicts with variables you may want set differently. If a var is already set, we print a warning like: + +Warning: Env var 'KEY' is already set; skipping override for tool 'npm'. + ``` ``` diff --git a/src/main.rs b/src/main.rs index f3d42f4..8d0f1d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,20 +12,16 @@ fn main() { let args = StopNaggingArgs::parse(); let yaml_path = &args.yaml; - // Parse the YAML let yaml_config = match YamlToolsConfig::from_yaml_file(yaml_path) { Ok(config) => config, Err(e) => { eprintln!("Failed to read YAML config '{}': {}", yaml_path, e); - std::process::exit(1); + std::process::exit(0); } }; - // Run the disable logic - if let Err(e) = disable_nags(&yaml_config, &args.ecosystems, &args.ignore_tools) { - eprintln!("Failed to disable nags: {}", e); - std::process::exit(1); - } + 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 83dff37..a036925 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -6,32 +6,24 @@ pub fn disable_nags( yaml_config: &YamlToolsConfig, selected_ecosystems: &[String], ignore_list: &[String], -) -> Result<(), StopNaggingError> { - // Convert user's ecosystem list to lowercase for simpler matching +) { let selected_ecosystems: HashSet = selected_ecosystems .iter() .map(|s| s.to_lowercase()) .collect(); - - // Convert ignore list to lowercase let ignore_list: HashSet = ignore_list.iter().map(|s| s.to_lowercase()).collect(); - // If user passed no ecosystems, we'll run them all let run_all_ecosystems = selected_ecosystems.is_empty(); - // Iterate over each ecosystem in the YAML for (ecosystem_name, ecosystem_config) in &yaml_config.ecosystems { let ecosystem_name_lower = ecosystem_name.to_lowercase(); - // Skip if user specified ecosystems AND this one isn't in that list if !run_all_ecosystems && !selected_ecosystems.contains(&ecosystem_name_lower) { println!("Skipping ecosystem: {}", ecosystem_name); continue; } - // Process each tool for tool in &ecosystem_config.tools { - // If tool is marked skip in YAML or in CLI ignore list, skip it if tool.skip.unwrap_or(false) || ignore_list.contains(&tool.name.to_lowercase()) { println!( "Ignoring tool: {} (ecosystem: {})", @@ -40,46 +32,53 @@ pub fn disable_nags( continue; } - if let Err(msg) = check_tool_executable(&tool.executable) { - eprintln!( - "Tool {} not found in ecosystem {}: {}", - tool.name, ecosystem_name, msg - ); - continue; + match check_tool_executable(&tool.executable) { + Ok(()) => { /* tool found, continue */ } + Err(msg) => { + eprintln!( + "Warning: Tool '{}' not found in ecosystem '{}': {}", + tool.name, ecosystem_name, msg + ); + continue; + } } - // Set environment variables if let Some(env_vars) = &tool.env { for (key, val) in env_vars { - env::set_var(key, val); - println!( - "Set {}={} for tool {} in {}", - key, val, tool.name, ecosystem_name - ); + if env::var_os(key).is_some() { + eprintln!( + "Warning: Env var '{}' is already set; skipping override for tool '{}'.", + key, tool.name + ); + } else { + env::set_var(key, val); + println!( + "Set {}={} for tool {} in {}", + key, val, tool.name, ecosystem_name + ); + } } } - // Run each command if let Some(cmds) = &tool.commands { for cmd_str in cmds { println!( "Running command for {} in {}: {}", tool.name, ecosystem_name, cmd_str ); - match run_shell_command(cmd_str) { - Ok(_) => println!("Command succeeded."), - Err(e) => eprintln!("Failed to run command '{}': {}", cmd_str, e), + if let Err(e) = run_shell_command(cmd_str) { + eprintln!("Warning: Failed to run command '{}': {}", cmd_str, e); + } else { + println!("Command succeeded."); } } } } } - Ok(()) } pub fn check_tool_executable(executable: &str) -> Result<(), String> { let which = Command::new("which").arg(executable).output(); - match which { Ok(output) => { if !output.status.success() { diff --git a/tests/runner_test.rs b/tests/runner_test.rs index 4405f2c..9a64256 100644 --- a/tests/runner_test.rs +++ b/tests/runner_test.rs @@ -1,84 +1,77 @@ use std::collections::HashMap; -use stop_nagging::runner::{check_tool_executable, disable_nags, run_shell_command}; use stop_nagging::yaml_config::{EcosystemConfig, ToolEntry, YamlToolsConfig}; -#[test] -fn test_check_tool_executable_missing() { - let result = check_tool_executable("some_nonexistent_tool_for_test123"); - assert!(result.is_err(), "Expected error for missing executable"); -} - -#[test] -fn test_run_shell_command_success() { - let result = run_shell_command("echo Hello"); - assert!(result.is_ok(), "Expected success running 'echo Hello'"); -} - #[test] fn test_disable_nags_empty_config() { - let cfg = YamlToolsConfig { + let config = YamlToolsConfig { ecosystems: HashMap::new(), }; - let result = disable_nags(&cfg, &[], &[]); - assert!( - result.is_ok(), - "disable_nags with empty config should succeed" - ); + stop_nagging::runner::disable_nags(&config, &[], &[]); } #[test] fn test_disable_nags_one_tool_skip_true() { - let mut ecosystems = HashMap::new(); - let tool = ToolEntry { - name: "TestTool".to_string(), - executable: "echo".to_string(), + let mut tools = Vec::new(); + tools.push(ToolEntry { + name: "test-tool".to_string(), + executable: "test-executable".to_string(), + skip: Some(true), env: None, commands: None, - skip: Some(true), - }; - let ecosystem = EcosystemConfig { tools: vec![tool] }; - ecosystems.insert("test".to_string(), ecosystem); - let cfg = YamlToolsConfig { ecosystems }; + }); + + let mut ecosystems = HashMap::new(); + ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); - let result = disable_nags(&cfg, &[], &[]); - assert!(result.is_ok(), "Skipping a tool should not fail"); + let config = YamlToolsConfig { ecosystems }; + stop_nagging::runner::disable_nags(&config, &[], &[]); } #[test] fn test_disable_nags_with_ignore_list() { - let mut ecosystems = HashMap::new(); - let tool = ToolEntry { - name: "TestTool".to_string(), - executable: "echo".to_string(), + let mut tools = Vec::new(); + tools.push(ToolEntry { + name: "test-tool".to_string(), + executable: "test-executable".to_string(), + skip: None, env: None, commands: None, - skip: None, - }; - let ecosystem = EcosystemConfig { tools: vec![tool] }; - ecosystems.insert("test".to_string(), ecosystem); - let cfg = YamlToolsConfig { ecosystems }; + }); + + let mut ecosystems = HashMap::new(); + ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); - let result = disable_nags(&cfg, &[], &["TestTool".to_string()]); - assert!(result.is_ok(), "Ignoring a tool via CLI should not fail"); + let config = YamlToolsConfig { ecosystems }; + stop_nagging::runner::disable_nags(&config, &[], &["test-tool".to_string()]); } #[test] fn test_disable_nags_with_ecosystem_filter() { - let mut ecosystems = HashMap::new(); - let tool = ToolEntry { - name: "TestTool".to_string(), - executable: "echo".to_string(), + let mut tools = Vec::new(); + tools.push(ToolEntry { + name: "test-tool".to_string(), + executable: "test-executable".to_string(), + skip: None, env: None, commands: None, - skip: None, - }; - let ecosystem = EcosystemConfig { tools: vec![tool] }; - ecosystems.insert("test".to_string(), ecosystem); - let cfg = YamlToolsConfig { ecosystems }; + }); + + let mut ecosystems = HashMap::new(); + ecosystems.insert("test-ecosystem".to_string(), EcosystemConfig { tools }); - let result = disable_nags(&cfg, &["other".to_string()], &[]); - assert!( - result.is_ok(), - "Filtering ecosystems via CLI should not fail" - ); + let config = YamlToolsConfig { ecosystems }; + stop_nagging::runner::disable_nags(&config, &["other-ecosystem".to_string()], &[]); +} + +#[test] +fn test_check_tool_executable_missing() { + let result = stop_nagging::runner::check_tool_executable("non-existent-tool-12345"); + assert!(result.is_err()); +} + +#[test] +fn test_run_shell_command_success() { + println!("Hello"); + let result = stop_nagging::runner::run_shell_command("echo test"); + assert!(result.is_ok()); }