windows: Implement shell environment loading for git operations (#39019)
Xiaobo Liu
created 1 month ago
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 <cppcoffee@gmail.com>
Change summary
crates/project/src/environment.rs | 23 ++++++++++++++++--
crates/util/src/shell_env.rs | 41 +++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 3 deletions(-)
Detailed changes
@@ -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<HashMap<String, String>>,
Option<EnvironmentErrorMessage>,
) {
- // 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")))]
@@ -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<collections::HashMap<String, String>> {
+ 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<String, String> = 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<String, String> = std::env::vars().collect();
let json = serde_json::to_string_pretty(&env_vars).unwrap_or_else(|err| {