diff --git a/Cargo.lock b/Cargo.lock index aed1866e..17dee84f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,6 +254,7 @@ dependencies = [ "rpassword", "serde", "serde_json", + "shlex", "signal-hook", "thiserror 2.0.12", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 071fb5bd..ff9d3461 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ toml = "0.8" whoami = "1.6" yn = "0.1" rpassword = "7.3.1" +shlex = "1.3.0" [lib] diff --git a/src/lib.rs b/src/lib.rs index 91ab7c76..d57d8a17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -448,7 +448,8 @@ pub fn make_deploy_data<'a, 's>( merged_settings.user = cmd_overrides.profile_user.clone(); } if let Some(ref ssh_opts) = cmd_overrides.ssh_opts { - merged_settings.ssh_opts = ssh_opts.split(' ').map(|x| x.to_owned()).collect(); + merged_settings.ssh_opts = + shlex::split(ssh_opts).unwrap_or(ssh_opts.split(' ').map(|x| x.to_owned()).collect()); } if let Some(fast_connection) = cmd_overrides.fast_connection { merged_settings.fast_connection = Some(fast_connection); diff --git a/src/push.rs b/src/push.rs index 6c777c64..6686511f 100644 --- a/src/push.rs +++ b/src/push.rs @@ -163,8 +163,15 @@ pub async fn build_profile_remotely(data: &PushProfileData<'_>, derivation_name: }; let store_address = format!("ssh-ng://{}@{}", data.deploy_defs.ssh_user, hostname); - let ssh_opts_str = data.deploy_data.merged_settings.ssh_opts.join(" "); - + let ssh_opts_str = shlex::try_join( + data.deploy_data + .merged_settings + .ssh_opts + .iter() + .map(String::as_str) + .collect::>(), + ) + .unwrap_or(data.deploy_data.merged_settings.ssh_opts.join(" ")); // copy the derivation to remote host so it can be built there let copy_command_status = Command::new("nix") @@ -289,15 +296,16 @@ pub async fn build_profile(data: PushProfileData<'_>) -> Result<(), PushProfileE } pub async fn push_profile(data: PushProfileData<'_>) -> Result<(), PushProfileError> { - let ssh_opts_str = data - .deploy_data - .merged_settings - .ssh_opts - // This should provide some extra safety, but it also breaks for some reason, oh well - // .iter() - // .map(|x| format!("'{}'", x)) - // .collect::>() - .join(" "); + let ssh_opts_str = shlex::try_join( + data.deploy_data + .merged_settings + .ssh_opts + .iter() + .map(String::as_str) + .collect::>(), + ) + .unwrap_or(data.deploy_data.merged_settings.ssh_opts.join(" ")); + // remote building guarantees that the resulting derivation is stored on the target system // no need to copy after building