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

hack: Run only relevant health checks #328

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() {
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?;
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);
}
}
}
check.tracing_log().await?;
if !check.result.green() {
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
32 changes: 32 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,37 @@ pub struct Check {
pub required: bool,
}

impl Check {
/// Log the results using tracing crate
pub async fn tracing_log(&self) -> 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 {
Expand Down