aboutsummaryrefslogtreecommitdiff
path: root/src/remote.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remote.rs')
-rw-r--r--src/remote.rs88
1 files changed, 76 insertions, 12 deletions
diff --git a/src/remote.rs b/src/remote.rs
index 990ffb7b..9a4cc774 100644
--- a/src/remote.rs
+++ b/src/remote.rs
@@ -54,6 +54,7 @@ impl RemoteKeybindings {
pub(crate) struct RemoteLaunch {
pub(crate) target: String,
pub(crate) keybindings: RemoteKeybindings,
+ pub(crate) live_handoff: bool,
}
pub(crate) fn extract_remote_args(
@@ -67,9 +68,15 @@ pub(crate) fn extract_remote_args(
let mut remote_target = None;
let mut keybindings = RemoteKeybindings::Local;
let mut keybindings_seen = false;
+ let mut live_handoff = false;
let mut index = 1;
while index < args.len() {
let arg = &args[index];
+ if arg == "--handoff" {
+ live_handoff = true;
+ index += 1;
+ continue;
+ }
if arg == "--remote" {
if remote_target.is_some() {
return Err("--remote can only be specified once".to_string());
@@ -118,10 +125,14 @@ pub(crate) fn extract_remote_args(
let remote = remote_target.map(|target| RemoteLaunch {
target,
keybindings,
+ live_handoff,
});
if remote.is_none() && keybindings_seen {
return Err("--remote-keybindings requires --remote".to_string());
}
+ if remote.is_none() && live_handoff {
+ cleaned.push("--handoff".to_string());
+ }
Ok((cleaned, remote))
}
@@ -143,13 +154,19 @@ pub(crate) fn run_remote(remote: RemoteLaunch) -> io::Result<()> {
let program = std::env::args()
.next()
.unwrap_or_else(|| "herdr".to_string());
- let reattach_command =
- reattach_command(&program, &remote.target, &session_name, remote.keybindings);
- let prepared_remote = prepare_remote_herdr(&remote.target)?;
+ let reattach_command = reattach_command(
+ &program,
+ &remote.target,
+ &session_name,
+ remote.keybindings,
+ remote.live_handoff,
+ );
+ let prepared_remote = prepare_remote_herdr(&remote.target, remote.live_handoff)?;
ensure_remote_server_ready(
&remote.target,
&prepared_remote.remote_herdr,
prepared_remote.installed_or_replaced,
+ remote.live_handoff,
)?;
let _bridge = SshStdioBridge::start(
@@ -318,7 +335,10 @@ impl InstallSource {
}
}
-fn prepare_remote_herdr(target: &str) -> io::Result<PreparedRemoteHerdr> {
+fn prepare_remote_herdr(
+ target: &str,
+ live_handoff_enabled: bool,
+) -> io::Result<PreparedRemoteHerdr> {
let platform = detect_remote_platform(target)?;
let remote_herdr = RemoteHerdr::for_platform(platform);
let override_binary = remote_binary_override_path()?;
@@ -347,7 +367,11 @@ fn prepare_remote_herdr(target: &str) -> io::Result<PreparedRemoteHerdr> {
.ok()
.and_then(|exists| exists.then_some(&remote_herdr))
}) {
- confirm_remote_install_with_running_server(target, status_probe_herdr)?;
+ confirm_remote_install_with_running_server(
+ target,
+ status_probe_herdr,
+ live_handoff_enabled,
+ )?;
}
confirm_remote_install(
target,
@@ -552,6 +576,7 @@ fn ensure_remote_server_ready(
target: &str,
remote_herdr: &RemoteHerdr,
remote_binary_changed: bool,
+ live_handoff_enabled: bool,
) -> io::Result<()> {
let status = remote_server_status(target, remote_herdr)?;
let RemoteServerStatus::Running {
@@ -569,7 +594,9 @@ fn ensure_remote_server_ready(
return Ok(());
};
- if live_handoff && confirm_remote_server_handoff(target, version.as_deref(), protocol, reason)?
+ if live_handoff_enabled
+ && live_handoff
+ && confirm_remote_server_handoff(target, version.as_deref(), protocol, reason)?
{
match live_handoff_remote_server(target, remote_herdr) {
Ok(()) => return Ok(()),
@@ -606,6 +633,7 @@ fn remote_server_restart_reason(
fn confirm_remote_install_with_running_server(
target: &str,
remote_herdr: &RemoteHerdr,
+ live_handoff_enabled: bool,
) -> io::Result<()> {
let status = match remote_server_status(target, remote_herdr) {
Ok(status) => status,
@@ -641,13 +669,13 @@ fn confirm_remote_install_with_running_server(
else {
return Ok(());
};
- if live_handoff {
+ if live_handoff_enabled && live_handoff {
return Ok(());
}
if !io::stdin().is_terminal() {
return Err(io::Error::other(format!(
- "remote herdr server on {target} is running v{} protocol {}, but it does not advertise live handoff; run from an interactive terminal to approve updating the remote binary",
+ "remote herdr server on {target} is running v{} protocol {}; run from an interactive terminal to approve updating the remote binary",
version_label(version.as_deref()),
protocol_label(protocol)
)));
@@ -660,10 +688,7 @@ fn confirm_remote_install_with_running_server(
protocol_label(protocol)
);
eprintln!(
- "this server does not advertise live handoff, so this attach cannot preserve its running panes during the update."
- );
- eprintln!(
- "future remote updates can preserve panes after the remote server has run a handoff-capable version once."
+ "this attach will not preserve running panes unless you pass --handoff and the remote server supports live handoff."
);
eprintln!();
eprint!("continue installing the remote herdr binary? [Y/n] ");
@@ -1118,6 +1143,7 @@ fn reattach_command(
target: &str,
session_name: &str,
keybindings: RemoteKeybindings,
+ live_handoff: bool,
) -> String {
let program = if program.is_empty() { "herdr" } else { program };
let mut command = format!("{} --remote {}", shell_quote(program), shell_quote(target));
@@ -1125,6 +1151,9 @@ fn reattach_command(
command.push_str(" --remote-keybindings ");
command.push_str(keybindings.as_str());
}
+ if live_handoff {
+ command.push_str(" --handoff");
+ }
if session_name != crate::session::DEFAULT_SESSION_NAME {
command.push_str(" --session ");
command.push_str(&shell_quote(session_name));
@@ -1471,6 +1500,28 @@ mod tests {
}
#[test]
+ fn extract_remote_args_accepts_explicit_handoff() {
+ let args = vec!["herdr".into(), "--remote=dev".into(), "--handoff".into()];
+
+ let (cleaned, remote) = extract_remote_args(&args).unwrap();
+
+ assert_eq!(cleaned, vec!["herdr"]);
+ let remote = remote.unwrap();
+ assert_eq!(remote.target, "dev");
+ assert!(remote.live_handoff);
+ }
+
+ #[test]
+ fn extract_remote_args_preserves_handoff_without_remote() {
+ let args = vec!["herdr".into(), "update".into(), "--handoff".into()];
+
+ let (cleaned, remote) = extract_remote_args(&args).unwrap();
+
+ assert_eq!(cleaned, args);
+ assert!(remote.is_none());
+ }
+
+ #[test]
fn extract_remote_args_rejects_remote_keybindings_without_remote() {
let args = vec!["herdr".into(), "--remote-keybindings=server".into()];
let err = extract_remote_args(&args).unwrap_err();
@@ -1551,6 +1602,7 @@ mod tests {
"user@host",
"work",
RemoteKeybindings::Local,
+ false,
),
"target/release/herdr --remote user@host --session work"
);
@@ -1560,6 +1612,7 @@ mod tests {
"host name",
crate::session::DEFAULT_SESSION_NAME,
RemoteKeybindings::Local,
+ false,
),
"herdr --remote 'host name'"
);
@@ -1569,9 +1622,20 @@ mod tests {
"host",
crate::session::DEFAULT_SESSION_NAME,
RemoteKeybindings::Server,
+ false,
),
"herdr --remote host --remote-keybindings server"
);
+ assert_eq!(
+ reattach_command(
+ "herdr",
+ "host",
+ crate::session::DEFAULT_SESSION_NAME,
+ RemoteKeybindings::Local,
+ true,
+ ),
+ "herdr --remote host --handoff"
+ );
}
#[test]