diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-19 01:13:03 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-19 01:13:03 +0300 |
| commit | 6e8132bf789a86b2cc9558e47ce172c65a96fe3b (patch) | |
| tree | 68add86a47a9233dff381d29d1edc3082f4aea1f /src | |
| parent | f412d171d0423e524056969463a3ef29d5da7a90 (diff) | |
fix: find remote herdr on path
refs #170
Diffstat (limited to 'src')
| -rw-r--r-- | src/remote.rs | 135 |
1 files changed, 133 insertions, 2 deletions
diff --git a/src/remote.rs b/src/remote.rs index d50a4e72..22f6ba74 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -200,6 +200,11 @@ impl RemoteHerdr { platform, } } + + fn with_shell_path(mut self, shell_path: String) -> Self { + self.shell_path = shell_path; + self + } } #[derive(Deserialize)] @@ -240,8 +245,13 @@ fn prepare_remote_herdr(target: &str) -> io::Result<RemoteHerdr> { let remote_herdr = RemoteHerdr::for_platform(platform); let override_binary = remote_binary_override_path()?; - if override_binary.is_none() && remote_binary_matches(target, &remote_herdr)? { - return Ok(remote_herdr); + if override_binary.is_none() { + if let Some(path_remote_herdr) = remote_binary_on_path(target, &remote_herdr)? { + return Ok(path_remote_herdr); + } + if remote_binary_matches(target, &remote_herdr)? { + return Ok(remote_herdr); + } } confirm_remote_install( @@ -284,6 +294,38 @@ fn detect_remote_platform(target: &str) -> io::Result<RemotePlatform> { }) } +fn remote_binary_on_path( + target: &str, + remote_herdr: &RemoteHerdr, +) -> io::Result<Option<RemoteHerdr>> { + let output = ssh_output(target, remote_path_probe_command())?; + if !output.status.success() { + return Ok(None); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + Ok(remote_herdr_from_path_probe(remote_herdr, &stdout)) +} + +fn remote_path_probe_command() -> &'static str { + r#"path=$(command -v herdr) || exit 1 +test -n "$path" || exit 1 +version=$("$path" --version) || exit 1 +printf '%s\n%s\n' "$path" "$version" +"# +} + +fn remote_herdr_from_path_probe(remote_herdr: &RemoteHerdr, stdout: &str) -> Option<RemoteHerdr> { + let mut lines = stdout.lines(); + let path = lines.next()?; + let version = lines.next()?.trim(); + if !path.starts_with('/') || version != format!("herdr {CURRENT_VERSION}") { + return None; + } + + Some(remote_herdr.clone().with_shell_path(shell_quote(path))) +} + fn remote_binary_matches(target: &str, remote_herdr: &RemoteHerdr) -> io::Result<bool> { let command = format!("test -x {0} && {0} --version", remote_herdr.shell_path); let output = ssh_output(target, &command)?; @@ -939,6 +981,95 @@ mod tests { } #[test] + fn remote_path_probe_uses_path_binary_when_version_matches() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "linux", + arch: "x86_64", + }); + let stdout = format!("/usr/bin/herdr\nherdr {CURRENT_VERSION}\n"); + let remote_herdr = + remote_herdr_from_path_probe(&remote_herdr, &stdout).expect("matching path binary"); + + assert_eq!( + remote_bridge_command(&remote_herdr, crate::session::DEFAULT_SESSION_NAME), + "exec /usr/bin/herdr remote-client-bridge" + ); + } + + #[test] + fn remote_path_probe_quotes_discovered_binary() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "linux", + arch: "x86_64", + }); + let stdout = format!("/opt/herdr bin/herdr\nherdr {CURRENT_VERSION}\n"); + let remote_herdr = + remote_herdr_from_path_probe(&remote_herdr, &stdout).expect("matching path binary"); + + assert_eq!( + remote_bridge_command(&remote_herdr, crate::session::DEFAULT_SESSION_NAME), + "exec '/opt/herdr bin/herdr' remote-client-bridge" + ); + } + + #[test] + fn remote_path_probe_uses_macos_path_binary_when_version_matches() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "macos", + arch: "aarch64", + }); + let stdout = format!("/opt/homebrew/bin/herdr\nherdr {CURRENT_VERSION}\n"); + let remote_herdr = + remote_herdr_from_path_probe(&remote_herdr, &stdout).expect("matching path binary"); + + assert_eq!( + remote_bridge_command(&remote_herdr, crate::session::DEFAULT_SESSION_NAME), + "exec /opt/homebrew/bin/herdr remote-client-bridge" + ); + assert_eq!(remote_herdr.platform.asset_key(), "macos-aarch64"); + } + + #[test] + fn remote_path_probe_quotes_single_quotes_in_discovered_binary() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "linux", + arch: "x86_64", + }); + let stdout = format!("/opt/herdr's/bin/herdr\nherdr {CURRENT_VERSION}\n"); + let remote_herdr = + remote_herdr_from_path_probe(&remote_herdr, &stdout).expect("matching path binary"); + + assert_eq!( + remote_bridge_command(&remote_herdr, crate::session::DEFAULT_SESSION_NAME), + "exec '/opt/herdr'\\''s/bin/herdr' remote-client-bridge" + ); + } + + #[test] + fn remote_path_probe_ignores_version_mismatch() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "linux", + arch: "x86_64", + }); + let remote_herdr = + remote_herdr_from_path_probe(&remote_herdr, "/usr/bin/herdr\nherdr 0.0.0\n"); + + assert!(remote_herdr.is_none()); + } + + #[test] + fn remote_path_probe_ignores_relative_paths() { + let remote_herdr = RemoteHerdr::for_platform(RemotePlatform { + os: "linux", + arch: "x86_64", + }); + let stdout = format!("bin/herdr\nherdr {CURRENT_VERSION}\n"); + let remote_herdr = remote_herdr_from_path_probe(&remote_herdr, &stdout); + + assert!(remote_herdr.is_none()); + } + + #[test] fn install_source_description_uses_override_binary() { let platform = RemotePlatform { os: "linux", |
