From 0a282effe84de7d2c0f5ca20885a7e87967725ad Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 16 Jan 2025 15:46:39 +0530 Subject: [PATCH 1/8] omnix-health: json output --- crates/omnix-cli/src/command/health.rs | 12 ++++++-- crates/omnix-develop/src/core.rs | 2 +- crates/omnix-health/src/check/caches.rs | 9 ++++-- crates/omnix-health/src/check/direnv.rs | 16 +++++----- .../omnix-health/src/check/flake_enabled.rs | 9 ++++-- crates/omnix-health/src/check/max_jobs.rs | 9 ++++-- .../omnix-health/src/check/min_nix_version.rs | 9 ++++-- crates/omnix-health/src/check/rosetta.rs | 10 ++++--- crates/omnix-health/src/check/shell.rs | 13 ++++---- .../omnix-health/src/check/trusted_users.rs | 9 ++++-- crates/omnix-health/src/lib.rs | 30 ++++++++++++++----- crates/omnix-health/src/traits.rs | 6 ++-- 12 files changed, 94 insertions(+), 40 deletions(-) diff --git a/crates/omnix-cli/src/command/health.rs b/crates/omnix-cli/src/command/health.rs index 0a164d9e..1b6f27c9 100644 --- a/crates/omnix-cli/src/command/health.rs +++ b/crates/omnix-cli/src/command/health.rs @@ -13,6 +13,10 @@ pub struct HealthCommand { /// a flake.nix) #[arg(long = "dump-schema")] pub dump_schema: bool, + + /// Print output in JSON + #[arg(long)] + json: bool, } impl HealthCommand { @@ -21,8 +25,12 @@ impl HealthCommand { println!("{}", NixHealth::schema()?); return Ok(()); } - let checks = run_all_checks_with(self.flake_url.clone()).await?; - let exit_code = NixHealth::print_report_returning_exit_code(&checks).await?; + let checks_map = run_all_checks_with(self.flake_url.clone()).await?; + let exit_code = if self.json { + NixHealth::print_json_report_returning_exit_code(&checks_map).await? + } else { + NixHealth::print_report_returning_exit_code(&checks_map).await? + }; if exit_code != 0 { std::process::exit(exit_code); } diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index 9ae5574d..0760aae0 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -73,7 +73,7 @@ pub async fn develop_on_pre_shell(prj: &Project) -> anyhow::Result<()> { }; for check_kind in relevant_checks.into_iter() { - for check in check_kind.check(nix_info, Some(&prj.flake)) { + for check in check_kind.check(nix_info, Some(&prj.flake)).values() { if !check.result.green() { check.tracing_log().await?; if !check.result.green() && check.required { diff --git a/crates/omnix-health/src/check/caches.rs b/crates/omnix-health/src/check/caches.rs index 484444a3..d22dd8d8 100644 --- a/crates/omnix-health/src/check/caches.rs +++ b/crates/omnix-health/src/check/caches.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::info; use serde::{Deserialize, Serialize}; use url::Url; @@ -25,7 +27,7 @@ impl Checkable for Caches { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { let missing_caches = self.get_missing_caches(nix_info); let result = if missing_caches.is_empty() { CheckResult::Green @@ -61,7 +63,10 @@ impl Checkable for Caches { result, required: true, }; - vec![check] + + let mut checks_map = HashMap::new(); + checks_map.insert("caches".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/check/direnv.rs b/crates/omnix-health/src/check/direnv.rs index 3b8e08bc..ba70b294 100644 --- a/crates/omnix-health/src/check/direnv.rs +++ b/crates/omnix-health/src/check/direnv.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::{flake::url::FlakeUrl, info}; use serde::{Deserialize, Serialize}; @@ -22,17 +24,17 @@ impl Default for Direnv { } impl Checkable for Direnv { - fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> Vec { - let mut checks = vec![]; + fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> HashMap { + let mut checks_map = HashMap::new(); if !self.enable { - return checks; + return checks_map; } let direnv_install_result = direnv::DirenvInstall::detect(); - checks.push(install_check(&direnv_install_result, self.required)); + checks_map.insert("direnv-install-check".to_string(), install_check(&direnv_install_result, self.required)); match direnv_install_result.as_ref() { - Err(_) => return checks, + Err(_) => return checks_map, Ok(direnv_install) => { // If direnv is installed, check for version and then allowed_check // This check is currently only relevant if the flake is local and an `.envrc` exists. @@ -40,14 +42,14 @@ impl Checkable for Direnv { None => {} Some(local_path) => { if local_path.join(".envrc").exists() { - checks.push(allowed_check(direnv_install, local_path, self.required)); + checks_map.insert("direnv-allowed-check".to_string(), allowed_check(direnv_install, local_path, self.required)); } } } } } - checks + checks_map } } diff --git a/crates/omnix-health/src/check/flake_enabled.rs b/crates/omnix-health/src/check/flake_enabled.rs index 6ee7969d..37810611 100644 --- a/crates/omnix-health/src/check/flake_enabled.rs +++ b/crates/omnix-health/src/check/flake_enabled.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::info; use serde::{Deserialize, Serialize}; @@ -13,7 +15,7 @@ impl Checkable for FlakeEnabled { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { let val = &nix_info.nix_config.experimental_features.value; let check = Check { title: "Flakes Enabled".to_string(), @@ -30,6 +32,9 @@ impl Checkable for FlakeEnabled { }, required: true, }; - vec![check] + + let mut checks_map = HashMap::new(); + checks_map.insert("flake-enabled".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/check/max_jobs.rs b/crates/omnix-health/src/check/max_jobs.rs index be1dd465..94f16603 100644 --- a/crates/omnix-health/src/check/max_jobs.rs +++ b/crates/omnix-health/src/check/max_jobs.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::info; use serde::{Deserialize, Serialize}; @@ -13,7 +15,7 @@ impl Checkable for MaxJobs { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { let max_jobs = nix_info.nix_config.max_jobs.value; let check = Check { title: "Max Jobs".to_string(), @@ -31,6 +33,9 @@ impl Checkable for MaxJobs { }, required: true, }; - vec![check] + + let mut checks_map = HashMap::new(); + checks_map.insert("max-jobs".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/check/min_nix_version.rs b/crates/omnix-health/src/check/min_nix_version.rs index b807f7ae..9d2cc630 100644 --- a/crates/omnix-health/src/check/min_nix_version.rs +++ b/crates/omnix-health/src/check/min_nix_version.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::version::NixVersion; use nix_rs::info; @@ -29,7 +31,7 @@ impl Checkable for MinNixVersion { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { let val = &nix_info.nix_version; let check = Check { title: "Minimum Nix Version".to_string(), @@ -44,6 +46,9 @@ impl Checkable for MinNixVersion { }, required: true, }; - vec![check] + + let mut checks_map = HashMap::new(); + checks_map.insert("nix-version".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/check/rosetta.rs b/crates/omnix-health/src/check/rosetta.rs index 6031a34d..f7ecb77b 100644 --- a/crates/omnix-health/src/check/rosetta.rs +++ b/crates/omnix-health/src/check/rosetta.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nix_rs::{ env::{AppleEmulation, MacOSArch, OS}, info, @@ -30,8 +32,8 @@ impl Checkable for Rosetta { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { - let mut checks = vec![]; + ) -> HashMap { + let mut checks_map = HashMap::new(); if let (true, Some(emulation)) = (self.enable, get_apple_emulation(&nix_info.nix_env.os)) { let check = Check { title: "Rosetta Not Active".to_string(), @@ -46,9 +48,9 @@ impl Checkable for Rosetta { }, required: self.required, }; - checks.push(check); + checks_map.insert("rosetta".to_string(), check); }; - checks + checks_map } } diff --git a/crates/omnix-health/src/check/shell.rs b/crates/omnix-health/src/check/shell.rs index b9724f21..c413230c 100644 --- a/crates/omnix-health/src/check/shell.rs +++ b/crates/omnix-health/src/check/shell.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; use std::{ - collections::HashMap, - path::{Path, PathBuf}, + collections::HashMap, hash::Hash, path::{Path, PathBuf} }; use crate::traits::{Check, CheckResult, Checkable}; @@ -27,9 +26,10 @@ impl Checkable for ShellCheck { &self, _nix_info: &nix_rs::info::NixInfo, _flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { + let mut checks_map = HashMap::new(); if !self.enable { - return vec![]; + return checks_map; } let user_shell_env = match CurrentUserShellEnv::new() { Ok(shell) => shell, @@ -39,7 +39,7 @@ impl Checkable for ShellCheck { panic!("Unable to determine user's shell environment (see above)"); } else { tracing::warn!("Skipping shell dotfile check! (see above)"); - return vec![]; + return checks_map; } } }; @@ -76,7 +76,8 @@ impl Checkable for ShellCheck { required: self.required, }; - vec![check] + checks_map.insert("shell".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/check/trusted_users.rs b/crates/omnix-health/src/check/trusted_users.rs index 43b77912..6bf2307a 100644 --- a/crates/omnix-health/src/check/trusted_users.rs +++ b/crates/omnix-health/src/check/trusted_users.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use nix_rs::config::TrustedUserValue; use serde::{Deserialize, Serialize}; @@ -15,7 +15,7 @@ impl Checkable for TrustedUsers { &self, nix_info: &nix_rs::info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec { + ) -> HashMap { let result = if is_current_user_trusted(nix_info) { CheckResult::Green } else { @@ -42,7 +42,10 @@ impl Checkable for TrustedUsers { result, required: true, }; - vec![check] + + let mut checks_map = HashMap::new(); + checks_map.insert("trusted-users".to_string(), check); + checks_map } } diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index b41b4081..43765e9c 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -5,6 +5,8 @@ pub mod check; pub mod report; pub mod traits; +use std::collections::HashMap; + use anyhow::Context; use check::shell::ShellCheck; use colored::Colorize; @@ -75,22 +77,36 @@ impl NixHealth { #[instrument(skip_all)] pub fn run_all_checks( &self, - nix_info: &nix_rs::info::NixInfo, + nix_info: &NixInfo, flake_url: Option, - ) -> Vec { + ) -> HashMap { self.into_iter() .flat_map(|c| c.check(nix_info, flake_url.as_ref())) .collect() } - pub async fn print_report_returning_exit_code(checks: &[traits::Check]) -> anyhow::Result { + pub async fn print_json_report_returning_exit_code(checks_map: &HashMap) -> anyhow::Result { + let mut res = AllChecksResult::new(); + for check in checks_map.values() { + if !check.result.green() { + res.register_failure(check.required); + }; + } + + let code = res.report(); + println!("{}", serde_json::to_string_pretty(checks_map)?); + Ok(code) + } + + pub async fn print_report_returning_exit_code(checks_map: &HashMap) -> anyhow::Result { let mut res = AllChecksResult::new(); - for check in checks { + for check in checks_map.values() { check.tracing_log().await?; if !check.result.green() { res.register_failure(check.required); }; } + let code = res.report(); Ok(code) } @@ -101,7 +117,7 @@ impl NixHealth { } /// Run all health checks, optionally using the given flake's configuration -pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result> { +pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result> { let nix_info = NixInfo::get() .await .as_ref() @@ -125,8 +141,8 @@ pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result< print_info_banner(flake_url.as_ref(), nix_info).await?; - let checks = health.run_all_checks(nix_info, flake_url); - Ok(checks) + let checks_map = health.run_all_checks(nix_info, flake_url); + Ok(checks_map) } async fn print_info_banner(flake_url: Option<&FlakeUrl>, nix_info: &NixInfo) -> anyhow::Result<()> { diff --git a/crates/omnix-health/src/traits.rs b/crates/omnix-health/src/traits.rs index b84f7d5b..a2afcb8b 100644 --- a/crates/omnix-health/src/traits.rs +++ b/crates/omnix-health/src/traits.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use colored::Colorize; use serde::{Deserialize, Serialize}; @@ -7,7 +9,7 @@ pub trait Checkable { /// Run and create the health check /// /// NOTE: Some checks may perform impure actions (IO, etc.). Returning an - /// empty vector indicates that the check is skipped on this environment. + /// empty hashmap indicates that the check is skipped on this environment. fn check( &self, nix_info: &nix_rs::info::NixInfo, @@ -16,7 +18,7 @@ pub trait Checkable { // If None, the check is run against the current environment, with no // specific configuration from a flake. flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> Vec; + ) -> HashMap; } /// A health check From 706678124ec1bd67583e0ab2f631736b90b75661 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 16 Jan 2025 22:30:18 +0530 Subject: [PATCH 2/8] use static string slice as the key; type name not required in the variable name --- crates/omnix-health/src/check/caches.rs | 6 ++--- crates/omnix-health/src/check/direnv.rs | 24 +++++++++++++------ .../omnix-health/src/check/flake_enabled.rs | 6 ++--- crates/omnix-health/src/check/max_jobs.rs | 6 ++--- .../omnix-health/src/check/min_nix_version.rs | 6 ++--- crates/omnix-health/src/check/rosetta.rs | 8 +++---- crates/omnix-health/src/check/shell.rs | 16 +++++++------ .../omnix-health/src/check/trusted_users.rs | 6 ++--- crates/omnix-health/src/lib.rs | 18 +++++++++----- crates/omnix-health/src/traits.rs | 2 +- 10 files changed, 53 insertions(+), 45 deletions(-) diff --git a/crates/omnix-health/src/check/caches.rs b/crates/omnix-health/src/check/caches.rs index d22dd8d8..006cb3c9 100644 --- a/crates/omnix-health/src/check/caches.rs +++ b/crates/omnix-health/src/check/caches.rs @@ -27,7 +27,7 @@ impl Checkable for Caches { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { let missing_caches = self.get_missing_caches(nix_info); let result = if missing_caches.is_empty() { CheckResult::Green @@ -64,9 +64,7 @@ impl Checkable for Caches { required: true, }; - let mut checks_map = HashMap::new(); - checks_map.insert("caches".to_string(), check); - checks_map + [("caches", check)].into_iter().collect() } } diff --git a/crates/omnix-health/src/check/direnv.rs b/crates/omnix-health/src/check/direnv.rs index ba70b294..24a0ff46 100644 --- a/crates/omnix-health/src/check/direnv.rs +++ b/crates/omnix-health/src/check/direnv.rs @@ -24,17 +24,24 @@ impl Default for Direnv { } impl Checkable for Direnv { - fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> HashMap { - let mut checks_map = HashMap::new(); + fn check( + &self, + _nix_info: &info::NixInfo, + flake_url: Option<&FlakeUrl>, + ) -> HashMap<&'static str, Check> { + let mut checks = HashMap::new(); if !self.enable { - return checks_map; + return checks; } let direnv_install_result = direnv::DirenvInstall::detect(); - checks_map.insert("direnv-install-check".to_string(), install_check(&direnv_install_result, self.required)); + checks.insert( + "direnv-install-check", + install_check(&direnv_install_result, self.required), + ); match direnv_install_result.as_ref() { - Err(_) => return checks_map, + Err(_) => return checks, Ok(direnv_install) => { // If direnv is installed, check for version and then allowed_check // This check is currently only relevant if the flake is local and an `.envrc` exists. @@ -42,14 +49,17 @@ impl Checkable for Direnv { None => {} Some(local_path) => { if local_path.join(".envrc").exists() { - checks_map.insert("direnv-allowed-check".to_string(), allowed_check(direnv_install, local_path, self.required)); + checks.insert( + "direnv-allowed-check", + allowed_check(direnv_install, local_path, self.required), + ); } } } } } - checks_map + checks } } diff --git a/crates/omnix-health/src/check/flake_enabled.rs b/crates/omnix-health/src/check/flake_enabled.rs index 37810611..afe07297 100644 --- a/crates/omnix-health/src/check/flake_enabled.rs +++ b/crates/omnix-health/src/check/flake_enabled.rs @@ -15,7 +15,7 @@ impl Checkable for FlakeEnabled { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { let val = &nix_info.nix_config.experimental_features.value; let check = Check { title: "Flakes Enabled".to_string(), @@ -33,8 +33,6 @@ impl Checkable for FlakeEnabled { required: true, }; - let mut checks_map = HashMap::new(); - checks_map.insert("flake-enabled".to_string(), check); - checks_map + [("flake-enabled", check)].into_iter().collect() } } diff --git a/crates/omnix-health/src/check/max_jobs.rs b/crates/omnix-health/src/check/max_jobs.rs index 94f16603..5e3edf11 100644 --- a/crates/omnix-health/src/check/max_jobs.rs +++ b/crates/omnix-health/src/check/max_jobs.rs @@ -15,7 +15,7 @@ impl Checkable for MaxJobs { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { let max_jobs = nix_info.nix_config.max_jobs.value; let check = Check { title: "Max Jobs".to_string(), @@ -34,8 +34,6 @@ impl Checkable for MaxJobs { required: true, }; - let mut checks_map = HashMap::new(); - checks_map.insert("max-jobs".to_string(), check); - checks_map + [("max-jobs", check)].into_iter().collect() } } diff --git a/crates/omnix-health/src/check/min_nix_version.rs b/crates/omnix-health/src/check/min_nix_version.rs index 9d2cc630..10295506 100644 --- a/crates/omnix-health/src/check/min_nix_version.rs +++ b/crates/omnix-health/src/check/min_nix_version.rs @@ -31,7 +31,7 @@ impl Checkable for MinNixVersion { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { let val = &nix_info.nix_version; let check = Check { title: "Minimum Nix Version".to_string(), @@ -47,8 +47,6 @@ impl Checkable for MinNixVersion { required: true, }; - let mut checks_map = HashMap::new(); - checks_map.insert("nix-version".to_string(), check); - checks_map + [("nix-version", check)].into_iter().collect() } } diff --git a/crates/omnix-health/src/check/rosetta.rs b/crates/omnix-health/src/check/rosetta.rs index f7ecb77b..6537a44d 100644 --- a/crates/omnix-health/src/check/rosetta.rs +++ b/crates/omnix-health/src/check/rosetta.rs @@ -32,8 +32,8 @@ impl Checkable for Rosetta { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { - let mut checks_map = HashMap::new(); + ) -> HashMap<&'static str, Check> { + let mut checks = HashMap::new(); if let (true, Some(emulation)) = (self.enable, get_apple_emulation(&nix_info.nix_env.os)) { let check = Check { title: "Rosetta Not Active".to_string(), @@ -48,9 +48,9 @@ impl Checkable for Rosetta { }, required: self.required, }; - checks_map.insert("rosetta".to_string(), check); + checks.insert("rosetta", check); }; - checks_map + checks } } diff --git a/crates/omnix-health/src/check/shell.rs b/crates/omnix-health/src/check/shell.rs index c413230c..5582a718 100644 --- a/crates/omnix-health/src/check/shell.rs +++ b/crates/omnix-health/src/check/shell.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; use std::{ - collections::HashMap, hash::Hash, path::{Path, PathBuf} + collections::HashMap, + hash::Hash, + path::{Path, PathBuf}, }; use crate::traits::{Check, CheckResult, Checkable}; @@ -26,10 +28,10 @@ impl Checkable for ShellCheck { &self, _nix_info: &nix_rs::info::NixInfo, _flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { - let mut checks_map = HashMap::new(); + ) -> HashMap<&'static str, Check> { + let mut checks = HashMap::new(); if !self.enable { - return checks_map; + return checks; } let user_shell_env = match CurrentUserShellEnv::new() { Ok(shell) => shell, @@ -39,7 +41,7 @@ impl Checkable for ShellCheck { panic!("Unable to determine user's shell environment (see above)"); } else { tracing::warn!("Skipping shell dotfile check! (see above)"); - return checks_map; + return checks; } } }; @@ -76,8 +78,8 @@ impl Checkable for ShellCheck { required: self.required, }; - checks_map.insert("shell".to_string(), check); - checks_map + checks.insert("shell", check); + checks } } diff --git a/crates/omnix-health/src/check/trusted_users.rs b/crates/omnix-health/src/check/trusted_users.rs index 6bf2307a..4b1e47e6 100644 --- a/crates/omnix-health/src/check/trusted_users.rs +++ b/crates/omnix-health/src/check/trusted_users.rs @@ -15,7 +15,7 @@ impl Checkable for TrustedUsers { &self, nix_info: &nix_rs::info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { let result = if is_current_user_trusted(nix_info) { CheckResult::Green } else { @@ -43,9 +43,7 @@ impl Checkable for TrustedUsers { required: true, }; - let mut checks_map = HashMap::new(); - checks_map.insert("trusted-users".to_string(), check); - checks_map + [("trusted-users", check)].into_iter().collect() } } diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index 43765e9c..b335729f 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -79,26 +79,30 @@ impl NixHealth { &self, nix_info: &NixInfo, flake_url: Option, - ) -> HashMap { + ) -> HashMap<&'static str, Check> { self.into_iter() .flat_map(|c| c.check(nix_info, flake_url.as_ref())) .collect() } - pub async fn print_json_report_returning_exit_code(checks_map: &HashMap) -> anyhow::Result { + pub async fn print_json_report_returning_exit_code( + checks_map: &HashMap<&'static str, Check>, + ) -> anyhow::Result { let mut res = AllChecksResult::new(); for check in checks_map.values() { if !check.result.green() { res.register_failure(check.required); }; } - + let code = res.report(); println!("{}", serde_json::to_string_pretty(checks_map)?); Ok(code) } - pub async fn print_report_returning_exit_code(checks_map: &HashMap) -> anyhow::Result { + pub async fn print_report_returning_exit_code( + checks_map: &HashMap<&'static str, Check>, + ) -> anyhow::Result { let mut res = AllChecksResult::new(); for check in checks_map.values() { check.tracing_log().await?; @@ -106,7 +110,7 @@ impl NixHealth { res.register_failure(check.required); }; } - + let code = res.report(); Ok(code) } @@ -117,7 +121,9 @@ impl NixHealth { } /// Run all health checks, optionally using the given flake's configuration -pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result> { +pub async fn run_all_checks_with( + flake_url: Option, +) -> anyhow::Result> { let nix_info = NixInfo::get() .await .as_ref() diff --git a/crates/omnix-health/src/traits.rs b/crates/omnix-health/src/traits.rs index a2afcb8b..3e14aa52 100644 --- a/crates/omnix-health/src/traits.rs +++ b/crates/omnix-health/src/traits.rs @@ -18,7 +18,7 @@ pub trait Checkable { // If None, the check is run against the current environment, with no // specific configuration from a flake. flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap; + ) -> HashMap<&'static str, Check>; } /// A health check From cd7fec5f852d32f79a75196cb0ce16247ede57c3 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 16 Jan 2025 22:30:24 +0530 Subject: [PATCH 3/8] update history --- doc/src/history.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/history.md b/doc/src/history.md index 301c4ec4..8291172e 100644 --- a/doc/src/history.md +++ b/doc/src/history.md @@ -12,6 +12,7 @@ - Display information in Markdown - Remove RAM/disk space checks, moving them to "information" section - Add shell check, to ensure its dotfiles are managed by Nix. + - Add `--json` that returns the health check results as JSON - `om ci` - Support for remote builds over SSH (via `--on` option) - Support for CI steps From 415f3e42411cdac7d058732efed213ca7224186c Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 16 Jan 2025 23:33:27 +0530 Subject: [PATCH 4/8] `json_only` arg to avoid defining an extra function --- crates/omnix-ci/src/command/run.rs | 2 +- crates/omnix-cli/src/command/health.rs | 8 ++------ crates/omnix-health/src/lib.rs | 22 +++++++--------------- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/crates/omnix-ci/src/command/run.rs b/crates/omnix-ci/src/command/run.rs index f1714752..4f437671 100644 --- a/crates/omnix-ci/src/command/run.rs +++ b/crates/omnix-ci/src/command/run.rs @@ -198,7 +198,7 @@ pub async fn check_nix_version(cfg: &OmConfig, nix_info: &NixInfo) -> anyhow::Re let checks = omnix_health .nix_version .check(nix_info, Some(&cfg.flake_url)); - let exit_code = NixHealth::print_report_returning_exit_code(&checks).await?; + let exit_code = NixHealth::print_report_returning_exit_code(&checks, false).await?; if exit_code != 0 { std::process::exit(exit_code); diff --git a/crates/omnix-cli/src/command/health.rs b/crates/omnix-cli/src/command/health.rs index 1b6f27c9..62a4afad 100644 --- a/crates/omnix-cli/src/command/health.rs +++ b/crates/omnix-cli/src/command/health.rs @@ -25,12 +25,8 @@ impl HealthCommand { println!("{}", NixHealth::schema()?); return Ok(()); } - let checks_map = run_all_checks_with(self.flake_url.clone()).await?; - let exit_code = if self.json { - NixHealth::print_json_report_returning_exit_code(&checks_map).await? - } else { - NixHealth::print_report_returning_exit_code(&checks_map).await? - }; + let checks = run_all_checks_with(self.flake_url.clone()).await?; + let exit_code = NixHealth::print_report_returning_exit_code(&checks, self.json).await?; if exit_code != 0 { std::process::exit(exit_code); } diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index b335729f..5957257d 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -85,33 +85,25 @@ impl NixHealth { .collect() } - pub async fn print_json_report_returning_exit_code( + pub async fn print_report_returning_exit_code( checks_map: &HashMap<&'static str, Check>, + json_only: bool, ) -> anyhow::Result { let mut res = AllChecksResult::new(); for check in checks_map.values() { + if !json_only { + check.tracing_log().await?; + } if !check.result.green() { res.register_failure(check.required); }; } let code = res.report(); - println!("{}", serde_json::to_string_pretty(checks_map)?); - Ok(code) - } - pub async fn print_report_returning_exit_code( - checks_map: &HashMap<&'static str, Check>, - ) -> anyhow::Result { - let mut res = AllChecksResult::new(); - for check in checks_map.values() { - check.tracing_log().await?; - if !check.result.green() { - res.register_failure(check.required); - }; + if json_only { + println!("{}", serde_json::to_string_pretty(checks_map)?); } - - let code = res.report(); Ok(code) } From c678a6c8b97f5b9957dae7882bce119b4614ccf0 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 16 Jan 2025 23:34:33 +0530 Subject: [PATCH 5/8] print json in single line --- crates/omnix-health/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index 5957257d..13e43cfc 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -102,7 +102,7 @@ impl NixHealth { let code = res.report(); if json_only { - println!("{}", serde_json::to_string_pretty(checks_map)?); + println!("{}", serde_json::to_string(checks_map)?); } Ok(code) } From 9d2a07aa883d44120628ad9c2cd7ba6c43a9feed Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 17 Jan 2025 00:18:30 +0530 Subject: [PATCH 6/8] Convert to HashMap moment before printing the json --- crates/omnix-develop/src/core.rs | 2 +- crates/omnix-health/src/check/caches.rs | 4 +--- crates/omnix-health/src/check/direnv.rs | 14 ++++++-------- crates/omnix-health/src/check/flake_enabled.rs | 6 ++---- crates/omnix-health/src/check/max_jobs.rs | 6 ++---- .../omnix-health/src/check/min_nix_version.rs | 6 ++---- crates/omnix-health/src/check/rosetta.rs | 8 +++----- crates/omnix-health/src/check/shell.rs | 18 ++++++++---------- crates/omnix-health/src/check/trusted_users.rs | 6 +++--- crates/omnix-health/src/lib.rs | 15 ++++++++------- crates/omnix-health/src/traits.rs | 4 +--- 11 files changed, 37 insertions(+), 52 deletions(-) diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index 0760aae0..f751522e 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -73,7 +73,7 @@ pub async fn develop_on_pre_shell(prj: &Project) -> anyhow::Result<()> { }; for check_kind in relevant_checks.into_iter() { - for check in check_kind.check(nix_info, Some(&prj.flake)).values() { + for (_, check) in check_kind.check(nix_info, Some(&prj.flake)) { if !check.result.green() { check.tracing_log().await?; if !check.result.green() && check.required { diff --git a/crates/omnix-health/src/check/caches.rs b/crates/omnix-health/src/check/caches.rs index 006cb3c9..35b50566 100644 --- a/crates/omnix-health/src/check/caches.rs +++ b/crates/omnix-health/src/check/caches.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::info; use serde::{Deserialize, Serialize}; use url::Url; @@ -27,7 +25,7 @@ impl Checkable for Caches { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { let missing_caches = self.get_missing_caches(nix_info); let result = if missing_caches.is_empty() { CheckResult::Green diff --git a/crates/omnix-health/src/check/direnv.rs b/crates/omnix-health/src/check/direnv.rs index 24a0ff46..7d9c6a37 100644 --- a/crates/omnix-health/src/check/direnv.rs +++ b/crates/omnix-health/src/check/direnv.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::{flake::url::FlakeUrl, info}; use serde::{Deserialize, Serialize}; @@ -28,17 +26,17 @@ impl Checkable for Direnv { &self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>, - ) -> HashMap<&'static str, Check> { - let mut checks = HashMap::new(); + ) -> Vec<(&'static str, Check)> { + let mut checks = vec![]; if !self.enable { return checks; } let direnv_install_result = direnv::DirenvInstall::detect(); - checks.insert( + checks.push(( "direnv-install-check", install_check(&direnv_install_result, self.required), - ); + )); match direnv_install_result.as_ref() { Err(_) => return checks, @@ -49,10 +47,10 @@ impl Checkable for Direnv { None => {} Some(local_path) => { if local_path.join(".envrc").exists() { - checks.insert( + checks.push(( "direnv-allowed-check", allowed_check(direnv_install, local_path, self.required), - ); + )); } } } diff --git a/crates/omnix-health/src/check/flake_enabled.rs b/crates/omnix-health/src/check/flake_enabled.rs index afe07297..884a1730 100644 --- a/crates/omnix-health/src/check/flake_enabled.rs +++ b/crates/omnix-health/src/check/flake_enabled.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::info; use serde::{Deserialize, Serialize}; @@ -15,7 +13,7 @@ impl Checkable for FlakeEnabled { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { let val = &nix_info.nix_config.experimental_features.value; let check = Check { title: "Flakes Enabled".to_string(), @@ -33,6 +31,6 @@ impl Checkable for FlakeEnabled { required: true, }; - [("flake-enabled", check)].into_iter().collect() + vec![("flake-enabled", check)] } } diff --git a/crates/omnix-health/src/check/max_jobs.rs b/crates/omnix-health/src/check/max_jobs.rs index 5e3edf11..24b70f34 100644 --- a/crates/omnix-health/src/check/max_jobs.rs +++ b/crates/omnix-health/src/check/max_jobs.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::info; use serde::{Deserialize, Serialize}; @@ -15,7 +13,7 @@ impl Checkable for MaxJobs { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { let max_jobs = nix_info.nix_config.max_jobs.value; let check = Check { title: "Max Jobs".to_string(), @@ -34,6 +32,6 @@ impl Checkable for MaxJobs { required: true, }; - [("max-jobs", check)].into_iter().collect() + vec![("max-jobs", check)] } } diff --git a/crates/omnix-health/src/check/min_nix_version.rs b/crates/omnix-health/src/check/min_nix_version.rs index 10295506..5cb25fe3 100644 --- a/crates/omnix-health/src/check/min_nix_version.rs +++ b/crates/omnix-health/src/check/min_nix_version.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::version::NixVersion; use nix_rs::info; @@ -31,7 +29,7 @@ impl Checkable for MinNixVersion { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { let val = &nix_info.nix_version; let check = Check { title: "Minimum Nix Version".to_string(), @@ -47,6 +45,6 @@ impl Checkable for MinNixVersion { required: true, }; - [("nix-version", check)].into_iter().collect() + vec![("nix-version", check)] } } diff --git a/crates/omnix-health/src/check/rosetta.rs b/crates/omnix-health/src/check/rosetta.rs index 6537a44d..c07859f0 100644 --- a/crates/omnix-health/src/check/rosetta.rs +++ b/crates/omnix-health/src/check/rosetta.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use nix_rs::{ env::{AppleEmulation, MacOSArch, OS}, info, @@ -32,8 +30,8 @@ impl Checkable for Rosetta { &self, nix_info: &info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { - let mut checks = HashMap::new(); + ) -> Vec<(&'static str, Check)> { + let mut checks = vec![]; if let (true, Some(emulation)) = (self.enable, get_apple_emulation(&nix_info.nix_env.os)) { let check = Check { title: "Rosetta Not Active".to_string(), @@ -48,7 +46,7 @@ impl Checkable for Rosetta { }, required: self.required, }; - checks.insert("rosetta", check); + checks.push(("rosetta", check)); }; checks } diff --git a/crates/omnix-health/src/check/shell.rs b/crates/omnix-health/src/check/shell.rs index 5582a718..7e0db2bb 100644 --- a/crates/omnix-health/src/check/shell.rs +++ b/crates/omnix-health/src/check/shell.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, hash::Hash, path::{Path, PathBuf}, }; @@ -28,10 +28,9 @@ impl Checkable for ShellCheck { &self, _nix_info: &nix_rs::info::NixInfo, _flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { - let mut checks = HashMap::new(); + ) -> Vec<(&'static str, Check)> { if !self.enable { - return checks; + return vec![]; } let user_shell_env = match CurrentUserShellEnv::new() { Ok(shell) => shell, @@ -41,7 +40,7 @@ impl Checkable for ShellCheck { panic!("Unable to determine user's shell environment (see above)"); } else { tracing::warn!("Skipping shell dotfile check! (see above)"); - return checks; + return vec![]; } } }; @@ -78,8 +77,7 @@ impl Checkable for ShellCheck { required: self.required, }; - checks.insert("shell", check); - checks + vec![("shell", check)] } } @@ -90,7 +88,7 @@ struct CurrentUserShellEnv { /// Current shell shell: Shell, /// *Absolute* paths to the dotfiles - dotfiles: HashMap<&'static str, PathBuf>, + dotfiles: BTreeMap<&'static str, PathBuf>, } impl CurrentUserShellEnv { @@ -164,8 +162,8 @@ impl Shell { /// Get the currently existing dotfiles under $HOME /// /// Returned paths will be absolute (i.e., symlinks are resolved). - fn get_dotfiles(&self, home_dir: &Path) -> std::io::Result> { - let mut paths = HashMap::new(); + fn get_dotfiles(&self, home_dir: &Path) -> std::io::Result> { + let mut paths = BTreeMap::new(); for dotfile in self.dotfile_names() { match std::fs::canonicalize(home_dir.join(dotfile)) { Ok(path) => { diff --git a/crates/omnix-health/src/check/trusted_users.rs b/crates/omnix-health/src/check/trusted_users.rs index 4b1e47e6..683529fc 100644 --- a/crates/omnix-health/src/check/trusted_users.rs +++ b/crates/omnix-health/src/check/trusted_users.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use nix_rs::config::TrustedUserValue; use serde::{Deserialize, Serialize}; @@ -15,7 +15,7 @@ impl Checkable for TrustedUsers { &self, nix_info: &nix_rs::info::NixInfo, _: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { let result = if is_current_user_trusted(nix_info) { CheckResult::Green } else { @@ -43,7 +43,7 @@ impl Checkable for TrustedUsers { required: true, }; - [("trusted-users", check)].into_iter().collect() + vec![("trusted-users", check)] } } diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index 13e43cfc..02a729b9 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -79,18 +79,18 @@ impl NixHealth { &self, nix_info: &NixInfo, flake_url: Option, - ) -> HashMap<&'static str, Check> { + ) -> Vec<(&'static str, Check)> { self.into_iter() .flat_map(|c| c.check(nix_info, flake_url.as_ref())) .collect() } pub async fn print_report_returning_exit_code( - checks_map: &HashMap<&'static str, Check>, + checks: &Vec<(&'static str, Check)>, json_only: bool, ) -> anyhow::Result { let mut res = AllChecksResult::new(); - for check in checks_map.values() { + for (_, check) in checks { if !json_only { check.tracing_log().await?; } @@ -102,7 +102,8 @@ impl NixHealth { let code = res.report(); if json_only { - println!("{}", serde_json::to_string(checks_map)?); + let json: HashMap<_, _> = checks.iter().map(|(k, v)| (*k, v)).collect(); + println!("{}", serde_json::to_string(&json)?); } Ok(code) } @@ -115,7 +116,7 @@ impl NixHealth { /// Run all health checks, optionally using the given flake's configuration pub async fn run_all_checks_with( flake_url: Option, -) -> anyhow::Result> { +) -> anyhow::Result> { let nix_info = NixInfo::get() .await .as_ref() @@ -139,8 +140,8 @@ pub async fn run_all_checks_with( print_info_banner(flake_url.as_ref(), nix_info).await?; - let checks_map = health.run_all_checks(nix_info, flake_url); - Ok(checks_map) + let checks = health.run_all_checks(nix_info, flake_url); + Ok(checks) } async fn print_info_banner(flake_url: Option<&FlakeUrl>, nix_info: &NixInfo) -> anyhow::Result<()> { diff --git a/crates/omnix-health/src/traits.rs b/crates/omnix-health/src/traits.rs index 3e14aa52..1110bfbc 100644 --- a/crates/omnix-health/src/traits.rs +++ b/crates/omnix-health/src/traits.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use colored::Colorize; use serde::{Deserialize, Serialize}; @@ -18,7 +16,7 @@ pub trait Checkable { // If None, the check is run against the current environment, with no // specific configuration from a flake. flake: Option<&nix_rs::flake::url::FlakeUrl>, - ) -> HashMap<&'static str, Check>; + ) -> Vec<(&'static str, Check)>; } /// A health check From 50cb76775cdffb2ec4dbf94244ea04a3e4a2cfd8 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 17 Jan 2025 00:20:24 +0530 Subject: [PATCH 7/8] refac --- crates/omnix-health/src/check/caches.rs | 2 +- crates/omnix-health/src/traits.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/omnix-health/src/check/caches.rs b/crates/omnix-health/src/check/caches.rs index 35b50566..64e72451 100644 --- a/crates/omnix-health/src/check/caches.rs +++ b/crates/omnix-health/src/check/caches.rs @@ -62,7 +62,7 @@ impl Checkable for Caches { required: true, }; - [("caches", check)].into_iter().collect() + vec![("caches", check)] } } diff --git a/crates/omnix-health/src/traits.rs b/crates/omnix-health/src/traits.rs index 1110bfbc..8ad23695 100644 --- a/crates/omnix-health/src/traits.rs +++ b/crates/omnix-health/src/traits.rs @@ -7,7 +7,7 @@ pub trait Checkable { /// Run and create the health check /// /// NOTE: Some checks may perform impure actions (IO, etc.). Returning an - /// empty hashmap indicates that the check is skipped on this environment. + /// empty vector indicates that the check is skipped on this environment. fn check( &self, nix_info: &nix_rs::info::NixInfo, From 224f049bb45f3522a70cb005e8e820e5b12adc5f Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 17 Jan 2025 00:31:48 +0530 Subject: [PATCH 8/8] revert irrelevant changes --- crates/omnix-health/src/check/shell.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/omnix-health/src/check/shell.rs b/crates/omnix-health/src/check/shell.rs index 7e0db2bb..ee8ac6bc 100644 --- a/crates/omnix-health/src/check/shell.rs +++ b/crates/omnix-health/src/check/shell.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, hash::Hash, path::{Path, PathBuf}, }; @@ -88,7 +88,7 @@ struct CurrentUserShellEnv { /// Current shell shell: Shell, /// *Absolute* paths to the dotfiles - dotfiles: BTreeMap<&'static str, PathBuf>, + dotfiles: HashMap<&'static str, PathBuf>, } impl CurrentUserShellEnv { @@ -162,8 +162,8 @@ impl Shell { /// Get the currently existing dotfiles under $HOME /// /// Returned paths will be absolute (i.e., symlinks are resolved). - fn get_dotfiles(&self, home_dir: &Path) -> std::io::Result> { - let mut paths = BTreeMap::new(); + fn get_dotfiles(&self, home_dir: &Path) -> std::io::Result> { + let mut paths = HashMap::new(); for dotfile in self.dotfile_names() { match std::fs::canonicalize(home_dir.join(dotfile)) { Ok(path) => {