Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

om health: Add --json #379

Merged
merged 9 commits into from
Jan 16, 2025
12 changes: 10 additions & 2 deletions crates/omnix-cli/src/command/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/omnix-develop/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 7 additions & 2 deletions crates/omnix-health/src/check/caches.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::info;
use serde::{Deserialize, Serialize};
use url::Url;
Expand Down Expand Up @@ -25,7 +27,7 @@ impl Checkable for Caches {
&self,
nix_info: &info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let missing_caches = self.get_missing_caches(nix_info);
let result = if missing_caches.is_empty() {
CheckResult::Green
Expand Down Expand Up @@ -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
}
}

Expand Down
16 changes: 9 additions & 7 deletions crates/omnix-health/src/check/direnv.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::{flake::url::FlakeUrl, info};
use serde::{Deserialize, Serialize};

Expand All @@ -22,32 +24,32 @@ impl Default for Direnv {
}

impl Checkable for Direnv {
fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> Vec<Check> {
let mut checks = vec![];
fn check(&self, _nix_info: &info::NixInfo, flake_url: Option<&FlakeUrl>) -> HashMap<String, Check> {
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.
match flake_url.as_ref().and_then(|url| url.as_local_path()) {
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
}
}

Expand Down
9 changes: 7 additions & 2 deletions crates/omnix-health/src/check/flake_enabled.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::info;
use serde::{Deserialize, Serialize};

Expand All @@ -13,7 +15,7 @@ impl Checkable for FlakeEnabled {
&self,
nix_info: &info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let val = &nix_info.nix_config.experimental_features.value;
let check = Check {
title: "Flakes Enabled".to_string(),
Expand All @@ -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
}
}
9 changes: 7 additions & 2 deletions crates/omnix-health/src/check/max_jobs.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::info;
use serde::{Deserialize, Serialize};

Expand All @@ -13,7 +15,7 @@ impl Checkable for MaxJobs {
&self,
nix_info: &info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let max_jobs = nix_info.nix_config.max_jobs.value;
let check = Check {
title: "Max Jobs".to_string(),
Expand All @@ -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
}
}
9 changes: 7 additions & 2 deletions crates/omnix-health/src/check/min_nix_version.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::version::NixVersion;

use nix_rs::info;
Expand Down Expand Up @@ -29,7 +31,7 @@ impl Checkable for MinNixVersion {
&self,
nix_info: &info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let val = &nix_info.nix_version;
let check = Check {
title: "Minimum Nix Version".to_string(),
Expand All @@ -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
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
}
}
10 changes: 6 additions & 4 deletions crates/omnix-health/src/check/rosetta.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use nix_rs::{
env::{AppleEmulation, MacOSArch, OS},
info,
Expand Down Expand Up @@ -30,8 +32,8 @@ impl Checkable for Rosetta {
&self,
nix_info: &info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
let mut checks = vec![];
) -> HashMap<String, Check> {
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(),
Expand All @@ -46,9 +48,9 @@ impl Checkable for Rosetta {
},
required: self.required,
};
checks.push(check);
checks_map.insert("rosetta".to_string(), check);
};
checks
checks_map
}
}

Expand Down
13 changes: 7 additions & 6 deletions crates/omnix-health/src/check/shell.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -27,9 +26,10 @@ impl Checkable for ShellCheck {
&self,
_nix_info: &nix_rs::info::NixInfo,
_flake: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let mut checks_map = HashMap::new();
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
if !self.enable {
return vec![];
return checks_map;
}
let user_shell_env = match CurrentUserShellEnv::new() {
Ok(shell) => shell,
Expand All @@ -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;
}
}
};
Expand Down Expand Up @@ -76,7 +76,8 @@ impl Checkable for ShellCheck {
required: self.required,
};

vec![check]
checks_map.insert("shell".to_string(), check);
checks_map
}
}

Expand Down
9 changes: 6 additions & 3 deletions crates/omnix-health/src/check/trusted_users.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use nix_rs::config::TrustedUserValue;
use serde::{Deserialize, Serialize};
Expand All @@ -15,7 +15,7 @@ impl Checkable for TrustedUsers {
&self,
nix_info: &nix_rs::info::NixInfo,
_: Option<&nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
) -> HashMap<String, Check> {
let result = if is_current_user_trusted(nix_info) {
CheckResult::Green
} else {
Expand All @@ -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
}
}

Expand Down
30 changes: 23 additions & 7 deletions crates/omnix-health/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<FlakeUrl>,
) -> Vec<traits::Check> {
) -> HashMap<String, 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: &[traits::Check]) -> anyhow::Result<i32> {
pub async fn print_json_report_returning_exit_code(checks_map: &HashMap<String, Check>) -> anyhow::Result<i32> {
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
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<String, Check>) -> anyhow::Result<i32> {
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)
}
Expand All @@ -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<FlakeUrl>) -> anyhow::Result<Vec<Check>> {
pub async fn run_all_checks_with(flake_url: Option<FlakeUrl>) -> anyhow::Result<HashMap<String, Check>> {
let nix_info = NixInfo::get()
.await
.as_ref()
Expand All @@ -125,8 +141,8 @@ pub async fn run_all_checks_with(flake_url: Option<FlakeUrl>) -> 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<()> {
Expand Down
6 changes: 4 additions & 2 deletions crates/omnix-health/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use colored::Colorize;
use serde::{Deserialize, Serialize};

Expand All @@ -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,
Expand All @@ -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<Check>;
) -> HashMap<String, Check>;
shivaraj-bh marked this conversation as resolved.
Show resolved Hide resolved
}

/// A health check
Expand Down
Loading