diff --git a/src/lib.rs b/src/lib.rs index 5c6b19a..dde4843 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ pub(crate) use os::{NixOperatingSystem, Verb}; use anyhow::{anyhow, bail, Context}; use os::Nixos; +use std::borrow::Cow; +use std::collections::HashMap; use std::{ fmt, path::{Path, PathBuf}, @@ -220,9 +222,32 @@ impl fmt::Display for Flavor { } impl Flavor { - pub fn on_connection(&self, host: &str, connection: openssh::Session) -> Arc { + pub fn on_connection(&self, dest: &Destination, connection: openssh::Session) -> Arc { match self { - Flavor::Nixos => Arc::new(Nixos::new(host.to_owned(), connection)), + Flavor::Nixos => Arc::new(Nixos::new(dest.clone(), connection)), + } + } +} + +/// Where and how to perform the build +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub enum BuildKind{ + /// Build on the deploy destination host + #[default] + OnDestination, + + /// Build on the system running deploy-flake + Local, +} + +impl FromStr for BuildKind { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "local" => Ok(BuildKind::Local), + "on_destination" => Ok(BuildKind::OnDestination), + _ => Err(anyhow::anyhow!("Could not parse build kind {:?}", s)) } } } @@ -232,6 +257,7 @@ pub struct Destination { pub os_flavor: Flavor, pub hostname: String, pub config_name: Option, + pub build_kind: BuildKind, } impl FromStr for Destination { @@ -239,6 +265,12 @@ impl FromStr for Destination { fn from_str(s: &str) -> Result { if let Ok(url) = Url::parse(s) { + let query: HashMap, Cow<'_, str>>= url.query_pairs().collect(); + let build_kind = if let Some(build_kind) = query.get("build_kind") { + build_kind.parse::().with_context(|| format!("Parsing {:?}", s))? + } else { + Default::default() + }; // we have a URL, let's see if it matches something we can deal with: match (url.scheme(), url.host_str(), url.path(), url.username()) { ("nixos", Some(host), path, username) => { @@ -254,6 +286,7 @@ impl FromStr for Destination { .strip_prefix('/') .filter(|path| !path.is_empty()) .map(String::from), + build_kind, }) } _ => anyhow::bail!("Unable to parse {s}"), @@ -263,11 +296,18 @@ impl FromStr for Destination { os_flavor: Flavor::Nixos, hostname: s.to_string(), config_name: None, + build_kind: Default::default() }) } } } +impl fmt::Display for Destination { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}://{}/{}", self.os_flavor, self.hostname, self.config_name.as_deref().unwrap_or("")) + } +} + #[cfg(test)] mod test { use super::Destination; diff --git a/src/main.rs b/src/main.rs index ece1a07..19da37b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,7 +156,7 @@ async fn deploy( log::debug!("Connecting"); let flavor = destination.os_flavor.on_connection( - &destination.hostname, + &destination, Session::connect(&destination.hostname, KnownHosts::Strict) .await .with_context(|| format!("Connecting to {:?}", &destination.hostname))?, diff --git a/src/os/nixos.rs b/src/os/nixos.rs index 61f342a..3b3d622 100644 --- a/src/os/nixos.rs +++ b/src/os/nixos.rs @@ -1,4 +1,4 @@ -use crate::read_and_log_messages; +use crate::{read_and_log_messages, Destination}; use anyhow::Context; use openssh::{Command, Stdio}; use tokio::io::AsyncReadExt; @@ -18,7 +18,7 @@ use crate::{NixOperatingSystem, Verb}; /// A nixos operating system instance. pub struct Nixos { - host: String, + host: Destination, session: openssh::Session, } @@ -36,7 +36,7 @@ fn strip_shell_output(output: Output) -> String { impl Nixos { /// Setup a new Nixos connection - pub(crate) fn new(host: String, session: openssh::Session) -> Self { + pub(crate) fn new(host: Destination, session: openssh::Session) -> Self { Self { host, session } } @@ -169,7 +169,7 @@ impl NixOperatingSystem for Nixos { let script_path = if script.is_none() { // Try to use the default pre-activation script name emitted by preflight-safety: let script_path = derivation.join(DEFAULT_PREFLIGHT_SCRIPT_NAME); - log::event!(log::Level::DEBUG, dest=?self.host, script=?script_path.file_name(), "Checking for existence of inferred pre-activation script"); + log::event!(log::Level::DEBUG, dest=%self.host, script=?script_path.file_name(), "Checking for existence of inferred pre-activation script"); if !self.test_file_existence(&script_path).await? { return Ok(()); } @@ -177,7 +177,7 @@ impl NixOperatingSystem for Nixos { } else { derivation.join(script.unwrap()) }; - log::event!(log::Level::INFO, dest=?self.host, script=?script_path.file_name(), "Running pre-activation script"); + log::event!(log::Level::INFO, dest=%self.host, script=?script_path.file_name(), "Running pre-activation script"); let mut cmd = self.session.command("sudo"); cmd.raw_arg(script_path); self.run_command(cmd) @@ -260,7 +260,7 @@ impl NixOperatingSystem for Nixos { Ok(()) } - #[instrument(level = "DEBUG", skip(self), fields(host=self.host), err)] + #[instrument(level = "DEBUG", skip(self), fields(host=%self.host), err)] async fn test_config(&self, derivation: &Path) -> Result<(), anyhow::Error> { let mut cmd = self.session.command("sudo"); let flake_base_name = derivation