summaryrefslogtreecommitdiff
path: root/src/remote.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 19:40:50 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 19:40:50 +0300
commit6f9453a480db8f087e16b8eda008a355bf57cfb6 (patch)
treeca5cf2264e9d448406fbc64a3c301001af3484b8 /src/remote.rs
parent53e4b27165caeeaaf206907d082d3614876b2e4c (diff)
fix: clarify update restart prompts
Diffstat (limited to 'src/remote.rs')
-rw-r--r--src/remote.rs173
1 files changed, 59 insertions, 114 deletions
diff --git a/src/remote.rs b/src/remote.rs
index fbc062e1..d3810d5b 100644
--- a/src/remote.rs
+++ b/src/remote.rs
@@ -166,6 +166,7 @@ pub(crate) fn run_remote(remote: RemoteLaunch) -> io::Result<()> {
&remote.target,
&prepared_remote.remote_herdr,
prepared_remote.installed_or_replaced,
+ prepared_remote.stop_after_install_approved,
remote.live_handoff,
)?;
@@ -222,10 +223,9 @@ fn ensure_remote_server_running() -> io::Result<()> {
if status.protocol == Some(CURRENT_PROTOCOL) {
return Ok(());
}
- return Err(io::Error::other(format!(
- "remote herdr server is running with protocol {}, but this bridge needs protocol {CURRENT_PROTOCOL}; rerun `herdr --remote` from an interactive terminal to approve stopping it",
- protocol_label(status.protocol)
- )));
+ return Err(io::Error::other(
+ "remote herdr server must restart before this bridge can attach; rerun `herdr --remote` from an interactive terminal to approve stopping it",
+ ));
}
crate::server::autodetect::spawn_server_daemon()?;
@@ -370,6 +370,7 @@ struct InstallSource {
struct PreparedRemoteHerdr {
remote_herdr: RemoteHerdr,
installed_or_replaced: bool,
+ stop_after_install_approved: bool,
}
impl InstallSource {
@@ -411,22 +412,25 @@ fn prepare_remote_herdr(
return Ok(PreparedRemoteHerdr {
remote_herdr: path_remote_herdr.clone(),
installed_or_replaced: false,
+ stop_after_install_approved: false,
});
}
if remote_binary_matches(target, &remote_herdr)? {
return Ok(PreparedRemoteHerdr {
remote_herdr,
installed_or_replaced: false,
+ stop_after_install_approved: false,
});
}
}
+ let mut stop_after_install_approved = false;
if let Some(status_probe_herdr) = path_remote_herdr.as_ref().or_else(|| {
remote_binary_exists(target, &remote_herdr)
.ok()
.and_then(|exists| exists.then_some(&remote_herdr))
}) {
- confirm_remote_install_with_running_server(
+ stop_after_install_approved = confirm_remote_install_with_running_server(
target,
status_probe_herdr,
live_handoff_enabled,
@@ -453,6 +457,7 @@ fn prepare_remote_herdr(
Ok(PreparedRemoteHerdr {
remote_herdr,
installed_or_replaced: true,
+ stop_after_install_approved,
})
}
@@ -659,6 +664,7 @@ fn ensure_remote_server_ready(
target: &str,
remote_herdr: &RemoteHerdr,
remote_binary_changed: bool,
+ stop_after_install_approved: bool,
live_handoff_enabled: bool,
) -> io::Result<()> {
let status = remote_server_status(target, remote_herdr)?;
@@ -677,10 +683,7 @@ fn ensure_remote_server_ready(
return Ok(());
};
- if live_handoff_enabled
- && live_handoff
- && confirm_remote_server_handoff(target, version.as_deref(), protocol, reason)?
- {
+ if live_handoff_enabled && live_handoff {
match live_handoff_remote_server(target, remote_herdr) {
Ok(()) => return Ok(()),
Err(err) => {
@@ -690,6 +693,11 @@ fn ensure_remote_server_ready(
}
}
+ if stop_after_install_approved {
+ stop_remote_server(target, remote_herdr)?;
+ return Ok(());
+ }
+
if confirm_remote_server_stop(target, version.as_deref(), protocol, reason)? {
stop_remote_server(target, remote_herdr)?;
}
@@ -717,7 +725,7 @@ fn confirm_remote_install_with_running_server(
target: &str,
remote_herdr: &RemoteHerdr,
live_handoff_enabled: bool,
-) -> io::Result<()> {
+) -> io::Result<bool> {
let status = match remote_server_status(target, remote_herdr) {
Ok(status) => status,
Err(err) => {
@@ -729,65 +737,69 @@ fn confirm_remote_install_with_running_server(
eprintln!(
"could not inspect the running remote herdr server on {target} before installing: {err}"
);
- eprint!("continue installing the remote herdr binary? [Y/n] ");
+ eprint!("continue installing the remote herdr binary? [y/N] ");
io::stderr().flush()?;
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let answer = answer.trim().to_ascii_lowercase();
- if answer == "n" || answer == "no" {
+ if answer != "y" && answer != "yes" {
return Err(io::Error::new(
io::ErrorKind::Interrupted,
"remote herdr install cancelled",
));
}
- return Ok(());
+ return Ok(false);
}
};
let RemoteServerStatus::Running {
version,
- protocol,
+ protocol: _,
live_handoff,
} = status
else {
- return Ok(());
+ return Ok(false);
};
- if live_handoff_enabled && live_handoff {
- return Ok(());
- }
-
if !io::stdin().is_terminal() {
+ if live_handoff_enabled && live_handoff {
+ return Ok(false);
+ }
return Err(io::Error::other(format!(
- "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)
+ "remote herdr server on {target} is running v{}; run from an interactive terminal to approve stopping it for the update",
+ version_label(version.as_deref())
)));
}
+ if live_handoff_enabled && live_handoff {
+ eprintln!("remote herdr server on {target} is currently running:");
+ eprintln!(" server: v{}", version_label(version.as_deref()));
+ eprintln!(
+ "Herdr will install v{CURRENT_VERSION} and hand off live pane processes to the prepared server."
+ );
+ return Ok(false);
+ }
+
eprintln!("remote herdr server on {target} is currently running:");
+ eprintln!(" server: v{}", version_label(version.as_deref()));
eprintln!(
- " server: v{} protocol {}",
- version_label(version.as_deref()),
- protocol_label(protocol)
- );
- eprintln!(
- "this attach will not preserve running panes unless you pass --handoff and the remote server supports live handoff."
+ "To complete the remote update, Herdr must stop the running remote server after installing."
);
+ eprintln!("This stops active remote pane processes, including shells, dev servers, and tests.");
eprintln!();
- eprint!("continue installing the remote herdr binary? [Y/n] ");
+ eprint!("Install v{CURRENT_VERSION} and stop the remote server now? [y/N] ");
io::stderr().flush()?;
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let answer = answer.trim().to_ascii_lowercase();
- if answer == "n" || answer == "no" {
+ if answer != "y" && answer != "yes" {
return Err(io::Error::new(
io::ErrorKind::Interrupted,
"remote herdr install cancelled",
));
}
- Ok(())
+ Ok(true)
}
fn remote_server_status(
@@ -848,14 +860,13 @@ fn parse_remote_server_status_json(status: &str) -> io::Result<RemoteServerStatu
fn confirm_remote_server_stop(
target: &str,
version: Option<&str>,
- protocol: Option<u32>,
+ _protocol: Option<u32>,
reason: RemoteServerRestartReason,
) -> io::Result<bool> {
if !io::stdin().is_terminal() {
if reason == RemoteServerRestartReason::ProtocolMismatch {
return Err(io::Error::other(format!(
- "remote herdr server on {target} is running with protocol {}, but this client needs protocol {CURRENT_PROTOCOL}; run from an interactive terminal to approve stopping it",
- protocol_label(protocol)
+ "remote herdr server on {target} must stop before this client can attach; run from an interactive terminal to approve stopping it"
)));
}
@@ -867,19 +878,13 @@ fn confirm_remote_server_stop(
}
eprintln!("remote herdr server on {target} is currently running:");
- eprintln!(
- " server: v{} protocol {}",
- version_label(version),
- protocol_label(protocol)
- );
- eprintln!(" prepared binary: v{CURRENT_VERSION} protocol {CURRENT_PROTOCOL}");
+ eprintln!(" server: v{}", version_label(version));
+ eprintln!(" prepared binary: v{CURRENT_VERSION}");
eprintln!();
match reason {
RemoteServerRestartReason::ProtocolMismatch => {
- eprintln!(
- "the remote server protocol does not match this client. the remote server must be stopped before attaching."
- );
+ eprintln!("the remote server must stop before this client can attach.");
}
RemoteServerRestartReason::BinaryUpdated => {
eprintln!(
@@ -896,7 +901,7 @@ fn confirm_remote_server_stop(
let prompt = if reason == RemoteServerRestartReason::ProtocolMismatch {
"stop the remote server and continue attaching? [Y/n] "
} else {
- "restart the remote server now? [Y/n] "
+ "restart the remote server now? [y/N] "
};
eprint!("{prompt}");
io::stderr().flush()?;
@@ -904,74 +909,20 @@ fn confirm_remote_server_stop(
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let answer = answer.trim().to_ascii_lowercase();
- if answer == "n" || answer == "no" {
- if reason == RemoteServerRestartReason::ProtocolMismatch {
- return Err(io::Error::new(
- io::ErrorKind::Interrupted,
- "remote herdr server stop cancelled",
- ));
- }
- return Ok(false);
+ if answer == "y" || answer == "yes" {
+ return Ok(true);
}
-
- Ok(true)
-}
-
-fn confirm_remote_server_handoff(
- target: &str,
- version: Option<&str>,
- protocol: Option<u32>,
- reason: RemoteServerRestartReason,
-) -> io::Result<bool> {
- if !io::stdin().is_terminal() {
- if reason == RemoteServerRestartReason::ProtocolMismatch {
- return Err(io::Error::other(format!(
- "remote herdr server on {target} is running with protocol {}, but this client needs protocol {CURRENT_PROTOCOL}; run from an interactive terminal to approve live handoff or stopping it",
- protocol_label(protocol)
- )));
- }
-
- eprintln!(
- "remote herdr server on {target} is still running v{}; it will use v{CURRENT_VERSION} after it restarts.",
- version_label(version)
- );
- return Ok(false);
+ if answer.is_empty() && reason == RemoteServerRestartReason::ProtocolMismatch {
+ return Ok(true);
}
-
- eprintln!("remote herdr server on {target} is currently running:");
- eprintln!(
- " server: v{} protocol {}",
- version_label(version),
- protocol_label(protocol)
- );
- eprintln!(" prepared binary: v{CURRENT_VERSION} protocol {CURRENT_PROTOCOL}");
- eprintln!();
-
- match reason {
- RemoteServerRestartReason::ProtocolMismatch => {
- eprintln!(
- "the remote server protocol does not match this client. herdr will try to hand off live pane processes to the prepared remote server before the old server exits."
- );
- }
- RemoteServerRestartReason::BinaryUpdated => {
- eprintln!(
- "the remote herdr binary was installed or replaced. herdr will try to hand off live pane processes to the prepared remote server."
- );
- }
- RemoteServerRestartReason::VersionMismatch => {
- eprintln!(
- "the remote server is still running a different herdr version. herdr will try to hand off live pane processes to the prepared remote server."
- );
- }
+ if reason == RemoteServerRestartReason::ProtocolMismatch {
+ return Err(io::Error::new(
+ io::ErrorKind::Interrupted,
+ "remote herdr server stop cancelled",
+ ));
}
- eprint!("live-handoff remote panes to the prepared server? [Y/n] ");
- io::stderr().flush()?;
-
- let mut answer = String::new();
- io::stdin().read_line(&mut answer)?;
- let answer = answer.trim().to_ascii_lowercase();
- Ok(answer != "n" && answer != "no")
+ Ok(false)
}
fn live_handoff_remote_server(target: &str, remote_herdr: &RemoteHerdr) -> io::Result<()> {
@@ -1026,12 +977,6 @@ fn version_label(version: Option<&str>) -> &str {
version.unwrap_or("unknown")
}
-fn protocol_label(protocol: Option<u32>) -> String {
- protocol
- .map(|value| value.to_string())
- .unwrap_or_else(|| "unknown".to_string())
-}
-
fn warn_if_remote_bin_not_on_path(target: &str) -> io::Result<()> {
let output = ssh_output(
target,