remote: Check if remote can --exec, fall back to spawning shell otherwise (#40112)

Bonus: fix passing env vars to the proxy server in WSL setting.

Closes #39710
Supersedes #39893

Release Notes:

- N/A
This commit is contained in:
Jakub Konka
2025-10-13 18:38:31 +02:00
committed by GitHub
parent f76eecd758
commit 96add6c9de
+70 -21
View File
@@ -38,12 +38,14 @@ impl From<settings::WslConnection> for WslConnectionOptions {
}
}
#[derive(Debug)]
pub(crate) struct WslRemoteConnection {
remote_binary_path: Option<Arc<RelPath>>,
platform: RemotePlatform,
shell: String,
default_system_shell: String,
connection_options: WslConnectionOptions,
can_exec: bool,
}
impl WslRemoteConnection {
@@ -71,18 +73,48 @@ impl WslRemoteConnection {
platform: RemotePlatform { os: "", arch: "" },
shell: String::new(),
default_system_shell: String::from("/bin/sh"),
can_exec: true,
};
delegate.set_status(Some("Detecting WSL environment"), cx);
this.can_exec = this.detect_can_exec().await?;
this.platform = this.detect_platform().await?;
this.shell = this.detect_shell().await?;
this.remote_binary_path = Some(
this.ensure_server_binary(&delegate, release_channel, version, commit, cx)
.await?,
);
log::debug!("Detected WSL environment: {this:#?}");
Ok(this)
}
async fn detect_can_exec(&self) -> Result<bool> {
let options = &self.connection_options;
let program = "uname";
let args = &["-m"];
let output = wsl_command_impl(options, program, args, true)
.output()
.await?;
if !output.status.success() {
let output = wsl_command_impl(options, program, args, false)
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
"Command '{}' failed: {}",
program,
String::from_utf8_lossy(&output.stderr).trim()
));
}
Ok(false)
} else {
Ok(true)
}
}
async fn detect_platform(&self) -> Result<RemotePlatform> {
let arch_str = self.run_wsl_command("uname", &["-m"]).await?;
let arch_str = arch_str.trim().to_string();
@@ -103,15 +135,15 @@ impl WslRemoteConnection {
}
async fn windows_path_to_wsl_path(&self, source: &Path) -> Result<String> {
windows_path_to_wsl_path_impl(&self.connection_options, source).await
windows_path_to_wsl_path_impl(&self.connection_options, source, self.can_exec).await
}
fn wsl_command(&self, program: &str, args: &[impl AsRef<OsStr>]) -> process::Command {
wsl_command_impl(&self.connection_options, program, args)
wsl_command_impl(&self.connection_options, program, args, self.can_exec)
}
async fn run_wsl_command(&self, program: &str, args: &[&str]) -> Result<String> {
run_wsl_command_impl(&self.connection_options, program, args).await
run_wsl_command_impl(&self.connection_options, program, args, self.can_exec).await
}
async fn ensure_server_binary(
@@ -296,7 +328,10 @@ impl RemoteConnection for WslRemoteConnection {
let mut proxy_args = vec![];
for env_var in ["RUST_LOG", "RUST_BACKTRACE", "ZED_GENERATE_MINIDUMPS"] {
if let Some(value) = std::env::var(env_var).ok() {
proxy_args.push(format!("{}='{}'", env_var, value));
// We don't quote the value here as it seems excessive and may result in invalid envs for the
// proxy server. For example, `RUST_LOG='debug'` will result in a warning "invalid logging spec 'debug'', ignoring it"
// in the proxy server. Therefore, we pass the env vars as is.
proxy_args.push(format!("{}={}", env_var, value));
}
}
proxy_args.push(remote_binary_path.display(PathStyle::Posix).into_owned());
@@ -335,19 +370,25 @@ impl RemoteConnection for WslRemoteConnection {
) -> Task<Result<()>> {
cx.background_spawn({
let options = self.connection_options.clone();
let can_exec = self.can_exec;
async move {
let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path).await?;
let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path, can_exec).await?;
run_wsl_command_impl(&options, "cp", &["-r", &wsl_src, &dest_path.to_string()])
.await
.map_err(|e| {
anyhow!(
"failed to upload directory {} -> {}: {}",
src_path.display(),
dest_path.to_string(),
e
)
})?;
run_wsl_command_impl(
&options,
"cp",
&["-r", &wsl_src, &dest_path.to_string()],
can_exec,
)
.await
.map_err(|e| {
anyhow!(
"failed to upload directory {} -> {}: {}",
src_path.display(),
dest_path.to_string(),
e
)
})?;
Ok(())
}
@@ -472,17 +513,21 @@ async fn sanitize_path(path: &Path) -> Result<String> {
async fn windows_path_to_wsl_path_impl(
options: &WslConnectionOptions,
source: &Path,
exec: bool,
) -> Result<String> {
let source = sanitize_path(source).await?;
run_wsl_command_impl(options, "wslpath", &["-u", &source]).await
run_wsl_command_impl(options, "wslpath", &["-u", &source], exec).await
}
async fn run_wsl_command_impl(
options: &WslConnectionOptions,
program: &str,
args: &[&str],
exec: bool,
) -> Result<String> {
let output = wsl_command_impl(options, program, args).output().await?;
let output = wsl_command_impl(options, program, args, exec)
.output()
.await?;
if !output.status.success() {
return Err(anyhow!(
@@ -502,6 +547,7 @@ fn wsl_command_impl(
options: &WslConnectionOptions,
program: &str,
args: &[impl AsRef<OsStr>],
exec: bool,
) -> process::Command {
let mut command = util::command::new_smol_command("wsl.exe");
@@ -516,10 +562,13 @@ fn wsl_command_impl(
.arg("--distribution")
.arg(&options.distro_name)
.arg("--cd")
.arg("~")
.arg("--exec")
.arg(program)
.args(args);
.arg("~");
if exec {
command.arg("--exec");
}
command.arg(program).args(args);
log::debug!("wsl {:?}", command);
command