diff --git a/src/config.rs b/src/config.rs index a34cb6fc..562b5413 100644 --- a/src/config.rs +++ b/src/config.rs @@ -49,8 +49,6 @@ pub enum Step { Sdkman, /// Don't run remote Togprades Remotes, - - #[cfg(windows)] /// Don't update Powershell modules Powershell, } diff --git a/src/executor.rs b/src/executor.rs index 4280037c..d0982513 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -2,6 +2,7 @@ use super::error::{Error, ErrorKind}; use super::utils::Check; use failure::ResultExt; +use log::trace; use std::ffi::{OsStr, OsString}; use std::path::Path; use std::process::{Child, Command, ExitStatus}; @@ -208,6 +209,7 @@ pub trait CommandExt { impl CommandExt for Command { fn check_output(&mut self) -> Result { let output = self.output().context(ErrorKind::ProcessExecution)?; + trace!("Output of {:?}: {:?}", self, output); let status = output.status; if !status.success() { Err(ErrorKind::ProcessFailed(status))? diff --git a/src/main.rs b/src/main.rs index fc10f166..44119821 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,10 +124,7 @@ fn run() -> Result<(), Error> { } } - #[cfg(windows)] - let powershell = windows::Powershell::new(); - - #[cfg(windows)] + let powershell = powershell::Powershell::new(); let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell); #[cfg(windows)] @@ -232,11 +229,8 @@ fn run() -> Result<(), Error> { git_repos.insert(base_dirs.config_dir().join("i3")); } - #[cfg(windows)] - { - if let Some(profile) = powershell.profile() { - git_repos.insert(profile); - } + if let Some(profile) = powershell.profile() { + git_repos.insert(profile); } if config.should_run(Step::GitRepos) { @@ -253,16 +247,13 @@ fn run() -> Result<(), Error> { )?; } - #[cfg(windows)] - { - if should_run_powershell { - execute( - &mut report, - "Powershell Modules Update", - || powershell.update_modules(run_type), - config.no_retry(), - )?; - } + if should_run_powershell { + execute( + &mut report, + "Powershell Modules Update", + || powershell.update_modules(run_type), + config.no_retry(), + )?; } #[cfg(unix)] diff --git a/src/steps/mod.rs b/src/steps/mod.rs index 4de7489f..1aeaf98d 100644 --- a/src/steps/mod.rs +++ b/src/steps/mod.rs @@ -3,6 +3,7 @@ pub mod generic; pub mod git; pub mod node; pub mod os; +pub mod powershell; #[cfg(unix)] pub mod tmux; pub mod vim; diff --git a/src/steps/os/windows.rs b/src/steps/os/windows.rs index fdf2074f..85606a4b 100644 --- a/src/steps/os/windows.rs +++ b/src/steps/os/windows.rs @@ -1,8 +1,7 @@ use crate::error::{Error, ErrorKind}; use crate::executor::{CommandExt, RunType}; -use crate::terminal::{is_dumb, print_separator}; -use crate::utils::{require, require_option, which, PathExt}; -use std::path::PathBuf; +use crate::terminal::print_separator; +use crate::utils::require; use std::process::Command; pub fn run_chocolatey(run_type: RunType) -> Result<(), Error> { @@ -21,67 +20,6 @@ pub fn run_scoop(run_type: RunType) -> Result<(), Error> { run_type.execute(&scoop).args(&["update", "*"]).check_run() } -pub struct Powershell { - path: Option, - profile: Option, -} - -impl Powershell { - /// Returns a powershell instance. - /// - /// If the powershell binary is not found, or the current terminal is dumb - /// then the instance of this struct will skip all the powershell steps. - pub fn new() -> Self { - let path = which("powershell").filter(|_| !is_dumb()); - - let profile = path.as_ref().and_then(|path| { - Command::new(path) - .args(&["-Command", "echo $profile"]) - .check_output() - .map(|output| PathBuf::from(output.trim())) - .and_then(|p| p.require()) - .ok() - }); - - Powershell { path, profile } - } - - pub fn has_command(powershell: &PathBuf, command: &str) -> bool { - || -> Result<(), Error> { - Command::new(&powershell) - .args(&["-Command", &format!("Get-Command {}", command)]) - .check_output()?; - Ok(()) - }() - .is_ok() - } - - pub fn profile(&self) -> Option<&PathBuf> { - self.profile.as_ref() - } - - pub fn update_modules(&self, run_type: RunType) -> Result<(), Error> { - let powershell = require_option(self.path.as_ref())?; - - print_separator("Powershell Modules Update"); - run_type.execute(&powershell).args(&["Update-Module", "-v"]).check_run() - } - - pub fn windows_update(&self, run_type: RunType) -> Result<(), Error> { - let powershell = require_option(self.path.as_ref())?; - - if !Self::has_command(&powershell, "Install-WindowsUpdate") { - Err(ErrorKind::SkipStep)?; - } - print_separator("Windows Update"); - - run_type - .execute(&powershell) - .args(&["-Command", "Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose"]) - .check_run() - } -} - pub fn run_wsl_topgrade(run_type: RunType) -> Result<(), Error> { let wsl = require("wsl")?; let topgrade = Command::new(&wsl) diff --git a/src/steps/powershell.rs b/src/steps/powershell.rs new file mode 100644 index 00000000..933593d9 --- /dev/null +++ b/src/steps/powershell.rs @@ -0,0 +1,77 @@ +use crate::error::Error; +#[cfg(windows)] +use crate::error::ErrorKind; +use crate::executor::{CommandExt, RunType}; +use crate::terminal::{is_dumb, print_separator}; +use crate::utils::{require_option, which, PathExt}; +use std::path::PathBuf; +use std::process::Command; + +pub struct Powershell { + path: Option, + profile: Option, +} + +impl Powershell { + /// Returns a powershell instance. + /// + /// If the powershell binary is not found, or the current terminal is dumb + /// then the instance of this struct will skip all the powershell steps. + pub fn new() -> Self { + let path = which("pwsh").or_else(|| which("powershell")).filter(|_| !is_dumb()); + + let profile = path.as_ref().and_then(|path| { + Command::new(path) + .args(&["-Command", "echo $profile"]) + .check_output() + .map(|output| PathBuf::from(output.trim())) + .and_then(|p| p.require()) + .ok() + }); + + Powershell { path, profile } + } + + #[cfg(windows)] + pub fn has_module(powershell: &PathBuf, command: &str) -> bool { + || -> Result<(), Error> { + Command::new(&powershell) + .args(&["-Command", &format!("Get-Module -ListAvailable {}", command)]) + .check_output()?; + Ok(()) + }() + .is_ok() + } + + pub fn profile(&self) -> Option<&PathBuf> { + self.profile.as_ref() + } + + pub fn update_modules(&self, run_type: RunType) -> Result<(), Error> { + let powershell = require_option(self.path.as_ref())?; + + print_separator("Powershell Modules Update"); + run_type + .execute(&powershell) + .args(&["-Command", "Update-Module", "-v"]) + .check_run() + } + + #[cfg(windows)] + pub fn windows_update(&self, run_type: RunType) -> Result<(), Error> { + let powershell = require_option(self.path.as_ref())?; + + if !Self::has_module(&powershell, "PSWindowsUpdate") { + Err(ErrorKind::SkipStep)?; + } + print_separator("Windows Update"); + + run_type + .execute(&powershell) + .args(&[ + "-Command", + "Import-Module PSWindowsUpdate; Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose", + ]) + .check_run() + } +} diff --git a/src/terminal.rs b/src/terminal.rs index 261e8615..d505a853 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -14,6 +14,8 @@ use std::os::windows::ffi::OsStrExt; use std::process::Command; use std::sync::Mutex; #[cfg(windows)] +use which_crate::which; +#[cfg(windows)] use winapi::um::wincon::SetConsoleTitleW; lazy_static! { @@ -27,7 +29,7 @@ fn shell() -> String { #[cfg(windows)] fn shell() -> &'static str { - "powershell" + which("pwsh").map(|_| "pwsh").unwrap_or("powershell") } pub fn run_shell() { @@ -196,7 +198,6 @@ pub fn print_result>(key: P, succeeded: bool) { TERMINAL.lock().unwrap().print_result(key, succeeded) } -#[cfg(windows)] /// Tells whether the terminal is dumb. pub fn is_dumb() -> bool { TERMINAL.lock().unwrap().width.is_none()