Skip to content

Commit

Permalink
hack: Run only relevant health checks
Browse files Browse the repository at this point in the history
  • Loading branch information
srid committed Oct 23, 2024
1 parent 22442f4 commit 0e3bc64
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 48 deletions.
18 changes: 14 additions & 4 deletions bacon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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.)
Expand Down
4 changes: 2 additions & 2 deletions crates/omnix-cli/src/command/health.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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);
Expand Down
37 changes: 30 additions & 7 deletions crates/omnix-hack/src/core.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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() {
let failed = check.tracing_log().await?;
// TODO: Auto-resolve some problems; like running 'cachix use' automatically
// ... after 'cachix authtoken' if that's available (but where?)
if failed {
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?;
Expand Down
50 changes: 15 additions & 35 deletions crates/omnix-health/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,33 @@ 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<Self::Item>;

/// Return an iterator to iterate on the fields of [NixHealth]
fn into_iter(self) -> Self::IntoIter {
let items: Vec<Self::Item> = 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,
Expand All @@ -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<FlakeUrl>,
Expand All @@ -84,32 +85,11 @@ impl NixHealth {

pub async fn print_report_returning_exit_code(checks: &[traits::Check]) -> anyhow::Result<i32> {
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);
}
}
}
let failed = check.tracing_log().await?;
if failed {
res.register_failure(check.required);
};
}
let code = res.report();
Ok(code)
Expand All @@ -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<FlakeUrl>) -> anyhow::Result<Vec<Check>> {
/// 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>> {
let nix_info = NixInfo::get()
.await
.as_ref()
Expand All @@ -136,7 +116,7 @@ pub async fn run_checks_with(flake_url: Option<FlakeUrl>) -> anyhow::Result<Vec<

print_info_banner(flake_url.as_ref(), nix_info).await?;

let checks = health.run_checks(nix_info, flake_url);
let checks = health.run_all_checks(nix_info, flake_url);
Ok(checks)
}

Expand Down
35 changes: 35 additions & 0 deletions crates/omnix-health/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use colored::Colorize;
use serde::{Deserialize, Serialize};

/// Types that can do specific "health check" for Nix
Expand Down Expand Up @@ -39,6 +40,40 @@ pub struct Check {
pub required: bool,
}

impl Check {
/// Log the results using tracing crate
///
/// Return true if this check has failed
pub async fn tracing_log(&self) -> anyhow::Result<bool> {
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());
Ok(false)
}
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(true)
}
}
}
}

/// The result of a health [Check]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum CheckResult {
Expand Down

0 comments on commit 0e3bc64

Please sign in to comment.