diff --git a/Cargo.lock b/Cargo.lock index 21f9b52..54dba3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -768,7 +768,7 @@ dependencies = [ [[package]] name = "nh" -version = "3.5.6" +version = "3.5.7" dependencies = [ "ambassador", "anstyle", @@ -787,6 +787,7 @@ dependencies = [ "owo-colors 4.0.0", "regex", "reqwest", + "semver", "serde", "serde_json", "subprocess", @@ -1090,6 +1091,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.197" diff --git a/Cargo.toml b/Cargo.toml index 83b7a4c..d14ecfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nh" -version = "3.5.6" +version = "3.5.7" edition = "2021" license = "EUPL-1.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -35,6 +35,7 @@ once_cell = "1.18.0" owo-colors = "4.0.0" regex = "1.8.4" reqwest = { version = "0.11.23", features = ["rustls-tls", "blocking", "json"], default-features = false } +semver = "1.0.22" serde = { version = "1.0.166", features = [ "derive", ] } diff --git a/flake.lock b/flake.lock index 2135ba9..b9bdcfa 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1709218635, - "narHash": "sha256-nytX/MkfqeTD4z7bMq4QRXcHxO9B3vRo9tM6fMtPFA8=", + "lastModified": 1710283656, + "narHash": "sha256-nI+AOy4uK6jLGBi9nsbHjL1EdSIzoo8oa+9oeVhbyFc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "068d4db604958d05d0b46c47f79b507d84dbc069", + "rev": "51063ed4f2343a59fdeebb279bb81d87d453942b", "type": "github" }, "original": { diff --git a/src/home.rs b/src/home.rs index 61d75ab..6e79975 100644 --- a/src/home.rs +++ b/src/home.rs @@ -11,6 +11,7 @@ use crate::*; use crate::{ interface::NHRunnable, interface::{FlakeRef, HomeArgs, HomeRebuildArgs, HomeSubcommand}, + util::{compare_semver, get_nix_version}, }; #[derive(Error, Debug)] @@ -59,8 +60,28 @@ impl HomeRebuildArgs { ); if self.common.update { + // Get the Nix version + let nix_version = get_nix_version().unwrap_or_else(|_| { + panic!("Failed to get Nix version. Custom Nix fork?"); + }); + + // Default interface for updating flake inputs + let mut update_args = vec!["nix", "flake", "update"]; + + // If user is on Nix 2.19.0 or above, --flake must be passed + if let Ok(ordering) = compare_semver(&nix_version, "2.19.0") { + if ordering == std::cmp::Ordering::Greater { + update_args.push("--flake"); + } + } + + update_args.push(&self.common.flakeref); + + debug!("nix_version: {:?}", nix_version); + debug!("update_args: {:?}", update_args); + commands::CommandBuilder::default() - .args(["nix", "flake", "update", "--flake", &self.common.flakeref]) + .args(&update_args) .message("Updating flake") .build()? .exec()?; diff --git a/src/main.rs b/src/main.rs index c2394cd..8b164d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod interface; mod logging; mod nixos; mod search; +mod util; use crate::interface::NHParser; use crate::interface::NHRunnable; diff --git a/src/nixos.rs b/src/nixos.rs index a39bd55..7d94ab7 100644 --- a/src/nixos.rs +++ b/src/nixos.rs @@ -8,6 +8,7 @@ use tracing::{debug, info}; use crate::interface::NHRunnable; use crate::interface::OsRebuildType::{self, Boot, Switch, Test}; use crate::interface::{self, OsRebuildArgs}; +use crate::util::{compare_semver, get_nix_version}; use crate::*; const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system"; @@ -48,11 +49,28 @@ impl OsRebuildArgs { ); if self.common.update { + // Get the Nix version + let nix_version = get_nix_version().unwrap_or_else(|_| { + panic!("Failed to get Nix version. Custom Nix fork?"); + }); + + // Default interface for updating flake inputs + let mut update_args = vec!["nix", "flake", "update"]; + + // If user is on Nix 2.19.0 or above, --flake must be passed + if let Ok(ordering) = compare_semver(&nix_version, "2.19.0") { + if ordering == std::cmp::Ordering::Greater { + update_args.push("--flake"); + } + } + + update_args.push(&self.common.flakeref); + + debug!("nix_version: {:?}", nix_version); + debug!("update_args: {:?}", update_args); + commands::CommandBuilder::default() - // FIXME: if user is running an older version of Nix (i.e pre-`nix flake lock` change) - // the below command will fail. maybe check for Nix version and decide on the - // command? - .args(["nix", "flake", "update", "--flake", &self.common.flakeref]) + .args(&update_args) .message("Updating flake") .build()? .exec()?; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6a4c950 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,58 @@ +extern crate semver; + +use color_eyre::{eyre, Result}; +use semver::Version; + +use std::process::Command; +use std::str; + +/// Compares two semantic versions and returns their order. +/// +/// This function takes two version strings, parses them into `semver::Version` objects, and compares them. +/// It returns an `Ordering` indicating whether the current version is less than, equal to, or +/// greater than the target version. +/// +/// # Arguments +/// +/// * `current` - A string slice representing the current version. +/// * `target` - A string slice representing the target version to compare against. +/// +/// # Returns +/// +/// * `Result` - The comparison result. +pub fn compare_semver(current: &str, target: &str) -> Result { + let current = Version::parse(current)?; + let target = Version::parse(target)?; + + Ok(current.cmp(&target)) +} + +/// Retrieves the installed Nix version as a string. +/// +/// This function executes the `nix --version` command, parses the output to extract the version string, +/// and returns it. If the version string cannot be found or parsed, it returns an error. +/// +/// # Returns +/// +/// * `Result` - The Nix version string or an error if the version cannot be retrieved. +pub fn get_nix_version() -> Result { + let output = Command::new("nix").arg("--version").output()?; + + let output_str = str::from_utf8(&output.stdout)?; + let version_str = output_str + .lines() + .next() + .ok_or_else(|| eyre::eyre!("No version string found"))?; + + // Extract the version substring using a regular expression + let re = regex::Regex::new(r"\d+\.\d+\.\d+")?; + if let Some(captures) = re.captures(version_str) { + let version = captures + .get(0) + .ok_or_else(|| eyre::eyre!("No version match found"))? + .as_str(); + return Ok(version.to_string()); + } + + Err(eyre::eyre!("Failed to extract version")) +}