Add check for Flakes support

This commit is contained in:
Zhaofeng Li 2021-06-29 01:02:43 -07:00
parent e50ba82bf2
commit 67db0e73d1
6 changed files with 169 additions and 1 deletions

View file

@ -73,6 +73,7 @@ For a sample configuration, see <https://github.com/zhaofengli/colmena>.
register_command!(introspect, app); register_command!(introspect, app);
register_command!(upload_keys, app); register_command!(upload_keys, app);
register_command!(exec, app); register_command!(exec, app);
register_command!(nix_info, app);
app app
} }
@ -87,6 +88,7 @@ pub async fn run() {
handle_command!(introspect, matches); handle_command!(introspect, matches);
handle_command!("upload-keys", upload_keys, matches); handle_command!("upload-keys", upload_keys, matches);
handle_command!(exec, matches); handle_command!(exec, matches);
handle_command!("nix-info", nix_info, matches);
if let Some(args) = matches.subcommand_matches("gen-completions") { if let Some(args) = matches.subcommand_matches("gen-completions") {
return gen_completions(args); return gen_completions(args);

View file

@ -4,3 +4,4 @@ pub mod introspect;
pub mod apply_local; pub mod apply_local;
pub mod upload_keys; pub mod upload_keys;
pub mod exec; pub mod exec;
pub mod nix_info;

14
src/command/nix_info.rs Normal file
View file

@ -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);
}

View file

@ -11,10 +11,11 @@ use validator::Validate;
use super::{ use super::{
StoreDerivation, StoreDerivation,
NixResult, NixResult,
NixError,
NodeConfig, NodeConfig,
ProfileMap, ProfileMap,
}; };
use super::NixCommand; use super::{NixCommand, NixCheck};
use crate::util::CommandExecution; use crate::util::CommandExecution;
use crate::progress::TaskProgress; use crate::progress::TaskProgress;
@ -58,6 +59,14 @@ impl HivePath {
_ => None, _ => None,
} }
} }
fn is_flake(&self) -> bool {
if let Self::Flake(_) = self {
true
} else {
false
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -120,6 +129,13 @@ impl Hive {
/// Retrieve deployment info for all nodes. /// Retrieve deployment info for all nodes.
pub async fn deployment_info(&self) -> NixResult<HashMap<String, NodeConfig>> { pub async fn deployment_info(&self) -> NixResult<HashMap<String, NodeConfig>> {
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 :( // FIXME: Really ugly :(
let s: String = self.nix_instantiate("hive.deploymentConfigJson").eval() let s: String = self.nix_instantiate("hive.deploymentConfigJson").eval()
.capture_json().await?; .capture_json().await?;

129
src/nix/info.rs Normal file
View file

@ -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<major>\d+)\.(?P<minor>\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<NixVersion>,
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
}
}

View file

@ -32,6 +32,9 @@ pub use profile::{Profile, ProfileMap};
pub mod deployment; pub mod deployment;
pub use deployment::{Goal, Target, Deployment}; pub use deployment::{Goal, Target, Deployment};
pub mod info;
pub use info::NixCheck;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -72,6 +75,9 @@ pub enum NixError {
#[snafu(display("Unknown active profile: {}", store_path))] #[snafu(display("Unknown active profile: {}", store_path))]
ActiveProfileUnknown { store_path: String }, ActiveProfileUnknown { store_path: String },
#[snafu(display("Current Nix version does not support Flakes"))]
NoFlakesSupport,
#[snafu(display("Nix Error: {}", message))] #[snafu(display("Nix Error: {}", message))]
Unknown { message: String }, Unknown { message: String },
} }