|
| 1 | +use std::{cmp::Ordering, env}; |
| 2 | + |
| 3 | +use color_eyre::{eyre, Result}; |
| 4 | +use semver::Version; |
| 5 | +use tracing::warn; |
| 6 | + |
| 7 | +use crate::util; |
| 8 | + |
| 9 | +/// Verifies if the installed Nix version meets requirements |
| 10 | +/// |
| 11 | +/// # Returns |
| 12 | +/// |
| 13 | +/// * `Result<()>` - Ok if version requirements are met, error otherwise |
| 14 | +pub fn check_nix_version() -> Result<()> { |
| 15 | + if env::var("NH_NO_CHECKS").is_ok() { |
| 16 | + return Ok(()); |
| 17 | + } |
| 18 | + |
| 19 | + let version = util::get_nix_version()?; |
| 20 | + let is_lix_binary = util::is_lix()?; |
| 21 | + |
| 22 | + // XXX: Both Nix and Lix follow semantic versioning (semver). Update the |
| 23 | + // versions below once latest stable for either of those packages change. |
| 24 | + // TODO: Set up a CI to automatically update those in the future. |
| 25 | + const MIN_LIX_VERSION: &str = "2.91.1"; |
| 26 | + const MIN_NIX_VERSION: &str = "2.24.14"; |
| 27 | + |
| 28 | + // Minimum supported versions. Those should generally correspond to |
| 29 | + // latest package versions in the stable branch. |
| 30 | + // |
| 31 | + // Q: Why are you doing this? |
| 32 | + // A: First of all to make sure we do not make baseless assumptions |
| 33 | + // about the user's system; we should only work around APIs that we |
| 34 | + // are fully aware of, and not try to work around every edge case. |
| 35 | + // Also, nh should be responsible for nudging the user to use the |
| 36 | + // relevant versions of the software it wraps, so that we do not have |
| 37 | + // to try and support too many versions. NixOS stable and unstable |
| 38 | + // will ALWAYS be supported, but outdated versions will not. If your |
| 39 | + // Nix fork uses a different versioning scheme, please open an issue. |
| 40 | + let min_version = if is_lix_binary { |
| 41 | + MIN_LIX_VERSION |
| 42 | + } else { |
| 43 | + MIN_NIX_VERSION |
| 44 | + }; |
| 45 | + |
| 46 | + let current = Version::parse(&version)?; |
| 47 | + let required = Version::parse(min_version)?; |
| 48 | + |
| 49 | + match current.cmp(&required) { |
| 50 | + Ordering::Less => { |
| 51 | + let binary_name = if is_lix_binary { "Lix" } else { "Nix" }; |
| 52 | + warn!( |
| 53 | + "Warning: {} version {} is older than the recommended minimum version {}. You may encounter issues.", |
| 54 | + binary_name, |
| 55 | + version, |
| 56 | + min_version |
| 57 | + ); |
| 58 | + Ok(()) |
| 59 | + } |
| 60 | + _ => Ok(()), |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +/// Verifies if the required experimental features are enabled |
| 65 | +/// |
| 66 | +/// # Returns |
| 67 | +/// |
| 68 | +/// * `Result<()>` - Ok if all required features are enabled, error otherwise |
| 69 | +pub fn check_nix_features() -> Result<()> { |
| 70 | + if env::var("NH_NO_CHECKS").is_ok() { |
| 71 | + return Ok(()); |
| 72 | + } |
| 73 | + |
| 74 | + let mut required_features = vec!["nix-command", "flakes"]; |
| 75 | + |
| 76 | + // Lix still uses repl-flake, which is removed in the latest version of Nix. |
| 77 | + if util::is_lix()? { |
| 78 | + required_features.push("repl-flake"); |
| 79 | + } |
| 80 | + |
| 81 | + tracing::debug!("Required Nix features: {}", required_features.join(", ")); |
| 82 | + |
| 83 | + // Get currently enabled features |
| 84 | + match util::get_nix_experimental_features() { |
| 85 | + Ok(enabled_features) => { |
| 86 | + let features_vec: Vec<_> = enabled_features.into_iter().collect(); |
| 87 | + tracing::debug!("Enabled Nix features: {}", features_vec.join(", ")); |
| 88 | + } |
| 89 | + Err(e) => { |
| 90 | + tracing::warn!("Failed to get enabled Nix features: {}", e); |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + let missing_features = util::get_missing_experimental_features(&required_features)?; |
| 95 | + |
| 96 | + if !missing_features.is_empty() { |
| 97 | + tracing::warn!( |
| 98 | + "Missing required Nix features: {}", |
| 99 | + missing_features.join(", ") |
| 100 | + ); |
| 101 | + return Err(eyre::eyre!( |
| 102 | + "Missing required experimental features. Please enable: {}", |
| 103 | + missing_features.join(", ") |
| 104 | + )); |
| 105 | + } |
| 106 | + |
| 107 | + tracing::debug!("All required Nix features are enabled"); |
| 108 | + Ok(()) |
| 109 | +} |
| 110 | + |
| 111 | +/// Handles environment variable setup and returns if a warning should be shown |
| 112 | +/// |
| 113 | +/// # Returns |
| 114 | +/// |
| 115 | +/// * `Result<bool>` - True if a warning should be shown about the FLAKE |
| 116 | +/// variable, false otherwise |
| 117 | +pub fn setup_environment() -> Result<bool> { |
| 118 | + let mut do_warn = false; |
| 119 | + |
| 120 | + if let Ok(f) = std::env::var("FLAKE") { |
| 121 | + // Set NH_FLAKE if it's not already set |
| 122 | + if std::env::var("NH_FLAKE").is_err() { |
| 123 | + std::env::set_var("NH_FLAKE", f); |
| 124 | + |
| 125 | + // Only warn if FLAKE is set and we're using it to set NH_FLAKE |
| 126 | + // AND none of the command-specific env vars are set |
| 127 | + if std::env::var("NH_OS_FLAKE").is_err() |
| 128 | + && std::env::var("NH_HOME_FLAKE").is_err() |
| 129 | + && std::env::var("NH_DARWIN_FLAKE").is_err() |
| 130 | + { |
| 131 | + do_warn = true; |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + Ok(do_warn) |
| 137 | +} |
| 138 | + |
| 139 | +/// Consolidate all necessary checks for Nix functionality into a single |
| 140 | +/// function. This will be executed in the main function, but can be executed |
| 141 | +/// before critical commands to double-check if necessary. |
| 142 | +/// |
| 143 | +/// # Returns |
| 144 | +/// |
| 145 | +/// * `Result<()>` - Ok if all checks pass, error otherwise |
| 146 | +pub fn verify_nix_environment() -> Result<()> { |
| 147 | + if env::var("NH_NO_CHECKS").is_ok() { |
| 148 | + return Ok(()); |
| 149 | + } |
| 150 | + |
| 151 | + check_nix_version()?; |
| 152 | + check_nix_features()?; |
| 153 | + Ok(()) |
| 154 | +} |
0 commit comments