diff --git a/crates/askpass/src/askpass.rs b/crates/askpass/src/askpass.rs index 0974409477d452958df13893e316845a919723c5..f7d81641f47f8be62adb9606ffd3e47e1d89ca73 100644 --- a/crates/askpass/src/askpass.rs +++ b/crates/askpass/src/askpass.rs @@ -250,6 +250,7 @@ impl PasswordProxy { .await .with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?; make_file_executable(&askpass_script_path).await?; + // todo(shell): There might be no powershell on the system #[cfg(target_os = "windows")] let askpass_helper = format!( "powershell.exe -ExecutionPolicy Bypass -File {}", diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index b7f13f1fab495b1040d1be8e7b86376c450b5f7e..110bc02633515b417edc6707347fbae77e2888e4 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -390,10 +390,12 @@ impl Platform for WindowsPlatform { clippy::disallowed_methods, reason = "We are restarting ourselves, using std command thus is fine" )] - let restart_process = util::command::new_std_command("powershell.exe") - .arg("-command") - .arg(script) - .spawn(); + // todo(shell): There might be no powershell on the system + let restart_process = + util::command::new_std_command(util::shell::get_windows_system_shell()) + .arg("-command") + .arg(script) + .spawn(); match restart_process { Ok(_) => self.quit(), diff --git a/crates/util/src/shell.rs b/crates/util/src/shell.rs index ba54f7b7784b45613b28067afe2748339e6b6c64..1eeb483defbe6f21d3018b3ce0cbdc8e4109a367 100644 --- a/crates/util/src/shell.rs +++ b/crates/util/src/shell.rs @@ -2,6 +2,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, fmt, path::Path, sync::LazyLock}; +use crate::command::new_std_command; + /// Shell configuration to open the terminal with. #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] #[serde(rename_all = "snake_case")] @@ -108,16 +110,12 @@ pub fn get_windows_system_shell() -> String { use std::path::PathBuf; fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option { - #[cfg(target_pointer_width = "64")] - let env_var = if find_alternate { - "ProgramFiles(x86)" - } else { - "ProgramFiles" - }; - - #[cfg(target_pointer_width = "32")] let env_var = if find_alternate { - "ProgramW6432" + if cfg!(target_pointer_width = "64") { + "ProgramFiles(x86)" + } else { + "ProgramW6432" + } } else { "ProgramFiles" }; @@ -165,23 +163,19 @@ pub fn get_windows_system_shell() -> String { } else { "Microsoft.PowerShell_" }; - msix_app_dir - .read_dir() - .ok()? - .filter_map(|entry| { - let entry = entry.ok()?; - if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) { - return None; - } + msix_app_dir.read_dir().ok()?.find_map(|entry| { + let entry = entry.ok()?; + if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) { + return None; + } - if !entry.file_name().to_string_lossy().starts_with(prefix) { - return None; - } + if !entry.file_name().to_string_lossy().starts_with(prefix) { + return None; + } - let exe_path = entry.path().join("pwsh.exe"); - exe_path.exists().then_some(exe_path) - }) - .next() + let exe_path = entry.path().join("pwsh.exe"); + exe_path.exists().then_some(exe_path) + }) } fn find_pwsh_in_scoop() -> Option { @@ -190,15 +184,37 @@ pub fn get_windows_system_shell() -> String { pwsh_exe.exists().then_some(pwsh_exe) } + // check whether the found powershell is executable for us static SYSTEM_SHELL: LazyLock = LazyLock::new(|| { - find_pwsh_in_programfiles(false, false) - .or_else(|| find_pwsh_in_programfiles(true, false)) - .or_else(|| find_pwsh_in_msix(false)) - .or_else(|| find_pwsh_in_programfiles(false, true)) - .or_else(|| find_pwsh_in_msix(true)) - .or_else(|| find_pwsh_in_programfiles(true, true)) - .or_else(find_pwsh_in_scoop) - .map(|p| p.to_string_lossy().into_owned()) + let can_execute_pwsh = |p: &PathBuf| { + #[allow(clippy::disallowed_methods)] + let status = new_std_command(p).arg("-NoProfile").arg("-Help").status(); + let success = status.as_ref().is_ok_and(|status| status.success()); + if !success { + log::warn!( + "Powershell found at `{}` is not executable: {status:?}", + p.display() + ); + } + success + }; + + let locations = [ + || find_pwsh_in_programfiles(false, false), + || find_pwsh_in_programfiles(true, false), + || find_pwsh_in_msix(false), + || find_pwsh_in_programfiles(false, true), + || find_pwsh_in_msix(true), + || find_pwsh_in_programfiles(true, true), + || find_pwsh_in_scoop(), + || which::which_global("pwsh.exe").ok(), + || which::which_global("powershell.exe").ok(), + ]; + locations + .into_iter() + .filter_map(|f| f()) + .find(|p| can_execute_pwsh(&p)) + .map(|p| p.to_string_lossy().trim().to_owned()) .inspect(|shell| log::info!("Found powershell in: {}", shell)) .unwrap_or_else(|| { log::warn!("Powershell not found, falling back to `cmd`");