diff --git a/src/execution_context.rs b/src/execution_context.rs index 86a08b14..cdd543e3 100644 --- a/src/execution_context.rs +++ b/src/execution_context.rs @@ -1,14 +1,16 @@ #![allow(dead_code)] +use color_eyre::eyre::Result; +use std::env::var; +use std::path::Path; +use std::sync::{LazyLock, Mutex}; + use crate::executor::RunType; +use crate::powershell::Powershell; #[cfg(target_os = "linux")] use crate::steps::linux::Distribution; use crate::sudo::Sudo; use crate::utils::{get_require_sudo_string, require_option}; use crate::{config::Config, executor::Executor}; -use color_eyre::eyre::Result; -use std::env::var; -use std::path::Path; -use std::sync::Mutex; pub struct ExecutionContext<'a> { run_type: RunType, @@ -22,6 +24,7 @@ pub struct ExecutionContext<'a> { under_ssh: bool, #[cfg(target_os = "linux")] distribution: &'a Result, + powershell: LazyLock, } impl<'a> ExecutionContext<'a> { @@ -40,6 +43,7 @@ impl<'a> ExecutionContext<'a> { under_ssh, #[cfg(target_os = "linux")] distribution, + powershell: LazyLock::new(Powershell::new), } } @@ -76,4 +80,8 @@ impl<'a> ExecutionContext<'a> { pub fn distribution(&self) -> &Result { self.distribution } + + pub fn powershell(&self) -> &Powershell { + &self.powershell + } } diff --git a/src/step.rs b/src/step.rs index da0d3622..9896bd79 100644 --- a/src/step.rs +++ b/src/step.rs @@ -464,7 +464,7 @@ impl Step { Pnpm => runner.execute(*self, "pnpm", || node::run_pnpm_upgrade(ctx))?, Poetry => runner.execute(*self, "Poetry", || generic::run_poetry(ctx))?, Powershell => { - let powershell = powershell::Powershell::new(); + let powershell = ctx.powershell(); if powershell.is_available() { runner.execute(Powershell, "Powershell Modules Update", || { powershell.update_modules(ctx) diff --git a/src/steps/os/windows.rs b/src/steps/os/windows.rs index 048234b4..b60be3e8 100644 --- a/src/steps/os/windows.rs +++ b/src/steps/os/windows.rs @@ -7,7 +7,6 @@ use tracing::debug; use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::powershell; use crate::step::Step; use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, which}; @@ -226,7 +225,7 @@ pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> { } pub fn windows_update(ctx: &ExecutionContext) -> Result<()> { - let powershell = powershell::Powershell::windows_powershell(); + let powershell = ctx.powershell(); print_separator(t!("Windows Update")); @@ -244,7 +243,7 @@ pub fn windows_update(ctx: &ExecutionContext) -> Result<()> { } pub fn microsoft_store(ctx: &ExecutionContext) -> Result<()> { - let powershell = powershell::Powershell::windows_powershell(); + let powershell = ctx.powershell(); print_separator(t!("Microsoft Store")); diff --git a/src/steps/powershell.rs b/src/steps/powershell.rs index e15876f3..15c97361 100644 --- a/src/steps/powershell.rs +++ b/src/steps/powershell.rs @@ -34,7 +34,7 @@ impl Powershell { .or_else(|| which("powershell").map(|p| (Some(p), false))) .unwrap_or((None, false)); - let profile = path.as_ref().and_then(Self::get_profile); + let profile = path.as_ref().and_then(|path| Self::get_profile(path, is_pwsh)); Self { path, profile, is_pwsh } } @@ -43,29 +43,12 @@ impl Powershell { self.path.is_some() } - #[cfg(windows)] - pub fn windows_powershell() -> Self { - if terminal::is_dumb() { - return Self { - path: None, - profile: None, - is_pwsh: false, - }; - } - - Self { - path: which("powershell"), - profile: None, - is_pwsh: false, - } - } - pub fn profile(&self) -> Option<&PathBuf> { self.profile.as_ref() } - fn get_profile(path: &PathBuf) -> Option { - let profile = Self::build_command_internal(path, "Split-Path $PROFILE") + fn get_profile(path: &PathBuf, is_pwsh: bool) -> Option { + let profile = Self::build_command_internal(path, is_pwsh, "Split-Path $PROFILE") .output_checked_utf8() .map(|output| output.stdout.trim().to_string()) .and_then(|s| PathBuf::from(s).require()) @@ -75,12 +58,18 @@ impl Powershell { } /// Builds an "internal" powershell command - fn build_command_internal(path: &PathBuf, cmd: &str) -> Command { + fn build_command_internal(path: &PathBuf, is_pwsh: bool, cmd: &str) -> Command { let mut command = Command::new(path); command.args(["-NoProfile", "-Command"]); command.arg(cmd); + // If topgrade was run from pwsh, but we are trying to run powershell, then + // the inherited PSModulePath breaks module imports + if !is_pwsh { + command.env_remove("PSModulePath"); + } + command } @@ -106,6 +95,12 @@ impl Powershell { command.args(["-NoProfile", "-Command"]); command.arg(cmd); + // If topgrade was run from pwsh, but we are trying to run powershell, then + // the inherited PSModulePath breaks module imports + if !self.is_pwsh { + command.env_remove("PSModulePath"); + } + Ok(command) } @@ -158,7 +153,7 @@ impl Powershell { // Find the index of our target policy let target_idx = valid_policies.iter().position(|&p| p == policy); - let mut command = Self::build_command_internal(powershell, "Get-ExecutionPolicy"); + let mut command = Self::build_command_internal(powershell, self.is_pwsh, "Get-ExecutionPolicy"); let current_policy = command .output_checked_utf8() @@ -187,7 +182,7 @@ impl Powershell { if let Some(powershell) = &self.path { let cmd = format!("Get-Module -ListAvailable {}", module_name); - return Self::build_command_internal(powershell, &cmd) + return Self::build_command_internal(powershell, self.is_pwsh, &cmd) .output_checked() .map(|output| !output.stdout.trim_ascii().is_empty()) .unwrap_or(false);