Skip to content

Commit c404d50

Browse files
authored
Merge pull request #44 from Zeus-Deus/codemux-duplicate-install-prompt
fix(hosts): probe ~/.local/bin/codemux-remote when PATH lookup fails
2 parents f8976da + 9ec51e5 commit c404d50

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

src-tauri/src/ssh/probe.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,22 @@ pub fn build_probe_argv(ssh_target: &str, timeout_secs: u64) -> Vec<String> {
7878
// `codemux-remote version` if available. The remote-side
7979
// `printf` separates the two with a sentinel so the laptop
8080
// can split.
81+
//
82+
// The `$HOME/.local/bin/codemux-remote` fallback exists because
83+
// bootstrap installs there, but a non-interactive SSH shell
84+
// (which is what `ssh user@host 'cmd'` runs) typically does NOT
85+
// source `~/.profile` — so `~/.local/bin` is missing from PATH
86+
// on most distros (Arch, Ubuntu, Debian, Fedora). Without the
87+
// fallback, `command -v` returns nothing immediately after a
88+
// successful install and the UI re-shows the install button on
89+
// every subsequent "Test connection" press. Mirrors the same
90+
// fix applied in `commands::hosts::ensure_remote_binary_current`
91+
// and the supervisor's tunnel-spawn command.
8192
"printf 'UNAME: ' ; uname -sm ; \
8293
if command -v codemux-remote >/dev/null 2>&1 ; then \
8394
printf 'CMR: ' ; codemux-remote version ; \
95+
elif [ -x \"$HOME/.local/bin/codemux-remote\" ] ; then \
96+
printf 'CMR: ' ; \"$HOME/.local/bin/codemux-remote\" version ; \
8497
else \
8598
printf 'CMR: NOT_INSTALLED\\n' ; \
8699
fi"
@@ -185,6 +198,37 @@ mod tests {
185198
assert!(argv.last().unwrap().contains("codemux-remote"));
186199
}
187200

201+
#[test]
202+
fn build_probe_argv_falls_back_to_home_local_bin() {
203+
// Bootstrap installs to ~/.local/bin/codemux-remote, but a
204+
// non-interactive ssh shell typically doesn't have that dir on
205+
// PATH (it's added by ~/.profile, which only login shells
206+
// source). Without an explicit fallback, `command -v` returns
207+
// nothing immediately after a successful install and the UI
208+
// re-shows the install button on every "Test" press.
209+
//
210+
// Lock in BOTH the PATH lookup AND the absolute-path fallback
211+
// so a future refactor doesn't silently drop either branch.
212+
let argv = build_probe_argv("zeus@10.0.0.5", 8);
213+
let cmd = argv.last().unwrap();
214+
assert!(
215+
cmd.contains("command -v codemux-remote"),
216+
"PATH lookup must remain (fast path when binary is on PATH)"
217+
);
218+
assert!(
219+
cmd.contains("$HOME/.local/bin/codemux-remote"),
220+
"absolute-path fallback must remain (covers the common case \
221+
where ~/.local/bin isn't on the non-interactive PATH)"
222+
);
223+
// The fallback must run the binary, not just stat it — otherwise
224+
// we'd report "installed" for a 0-byte file or a half-uploaded
225+
// binary that doesn't actually execute.
226+
assert!(
227+
cmd.contains("\"$HOME/.local/bin/codemux-remote\" version"),
228+
"fallback must invoke the binary's version subcommand"
229+
);
230+
}
231+
188232
#[test]
189233
fn parse_probe_stdout_reachable_with_installed_binary() {
190234
let payload = r#"UNAME: Linux x86_64

0 commit comments

Comments
 (0)