From 67db0e73d15c626a0a4c4ed1a960736414f5f334 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Tue, 29 Jun 2021 01:02:43 -0700 Subject: [PATCH] Add check for Flakes support --- src/cli.rs | 2 + src/command/mod.rs | 1 + src/command/nix_info.rs | 14 +++++ src/nix/hive.rs | 18 +++++- src/nix/info.rs | 129 ++++++++++++++++++++++++++++++++++++++++ src/nix/mod.rs | 6 ++ 6 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 src/command/nix_info.rs create mode 100644 src/nix/info.rs diff --git a/src/cli.rs b/src/cli.rs index d33e1e9..2400ea4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -73,6 +73,7 @@ For a sample configuration, see . register_command!(introspect, app); register_command!(upload_keys, app); register_command!(exec, app); + register_command!(nix_info, app); app } @@ -87,6 +88,7 @@ pub async fn run() { handle_command!(introspect, matches); handle_command!("upload-keys", upload_keys, matches); handle_command!(exec, matches); + handle_command!("nix-info", nix_info, matches); if let Some(args) = matches.subcommand_matches("gen-completions") { return gen_completions(args); diff --git a/src/command/mod.rs b/src/command/mod.rs index fdace5a..7868eb7 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -4,3 +4,4 @@ pub mod introspect; pub mod apply_local; pub mod upload_keys; pub mod exec; +pub mod nix_info; diff --git a/src/command/nix_info.rs b/src/command/nix_info.rs new file mode 100644 index 0000000..62f8716 --- /dev/null +++ b/src/command/nix_info.rs @@ -0,0 +1,14 @@ +use clap::{App, SubCommand, ArgMatches}; + +use crate::nix::NixCheck; + +pub fn subcommand() -> App<'static, 'static> { + SubCommand::with_name("nix-info") + .about("Show information about the current Nix installation") +} + +pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) { + let check = NixCheck::detect().await; + check.print_version_info(); + check.print_flakes_info(false); +} diff --git a/src/nix/hive.rs b/src/nix/hive.rs index f38859b..9e5a40e 100644 --- a/src/nix/hive.rs +++ b/src/nix/hive.rs @@ -11,10 +11,11 @@ use validator::Validate; use super::{ StoreDerivation, NixResult, + NixError, NodeConfig, ProfileMap, }; -use super::NixCommand; +use super::{NixCommand, NixCheck}; use crate::util::CommandExecution; use crate::progress::TaskProgress; @@ -58,6 +59,14 @@ impl HivePath { _ => None, } } + + fn is_flake(&self) -> bool { + if let Self::Flake(_) = self { + true + } else { + false + } + } } #[derive(Debug)] @@ -120,6 +129,13 @@ impl Hive { /// Retrieve deployment info for all nodes. pub async fn deployment_info(&self) -> NixResult> { + let nix_check = NixCheck::detect().await; + + if self.path.is_flake() && !nix_check.flakes_supported() { + nix_check.print_flakes_info(true); + return Err(NixError::NoFlakesSupport); + } + // FIXME: Really ugly :( let s: String = self.nix_instantiate("hive.deploymentConfigJson").eval() .capture_json().await?; diff --git a/src/nix/info.rs b/src/nix/info.rs new file mode 100644 index 0000000..464c496 --- /dev/null +++ b/src/nix/info.rs @@ -0,0 +1,129 @@ +use std::fmt; +use std::process::Stdio; + +use log::Level; +use regex::Regex; + +use tokio::process::Command; + +struct NixVersion { + major: usize, + minor: usize, + string: String, +} + +impl NixVersion { + fn parse(string: String) -> Self { + let re = Regex::new(r" (?P\d+)\.(?P\d+)").unwrap(); + if let Some(caps) = re.captures(&string) { + let major = caps.name("major").unwrap().as_str().parse().unwrap(); + let minor = caps.name("minor").unwrap().as_str().parse().unwrap(); + + Self { major, minor, string } + } else { + Self { + major: 0, + minor: 0, + string: String::from("unknown"), + } + } + } + + fn has_flakes(&self) -> bool { + self.major > 2 || (self.major == 2 && self.minor >= 4) + } +} + +impl fmt::Display for NixVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.major != 0 { + write!(f, "{}.{}", self.major, self.minor) + } else { + write!(f, "{}???", self.string) + } + } +} + +pub struct NixCheck { + version: Option, + flakes_supported: bool, + flakes_enabled: bool, +} + +impl NixCheck { + const NO_NIX: Self = Self { + version: None, + flakes_supported: false, + flakes_enabled: false, + }; + + pub async fn detect() -> Self { + let version_cmd = Command::new("nix-instantiate") + .arg("--version") + .output().await; + + if version_cmd.is_err() { + return Self::NO_NIX; + } + + let version = NixVersion::parse(String::from_utf8_lossy(&version_cmd.unwrap().stdout).to_string()); + let flakes_supported = version.has_flakes(); + + let flake_cmd = Command::new("nix-instantiate") + .args(&["--eval", "-E", "builtins.getFlake"]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status().await; + + if flake_cmd.is_err() { + return Self::NO_NIX; + } + + let flakes_enabled = flake_cmd.unwrap().success(); + + Self { + version: Some(version), + flakes_supported, + flakes_enabled, + } + } + + pub fn print_version_info(&self) { + if let Some(v) = &self.version { + log::info!("Nix Version: {}", v); + } else { + log::info!("Nix Version: Not found"); + } + } + + pub fn print_flakes_info(&self, required: bool) { + if self.version.is_none() { + log::error!("Nix doesn't appear to be installed."); + return; + } + + if self.flakes_enabled { + log::info!("The Nix version you are using supports Flakes and it's enabled."); + } else if self.flakes_supported { + log::warn!("The Nix version you are using supports Flakes but it's disabled."); + log::warn!("Colmena will automatically enable Flakes for its operations, but you should enable it in your Nix configuration:"); + log::warn!(" experimental-features = nix-command flakes"); + } else { + let level = if required { + Level::Error + } else { + Level::Warn + }; + log::log!(level, "The Nix version you are using does not support Flakes."); + log::log!(level, "Please install nixUnstable for a version that includes Flakes support."); + + if required { + log::log!(level, "Cannot continue since Flakes support is required for this operation."); + } + } + } + + pub fn flakes_supported(&self) -> bool { + self.flakes_supported + } +} diff --git a/src/nix/mod.rs b/src/nix/mod.rs index e086609..c361ae5 100644 --- a/src/nix/mod.rs +++ b/src/nix/mod.rs @@ -32,6 +32,9 @@ pub use profile::{Profile, ProfileMap}; pub mod deployment; pub use deployment::{Goal, Target, Deployment}; +pub mod info; +pub use info::NixCheck; + #[cfg(test)] mod tests; @@ -72,6 +75,9 @@ pub enum NixError { #[snafu(display("Unknown active profile: {}", store_path))] ActiveProfileUnknown { store_path: String }, + #[snafu(display("Current Nix version does not support Flakes"))] + NoFlakesSupport, + #[snafu(display("Nix Error: {}", message))] Unknown { message: String }, }