fix: SSH compatibility with fish shell on remote host
- Remove bash -c from remote_exec (pass command as single SSH arg) - Fix remote_write_file to pass bash -c script as single quoted string - Add LogLevel=ERROR to suppress SSH warning messages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ def _ssh_base_cmd() -> list[str]:
|
|||||||
"ssh",
|
"ssh",
|
||||||
"-o", "StrictHostKeyChecking=no",
|
"-o", "StrictHostKeyChecking=no",
|
||||||
"-o", "UserKnownHostsFile=/dev/null",
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
|
"-o", "LogLevel=ERROR",
|
||||||
"-o", "BatchMode=yes",
|
"-o", "BatchMode=yes",
|
||||||
"-o", "ConnectTimeout=10",
|
"-o", "ConnectTimeout=10",
|
||||||
"-p", str(SSH_PORT),
|
"-p", str(SSH_PORT),
|
||||||
@@ -35,6 +36,9 @@ def _ssh_base_cmd() -> list[str]:
|
|||||||
def remote_exec(command: str, timeout: int = SUBPROCESS_TIMEOUT) -> subprocess.CompletedProcess:
|
def remote_exec(command: str, timeout: int = SUBPROCESS_TIMEOUT) -> subprocess.CompletedProcess:
|
||||||
"""Execute a command on the remote host via SSH.
|
"""Execute a command on the remote host via SSH.
|
||||||
|
|
||||||
|
The command is passed as a single SSH argument so the remote shell
|
||||||
|
interprets it as-is (compatible with fish, bash, zsh).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
command: Shell command to execute remotely
|
command: Shell command to execute remotely
|
||||||
timeout: Command timeout in seconds
|
timeout: Command timeout in seconds
|
||||||
@@ -46,7 +50,7 @@ def remote_exec(command: str, timeout: int = SUBPROCESS_TIMEOUT) -> subprocess.C
|
|||||||
subprocess.TimeoutExpired: If command times out
|
subprocess.TimeoutExpired: If command times out
|
||||||
OSError: If SSH command fails to execute
|
OSError: If SSH command fails to execute
|
||||||
"""
|
"""
|
||||||
ssh_cmd = _ssh_base_cmd() + ["bash", "-c", command]
|
ssh_cmd = _ssh_base_cmd() + [command]
|
||||||
logger.debug("SSH exec: %s", command)
|
logger.debug("SSH exec: %s", command)
|
||||||
return subprocess.run(
|
return subprocess.run(
|
||||||
ssh_cmd,
|
ssh_cmd,
|
||||||
@@ -81,7 +85,8 @@ def remote_read_file(path: str) -> str:
|
|||||||
def remote_write_file(path: str, content: str) -> None:
|
def remote_write_file(path: str, content: str) -> None:
|
||||||
"""Write content to a file on the remote host atomically.
|
"""Write content to a file on the remote host atomically.
|
||||||
|
|
||||||
Uses temp file + mv for atomic write, matching local behavior.
|
Uses bash explicitly for bash-specific syntax ($(mktemp), $tmpf).
|
||||||
|
The entire script is passed as a single argument to 'bash -c'.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: Absolute file path on remote host
|
path: Absolute file path on remote host
|
||||||
@@ -91,9 +96,9 @@ def remote_write_file(path: str, content: str) -> None:
|
|||||||
IOError: If write fails
|
IOError: If write fails
|
||||||
"""
|
"""
|
||||||
ssh_cmd = _ssh_base_cmd()
|
ssh_cmd = _ssh_base_cmd()
|
||||||
# Atomic write: write to temp file, then rename (force bash for compatibility)
|
# Atomic write via bash (single-quoted to survive remote shell interpretation)
|
||||||
remote_script = f"tmpf=$(mktemp {path}.tmp.XXXXXX) && cat > \"$tmpf\" && mv \"$tmpf\" {path}"
|
remote_script = f"tmpf=$(mktemp {path}.tmp.XXXXXX) && cat >\"$tmpf\" && mv \"$tmpf\" {path}"
|
||||||
ssh_cmd.extend(["bash", "-c", remote_script])
|
ssh_cmd.append(f"bash -c '{remote_script}'")
|
||||||
|
|
||||||
logger.debug("SSH write: %s (%d bytes)", path, len(content))
|
logger.debug("SSH write: %s (%d bytes)", path, len(content))
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
|
|||||||
Reference in New Issue
Block a user