From 2936e3457db95522132dee5996888fb696dc8ad9 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Wed, 23 Oct 2024 16:11:00 -0400 Subject: [PATCH] hack: Run only relevant health checks --- bacon.toml | 18 +++++++--- crates/omnix-cli/src/command/health.rs | 4 +-- crates/omnix-hack/src/core.rs | 37 +++++++++++++++---- crates/omnix-health/src/lib.rs | 50 ++++++++------------------ crates/omnix-health/src/traits.rs | 32 +++++++++++++++++ 5 files changed, 93 insertions(+), 48 deletions(-) diff --git a/bacon.toml b/bacon.toml index 755d4830..0b2c6679 100644 --- a/bacon.toml +++ b/bacon.toml @@ -83,10 +83,6 @@ need_stdout = true allow_warnings = true background = true -# This parameterized job runs the example of your choice, as soon -# as the code compiles. -# Call it as -# bacon ex -- my-example [jobs.health-failing] command = [ "cargo", @@ -100,6 +96,20 @@ command = [ need_stdout = true allow_warnings = true +[jobs.hack] +command = [ + "cargo", + "run", + "--color", + "always", + "--", + "hack", + ".", +] +need_stdout = true +allow_warnings = true + + # You may define here keybindings that would be specific to # a project, for example a shortcut to launch a specific job. # Shortcuts to internal functions (scrolling, toggling, etc.) diff --git a/crates/omnix-cli/src/command/health.rs b/crates/omnix-cli/src/command/health.rs index 3195b6fc..0a164d9e 100644 --- a/crates/omnix-cli/src/command/health.rs +++ b/crates/omnix-cli/src/command/health.rs @@ -1,6 +1,6 @@ use clap::Parser; use nix_rs::flake::url::FlakeUrl; -use omnix_health::{run_checks_with, NixHealth}; +use omnix_health::{run_all_checks_with, NixHealth}; /// Display the health of your Nix dev environment #[derive(Parser, Debug)] @@ -21,7 +21,7 @@ impl HealthCommand { println!("{}", NixHealth::schema()?); return Ok(()); } - let checks = run_checks_with(self.flake_url.clone()).await?; + let checks = run_all_checks_with(self.flake_url.clone()).await?; let exit_code = NixHealth::print_report_returning_exit_code(&checks).await?; if exit_code != 0 { std::process::exit(exit_code); diff --git a/crates/omnix-hack/src/core.rs b/crates/omnix-hack/src/core.rs index d85c1459..c9616f14 100644 --- a/crates/omnix-hack/src/core.rs +++ b/crates/omnix-hack/src/core.rs @@ -1,7 +1,9 @@ +use anyhow::Context; use std::path::Path; -use nix_rs::flake::url::FlakeUrl; +use nix_rs::{flake::url::FlakeUrl, info::NixInfo}; use omnix_common::markdown::print_markdown; +use omnix_health::{traits::Checkable, NixHealth}; use crate::config::HackConfig; @@ -12,13 +14,34 @@ pub async fn hack_on(dir: &Path) -> anyhow::Result<()> { // TODO: cachix check - // Run `om health` foremost - // TODO: Run with --quiet, possibly using `tracing::subscriber::with_default` (it doesn't work for some reason) - let checks = omnix_health::run_checks_with(Some(here_flake)).await?; - let exit_code = omnix_health::NixHealth::print_report_returning_exit_code(&checks).await?; - if exit_code != 0 { - anyhow::bail!("Health checks failed"); + // Run relevant `om health` checks + let health = NixHealth::from_flake(&here_flake).await?; + let nix_info = NixInfo::get() + .await + .as_ref() + .with_context(|| "Unable to gather nix info")?; + let relevant_checks: Vec<&'_ dyn Checkable> = vec![ + &health.nix_version, + &health.rosetta, + &health.max_jobs, + &health.trusted_users, + &health.caches, + ]; + for check_kind in relevant_checks.into_iter() { + let checks = check_kind.check(nix_info, Some(&here_flake)); + for check in checks { + if !check.result.green() { + check.tracing_log().await?; + // TODO: Auto-resolve some problems; like running 'cachix use' automatically + // ... after 'cachix authtoken' if that's available (but where?) + if !check.result.green() && check.required { + tracing::error!("ERROR: Your Nix invironment is not properly setup. Run `om health` for details."); + anyhow::bail!("Cannot proceed"); + }; + }; + } } + tracing::info!("Healthy"); eprintln!(); print_markdown(&dir, &cfg.readme.get_markdown()).await?; diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index f1696450..65736c61 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -23,21 +23,22 @@ use self::check::{ rosetta::Rosetta, trusted_users::TrustedUsers, }; -/// Nix Health check information for user's install +/// Nix Health check of user's install /// -/// Each field represents an individual check which satisfies the [traits::Checkable] trait. +/// Each check field is expected to implement [traits::Checkable]. #[derive(Debug, Default, Serialize, Deserialize, Clone)] #[serde(default, rename_all = "kebab-case")] pub struct NixHealth { - pub nix_version: MinNixVersion, pub flake_enabled: FlakeEnabled, - pub max_jobs: MaxJobs, + pub nix_version: MinNixVersion, pub rosetta: Rosetta, + pub max_jobs: MaxJobs, pub trusted_users: TrustedUsers, pub caches: Caches, pub direnv: Direnv, } +/// Convert [NixHealth] into a generic [Vec] of checks impl<'a> IntoIterator for &'a NixHealth { type Item = &'a dyn traits::Checkable; type IntoIter = std::vec::IntoIter; @@ -45,10 +46,10 @@ impl<'a> IntoIterator for &'a NixHealth { /// Return an iterator to iterate on the fields of [NixHealth] fn into_iter(self) -> Self::IntoIter { let items: Vec = vec![ - &self.nix_version, &self.flake_enabled, - &self.max_jobs, + &self.nix_version, &self.rosetta, + &self.max_jobs, &self.trusted_users, &self.caches, &self.direnv, @@ -72,7 +73,7 @@ impl NixHealth { /// Run all checks and collect the results #[instrument(skip_all)] - pub fn run_checks( + pub fn run_all_checks( &self, nix_info: &nix_rs::info::NixInfo, flake_url: Option, @@ -84,32 +85,11 @@ impl NixHealth { pub async fn print_report_returning_exit_code(checks: &[traits::Check]) -> anyhow::Result { let mut res = AllChecksResult::new(); - let pwd = std::env::current_dir()?; - let md = async |s: &str| render_markdown(&pwd, s).await; for check in checks { - match &check.result { - traits::CheckResult::Green => { - tracing::info!("✅ {}", check.title.green().bold()); - tracing::info!("{}", md(&check.info).await?.dimmed()); - } - traits::CheckResult::Red { msg, suggestion } => { - res.register_failure(check.required); - let solution = md(&format!( - "**Problem**: {}\\\n**Fix**: {}\n", - msg, suggestion - )) - .await?; - if check.required { - tracing::error!("❌ {}", md(&check.title).await?.red().bold()); - tracing::error!("{}", md(&check.info).await?.dimmed()); - tracing::error!("{}", solution); - } else { - tracing::warn!("🟧 {}", md(&check.title).await?.yellow().bold()); - tracing::warn!("{}", md(&check.info).await?.dimmed()); - tracing::warn!("{}", solution); - } - } - } + check.tracing_log().await?; + if !check.result.green() { + res.register_failure(check.required); + }; } let code = res.report(); Ok(code) @@ -120,8 +100,8 @@ impl NixHealth { } } -/// Run health checks, optionally using the given flake's configuration -pub async fn run_checks_with(flake_url: Option) -> anyhow::Result> { +/// Run all health checks, optionally using the given flake's configuration +pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result> { let nix_info = NixInfo::get() .await .as_ref() @@ -136,7 +116,7 @@ pub async fn run_checks_with(flake_url: Option) -> anyhow::Result anyhow::Result<()> { + let pwd = std::env::current_dir()?; + let md = async |s: &str| omnix_common::markdown::render_markdown(&pwd, s).await; + match &self.result { + CheckResult::Green => { + tracing::info!("✅ {}", self.title.green().bold()); + tracing::info!("{}", md(&self.info).await?.dimmed()); + } + CheckResult::Red { msg, suggestion } => { + let solution = md(&format!( + "**Problem**: {}\\\n**Fix**: {}\n", + msg, suggestion + )) + .await?; + if self.required { + tracing::error!("❌ {}", md(&self.title).await?.red().bold()); + tracing::error!("{}", md(&self.info).await?.dimmed()); + tracing::error!("{}", solution); + } else { + tracing::warn!("🟧 {}", md(&self.title).await?.yellow().bold()); + tracing::warn!("{}", md(&self.info).await?.dimmed()); + tracing::warn!("{}", solution); + } + } + } + Ok(()) + } +} + /// The result of a health [Check] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub enum CheckResult {