fix: scope ssh shell niceties to cmux ssh
This commit is contained in:
parent
fb35f3bc39
commit
bbd8b2b311
4 changed files with 12 additions and 89 deletions
|
|
@ -1899,7 +1899,10 @@ struct CMUXCLI {
|
||||||
}
|
}
|
||||||
parts.append(options.destination)
|
parts.append(options.destination)
|
||||||
parts.append(contentsOf: options.extraArguments)
|
parts.append(contentsOf: options.extraArguments)
|
||||||
return parts.map(shellQuote).joined(separator: " ")
|
let sshCommand = parts.map(shellQuote).joined(separator: " ")
|
||||||
|
// Scope Ghostty SSH niceties to `cmux ssh ...` launches only.
|
||||||
|
let shellFeatures = "GHOSTTY_SHELL_FEATURES=${GHOSTTY_SHELL_FEATURES:+$GHOSTTY_SHELL_FEATURES,}ssh-env,ssh-terminfo"
|
||||||
|
return shellFeatures + " " + sshCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
private func shellQuote(_ value: String) -> String {
|
private func shellQuote(_ value: String) -> String {
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,6 @@ fi
|
||||||
[[ ! -r "$_cmux_file" ]] || builtin source -- "$_cmux_file"
|
[[ ! -r "$_cmux_file" ]] || builtin source -- "$_cmux_file"
|
||||||
} always {
|
} always {
|
||||||
if [[ -o interactive ]]; then
|
if [[ -o interactive ]]; then
|
||||||
# Opt into Ghostty's SSH shell niceties by default so plain `ssh ...`
|
|
||||||
# gets TERM compatibility + remote terminfo setup.
|
|
||||||
if [[ -n "${GHOSTTY_SHELL_FEATURES:-}" ]]; then
|
|
||||||
builtin typeset _cmux_features=",$GHOSTTY_SHELL_FEATURES,"
|
|
||||||
if [[ "$_cmux_features" != *",ssh-env,"* ]]; then
|
|
||||||
builtin export GHOSTTY_SHELL_FEATURES="${GHOSTTY_SHELL_FEATURES},ssh-env"
|
|
||||||
_cmux_features=",$GHOSTTY_SHELL_FEATURES,"
|
|
||||||
fi
|
|
||||||
if [[ "$_cmux_features" != *",ssh-terminfo,"* ]]; then
|
|
||||||
builtin export GHOSTTY_SHELL_FEATURES="${GHOSTTY_SHELL_FEATURES},ssh-terminfo"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# We overwrote GhosttyKit's injected ZDOTDIR, so manually load Ghostty's
|
# We overwrote GhosttyKit's injected ZDOTDIR, so manually load Ghostty's
|
||||||
# zsh integration if available.
|
# zsh integration if available.
|
||||||
if [[ -n "${GHOSTTY_RESOURCES_DIR:-}" ]]; then
|
if [[ -n "${GHOSTTY_RESOURCES_DIR:-}" ]]; then
|
||||||
|
|
@ -56,5 +43,5 @@ fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
builtin unset _cmux_file _cmux_features _cmux_ghostty _cmux_integ
|
builtin unset _cmux_file _cmux_ghostty _cmux_integ
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,13 @@ def main() -> int:
|
||||||
workspace_id = str(row.get("id") or "")
|
workspace_id = str(row.get("id") or "")
|
||||||
break
|
break
|
||||||
_must(bool(workspace_id), f"cmux ssh output missing workspace_id: {payload}")
|
_must(bool(workspace_id), f"cmux ssh output missing workspace_id: {payload}")
|
||||||
|
ssh_command = str(payload.get("ssh_command") or "")
|
||||||
|
_must(bool(ssh_command), f"cmux ssh output missing ssh_command: {payload}")
|
||||||
|
_must(
|
||||||
|
"GHOSTTY_SHELL_FEATURES=${GHOSTTY_SHELL_FEATURES:+$GHOSTTY_SHELL_FEATURES,}ssh-env,ssh-terminfo" in ssh_command,
|
||||||
|
f"cmux ssh should scope ssh niceties to this command: {ssh_command!r}",
|
||||||
|
)
|
||||||
|
_must("ssh -o StrictHostKeyChecking=accept-new" in ssh_command, f"ssh command prefix mismatch: {ssh_command!r}")
|
||||||
|
|
||||||
listed_row = None
|
listed_row = None
|
||||||
deadline = time.time() + 8.0
|
deadline = time.time() + 8.0
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""Regression: cmux shell integration enables ssh niceties by default."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
|
||||||
from cmux import cmux, cmuxError
|
|
||||||
|
|
||||||
|
|
||||||
SOCKET_PATH = os.environ.get("CMUX_SOCKET", "/tmp/cmux-debug.sock")
|
|
||||||
|
|
||||||
|
|
||||||
def _must(cond: bool, msg: str) -> None:
|
|
||||||
if not cond:
|
|
||||||
raise cmuxError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_surface(client: cmux, workspace_id: str, timeout_s: float = 8.0) -> str:
|
|
||||||
deadline = time.time() + timeout_s
|
|
||||||
while time.time() < deadline:
|
|
||||||
surfaces = client.list_surfaces(workspace_id)
|
|
||||||
if surfaces:
|
|
||||||
return str(surfaces[0][1])
|
|
||||||
time.sleep(0.1)
|
|
||||||
raise cmuxError(f"workspace {workspace_id} did not create a terminal surface in time")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
output_path = Path(tempfile.gettempdir()) / f"cmux_ssh_shell_features_{os.getpid()}_{int(time.time() * 1000)}.txt"
|
|
||||||
workspace_id = ""
|
|
||||||
|
|
||||||
with cmux(SOCKET_PATH) as client:
|
|
||||||
try:
|
|
||||||
workspace_id = client.new_workspace()
|
|
||||||
surface_id = _wait_for_surface(client, workspace_id)
|
|
||||||
|
|
||||||
probe = f"echo \"$GHOSTTY_SHELL_FEATURES\" > {shlex.quote(str(output_path))}\n"
|
|
||||||
deadline = time.time() + 8.0
|
|
||||||
last_send = 0.0
|
|
||||||
while time.time() < deadline and not output_path.exists():
|
|
||||||
now = time.time()
|
|
||||||
# Surface creation can race the first shell prompt; retry until one sticks.
|
|
||||||
if now - last_send >= 0.5:
|
|
||||||
client.send_surface(surface_id, probe)
|
|
||||||
last_send = now
|
|
||||||
time.sleep(0.05)
|
|
||||||
_must(output_path.exists(), "Timed out waiting for shell feature probe output")
|
|
||||||
|
|
||||||
raw = output_path.read_text(encoding="utf-8", errors="replace").strip()
|
|
||||||
features = {token.strip() for token in raw.split(",") if token.strip()}
|
|
||||||
_must("ssh-env" in features, f"GHOSTTY_SHELL_FEATURES missing ssh-env: {raw!r}")
|
|
||||||
_must("ssh-terminfo" in features, f"GHOSTTY_SHELL_FEATURES missing ssh-terminfo: {raw!r}")
|
|
||||||
finally:
|
|
||||||
if workspace_id:
|
|
||||||
try:
|
|
||||||
client.close_workspace(workspace_id)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
output_path.unlink()
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print("PASS: shell integration defaults include ssh-env and ssh-terminfo")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue