From 682cf023ca627c0d2d8f0f45797e2a41496f6e02 Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Sat, 27 Sep 2025 23:10:06 +0800 Subject: [PATCH] windows: Implement shell environment loading for git operations (#39019) Fixes the "failed to get working directory environment for repository" error on Windows by implementing proper shell environment variable capture. Release Notes: - Fixed failed to get working directory environment for repository --------- Signed-off-by: Xiaobo Liu --- crates/project/src/environment.rs | 23 ++++++++++++++--- crates/util/src/shell_env.rs | 41 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/crates/project/src/environment.rs b/crates/project/src/environment.rs index f5120b4c9f9a746bd700dcaecedc2eed6c06ffaa..7b016f05b6475d393095b6a576735d87cebf6203 100644 --- a/crates/project/src/environment.rs +++ b/crates/project/src/environment.rs @@ -227,14 +227,31 @@ async fn load_shell_environment( #[cfg(all(target_os = "windows", not(any(test, feature = "test-support"))))] async fn load_shell_environment( - _dir: &Path, + dir: &Path, _load_direnv: &DirenvSettings, ) -> ( Option>, Option, ) { - // TODO the current code works with Unix $SHELL only, implement environment loading on windows - (None, None) + use util::shell_env; + + let envs = match shell_env::capture(dir).await { + Ok(envs) => envs, + Err(err) => { + util::log_err(&err); + return ( + None, + Some(EnvironmentErrorMessage(format!( + "Failed to load environment variables: {}", + err + ))), + ); + } + }; + + // Note: direnv is not available on Windows, so we skip direnv processing + // and just return the shell environment + (Some(envs), None) } #[cfg(not(any(target_os = "windows", test, feature = "test-support")))] diff --git a/crates/util/src/shell_env.rs b/crates/util/src/shell_env.rs index 7a5da8e522af23c8d1df78e58e2a8a63e496358f..475e51fb0e50d02a595f29d6362300f6ad9ed8d9 100644 --- a/crates/util/src/shell_env.rs +++ b/crates/util/src/shell_env.rs @@ -99,6 +99,47 @@ async fn spawn_and_read_fd( Ok((buffer, process.output().await?)) } +/// Capture all environment variables from the shell on Windows. +#[cfg(windows)] +pub async fn capture(directory: &std::path::Path) -> Result> { + use std::process::Stdio; + + let zed_path = + std::env::current_exe().context("Failed to determine current zed executable path.")?; + + // Use PowerShell to get environment variables in the directory context + let mut command = std::process::Command::new(crate::get_windows_system_shell()); + command + .arg("-NonInteractive") + .arg("-NoProfile") + .arg("-Command") + .arg(format!( + "Set-Location '{}'; & '{}' --printenv", + directory.display(), + zed_path.display() + )) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let output = smol::process::Command::from(command).output().await?; + + anyhow::ensure!( + output.status.success(), + "PowerShell command failed with {}. stdout: {:?}, stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + + let env_output = String::from_utf8_lossy(&output.stdout); + + // Parse the JSON output from zed --printenv + let env_map: collections::HashMap = serde_json::from_str(&env_output) + .with_context(|| "Failed to deserialize environment variables from json")?; + Ok(env_map) +} + pub fn print_env() { let env_vars: HashMap = std::env::vars().collect(); let json = serde_json::to_string_pretty(&env_vars).unwrap_or_else(|err| {