diff --git a/src/nix/deployment/mod.rs b/src/nix/deployment/mod.rs index e941d83..b098a55 100644 --- a/src/nix/deployment/mod.rs +++ b/src/nix/deployment/mod.rs @@ -428,16 +428,18 @@ impl Deployment { if !target.config.replace_unknown_profiles { job.message("Checking remote profile...".to_string())?; - match host.active_derivation_known().await { - Ok(_) => { - job.message("Remote profile known".to_string())?; - } - Err(e) => { - if arc_self.options.force_replace_unknown_profiles { - job.message("warning: remote profile is unknown, but unknown profiles are being ignored".to_string())?; - } else { - return Err(e); - } + + let profile = host.get_main_system_profile().await?; + + if profile.exists() { + job.message("Remote profile known".to_string())?; + } else { + if arc_self.options.force_replace_unknown_profiles { + job.message("Warning: Remote profile is unknown, but unknown profiles are being ignored".to_string())?; + } else { + return Err(NixError::ActiveProfileUnknown { + store_path: profile, + }); } } } diff --git a/src/nix/host/local.rs b/src/nix/host/local.rs index 63ce057..dfc445e 100644 --- a/src/nix/host/local.rs +++ b/src/nix/host/local.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use tokio::process::Command; use super::{CopyDirection, CopyOptions, Host, key_uploader}; -use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, NixCommand, Key, SYSTEM_PROFILE}; +use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, NixCommand, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; use crate::util::CommandExecution; use crate::job::JobHandle; @@ -90,8 +90,19 @@ impl Host for Local { result } - async fn active_derivation_known(&mut self) -> NixResult { - Ok(true) + + async fn get_main_system_profile(&mut self) -> NixResult { + let paths = Command::new("sh") + .args(&["-c", &format!("readlink -e {} || readlink -e {}", SYSTEM_PROFILE, CURRENT_PROFILE)]) + .capture_output() + .await?; + + let path = paths.lines().into_iter().next() + .ok_or(NixError::FailedToGetCurrentProfile)? + .to_string() + .try_into()?; + + Ok(path) } fn set_job(&mut self, job: Option) { diff --git a/src/nix/host/mod.rs b/src/nix/host/mod.rs index a0718e8..eb849fd 100644 --- a/src/nix/host/mod.rs +++ b/src/nix/host/mod.rs @@ -110,8 +110,12 @@ pub trait Host: Send + Sync + std::fmt::Debug { Err(NixError::Unsupported) } - /// Check if the active profile is known to the host running Colmena - async fn active_derivation_known(&mut self) -> NixResult; + /// Returns the main system profile on the host. + /// + /// This may _not_ be the system profile that's currently activated! + /// It will first try `/nix/var/nix/profiles/system`, falling back + /// to `/run/current-system` if it doesn't exist. + async fn get_main_system_profile(&mut self) -> NixResult; /// Activates a system profile on the host, if it runs NixOS. /// diff --git a/src/nix/host/ssh.rs b/src/nix/host/ssh.rs index b8513b9..bd8e097 100644 --- a/src/nix/host/ssh.rs +++ b/src/nix/host/ssh.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use tokio::process::Command; use super::{CopyDirection, CopyOptions, Host, key_uploader}; -use crate::nix::{StorePath, Profile, Goal, NixResult, NixCommand, NixError, Key, SYSTEM_PROFILE}; +use crate::nix::{StorePath, Profile, Goal, NixResult, NixCommand, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; use crate::util::CommandExecution; use crate::job::JobHandle; @@ -78,26 +78,19 @@ impl Host for Ssh { let command = self.ssh(command); self.run_command(command).await } - async fn active_derivation_known(&mut self) -> NixResult { - let paths = self.ssh(&["realpath", SYSTEM_PROFILE]) - .capture_output() - .await; + async fn get_main_system_profile(&mut self) -> NixResult { + let command = format!("\"readlink -e {} || readlink -e {}\"", SYSTEM_PROFILE, CURRENT_PROFILE); - match paths { - Ok(paths) => { - if let Some(path) = paths.lines().into_iter().next() { - let remote_profile: StorePath = path.to_string().try_into().unwrap(); - if remote_profile.exists() { - return Ok(true); - } - return Err(NixError::ActiveProfileUnknown { - store_path: path.to_string(), - }); - } - return Ok(false); - } - Err(e) => Err(e), - } + let paths = self.ssh(&["sh", "-c", &command]) + .capture_output() + .await?; + + let path = paths.lines().into_iter().next() + .ok_or(NixError::FailedToGetCurrentProfile)? + .to_string() + .try_into()?; + + Ok(path) } fn set_job(&mut self, job: Option) { self.job = job; diff --git a/src/nix/mod.rs b/src/nix/mod.rs index 316e1aa..853cb5f 100644 --- a/src/nix/mod.rs +++ b/src/nix/mod.rs @@ -44,8 +44,12 @@ pub use flake::Flake; pub mod node_filter; pub use node_filter::NodeFilter; +/// Path to the main system profile. pub const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system"; +/// Path to the system profile that's currently active. +pub const CURRENT_PROFILE: &str = "/run/current-system"; + pub type NixResult = Result; #[non_exhaustive] @@ -81,8 +85,11 @@ pub enum NixError { #[snafu(display("Invalid NixOS system profile"))] InvalidProfile, - #[snafu(display("Unknown active profile: {}", store_path))] - ActiveProfileUnknown { store_path: String }, + #[snafu(display("Unknown active profile: {:?}", store_path))] + ActiveProfileUnknown { store_path: StorePath }, + + #[snafu(display("Could not determine current profile"))] + FailedToGetCurrentProfile, #[snafu(display("Current Nix version does not support Flakes"))] NoFlakesSupport,