remote: Fetch shell on ssh remote to use for preparing commands (#36690)

Prerequisite for https://github.com/zed-industries/zed/pull/36576 to
allow us to differentiate the shell in a remote.

Release Notes:

- N/A
This commit is contained in:
Lukas Wirth 2025-08-21 19:08:26 +02:00 committed by GitHub
parent 6f32d36ec9
commit b284b1a0b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 121 additions and 52 deletions

View file

@ -4,6 +4,6 @@ pub mod proxy;
pub mod ssh_session;
pub use ssh_session::{
ConnectionState, SshClientDelegate, SshConnectionOptions, SshPlatform, SshRemoteClient,
SshRemoteEvent,
ConnectionState, SshClientDelegate, SshConnectionOptions, SshInfo, SshPlatform,
SshRemoteClient, SshRemoteEvent,
};

View file

@ -89,11 +89,19 @@ pub struct SshConnectionOptions {
pub upload_binary_over_ssh: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SshArgs {
pub arguments: Vec<String>,
pub envs: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SshInfo {
pub args: SshArgs,
pub path_style: PathStyle,
pub shell: String,
}
#[macro_export]
macro_rules! shell_script {
($fmt:expr, $($name:ident = $arg:expr),+ $(,)?) => {{
@ -471,6 +479,16 @@ impl SshSocket {
Ok(SshPlatform { os, arch })
}
async fn shell(&self) -> String {
match self.run_command("sh", &["-c", "echo $SHELL"]).await {
Ok(shell) => shell.trim().to_owned(),
Err(e) => {
log::error!("Failed to get shell: {e}");
"sh".to_owned()
}
}
}
}
const MAX_MISSED_HEARTBEATS: usize = 5;
@ -1152,12 +1170,16 @@ impl SshRemoteClient {
cx.notify();
}
pub fn ssh_info(&self) -> Option<(SshArgs, PathStyle)> {
pub fn ssh_info(&self) -> Option<SshInfo> {
self.state
.lock()
.as_ref()
.and_then(|state| state.ssh_connection())
.map(|ssh_connection| (ssh_connection.ssh_args(), ssh_connection.path_style()))
.map(|ssh_connection| SshInfo {
args: ssh_connection.ssh_args(),
path_style: ssh_connection.path_style(),
shell: ssh_connection.shell(),
})
}
pub fn upload_directory(
@ -1392,6 +1414,7 @@ trait RemoteConnection: Send + Sync {
fn ssh_args(&self) -> SshArgs;
fn connection_options(&self) -> SshConnectionOptions;
fn path_style(&self) -> PathStyle;
fn shell(&self) -> String;
#[cfg(any(test, feature = "test-support"))]
fn simulate_disconnect(&self, _: &AsyncApp) {}
@ -1403,6 +1426,7 @@ struct SshRemoteConnection {
remote_binary_path: Option<RemotePathBuf>,
ssh_platform: SshPlatform,
ssh_path_style: PathStyle,
ssh_shell: String,
_temp_dir: TempDir,
}
@ -1429,6 +1453,10 @@ impl RemoteConnection for SshRemoteConnection {
self.socket.connection_options.clone()
}
fn shell(&self) -> String {
self.ssh_shell.clone()
}
fn upload_directory(
&self,
src_path: PathBuf,
@ -1642,6 +1670,7 @@ impl SshRemoteConnection {
"windows" => PathStyle::Windows,
_ => PathStyle::Posix,
};
let ssh_shell = socket.shell().await;
let mut this = Self {
socket,
@ -1650,6 +1679,7 @@ impl SshRemoteConnection {
remote_binary_path: None,
ssh_path_style,
ssh_platform,
ssh_shell,
};
let (release_channel, version, commit) = cx.update(|cx| {
@ -2686,6 +2716,10 @@ mod fake {
fn path_style(&self) -> PathStyle {
PathStyle::current()
}
fn shell(&self) -> String {
"sh".to_owned()
}
}
pub(super) struct Delegate;