Skip to content

Commit

Permalink
nix-health - Accept config from flake.nix (#67)
Browse files Browse the repository at this point in the history
Also add a nix-health config to this project flake
  • Loading branch information
srid authored Sep 15, 2023
1 parent 34f32ff commit ec4927e
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 30 deletions.
6 changes: 1 addition & 5 deletions crates/nix_health/src/check/caches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ impl Default for Caches {
}

fn default_caches() -> Vec<Url> {
vec![
Url::parse("https://cache.nixos.org").unwrap(),
// TODO: Hardcoding this for now, so as to test failed reports
Url::parse("https://nix-community.cachix.org").unwrap(),
]
vec![Url::parse("https://cache.nixos.org").unwrap()]
}

impl Checkable for Caches {
Expand Down
27 changes: 15 additions & 12 deletions crates/nix_health/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pub mod check;
pub mod report;
pub mod traits;

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

Expand All @@ -28,7 +27,7 @@ pub struct NixHealth {
#[serde(default)]
pub flake_enabled: FlakeEnabled,
#[serde(default)]
pub min_nix_version: MinNixVersion,
pub nix_version: MinNixVersion,
#[serde(default)]
pub trusted_users: TrustedUsers,
}
Expand All @@ -40,7 +39,7 @@ 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<Self::Item> = vec![
&self.min_nix_version,
&self.nix_version,
&self.flake_enabled,
&self.max_jobs,
&self.caches,
Expand All @@ -51,12 +50,16 @@ impl<'a> IntoIterator for &'a NixHealth {
}

impl NixHealth {
pub fn new(m_flake: Option<FlakeUrl>) -> Self {
match m_flake {
None => Self::default(),
// cf. https://github.com/juspay/nix-browser/issues/60
Some(_) => unimplemented!("Per-flake health checks are not yet supported"),
}
/// Create [NixHealth] using configuration from the given flake
///
/// Fallback to using the default health check config if the flake doesn't
/// override it.
#[cfg(feature = "ssr")]
pub async fn from_flake(
url: nix_rs::flake::url::FlakeUrl,
) -> Result<Self, nix_rs::command::NixCmdError> {
use nix_rs::flake::eval::nix_eval_attr_json;
nix_eval_attr_json(url).await
}

/// Run all checks and collect the results
Expand All @@ -75,16 +78,16 @@ mod tests {
fn test_json_deserialize_empty() {
let json = r#"{}"#;
let v: super::NixHealth = serde_json::from_str(json).unwrap();
assert_eq!(v.min_nix_version, MinNixVersion::default());
assert_eq!(v.nix_version, MinNixVersion::default());
assert_eq!(v.caches, Caches::default());
println!("{:?}", v);
}

#[test]
fn test_json_deserialize_some() {
let json = r#"{ "min-nix-version": { "min-required": "2.17.0" } }"#;
let json = r#"{ "nix-version": { "min-required": "2.17.0" } }"#;
let v: super::NixHealth = serde_json::from_str(json).unwrap();
assert_eq!(v.min_nix_version.min_required.to_string(), "2.17.0");
assert_eq!(v.nix_version.min_required.to_string(), "2.17.0");
assert_eq!(v.caches, Caches::default());
}
}
43 changes: 32 additions & 11 deletions crates/nix_health/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
use std::path::Path;

use anyhow::Context;
use colored::Colorize;
use nix_health::{traits::CheckResult, NixHealth};
use nix_health::{
traits::{Check, CheckResult},
NixHealth,
};
use nix_rs::{command::NixCmd, env::NixEnv, info::NixInfo};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
human_panic::setup_panic!();
let nix_info = NixInfo::from_nix(&NixCmd::default())
.await
.with_context(|| "Unable to gather nix info")?;
let nix_env = NixEnv::detect()
.await
.with_context(|| "Unable to gather system info")?;
let health = NixHealth::new(None);
let checks = &health.run_checks(&nix_info, &nix_env);
println!("Checking the health of your Nix setup:\n");
for check in checks {
let checks = run_checks().await?;
for check in &checks {
match &check.result {
CheckResult::Green => {
println!("{}", format!("✅ {}", check.title).green().bold());
Expand All @@ -41,3 +38,27 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}
}

/// Run health checks, taking current directory flake into account if there is
/// one.
async fn run_checks() -> anyhow::Result<Vec<Check>> {
let nix_info = NixInfo::from_nix(&NixCmd::default())
.await
.with_context(|| "Unable to gather nix info")?;
let nix_env = NixEnv::detect()
.await
.with_context(|| "Unable to gather system info")?;
let health: NixHealth = if Path::new("flake.nix").exists() {
let flake_cfg = ".#nix-health.default".into();
println!(
"🩺️ Checking the health of your Nix setup, using config from local flake ({}):\n",
flake_cfg
);
NixHealth::from_flake(flake_cfg).await
} else {
println!("🩺️️ Checking the health of your Nix setup:\n");
Ok(NixHealth::default())
}?;
let checks = health.run_checks(&nix_info, &nix_env);
Ok(checks)
}
34 changes: 34 additions & 0 deletions crates/nix_rs/src/flake/eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::command::{CommandError, NixCmd, NixCmdError};

use super::url::FlakeUrl;

/// Run `nix eval <url> --json` and parse its JSON
///
/// If the flake does not output the given attribute, return the [Default]
/// value of `T`.
pub async fn nix_eval_attr_json<T>(url: FlakeUrl) -> Result<T, NixCmdError>
where
T: Default + serde::de::DeserializeOwned,
{
let nix = NixCmd::default();
let result = nix
.run_with_args_expecting_json(&["eval", url.0.as_str(), "--json"])
.await;
match result {
Err(err) if error_is_missing_attribute(&err) => {
// The 'nixci' flake output attr is missing. User wants the default config.
Ok(T::default())
}
r => r,
}
}

/// Check that [NixCmdError] is a missing attribute error
fn error_is_missing_attribute(err: &NixCmdError) -> bool {
match err {
NixCmdError::CmdError(CommandError::ProcessFailed { stderr, .. }) => {
stderr.contains("does not provide attribute")
}
_ => false,
}
}
2 changes: 2 additions & 0 deletions crates/nix_rs/src/flake/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Rust module for Nix flakes
#[cfg(feature = "ssr")]
pub mod eval;
pub mod outputs;
pub mod schema;
pub mod system;
Expand Down
2 changes: 1 addition & 1 deletion crates/nix_rs/src/flake/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
/// Use `FromStr` to parse a string into a `FlakeUrl`. Or `From` or `Into` if
/// you know the URL is valid.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FlakeUrl(String);
pub struct FlakeUrl(pub String);

impl FlakeUrl {
/// Provide real-world examples of flake URLs
Expand Down
6 changes: 6 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
(inputs.leptos-fullstack + /nix/flake-module.nix)
./e2e/flake-module.nix
];
flake = {
nix-health.default = {
nix-version.min-required = "2.16.0";
caches.required = [ "https://cache.garnix.io" ];
};
};
perSystem = { config, self', pkgs, lib, system, ... }: {
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
Expand Down
2 changes: 1 addition & 1 deletion src/app/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub async fn get_nix_health(_unit: ()) -> Result<Vec<nix_health::traits::Check>,
use nix_rs::{env, info};
let nix_info = info::NixInfo::from_nix(&nix_rs::command::NixCmd::default()).await?;
let nix_env = env::NixEnv::detect().await?;
let health = NixHealth::new(None);
let health = NixHealth::default();
let checks = health.run_checks(&nix_info, &nix_env);
Ok(checks)
}

0 comments on commit ec4927e

Please sign in to comment.