From a7ddce87bdab6684a4c86291727410bb871c321e Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Sat, 9 Nov 2024 01:04:30 +0530 Subject: [PATCH 01/22] PoC: Demonstrate reusing om config in `om develop` --- Cargo.lock | 1 + crates/omnix-cli/src/command/develop.rs | 14 +++-- crates/omnix-common/Cargo.toml | 1 + crates/omnix-common/src/config.rs | 82 ++++++++++++++++++++++++- crates/omnix-develop/src/config.rs | 11 +++- crates/omnix-develop/src/core.rs | 14 ++--- crates/omnix-health/src/lib.rs | 7 ++- 7 files changed, 114 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 133b91bb..b2c7c221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1868,6 +1868,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-mdcat", "serde", + "serde_json", "syntect", "thiserror", "tokio", diff --git a/crates/omnix-cli/src/command/develop.rs b/crates/omnix-cli/src/command/develop.rs index 27bc7985..4f418c08 100644 --- a/crates/omnix-cli/src/command/develop.rs +++ b/crates/omnix-cli/src/command/develop.rs @@ -1,5 +1,6 @@ use clap::Parser; -use nix_rs::flake::url::FlakeUrl; +use nix_rs::{command::NixCmd, flake::url::FlakeUrl}; +use omnix_common::config::OmnixConfig; /// Prepare to develop on a flake project #[derive(Parser, Debug)] @@ -26,12 +27,17 @@ enum Stage { impl DevelopCommand { pub async fn run(&self) -> anyhow::Result<()> { let flake = self.flake_shell.without_attr(); + + let om_config = OmnixConfig::from_flake_url(NixCmd::get().await, &self.flake_shell).await?; + tracing::info!("⌨️ Preparing to develop project: {:}", &flake); - let prj = omnix_develop::core::Project::new(flake).await?; + let prj = omnix_develop::core::Project::new(flake, &om_config).await?; match self.stage { - Some(Stage::PreShell) => omnix_develop::core::develop_on_pre_shell(&prj).await?, + Some(Stage::PreShell) => { + omnix_develop::core::develop_on_pre_shell(&prj, &om_config).await? + } Some(Stage::PostShell) => omnix_develop::core::develop_on_post_shell(&prj).await?, - None => omnix_develop::core::develop_on(&prj).await?, + None => omnix_develop::core::develop_on(&prj, &om_config).await?, } Ok(()) } diff --git a/crates/omnix-common/Cargo.toml b/crates/omnix-common/Cargo.toml index e5419d65..4190abec 100644 --- a/crates/omnix-common/Cargo.toml +++ b/crates/omnix-common/Cargo.toml @@ -26,3 +26,4 @@ tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } which = { workspace = true } +serde_json = { workspace = true } diff --git a/crates/omnix-common/src/config.rs b/crates/omnix-common/src/config.rs index 5186c572..5923a3c0 100644 --- a/crates/omnix-common/src/config.rs +++ b/crates/omnix-common/src/config.rs @@ -4,13 +4,77 @@ use std::collections::BTreeMap; use nix_rs::{ command::NixCmd, - flake::url::{ - qualified_attr::{nix_eval_qualified_attr, QualifiedAttrError}, - FlakeUrl, + flake::{ + eval::nix_eval_attr, + url::{ + qualified_attr::{nix_eval_qualified_attr, QualifiedAttrError}, + FlakeUrl, + }, }, }; use serde::de::DeserializeOwned; +/// Reference to the whole `om` configuration in a flake +pub struct OmnixConfig { + /// The flake URL used to load this configuration + pub flake_url: FlakeUrl, + + /// The (nested) key reference into the flake config. + pub reference: Vec, + + /// The whole `om` configuration parsed from JSON + pub config: BTreeMap>, +} + +impl OmnixConfig { + /// Read the om configuration from the flake url + pub async fn from_flake_url(cmd: &NixCmd, flake_url: &FlakeUrl) -> Result { + let qualified_url = flake_url.with_attr("om"); + let config = nix_eval_attr(cmd, &qualified_url) + .await? + .unwrap_or_default(); + + Ok(OmnixConfig { + flake_url: flake_url.clone(), + reference: flake_url.get_attr().as_list(), + config, + }) + } + /// Get the user-referenced config value `T` for a given sub-config + pub fn get_referenced_for(&self, sub_config: &str) -> Result<(T, &[String]), OmConfigError> + where + T: Default + DeserializeOwned, + { + // Early return if sub_config doesn't exist + let config = match self.config.get(sub_config) { + Some(config) => config, + None => { + return match self.flake_url.get_attr().0 { + None => Ok((T::default(), &[])), + Some(attr) => Err(OmConfigError::UnexpectedAttribute(attr)), + } + } + }; + + // Try to get value from reference path first + if let Some((k, rest)) = self.reference.split_first() { + let value = config + .get(k) + .ok_or_else(|| OmConfigError::MissingConfigAttribute(k.to_string()))?; + return Ok((serde_json::from_value(value.clone())?, rest)); + } + + // Fall back to default or T::default() + let value = config + .get("default") + .map(|v| serde_json::from_value(v.clone())) + .transpose()? + .unwrap_or_default(); + + Ok((value, &[])) + } +} + /// Reference to some Omnix configuration of type `BTeeMap` in a flake /// /// For example, CI configuration at `om.ci.default` is captured by the `T` type. @@ -84,4 +148,16 @@ pub enum OmConfigError { /// Missing configuration attribute #[error("Missing configuration attribute: {0}")] MissingConfigAttribute(String), + + /// Unexpected attribute + #[error("Unexpected attribute: {0}")] + UnexpectedAttribute(String), + + /// A [nix_rs::command::NixCmdError] + #[error("Nix command error: {0}")] + NixCmdError(#[from] nix_rs::command::NixCmdError), + + /// Failed to parse JSON + #[error("Failed to decode (json error): {0}")] + DecodeErrorJson(#[from] serde_json::Error), } diff --git a/crates/omnix-develop/src/config.rs b/crates/omnix-develop/src/config.rs index 4633e8bb..6247ad94 100644 --- a/crates/omnix-develop/src/config.rs +++ b/crates/omnix-develop/src/config.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use nix_rs::{command::NixCmd, flake::url::FlakeUrl}; -use omnix_common::config::OmConfig; +use omnix_common::config::{OmConfig, OmnixConfig}; use crate::readme::Readme; @@ -17,6 +17,15 @@ pub struct CacheConfig { } impl DevelopConfig { + pub async fn from_om_config(om_config: &OmnixConfig) -> anyhow::Result { + if let Some(v) = om_config.config.get("develop") { + let config = v.get("default").cloned().unwrap_or_default(); + let v1 = serde_json::from_value(config)?; + Ok(v1) + } else { + Ok(Default::default()) + } + } pub async fn from_flake(url: &FlakeUrl) -> anyhow::Result { let v = OmConfig::::from_flake_url(NixCmd::get().await, url, &["om.develop"]) .await? diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index 56f0f104..3e49b30f 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -2,7 +2,7 @@ use anyhow::Context; use std::{env::current_dir, path::PathBuf}; use nix_rs::{flake::url::FlakeUrl, info::NixInfo}; -use omnix_common::markdown::print_markdown; +use omnix_common::{config::OmnixConfig, markdown::print_markdown}; use omnix_health::{check::caches::CachixCache, traits::Checkable, NixHealth}; use crate::config::DevelopConfig; @@ -18,18 +18,18 @@ pub struct Project { } impl Project { - pub async fn new(flake: FlakeUrl) -> anyhow::Result { + pub async fn new(flake: FlakeUrl, om_config: &OmnixConfig) -> anyhow::Result { let dir = match flake.as_local_path() { Some(path) => Some(path.canonicalize()?), None => None, }; - let cfg = DevelopConfig::from_flake(&flake).await?; + let cfg = DevelopConfig::from_om_config(&om_config).await?; Ok(Self { dir, flake, cfg }) } } -pub async fn develop_on(prj: &Project) -> anyhow::Result<()> { - develop_on_pre_shell(prj).await?; +pub async fn develop_on(prj: &Project, om_config: &OmnixConfig) -> anyhow::Result<()> { + develop_on_pre_shell(prj, om_config).await?; develop_on_post_shell(prj).await?; tracing::warn!(""); @@ -39,9 +39,9 @@ pub async fn develop_on(prj: &Project) -> anyhow::Result<()> { Ok(()) } -pub async fn develop_on_pre_shell(prj: &Project) -> anyhow::Result<()> { +pub async fn develop_on_pre_shell(prj: &Project, om_config: &OmnixConfig) -> anyhow::Result<()> { // Run relevant `om health` checks - let health = NixHealth::from_flake(&prj.flake).await?; + let health = NixHealth::from_om_config(om_config)?; let nix_info = NixInfo::get() .await .as_ref() diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index 61c1e0a6..c15b6834 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -13,7 +13,7 @@ use check::direnv::Direnv; use nix_rs::env::OS; use nix_rs::flake::url::FlakeUrl; use nix_rs::{command::NixCmd, info::NixInfo}; -use omnix_common::config::{OmConfig, OmConfigError}; +use omnix_common::config::{OmConfig, OmConfigError, OmnixConfig}; use omnix_common::markdown::render_markdown; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -62,6 +62,11 @@ impl<'a> IntoIterator for &'a NixHealth { } impl NixHealth { + pub fn from_om_config(om_config: &OmnixConfig) -> Result { + // TODO: handle nix-health + let (cfg, _rest) = om_config.get_referenced_for::("health")?; + Ok(cfg.clone()) + } /// Create [NixHealth] using configuration from the given flake /// /// Fallback to using the default health check config if the flake doesn't From e11ba5618cb06bb40745eea5ed51bcf5e1eca8a9 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Sat, 9 Nov 2024 01:19:28 +0530 Subject: [PATCH 02/22] needless borrow --- crates/omnix-develop/src/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index 3e49b30f..cde5d31a 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -23,7 +23,7 @@ impl Project { Some(path) => Some(path.canonicalize()?), None => None, }; - let cfg = DevelopConfig::from_om_config(&om_config).await?; + let cfg = DevelopConfig::from_om_config(om_config).await?; Ok(Self { dir, flake, cfg }) } } From f0e792f6687c4418feb740180018c14824887996 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 11 Nov 2024 18:01:18 +0530 Subject: [PATCH 03/22] Project takes the ownership of OmnixConfig --- crates/omnix-cli/src/command/develop.rs | 8 +++----- crates/omnix-develop/src/config.rs | 2 +- crates/omnix-develop/src/core.rs | 24 ++++++++++++++---------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/omnix-cli/src/command/develop.rs b/crates/omnix-cli/src/command/develop.rs index 4f418c08..fb4b411c 100644 --- a/crates/omnix-cli/src/command/develop.rs +++ b/crates/omnix-cli/src/command/develop.rs @@ -31,13 +31,11 @@ impl DevelopCommand { let om_config = OmnixConfig::from_flake_url(NixCmd::get().await, &self.flake_shell).await?; tracing::info!("⌨️ Preparing to develop project: {:}", &flake); - let prj = omnix_develop::core::Project::new(flake, &om_config).await?; + let prj = omnix_develop::core::Project::new(flake, om_config).await?; match self.stage { - Some(Stage::PreShell) => { - omnix_develop::core::develop_on_pre_shell(&prj, &om_config).await? - } + Some(Stage::PreShell) => omnix_develop::core::develop_on_pre_shell(&prj).await?, Some(Stage::PostShell) => omnix_develop::core::develop_on_post_shell(&prj).await?, - None => omnix_develop::core::develop_on(&prj, &om_config).await?, + None => omnix_develop::core::develop_on(&prj).await?, } Ok(()) } diff --git a/crates/omnix-develop/src/config.rs b/crates/omnix-develop/src/config.rs index 6247ad94..3a65180a 100644 --- a/crates/omnix-develop/src/config.rs +++ b/crates/omnix-develop/src/config.rs @@ -17,7 +17,7 @@ pub struct CacheConfig { } impl DevelopConfig { - pub async fn from_om_config(om_config: &OmnixConfig) -> anyhow::Result { + pub fn from_om_config(om_config: &OmnixConfig) -> anyhow::Result { if let Some(v) = om_config.config.get("develop") { let config = v.get("default").cloned().unwrap_or_default(); let v1 = serde_json::from_value(config)?; diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index cde5d31a..74643cd9 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -13,23 +13,26 @@ pub struct Project { pub dir: Option, /// [FlakeUrl] corresponding to the project. pub flake: FlakeUrl, - /// The develop configuration - pub cfg: DevelopConfig, + /// The `om` configuration + pub om_config: OmnixConfig, } impl Project { - pub async fn new(flake: FlakeUrl, om_config: &OmnixConfig) -> anyhow::Result { + pub async fn new(flake: FlakeUrl, om_config: OmnixConfig) -> anyhow::Result { let dir = match flake.as_local_path() { Some(path) => Some(path.canonicalize()?), None => None, }; - let cfg = DevelopConfig::from_om_config(om_config).await?; - Ok(Self { dir, flake, cfg }) + Ok(Self { + dir, + flake, + om_config, + }) } } -pub async fn develop_on(prj: &Project, om_config: &OmnixConfig) -> anyhow::Result<()> { - develop_on_pre_shell(prj, om_config).await?; +pub async fn develop_on(prj: &Project) -> anyhow::Result<()> { + develop_on_pre_shell(prj).await?; develop_on_post_shell(prj).await?; tracing::warn!(""); @@ -39,9 +42,9 @@ pub async fn develop_on(prj: &Project, om_config: &OmnixConfig) -> anyhow::Resul Ok(()) } -pub async fn develop_on_pre_shell(prj: &Project, om_config: &OmnixConfig) -> anyhow::Result<()> { +pub async fn develop_on_pre_shell(prj: &Project) -> anyhow::Result<()> { // Run relevant `om health` checks - let health = NixHealth::from_om_config(om_config)?; + let health = NixHealth::from_om_config(&prj.om_config)?; let nix_info = NixInfo::get() .await .as_ref() @@ -89,7 +92,8 @@ pub async fn develop_on_post_shell(prj: &Project) -> anyhow::Result<()> { eprintln!(); let pwd = current_dir()?; let dir = prj.dir.as_ref().unwrap_or(&pwd); - print_markdown(dir, prj.cfg.readme.get_markdown()).await?; + let cfg = DevelopConfig::from_om_config(&prj.om_config)?; + print_markdown(dir, cfg.readme.get_markdown()).await?; Ok(()) } From 8fb2156c332f95db468abfdae4d13116fe0ebd05 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 11 Nov 2024 19:43:06 +0530 Subject: [PATCH 04/22] Switch all sub-commands to use the new OmConfig --- crates/omnix-ci/src/command/core.rs | 4 +- crates/omnix-ci/src/command/gh_matrix.rs | 4 +- crates/omnix-ci/src/command/run.rs | 29 +++--- crates/omnix-ci/src/command/run_remote.rs | 9 +- crates/omnix-ci/src/config/core.rs | 18 ++-- crates/omnix-cli/src/command/develop.rs | 4 +- crates/omnix-common/src/config.rs | 102 ++++++---------------- crates/omnix-develop/src/config.rs | 12 +-- crates/omnix-develop/src/core.rs | 6 +- crates/omnix-health/src/lib.rs | 20 ++--- crates/omnix-init/src/config.rs | 7 +- 11 files changed, 66 insertions(+), 149 deletions(-) diff --git a/crates/omnix-ci/src/command/core.rs b/crates/omnix-ci/src/command/core.rs index f2ed0671..44cbbb70 100644 --- a/crates/omnix-ci/src/command/core.rs +++ b/crates/omnix-ci/src/command/core.rs @@ -5,7 +5,7 @@ use nix_rs::command::NixCmd; use omnix_common::config::OmConfig; use tracing::instrument; -use crate::{config::subflakes::SubflakesConfig, flake_ref::FlakeRef}; +use crate::flake_ref::FlakeRef; use super::{gh_matrix::GHMatrixCommand, run::RunCommand}; @@ -46,7 +46,7 @@ impl Command { } /// Get the omnix-ci [config::Config] associated with this subcommand - async fn get_config(&self, cmd: &NixCmd) -> anyhow::Result> { + async fn get_config(&self, cmd: &NixCmd) -> anyhow::Result { let url = self.get_flake_ref().to_flake_url().await?; let cfg = crate::config::core::ci_config_from_flake_url(cmd, &url).await?; tracing::debug!("Config: {cfg:?}"); diff --git a/crates/omnix-ci/src/command/gh_matrix.rs b/crates/omnix-ci/src/command/gh_matrix.rs index 6473c304..3c075304 100644 --- a/crates/omnix-ci/src/command/gh_matrix.rs +++ b/crates/omnix-ci/src/command/gh_matrix.rs @@ -22,8 +22,8 @@ pub struct GHMatrixCommand { impl GHMatrixCommand { /// Run the command - pub async fn run(&self, cfg: OmConfig) -> anyhow::Result<()> { - let (config, _rest) = cfg.get_referenced()?; + pub async fn run(&self, cfg: OmConfig) -> anyhow::Result<()> { + let (config, _rest) = cfg.get_referenced_for::("ci")?; let matrix = github::matrix::GitHubMatrix::from(self.systems.clone(), &config); println!("{}", serde_json::to_string(&matrix)?); Ok(()) diff --git a/crates/omnix-ci/src/command/run.rs b/crates/omnix-ci/src/command/run.rs index 9e4164f8..b298bd9f 100644 --- a/crates/omnix-ci/src/command/run.rs +++ b/crates/omnix-ci/src/command/run.rs @@ -65,12 +65,7 @@ impl RunCommand { } /// Run the build command which decides whether to do ci run on current machine or a remote machine - pub async fn run( - &self, - nixcmd: &NixCmd, - verbose: bool, - cfg: OmConfig, - ) -> anyhow::Result<()> { + pub async fn run(&self, nixcmd: &NixCmd, verbose: bool, cfg: OmConfig) -> anyhow::Result<()> { match &self.on { Some(store_uri) => run_remote::run_on_remote_store(nixcmd, self, &cfg, store_uri).await, None => self.run_local(nixcmd, verbose, cfg).await, @@ -78,12 +73,7 @@ impl RunCommand { } /// Run [RunCommand] on local Nix store. - async fn run_local( - &self, - nixcmd: &NixCmd, - verbose: bool, - cfg: OmConfig, - ) -> anyhow::Result<()> { + async fn run_local(&self, nixcmd: &NixCmd, verbose: bool, cfg: OmConfig) -> anyhow::Result<()> { // TODO: We'll refactor this function to use steps // https://github.com/juspay/omnix/issues/216 @@ -95,7 +85,7 @@ impl RunCommand { // First, run the necessary health checks tracing::info!("{}", "\n🫀 Performing health check".bold()); - check_nix_version(&cfg.flake_url, nix_info).await?; + check_nix_version(&cfg, nix_info).await?; // Then, do the CI steps tracing::info!( @@ -153,9 +143,11 @@ impl RunCommand { } /// Check that Nix version is not too old. -pub async fn check_nix_version(flake_url: &FlakeUrl, nix_info: &NixInfo) -> anyhow::Result<()> { - let omnix_health = NixHealth::from_flake(flake_url).await?; - let checks = omnix_health.nix_version.check(nix_info, Some(flake_url)); +pub async fn check_nix_version(cfg: &OmConfig, nix_info: &NixInfo) -> anyhow::Result<()> { + let omnix_health = NixHealth::from_om_config(cfg)?; + let checks = omnix_health + .nix_version + .check(nix_info, Some(&cfg.flake_url)); let exit_code = NixHealth::print_report_returning_exit_code(&checks).await?; if exit_code != 0 { @@ -169,13 +161,14 @@ pub async fn ci_run( cmd: &NixCmd, verbose: bool, run_cmd: &RunCommand, - cfg: &OmConfig, + cfg: &OmConfig, nix_config: &NixConfig, ) -> anyhow::Result { let mut res = HashMap::new(); let systems = run_cmd.get_systems(cmd, nix_config).await?; - let (config, attrs) = cfg.get_referenced()?; + let (config, attrs) = cfg.get_referenced_for::("ci")?; + // User's filter by subflake name let only_subflake = attrs.first(); diff --git a/crates/omnix-ci/src/command/run_remote.rs b/crates/omnix-ci/src/command/run_remote.rs index c4a9adbe..30673c06 100644 --- a/crates/omnix-ci/src/command/run_remote.rs +++ b/crates/omnix-ci/src/command/run_remote.rs @@ -10,8 +10,6 @@ use omnix_common::config::OmConfig; use std::path::PathBuf; use tokio::process::Command; -use crate::config::subflakes::SubflakesConfig; - use super::run::RunCommand; /// Path to Rust source corresponding to this (running) instance of Omnix @@ -21,7 +19,7 @@ const OMNIX_SOURCE: &str = env!("OMNIX_SOURCE"); pub async fn run_on_remote_store( nixcmd: &NixCmd, run_cmd: &RunCommand, - cfg: &OmConfig, + cfg: &OmConfig, store_uri: &StoreURI, ) -> anyhow::Result<()> { tracing::info!( @@ -48,10 +46,7 @@ pub async fn run_on_remote_store( } /// Return the locally cached [FlakeUrl] for the given flake url that points to same selected [ConfigRef]. -async fn cache_flake( - nixcmd: &NixCmd, - cfg: &OmConfig, -) -> anyhow::Result<(PathBuf, FlakeUrl)> { +async fn cache_flake(nixcmd: &NixCmd, cfg: &OmConfig) -> anyhow::Result<(PathBuf, FlakeUrl)> { let metadata = FlakeMetadata::from_nix(nixcmd, &cfg.flake_url).await?; let path = metadata.path.to_string_lossy().into_owned(); let attr = cfg.reference.join("."); diff --git a/crates/omnix-ci/src/config/core.rs b/crates/omnix-ci/src/config/core.rs index 713926d2..2e87446d 100644 --- a/crates/omnix-ci/src/config/core.rs +++ b/crates/omnix-ci/src/config/core.rs @@ -3,8 +3,6 @@ use anyhow::Result; use nix_rs::{command::NixCmd, flake::url::FlakeUrl}; use omnix_common::config::OmConfig; -use super::subflakes::SubflakesConfig; - /// Create a `Config` pointed to by this [FlakeUrl] /// /// Example: @@ -13,21 +11,15 @@ use super::subflakes::SubflakesConfig; /// let cfg = Config::from_flake_url(&url).await?; /// ``` /// along with the config. -pub async fn ci_config_from_flake_url( - cmd: &NixCmd, - url: &FlakeUrl, -) -> Result> { - let v = omnix_common::config::OmConfig::::from_flake_url( - cmd, - url, - &["om.ci", "nixci"], - ) - .await?; +pub async fn ci_config_from_flake_url(cmd: &NixCmd, url: &FlakeUrl) -> Result { + let v = omnix_common::config::OmConfig::from_flake_url(cmd, url).await?; Ok(v) } #[cfg(test)] mod tests { + use crate::config::subflakes::SubflakesConfig; + use super::*; #[tokio::test] @@ -41,7 +33,7 @@ mod tests { let cfg = ci_config_from_flake_url(&NixCmd::default(), url) .await .unwrap(); - let (config, attrs) = cfg.get_referenced().unwrap(); + let (config, attrs) = cfg.get_referenced_for::("ci").unwrap(); assert_eq!(attrs, &["dev"]); // assert_eq!(cfg.selected_subconfig, Some("dev".to_string())); assert_eq!(config.0.len(), 7); diff --git a/crates/omnix-cli/src/command/develop.rs b/crates/omnix-cli/src/command/develop.rs index fb4b411c..c7e44983 100644 --- a/crates/omnix-cli/src/command/develop.rs +++ b/crates/omnix-cli/src/command/develop.rs @@ -1,6 +1,6 @@ use clap::Parser; use nix_rs::{command::NixCmd, flake::url::FlakeUrl}; -use omnix_common::config::OmnixConfig; +use omnix_common::config::OmConfig; /// Prepare to develop on a flake project #[derive(Parser, Debug)] @@ -28,7 +28,7 @@ impl DevelopCommand { pub async fn run(&self) -> anyhow::Result<()> { let flake = self.flake_shell.without_attr(); - let om_config = OmnixConfig::from_flake_url(NixCmd::get().await, &self.flake_shell).await?; + let om_config = OmConfig::from_flake_url(NixCmd::get().await, &self.flake_shell).await?; tracing::info!("⌨️ Preparing to develop project: {:}", &flake); let prj = omnix_develop::core::Project::new(flake, om_config).await?; diff --git a/crates/omnix-common/src/config.rs b/crates/omnix-common/src/config.rs index 5923a3c0..42ce9ba6 100644 --- a/crates/omnix-common/src/config.rs +++ b/crates/omnix-common/src/config.rs @@ -4,18 +4,13 @@ use std::collections::BTreeMap; use nix_rs::{ command::NixCmd, - flake::{ - eval::nix_eval_attr, - url::{ - qualified_attr::{nix_eval_qualified_attr, QualifiedAttrError}, - FlakeUrl, - }, - }, + flake::{eval::nix_eval_attr, url::FlakeUrl}, }; use serde::de::DeserializeOwned; /// Reference to the whole `om` configuration in a flake -pub struct OmnixConfig { +#[derive(Debug)] +pub struct OmConfig { /// The flake URL used to load this configuration pub flake_url: FlakeUrl, @@ -26,7 +21,7 @@ pub struct OmnixConfig { pub config: BTreeMap>, } -impl OmnixConfig { +impl OmConfig { /// Read the om configuration from the flake url pub async fn from_flake_url(cmd: &NixCmd, flake_url: &FlakeUrl) -> Result { let qualified_url = flake_url.with_attr("om"); @@ -34,12 +29,32 @@ impl OmnixConfig { .await? .unwrap_or_default(); - Ok(OmnixConfig { + Ok(OmConfig { flake_url: flake_url.clone(), reference: flake_url.get_attr().as_list(), config, }) } + + /// Get all the configs of type `T` for a given sub-config + pub fn get_sub_config(&self, sub_config: &str) -> Result, OmConfigError> + where + T: DeserializeOwned, + { + let config = self + .config + .get(sub_config) + .ok_or_else(|| OmConfigError::MissingConfigAttribute(sub_config.to_string()))?; + config + .iter() + .map(|(k, v)| { + serde_json::from_value(v.clone()) + .map_err(OmConfigError::from) + .map(|converted| (k.clone(), converted)) + }) + .collect() + } + /// Get the user-referenced config value `T` for a given sub-config pub fn get_referenced_for(&self, sub_config: &str) -> Result<(T, &[String]), OmConfigError> where @@ -75,76 +90,9 @@ impl OmnixConfig { } } -/// Reference to some Omnix configuration of type `BTeeMap` in a flake -/// -/// For example, CI configuration at `om.ci.default` is captured by the `T` type. -#[derive(Debug)] -pub struct OmConfig { - /// The flake URL used to load this configuration - pub flake_url: FlakeUrl, - - /// The (nested) key reference into the flake config. - pub reference: Vec, - - /// The whole `om.??` configuration parsed as `T` - pub config: BTreeMap, -} - -impl OmConfig { - /// Read the Om configuration from the flake URL - pub async fn from_flake_url( - cmd: &NixCmd, - url: &FlakeUrl, - k: &[S], - ) -> Result, OmConfigError> - where - S: AsRef, - T: DeserializeOwned, - { - let (config, reference) = - nix_eval_qualified_attr::, _>(cmd, url, k).await?; - Ok(OmConfig { - flake_url: url.without_attr(), - reference, - config, - }) - } - - /// Get the user-referenced config value `T` - /// - /// If the user passes `.#foo.bar` this selects "foo" from the config tree, along with returning ["bar"]. - /// - /// If nothing is specifically passed, a default value is returned, either from config tree (key "default") or `T::default()`. - /// - /// TODO: This needs to be adjusted to support `om.templates` style configuration as well, where this default behaviour makes no sense. - pub fn get_referenced(&self) -> Result<(T, &[String]), OmConfigError> - where - T: Default + Clone, - { - if let Some((k, rest)) = self.reference.split_first() { - if let Some(v) = self.config.get(k) { - Ok((v.clone(), rest)) - } else { - Err(OmConfigError::MissingConfigAttribute(k.to_string())) - } - } else { - // Use default - if let Some(v) = self.config.get("default") { - Ok((v.clone(), &[])) - } else { - Ok((T::default(), &[])) - } - } - } -} - /// Error type for OmConfig #[derive(thiserror::Error, Debug)] pub enum OmConfigError { - /// Qualified attribute error - #[error("Qualified attribute error: {0}")] - QualifiedAttrError(#[from] QualifiedAttrError), - /// Missing configuration attribute #[error("Missing configuration attribute: {0}")] MissingConfigAttribute(String), diff --git a/crates/omnix-develop/src/config.rs b/crates/omnix-develop/src/config.rs index 3a65180a..11e9e87a 100644 --- a/crates/omnix-develop/src/config.rs +++ b/crates/omnix-develop/src/config.rs @@ -1,7 +1,6 @@ use serde::Deserialize; -use nix_rs::{command::NixCmd, flake::url::FlakeUrl}; -use omnix_common::config::{OmConfig, OmnixConfig}; +use omnix_common::config::OmConfig; use crate::readme::Readme; @@ -17,7 +16,7 @@ pub struct CacheConfig { } impl DevelopConfig { - pub fn from_om_config(om_config: &OmnixConfig) -> anyhow::Result { + pub fn from_om_config(om_config: &OmConfig) -> anyhow::Result { if let Some(v) = om_config.config.get("develop") { let config = v.get("default").cloned().unwrap_or_default(); let v1 = serde_json::from_value(config)?; @@ -26,11 +25,4 @@ impl DevelopConfig { Ok(Default::default()) } } - pub async fn from_flake(url: &FlakeUrl) -> anyhow::Result { - let v = OmConfig::::from_flake_url(NixCmd::get().await, url, &["om.develop"]) - .await? - .config; - let config = v.get("default").cloned().unwrap_or_default(); - Ok(config) - } } diff --git a/crates/omnix-develop/src/core.rs b/crates/omnix-develop/src/core.rs index 74643cd9..9ae5574d 100644 --- a/crates/omnix-develop/src/core.rs +++ b/crates/omnix-develop/src/core.rs @@ -2,7 +2,7 @@ use anyhow::Context; use std::{env::current_dir, path::PathBuf}; use nix_rs::{flake::url::FlakeUrl, info::NixInfo}; -use omnix_common::{config::OmnixConfig, markdown::print_markdown}; +use omnix_common::{config::OmConfig, markdown::print_markdown}; use omnix_health::{check::caches::CachixCache, traits::Checkable, NixHealth}; use crate::config::DevelopConfig; @@ -14,11 +14,11 @@ pub struct Project { /// [FlakeUrl] corresponding to the project. pub flake: FlakeUrl, /// The `om` configuration - pub om_config: OmnixConfig, + pub om_config: OmConfig, } impl Project { - pub async fn new(flake: FlakeUrl, om_config: OmnixConfig) -> anyhow::Result { + pub async fn new(flake: FlakeUrl, om_config: OmConfig) -> anyhow::Result { let dir = match flake.as_local_path() { Some(path) => Some(path.canonicalize()?), None => None, diff --git a/crates/omnix-health/src/lib.rs b/crates/omnix-health/src/lib.rs index c15b6834..9845232d 100644 --- a/crates/omnix-health/src/lib.rs +++ b/crates/omnix-health/src/lib.rs @@ -13,7 +13,7 @@ use check::direnv::Direnv; use nix_rs::env::OS; use nix_rs::flake::url::FlakeUrl; use nix_rs::{command::NixCmd, info::NixInfo}; -use omnix_common::config::{OmConfig, OmConfigError, OmnixConfig}; +use omnix_common::config::{OmConfig, OmConfigError}; use omnix_common::markdown::render_markdown; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -62,20 +62,13 @@ impl<'a> IntoIterator for &'a NixHealth { } impl NixHealth { - pub fn from_om_config(om_config: &OmnixConfig) -> Result { - // TODO: handle nix-health - let (cfg, _rest) = om_config.get_referenced_for::("health")?; - Ok(cfg.clone()) - } /// Create [NixHealth] using configuration from the given flake /// /// Fallback to using the default health check config if the flake doesn't /// override it. - pub async fn from_flake(url: &FlakeUrl) -> Result { - let cmd = NixCmd::get().await; - let cfg = - OmConfig::::from_flake_url(cmd, url, &["om.health", "nix-health"]).await?; - let (cfg, _rest) = cfg.get_referenced()?; + pub fn from_om_config(om_config: &OmConfig) -> Result { + // TODO: handle nix-health + let (cfg, _rest) = om_config.get_referenced_for::("health")?; Ok(cfg.clone()) } @@ -116,7 +109,10 @@ pub async fn run_all_checks_with(flake_url: Option) -> anyhow::Result< .with_context(|| "Unable to gather nix info")?; let health: NixHealth = match flake_url.as_ref() { - Some(flake_url) => NixHealth::from_flake(flake_url).await, + Some(flake_url) => { + let om_config = OmConfig::from_flake_url(NixCmd::get().await, flake_url).await?; + NixHealth::from_om_config(&om_config) + } None => Ok(NixHealth::default()), }?; diff --git a/crates/omnix-init/src/config.rs b/crates/omnix-init/src/config.rs index ca2216b6..72574f93 100644 --- a/crates/omnix-init/src/config.rs +++ b/crates/omnix-init/src/config.rs @@ -40,9 +40,10 @@ pub async fn load_templates<'a>(url: &FlakeUrl) -> anyhow::Result::from_flake_url(NixCmd::get().await, url, &["om.templates"]) - .await? - .config; + let om_config = OmConfig::from_flake_url(NixCmd::get().await, url).await?; + + let v = om_config.get_sub_config::