From d0cbb645c890e20d61930a1e48c543c763a00b3b Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Thu, 21 Aug 2025 15:57:18 +0530 Subject: [PATCH 1/3] fix(health): Flake check for detSys installed nix --- crates/nix_rs/src/info.rs | 113 +++++++++++++++++- .../omnix-health/src/check/flake_enabled.rs | 30 ++++- 2 files changed, 137 insertions(+), 6 deletions(-) diff --git a/crates/nix_rs/src/info.rs b/crates/nix_rs/src/info.rs index 2ecefab0..e1a22414 100644 --- a/crates/nix_rs/src/info.rs +++ b/crates/nix_rs/src/info.rs @@ -1,8 +1,111 @@ //! Information about the user's Nix installation use serde::{Deserialize, Serialize}; +use std::fmt; use tokio::sync::OnceCell; -use crate::{config::NixConfig, env::NixEnv, version::NixVersion}; +use crate::{command::NixCmd, config::NixConfig, env::NixEnv, version::NixVersion}; +use regex::Regex; + +/// Type of Nix installation +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum NixInstallationType { + /// Official Nix installation + Official, + /// Determinate Systems Nix + DeterminateSystems, +} + +impl fmt::Display for NixInstallationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NixInstallationType::Official => write!(f, "official"), + NixInstallationType::DeterminateSystems => write!(f, "determinate-systems"), + } + } +} + +impl NixInstallationType { + /// Detect the installation type by examining `nix --version` output + async fn detect() -> Result { + let cmd = NixCmd::default(); + let output = cmd + .run_with_returning_stdout(&[], |cmd| { + cmd.arg("--version"); + }) + .await + .map_err(|e| NixInfoError::NixCmdError(crate::command::NixCmdError::CmdError(e)))?; + let version_str = String::from_utf8_lossy(&output).trim().to_string(); + + let patterns = [ + ( + r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::DeterminateSystems, + ), + ( + r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::Official, + ), + (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), + ]; + + for (pattern, installation_type) in patterns { + let re = + Regex::new(pattern).map_err(|_| NixInfoError::InstallationTypeDetectionError)?; + if re.is_match(&version_str) { + return Ok(installation_type); + } + } + + // Default to Official if no pattern matches + Ok(NixInstallationType::Official) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_installation_type_detection() { + let test_cases = [ + ("nix (Nix) 2.28.4", NixInstallationType::Official), + ( + "nix (Determinate Nix 3.8.5) 2.30.2", + NixInstallationType::DeterminateSystems, + ), + ("2.28.4", NixInstallationType::Official), + ]; + + for (version_str, expected_type) in test_cases { + let patterns = [ + ( + r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::DeterminateSystems, + ), + ( + r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::Official, + ), + (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), + ]; + + let mut detected_type = NixInstallationType::Official; // default + for (pattern, installation_type) in patterns { + let re = Regex::new(pattern).unwrap(); + if re.is_match(version_str) { + detected_type = installation_type; + break; + } + } + + assert_eq!( + detected_type, expected_type, + "Failed for version string: '{}'", + version_str + ); + } + } +} /// All the information about the user's Nix installation #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -13,6 +116,8 @@ pub struct NixInfo { pub nix_config: NixConfig, /// Environment in which Nix was installed pub nix_env: NixEnv, + /// Type of Nix installation + pub installation_type: NixInstallationType, } static NIX_INFO: OnceCell> = OnceCell::const_new(); @@ -36,10 +141,12 @@ impl NixInfo { nix_config: NixConfig, ) -> Result { let nix_env = NixEnv::detect().await?; + let installation_type = NixInstallationType::detect().await?; Ok(NixInfo { nix_version, nix_config, nix_env, + installation_type, }) } } @@ -62,4 +169,8 @@ pub enum NixInfoError { /// A [crate::config::NixConfigError] #[error("Nix config error: {0}")] NixConfigError(#[from] &'static crate::config::NixConfigError), + + /// Installation type detection error + #[error("Failed to detect installation type")] + InstallationTypeDetectionError, } diff --git a/crates/omnix-health/src/check/flake_enabled.rs b/crates/omnix-health/src/check/flake_enabled.rs index 884a1730..951b65d2 100644 --- a/crates/omnix-health/src/check/flake_enabled.rs +++ b/crates/omnix-health/src/check/flake_enabled.rs @@ -1,4 +1,4 @@ -use nix_rs::info; +use nix_rs::info::{self, NixInstallationType}; use serde::{Deserialize, Serialize}; use crate::traits::*; @@ -15,12 +15,32 @@ impl Checkable for FlakeEnabled { _: Option<&nix_rs::flake::url::FlakeUrl>, ) -> Vec<(&'static str, Check)> { let val = &nix_info.nix_config.experimental_features.value; + + // Check if flakes are enabled either through configuration or installation type + let flakes_enabled = match nix_info.installation_type { + NixInstallationType::DeterminateSystems => { + // Determinate Systems Nix has flakes enabled by default + true + } + NixInstallationType::Official => { + // Official Nix requires explicit configuration + val.contains(&"flakes".to_string()) && val.contains(&"nix-command".to_string()) + } + }; + + let info_msg = match nix_info.installation_type { + NixInstallationType::DeterminateSystems => { + "Determinate Systems Nix (flakes enabled by default)".to_string() + } + NixInstallationType::Official => { + format!("experimental-features = {}", val.join(" ")) + } + }; + let check = Check { title: "Flakes Enabled".to_string(), - info: format!("experimental-features = {}", val.join(" ")), - result: if val.contains(&"flakes".to_string()) - && val.contains(&"nix-command".to_string()) - { + info: info_msg, + result: if flakes_enabled { CheckResult::Green } else { CheckResult::Red { From 645964b17ecdef5d1cb20ceb476fb048e1946652 Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Thu, 21 Aug 2025 23:25:40 +0530 Subject: [PATCH 2/3] review: copilot review on the PR --- crates/nix_rs/src/info.rs | 91 +++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/crates/nix_rs/src/info.rs b/crates/nix_rs/src/info.rs index e1a22414..14bb51bc 100644 --- a/crates/nix_rs/src/info.rs +++ b/crates/nix_rs/src/info.rs @@ -1,13 +1,15 @@ //! Information about the user's Nix installation use serde::{Deserialize, Serialize}; -use std::fmt; +use std::{fmt, sync::OnceLock}; use tokio::sync::OnceCell; use crate::{command::NixCmd, config::NixConfig, env::NixEnv, version::NixVersion}; use regex::Regex; +static INSTALLATION_TYPE_PATTERNS: OnceLock> = OnceLock::new(); + /// Type of Nix installation -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum NixInstallationType { /// Official Nix installation Official, @@ -25,6 +27,44 @@ impl fmt::Display for NixInstallationType { } impl NixInstallationType { + /// Get or initialize the compiled regex patterns + fn get_patterns() -> &'static Vec<(Regex, NixInstallationType)> { + INSTALLATION_TYPE_PATTERNS.get_or_init(|| { + let pattern_strings = [ + ( + r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::DeterminateSystems, + ), + ( + r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", + NixInstallationType::Official, + ), + (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), + ]; + + let mut compiled_patterns = Vec::new(); + for (pattern_str, installation_type) in pattern_strings { + // If regex compilation fails, we'll panic at startup which is acceptable + let regex = Regex::new(pattern_str).expect("Invalid regex pattern"); + compiled_patterns.push((regex, installation_type)); + } + compiled_patterns + }) + } + + /// Detect installation type from a version string + fn from_version_str(version_str: &str) -> Self { + let patterns = Self::get_patterns(); + for (regex, installation_type) in patterns { + if regex.is_match(version_str) { + return *installation_type; + } + } + + // Default to Official if no pattern matches + NixInstallationType::Official + } + /// Detect the installation type by examining `nix --version` output async fn detect() -> Result { let cmd = NixCmd::default(); @@ -33,31 +73,10 @@ impl NixInstallationType { cmd.arg("--version"); }) .await - .map_err(|e| NixInfoError::NixCmdError(crate::command::NixCmdError::CmdError(e)))?; + .map_err(|_| NixInfoError::InstallationTypeDetectionError)?; let version_str = String::from_utf8_lossy(&output).trim().to_string(); - let patterns = [ - ( - r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::DeterminateSystems, - ), - ( - r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::Official, - ), - (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), - ]; - - for (pattern, installation_type) in patterns { - let re = - Regex::new(pattern).map_err(|_| NixInfoError::InstallationTypeDetectionError)?; - if re.is_match(&version_str) { - return Ok(installation_type); - } - } - - // Default to Official if no pattern matches - Ok(NixInstallationType::Official) + Ok(Self::from_version_str(&version_str)) } } @@ -77,27 +96,7 @@ mod tests { ]; for (version_str, expected_type) in test_cases { - let patterns = [ - ( - r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::DeterminateSystems, - ), - ( - r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::Official, - ), - (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), - ]; - - let mut detected_type = NixInstallationType::Official; // default - for (pattern, installation_type) in patterns { - let re = Regex::new(pattern).unwrap(); - if re.is_match(version_str) { - detected_type = installation_type; - break; - } - } - + let detected_type = NixInstallationType::from_version_str(version_str); assert_eq!( detected_type, expected_type, "Failed for version string: '{}'", From 3879dac91c46ffdb17455cecc7c6f6017444e950 Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Sun, 24 Aug 2025 11:25:34 +0530 Subject: [PATCH 3/3] refactor: Improve version parsing and comparison logic --- crates/nix_rs/src/config.rs | 6 +- crates/nix_rs/src/info.rs | 123 ++--------- crates/nix_rs/src/version.rs | 198 +++++++++++++++--- crates/nix_rs/src/version_spec.rs | 14 +- .../omnix-health/src/check/flake_enabled.rs | 6 +- 5 files changed, 196 insertions(+), 151 deletions(-) diff --git a/crates/nix_rs/src/config.rs b/crates/nix_rs/src/config.rs index 7f1f1784..d0cadebf 100644 --- a/crates/nix_rs/src/config.rs +++ b/crates/nix_rs/src/config.rs @@ -10,7 +10,7 @@ use url::Url; use crate::{ command::{NixCmd, NixCmdError}, - version::NixVersion, + version::{NixVersion, VersionSpec}, }; use super::flake::system::System; @@ -51,11 +51,11 @@ pub struct ConfigVal { static NIX_CONFIG: OnceCell> = OnceCell::const_new(); -static NIX_2_20_0: NixVersion = NixVersion { +static NIX_2_20_0: NixVersion = NixVersion::Official(VersionSpec { major: 2, minor: 20, patch: 0, -}; +}); impl NixConfig { /// Get the once version of `NixConfig`. diff --git a/crates/nix_rs/src/info.rs b/crates/nix_rs/src/info.rs index 14bb51bc..63bb4c7c 100644 --- a/crates/nix_rs/src/info.rs +++ b/crates/nix_rs/src/info.rs @@ -1,110 +1,12 @@ //! Information about the user's Nix installation use serde::{Deserialize, Serialize}; -use std::{fmt, sync::OnceLock}; use tokio::sync::OnceCell; -use crate::{command::NixCmd, config::NixConfig, env::NixEnv, version::NixVersion}; -use regex::Regex; - -static INSTALLATION_TYPE_PATTERNS: OnceLock> = OnceLock::new(); - -/// Type of Nix installation -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum NixInstallationType { - /// Official Nix installation - Official, - /// Determinate Systems Nix - DeterminateSystems, -} - -impl fmt::Display for NixInstallationType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - NixInstallationType::Official => write!(f, "official"), - NixInstallationType::DeterminateSystems => write!(f, "determinate-systems"), - } - } -} - -impl NixInstallationType { - /// Get or initialize the compiled regex patterns - fn get_patterns() -> &'static Vec<(Regex, NixInstallationType)> { - INSTALLATION_TYPE_PATTERNS.get_or_init(|| { - let pattern_strings = [ - ( - r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::DeterminateSystems, - ), - ( - r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$", - NixInstallationType::Official, - ), - (r"^(\d+)\.(\d+)\.(\d+)$", NixInstallationType::Official), - ]; - - let mut compiled_patterns = Vec::new(); - for (pattern_str, installation_type) in pattern_strings { - // If regex compilation fails, we'll panic at startup which is acceptable - let regex = Regex::new(pattern_str).expect("Invalid regex pattern"); - compiled_patterns.push((regex, installation_type)); - } - compiled_patterns - }) - } - - /// Detect installation type from a version string - fn from_version_str(version_str: &str) -> Self { - let patterns = Self::get_patterns(); - for (regex, installation_type) in patterns { - if regex.is_match(version_str) { - return *installation_type; - } - } - - // Default to Official if no pattern matches - NixInstallationType::Official - } - - /// Detect the installation type by examining `nix --version` output - async fn detect() -> Result { - let cmd = NixCmd::default(); - let output = cmd - .run_with_returning_stdout(&[], |cmd| { - cmd.arg("--version"); - }) - .await - .map_err(|_| NixInfoError::InstallationTypeDetectionError)?; - let version_str = String::from_utf8_lossy(&output).trim().to_string(); - - Ok(Self::from_version_str(&version_str)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_installation_type_detection() { - let test_cases = [ - ("nix (Nix) 2.28.4", NixInstallationType::Official), - ( - "nix (Determinate Nix 3.8.5) 2.30.2", - NixInstallationType::DeterminateSystems, - ), - ("2.28.4", NixInstallationType::Official), - ]; - - for (version_str, expected_type) in test_cases { - let detected_type = NixInstallationType::from_version_str(version_str); - assert_eq!( - detected_type, expected_type, - "Failed for version string: '{}'", - version_str - ); - } - } -} +use crate::{ + config::NixConfig, + env::NixEnv, + version::{NixInstallationType, NixVersion}, +}; /// All the information about the user's Nix installation #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -115,8 +17,13 @@ pub struct NixInfo { pub nix_config: NixConfig, /// Environment in which Nix was installed pub nix_env: NixEnv, - /// Type of Nix installation - pub installation_type: NixInstallationType, +} + +impl NixInfo { + /// Get the installation type (derived from nix_version) + pub fn installation_type(&self) -> NixInstallationType { + self.nix_version.installation_type() + } } static NIX_INFO: OnceCell> = OnceCell::const_new(); @@ -140,12 +47,10 @@ impl NixInfo { nix_config: NixConfig, ) -> Result { let nix_env = NixEnv::detect().await?; - let installation_type = NixInstallationType::detect().await?; Ok(NixInfo { nix_version, nix_config, nix_env, - installation_type, }) } } @@ -168,8 +73,4 @@ pub enum NixInfoError { /// A [crate::config::NixConfigError] #[error("Nix config error: {0}")] NixConfigError(#[from] &'static crate::config::NixConfigError), - - /// Installation type detection error - #[error("Failed to detect installation type")] - InstallationTypeDetectionError, } diff --git a/crates/nix_rs/src/version.rs b/crates/nix_rs/src/version.rs index b0e74ac8..9cd6be2a 100644 --- a/crates/nix_rs/src/version.rs +++ b/crates/nix_rs/src/version.rs @@ -1,7 +1,7 @@ //! Rust module for `nix --version` use regex::Regex; use serde_with::{DeserializeFromStr, SerializeDisplay}; -use std::{fmt, str::FromStr}; +use std::{fmt, str::FromStr, sync::LazyLock}; use thiserror::Error; use tokio::sync::OnceCell; @@ -9,9 +9,9 @@ use tracing::instrument; use crate::command::{NixCmd, NixCmdError}; -/// Nix version as parsed from `nix --version` -#[derive(Clone, Copy, PartialOrd, PartialEq, Eq, Debug, SerializeDisplay, DeserializeFromStr)] -pub struct NixVersion { +/// Simple version triple (major.minor.patch) +#[derive(Clone, Copy, PartialOrd, PartialEq, Eq, Ord, Debug)] +pub struct VersionSpec { /// Major version pub major: u32, /// Minor version @@ -20,6 +20,82 @@ pub struct NixVersion { pub patch: u32, } +/// Nix version as parsed from `nix --version`, capturing both version and installation type +#[derive(Clone, Copy, Debug, SerializeDisplay, DeserializeFromStr)] +pub enum NixVersion { + /// Official Nix installation: "nix (Nix) 2.28.4" or "2.28.4" + Official(VersionSpec), + /// Determinate Systems Nix: "nix (Determinate Nix 3.8.5) 2.30.2" + DeterminateSystems { + /// The Determinate Systems version (e.g., 3.8.5) + det_sys_version: VersionSpec, + /// The underlying Nix version (e.g., 2.30.2) + nix_version: VersionSpec, + }, +} + +impl NixVersion { + /// Get the effective Nix version (the actual Nix version being used) + pub fn nix_version(&self) -> VersionSpec { + match self { + NixVersion::Official(version) => *version, + NixVersion::DeterminateSystems { nix_version, .. } => *nix_version, + } + } + + /// Check if this is a Determinate Systems installation + pub fn is_determinate_systems(&self) -> bool { + matches!(self, NixVersion::DeterminateSystems { .. }) + } + + /// Get the installation type + pub fn installation_type(&self) -> NixInstallationType { + match self { + NixVersion::Official(_) => NixInstallationType::Official, + NixVersion::DeterminateSystems { .. } => NixInstallationType::DeterminateSystems, + } + } +} + +impl PartialEq for NixVersion { + fn eq(&self, other: &Self) -> bool { + self.nix_version() == other.nix_version() + } +} + +impl Eq for NixVersion {} + +#[allow(clippy::non_canonical_partial_ord_impl)] +impl PartialOrd for NixVersion { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for NixVersion { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.nix_version().cmp(&other.nix_version()) + } +} + +/// Type of Nix installation (derived from NixVersion) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NixInstallationType { + /// Official Nix installation + Official, + /// Determinate Systems Nix + DeterminateSystems, +} + +impl std::fmt::Display for NixInstallationType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NixInstallationType::Official => write!(f, "official"), + NixInstallationType::DeterminateSystems => write!(f, "determinate-systems"), + } + } +} + /// Error type for parsing `nix --version` #[derive(Error, Debug, Clone, PartialEq)] pub enum BadNixVersion { @@ -36,25 +112,58 @@ pub enum BadNixVersion { Command, } +impl VersionSpec { + /// Parse a version string like "2.28.4" into a VersionSpec + fn parse_version_string(s: &str) -> Result { + let parts: Vec<&str> = s.split('.').collect(); + if parts.len() != 3 { + return Err(BadNixVersion::Command); + } + + let major = parts[0].parse::()?; + let minor = parts[1].parse::()?; + let patch = parts[2].parse::()?; + + Ok(VersionSpec { + major, + minor, + patch, + }) + } +} + +impl std::fmt::Display for VersionSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch) + } +} + +static DET_SYS_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^nix \(Determinate Nix ([\d.]+)\) ([\d.]+)$").unwrap()); +static OFFICIAL_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^(?:nix \(Nix\) )?([\d.]+)$").unwrap()); + impl FromStr for NixVersion { type Err = BadNixVersion; /// Parse the string output of `nix --version` into a [NixVersion] fn from_str(s: &str) -> Result { - // NOTE: The parser is lenient in allowing pure nix version (produced - // by [Display] instance), so as to work with serde_with instances. - let re = Regex::new(r"(?:nix \(Nix\) )?(\d+)\.(\d+)\.(\d+)$")?; + // Try to match Determinate Systems format: "nix (Determinate Nix 3.8.5) 2.30.2" + if let Some(captures) = DET_SYS_REGEX.captures(s) { + return Ok(NixVersion::DeterminateSystems { + det_sys_version: VersionSpec::parse_version_string(&captures[1])?, + nix_version: VersionSpec::parse_version_string(&captures[2])?, + }); + } - let captures = re.captures(s).ok_or(BadNixVersion::Command)?; - let major = captures[1].parse::()?; - let minor = captures[2].parse::()?; - let patch = captures[3].parse::()?; + // Try to match official format: "nix (Nix) 2.28.4" or plain format: "2.28.4" + if let Some(captures) = OFFICIAL_REGEX.captures(s) { + return Ok(NixVersion::Official(VersionSpec::parse_version_string( + &captures[1], + )?)); + } - Ok(NixVersion { - major, - minor, - patch, - }) + Err(BadNixVersion::Command) } } @@ -84,7 +193,10 @@ impl NixVersion { /// The String view for [NixVersion] impl fmt::Display for NixVersion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}.{}.{}", self.major, self.minor, self.patch) + match self { + NixVersion::Official(version) => write!(f, "{}", version), + NixVersion::DeterminateSystems { nix_version, .. } => write!(f, "{}", nix_version), + } } } @@ -96,32 +208,64 @@ async fn test_run_nix_version() { #[tokio::test] async fn test_parse_nix_version() { + // Test official Nix format assert_eq!( NixVersion::from_str("nix (Nix) 2.13.0"), - Ok(NixVersion { + Ok(NixVersion::Official(VersionSpec { major: 2, minor: 13, patch: 0 - }) + })) ); - // Parse simple nix version + // Test simple version format (treated as official) assert_eq!( NixVersion::from_str("2.13.0"), - Ok(NixVersion { + Ok(NixVersion::Official(VersionSpec { major: 2, minor: 13, patch: 0 - }) + })) ); - // Parse Determinate Nix Version + // Test Determinate Systems format assert_eq!( NixVersion::from_str("nix (Determinate Nix 3.6.6) 2.29.0"), - Ok(NixVersion { - major: 2, - minor: 29, - patch: 0 + Ok(NixVersion::DeterminateSystems { + det_sys_version: VersionSpec { + major: 3, + minor: 6, + patch: 6 + }, + nix_version: VersionSpec { + major: 2, + minor: 29, + patch: 0 + } }) ); + + // Test installation type detection + let official = NixVersion::from_str("nix (Nix) 2.28.4").unwrap(); + assert_eq!(official.installation_type(), NixInstallationType::Official); + assert!(!official.is_determinate_systems()); + + let det_sys = NixVersion::from_str("nix (Determinate Nix 3.8.5) 2.30.2").unwrap(); + assert_eq!( + det_sys.installation_type(), + NixInstallationType::DeterminateSystems + ); + assert!(det_sys.is_determinate_systems()); + + // Test version comparison between Official and DeterminateSystems + let official_v2_30 = NixVersion::from_str("2.30.0").unwrap(); + let det_sys_v2_30 = NixVersion::from_str("nix (Determinate Nix 3.8.5) 2.30.0").unwrap(); + let official_v2_28 = NixVersion::from_str("2.28.0").unwrap(); + + // Same underlying Nix version should be equal + assert_eq!(official_v2_30, det_sys_v2_30); + + // Both should be greater than older version + assert!(official_v2_30 > official_v2_28); + assert!(det_sys_v2_30 > official_v2_28); } diff --git a/crates/nix_rs/src/version_spec.rs b/crates/nix_rs/src/version_spec.rs index 5a15cff0..ce15a332 100644 --- a/crates/nix_rs/src/version_spec.rs +++ b/crates/nix_rs/src/version_spec.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_with::{DeserializeFromStr, SerializeDisplay}; use thiserror::Error; -use crate::version::NixVersion; +use crate::version::{NixVersion, VersionSpec}; /// An individual component of [NixVersionReq] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -119,11 +119,11 @@ impl FromStr for NixVersionSpec { .name("patch") .map_or(Ok(0), |m| m.as_str().parse::())?; - let nix_version = NixVersion { + let nix_version = NixVersion::Official(VersionSpec { major, minor, patch, - }; + }); match op { ">=" => Ok(Gteq(nix_version)), @@ -170,19 +170,19 @@ mod tests { fn test_parse() { assert_eq!( NixVersionSpec::from_str(">2.8").unwrap(), - NixVersionSpec::Gt(NixVersion { + NixVersionSpec::Gt(NixVersion::Official(VersionSpec { major: 2, minor: 8, patch: 0 - }) + })) ); assert_eq!( NixVersionSpec::from_str(">2").unwrap(), - NixVersionSpec::Gt(NixVersion { + NixVersionSpec::Gt(NixVersion::Official(VersionSpec { major: 2, minor: 0, patch: 0 - }) + })) ); } diff --git a/crates/omnix-health/src/check/flake_enabled.rs b/crates/omnix-health/src/check/flake_enabled.rs index 951b65d2..d9c316f5 100644 --- a/crates/omnix-health/src/check/flake_enabled.rs +++ b/crates/omnix-health/src/check/flake_enabled.rs @@ -1,4 +1,4 @@ -use nix_rs::info::{self, NixInstallationType}; +use nix_rs::{info, version::NixInstallationType}; use serde::{Deserialize, Serialize}; use crate::traits::*; @@ -17,7 +17,7 @@ impl Checkable for FlakeEnabled { let val = &nix_info.nix_config.experimental_features.value; // Check if flakes are enabled either through configuration or installation type - let flakes_enabled = match nix_info.installation_type { + let flakes_enabled = match nix_info.installation_type() { NixInstallationType::DeterminateSystems => { // Determinate Systems Nix has flakes enabled by default true @@ -28,7 +28,7 @@ impl Checkable for FlakeEnabled { } }; - let info_msg = match nix_info.installation_type { + let info_msg = match nix_info.installation_type() { NixInstallationType::DeterminateSystems => { "Determinate Systems Nix (flakes enabled by default)".to_string() }