From 5c7f04c2cfaaa889b298413f13a2f3fb60f5720f Mon Sep 17 00:00:00 2001 From: Roey Darwish Dror Date: Thu, 2 Jul 2020 11:15:56 +0300 Subject: [PATCH] Add ability to ignore certain failures (#461) --- config.example.toml | 3 + src/config.rs | 14 +- src/main.rs | 308 ++++++++++++++------------------------------ src/report.rs | 23 +++- src/runner.rs | 24 +++- src/terminal.rs | 15 ++- 6 files changed, 161 insertions(+), 226 deletions(-) diff --git a/config.example.toml b/config.example.toml index 3833d5b3..c5119cf4 100644 --- a/config.example.toml +++ b/config.example.toml @@ -4,6 +4,9 @@ # Disable specific steps - same options as the command line flag #disable = ["system", "emacs"] +# Ignore failures for these steps +#ignore_failures = ["powershell"] + # Run specific steps - same options as the command line flag #only = ["system", "emacs"] diff --git a/src/config.rs b/src/config.rs index 1c115fbe..599783fe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,7 +58,7 @@ macro_rules! get_deprecated { type Commands = BTreeMap; -#[derive(EnumString, EnumVariantNames, Debug, Clone, PartialEq, Deserialize, EnumIter)] +#[derive(EnumString, EnumVariantNames, Debug, Clone, PartialEq, Deserialize, EnumIter, Copy)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum Step { @@ -103,6 +103,8 @@ pub enum Step { Flatpak, Snap, Pkg, + Powershell, + CustomCommands, } #[derive(Deserialize, Default, Debug)] @@ -161,6 +163,7 @@ pub struct ConfigFile { git_repos: Option>, predefined_git_repos: Option, disable: Option>, + ignore_failures: Option>, remote_topgrades: Option>, ssh_arguments: Option, git_arguments: Option, @@ -605,6 +608,15 @@ impl Config { .unwrap_or(false) } + /// Should we ignore failures for this step + pub fn ignore_failure(&self, step: Step) -> bool { + self.config_file + .ignore_failures + .as_ref() + .map(|v| v.contains(&step)) + .unwrap_or(false) + } + pub fn use_predefined_git_repos(&self) -> bool { !self.opt.disable_predefined_git_repos && get_deprecated!(&self.config_file, predefined_git_repos, git, pull_predefined).unwrap_or(true) diff --git a/src/main.rs b/src/main.rs index 3ab560f5..103f7ac4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,22 +108,16 @@ fn run() -> Result<()> { } let powershell = powershell::Powershell::new(); - let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Shell); + let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell); #[cfg(windows)] - { - if config.should_run(Step::Wsl) { - runner.execute("WSL", || windows::run_wsl_topgrade(run_type))?; - } - } + runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(run_type))?; if let Some(topgrades) = config.remote_topgrades() { - if config.should_run(Step::Remotes) { - for remote_topgrade in topgrades { - runner.execute(format!("Remote ({})", remote_topgrade), || { - generic::run_remote_topgrade(&ctx, remote_topgrade) - })?; - } + for remote_topgrade in topgrades { + runner.execute(Step::Remotes, format!("Remote ({})", remote_topgrade), || { + generic::run_remote_topgrade(&ctx, remote_topgrade) + })?; } } @@ -132,75 +126,52 @@ fn run() -> Result<()> { #[cfg(target_os = "linux")] { - if config.should_run(Step::System) { - match &distribution { - Ok(distribution) => { - runner.execute("System update", || distribution.upgrade(&ctx))?; - } - Err(e) => { - println!("Error detecting current distribution: {}", e); - } + match &distribution { + Ok(distribution) => { + runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?; + } + Err(e) => { + println!("Error detecting current distribution: {}", e); } - runner.execute("etc-update", || linux::run_etc_update(sudo.as_ref(), run_type))?; } + runner.execute(Step::System, "etc-update", || { + linux::run_etc_update(sudo.as_ref(), run_type) + })?; } #[cfg(windows)] { - if config.should_run(Step::Chocolatey) { - runner.execute("Chocolatey", || windows::run_chocolatey(&ctx))?; - } - - if config.should_run(Step::Scoop) { - runner.execute("Scoop", || windows::run_scoop(config.cleanup(), run_type))?; - } + runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?; + runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?; } #[cfg(unix)] { - if config.should_run(Step::Brew) { - runner.execute("Brew", || unix::run_brew(&ctx))?; - runner.execute("Brew Cask", || unix::run_brew_cask(&ctx))?; - } + runner.execute(Step::Brew, "Brew", || unix::run_brew(&ctx))?; + runner.execute(Step::Brew, "Brew Cask", || unix::run_brew_cask(&ctx))?; + #[cfg(target_os = "macos")] { - if config.should_run(Step::MacPorts) { - runner.execute("MacPorts", || macos::run_macports(&ctx))?; - } - if config.should_run(Step::MicrosoftAutoUpdate) { - runner.execute("Microsoft AutoUpdate", || macos::run_msupdate(&ctx))?; - } - } - if config.should_run(Step::Nix) { - runner.execute("nix", || unix::run_nix(&ctx))?; + runner.execute(Step::MacPorts, "MacPorts", || macos::run_macports(&ctx))?; + runner.execute(Step::MicrosoftAutoUpdate, "Microsoft AutoUpdate", || { + macos::run_msupdate(&ctx) + })?; } - if config.should_run(Step::HomeManager) { - runner.execute("home-manager", || unix::run_home_manager(run_type))?; - } - - if config.should_run(Step::Asdf) { - runner.execute("asdf", || unix::run_asdf(run_type))?; - } + runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?; + runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(run_type))?; + runner.execute(Step::Asdf, "asdf", || unix::run_asdf(run_type))?; } #[cfg(target_os = "dragonfly")] - { - if config.should_run(Step::Pkg) { - runner.execute("DragonFly BSD Packages", || { - dragonfly::upgrade_packages(sudo.as_ref(), run_type) - })?; - } - } + runner.execute(Step::Pkg, "DragonFly BSD Packages", || { + dragonfly::upgrade_packages(sudo.as_ref(), run_type) + })?; #[cfg(target_os = "freebsd")] - { - if config.should_run(Step::Pkg) { - runner.execute("FreeBSD Packages", || { - freebsd::upgrade_packages(sudo.as_ref(), run_type) - })?; - } - } + runner.execute(Step::Pkg, "FreeBSD Packages", || { + freebsd::upgrade_packages(sudo.as_ref(), run_type) + })?; let emacs = emacs::Emacs::new(&base_dirs); if config.use_predefined_git_repos() { @@ -249,106 +220,32 @@ fn run() -> Result<()> { git_repos.glob_insert(git_repo); } } - runner.execute("Git repositories", || git.multi_pull_step(&git_repos, &ctx))?; + runner.execute(Step::GitRepos, "Git repositories", || { + git.multi_pull_step(&git_repos, &ctx) + })?; } if should_run_powershell { - runner.execute("Powershell Modules Update", || powershell.update_modules(run_type))?; + runner.execute(Step::Powershell, "Powershell Modules Update", || { + powershell.update_modules(run_type) + })?; } #[cfg(unix)] { - if config.should_run(Step::Shell) { - runner.execute("zr", || zsh::run_zr(&base_dirs, run_type))?; - runner.execute("antibody", || zsh::run_antibody(run_type))?; - runner.execute("antigen", || zsh::run_antigen(&base_dirs, run_type))?; - runner.execute("zplug", || zsh::run_zplug(&base_dirs, run_type))?; - runner.execute("zinit", || zsh::run_zinit(&base_dirs, run_type))?; - runner.execute("oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?; - runner.execute("fisher", || unix::run_fisher(&base_dirs, run_type))?; - } - - if config.should_run(Step::Tmux) { - runner.execute("tmux", || tmux::run_tpm(&base_dirs, run_type))?; - } - - if config.should_run(Step::Tldr) { - runner.execute("TLDR", || unix::run_tldr(run_type))?; - } - } - - if config.should_run(Step::Rustup) { - runner.execute("rustup", || generic::run_rustup(&base_dirs, run_type))?; - } - - if config.should_run(Step::Cargo) { - runner.execute("cargo", || generic::run_cargo_update(run_type))?; - } - - if config.should_run(Step::Flutter) { - runner.execute("Flutter", || generic::run_flutter_upgrade(run_type))?; - } - - if config.should_run(Step::Go) { - runner.execute("Go", || generic::run_go(&base_dirs, run_type))?; - } - - if config.should_run(Step::Emacs) { - runner.execute("Emacs", || emacs.upgrade(run_type))?; - } - - if config.should_run(Step::Opam) { - runner.execute("opam", || generic::run_opam_update(run_type))?; - } - - if config.should_run(Step::Vcpkg) { - runner.execute("vcpkg", || generic::run_vcpkg_update(run_type))?; - } - - if config.should_run(Step::Pipx) { - runner.execute("pipx", || generic::run_pipx_update(run_type))?; - } - - if config.should_run(Step::Stack) { - runner.execute("stack", || generic::run_stack_update(run_type))?; - } - - if config.should_run(Step::Tlmgr) { - runner.execute("tlmgr", || generic::run_tlmgr_update(&ctx))?; - } - - if config.should_run(Step::Myrepos) { - runner.execute("myrepos", || generic::run_myrepos_update(&base_dirs, run_type))?; - } - - #[cfg(unix)] - { - if config.should_run(Step::Pearl) { - runner.execute("pearl", || unix::run_pearl(run_type))?; - } - } - - if config.should_run(Step::Jetpack) { - runner.execute("jetpak", || generic::run_jetpack(run_type))?; - } - - if config.should_run(Step::Vim) { - runner.execute("vim", || vim::upgrade_vim(&base_dirs, &ctx))?; - runner.execute("Neovim", || vim::upgrade_neovim(&base_dirs, &ctx))?; - runner.execute("voom", || vim::run_voom(&base_dirs, run_type))?; - } - - if config.should_run(Step::Node) { - runner.execute("npm", || node::run_npm_upgrade(&base_dirs, run_type))?; - runner.execute("yarn", || node::yarn_global_update(run_type))?; - } - - if config.should_run(Step::Composer) { - runner.execute("composer", || generic::run_composer_update(&ctx))?; - } - - if config.should_run(Step::Krew) { - runner.execute("krew", || generic::run_krew_upgrade(run_type))?; + runner.execute(Step::Shell, "zr", || zsh::run_zr(&base_dirs, run_type))?; + runner.execute(Step::Shell, "antibody", || zsh::run_antibody(run_type))?; + runner.execute(Step::Shell, "antigen", || zsh::run_antigen(&base_dirs, run_type))?; + runner.execute(Step::Shell, "zplug", || zsh::run_zplug(&base_dirs, run_type))?; + runner.execute(Step::Shell, "zinit", || zsh::run_zinit(&base_dirs, run_type))?; + runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?; + runner.execute(Step::Shell, "fisher", || unix::run_fisher(&base_dirs, run_type))?; + runner.execute(Step::Tmux, "tmux", || tmux::run_tpm(&base_dirs, run_type))?; + runner.execute(Step::Tldr, "TLDR", || unix::run_tldr(run_type))?; + runner.execute(Step::Pearl, "pearl", || unix::run_pearl(run_type))?; + runner.execute(Step::Sdkman, "SDKMAN!", || { + unix::run_sdkman(&base_dirs, config.cleanup(), run_type) + })?; } #[cfg(not(any( @@ -357,80 +254,73 @@ fn run() -> Result<()> { target_os = "netbsd", target_os = "dragonfly" )))] - { - if config.should_run(Step::Atom) { - runner.execute("apm", || generic::run_apm(run_type))?; - } - } - - if config.should_run(Step::Gem) { - runner.execute("gem", || generic::run_gem(&base_dirs, run_type))?; - } + runner.execute(Step::Atom, "apm", || generic::run_apm(run_type))?; + runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&base_dirs, run_type))?; + runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(run_type))?; + runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(run_type))?; + runner.execute(Step::Go, "Go", || generic::run_go(&base_dirs, run_type))?; + runner.execute(Step::Emacs, "Emacs", || emacs.upgrade(run_type))?; + runner.execute(Step::Opam, "opam", || generic::run_opam_update(run_type))?; + runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(run_type))?; + runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(run_type))?; + runner.execute(Step::Stack, "stack", || generic::run_stack_update(run_type))?; + runner.execute(Step::Tlmgr, "tlmgr", || generic::run_tlmgr_update(&ctx))?; + runner.execute(Step::Myrepos, "myrepos", || { + generic::run_myrepos_update(&base_dirs, run_type) + })?; + runner.execute(Step::Jetpack, "jetpak", || generic::run_jetpack(run_type))?; + runner.execute(Step::Vim, "vim", || vim::upgrade_vim(&base_dirs, &ctx))?; + runner.execute(Step::Vim, "Neovim", || vim::upgrade_neovim(&base_dirs, &ctx))?; + runner.execute(Step::Vim, "voom", || vim::run_voom(&base_dirs, run_type))?; + runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&base_dirs, run_type))?; + runner.execute(Step::Node, "yarn", || node::yarn_global_update(run_type))?; + runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?; + runner.execute(Step::Krew, "krew", || generic::run_krew_upgrade(run_type))?; + runner.execute(Step::Gem, "gem", || generic::run_gem(&base_dirs, run_type))?; #[cfg(target_os = "linux")] { - if config.should_run(Step::Flatpak) { - runner.execute("Flatpak", || linux::flatpak_update(run_type))?; - } - if config.should_run(Step::Snap) { - runner.execute("snap", || linux::run_snap(sudo.as_ref(), run_type))?; - } + runner.execute(Step::Flatpak, "Flatpak", || linux::flatpak_update(run_type))?; + runner.execute(Step::Snap, "snap", || linux::run_snap(sudo.as_ref(), run_type))?; } if let Some(commands) = config.commands() { for (name, command) in commands { - runner.execute(name, || generic::run_custom_command(&name, &command, &ctx))?; + runner.execute(Step::CustomCommands, name, || { + generic::run_custom_command(&name, &command, &ctx) + })?; } } #[cfg(target_os = "linux")] { - if config.should_run(Step::System) { - runner.execute("pihole", || linux::run_pihole_update(sudo.as_ref(), run_type))?; - } - - if config.should_run(Step::Firmware) { - runner.execute("Firmware upgrades", || linux::run_fwupdmgr(run_type))?; - } - - if config.should_run(Step::Restarts) { - runner.execute("Restarts", || linux::run_needrestart(sudo.as_ref(), run_type))?; - } + runner.execute(Step::System, "pihole", || { + linux::run_pihole_update(sudo.as_ref(), run_type) + })?; + runner.execute(Step::Firmware, "Firmware upgrades", || linux::run_fwupdmgr(run_type))?; + runner.execute(Step::Restarts, "Restarts", || { + linux::run_needrestart(sudo.as_ref(), run_type) + })?; } #[cfg(target_os = "macos")] { - if config.should_run(Step::System) { - runner.execute("App Store", || macos::run_mas(run_type))?; - runner.execute("System upgrade", || macos::upgrade_macos(&ctx))?; - } + runner.execute(Step::System, "App Store", || macos::run_mas(run_type))?; + runner.execute(Step::System, "System upgrade", || macos::upgrade_macos(&ctx))?; } #[cfg(target_os = "freebsd")] - { - if config.should_run(Step::System) { - runner.execute("FreeBSD Upgrade", || freebsd::upgrade_freebsd(sudo.as_ref(), run_type))?; - } - } + runner.execute(Step::System, "FreeBSD Upgrade", || { + freebsd::upgrade_freebsd(sudo.as_ref(), run_type) + })?; #[cfg(windows)] - { - if config.should_run(Step::System) { - runner.execute("Windows update", || windows::windows_update(&ctx))?; - } - } - - #[cfg(unix)] - { - if config.should_run(Step::Sdkman) { - runner.execute("SDKMAN!", || unix::run_sdkman(&base_dirs, config.cleanup(), run_type))?; - } - } + runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?; if config.should_run(Step::Vagrant) { if let Ok(boxes) = vagrant::collect_boxes(&ctx) { for vagrant_box in boxes { - runner.execute(format!("Vagrant ({})", vagrant_box.smart_name()), || { + runner.execute(Step::Vagrant, format!("Vagrant ({})", vagrant_box.smart_name()), || { vagrant::topgrade_vagrant_box(&ctx, &vagrant_box) })?; } @@ -440,8 +330,8 @@ fn run() -> Result<()> { if !runner.report().data().is_empty() { print_separator("Summary"); - for (key, succeeded) in runner.report().data() { - print_result(key, *succeeded); + for (key, result) in runner.report().data() { + print_result(key, *result); } #[cfg(target_os = "linux")] @@ -477,10 +367,10 @@ fn run() -> Result<()> { } } - if runner.report().data().iter().all(|(_, succeeded)| *succeeded) { - Ok(()) - } else { + if runner.report().data().iter().any(|(_, result)| result.failed()) { Err(StepFailed.into()) + } else { + Ok(()) } } diff --git a/src/report.rs b/src/report.rs index 5b6a77ac..e6fe7d5a 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,8 +1,25 @@ use std::borrow::Cow; +#[derive(Clone, Copy)] +pub enum StepResult { + Success, + Failure, + Ignored, +} + +impl StepResult { + pub fn failed(self) -> bool { + match self { + StepResult::Success | StepResult::Ignored => false, + StepResult::Failure => true, + } + } +} + type CowString<'a> = Cow<'a, str>; +type ReportData<'a> = Vec<(CowString<'a>, StepResult)>; pub struct Report<'a> { - data: Vec<(CowString<'a>, bool)>, + data: ReportData<'a>, } impl<'a> Report<'a> { @@ -10,7 +27,7 @@ impl<'a> Report<'a> { Self { data: Vec::new() } } - pub fn push_result(&mut self, result: Option<(M, bool)>) + pub fn push_result(&mut self, result: Option<(M, StepResult)>) where M: Into>, { @@ -22,7 +39,7 @@ impl<'a> Report<'a> { } } - pub fn data(&self) -> &Vec<(CowString<'a>, bool)> { + pub fn data(&self) -> &ReportData<'a> { &self.data } } diff --git a/src/runner.rs b/src/runner.rs index be14f518..74518398 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,8 +1,8 @@ use crate::ctrlc; use crate::error::SkipStep; use crate::execution_context::ExecutionContext; -use crate::report::Report; -use crate::terminal::should_retry; +use crate::report::{Report, StepResult}; +use crate::{config::Step, terminal::should_retry}; use anyhow::Result; use log::debug; use std::borrow::Cow; @@ -21,18 +21,22 @@ impl<'a> Runner<'a> { } } - pub fn execute(&mut self, key: M, func: F) -> Result<()> + pub fn execute(&mut self, step: Step, key: M, func: F) -> Result<()> where F: Fn() -> Result<()>, M: Into> + Debug, { + if !self.ctx.config().should_run(step) { + return Ok(()); + } + let key = key.into(); debug!("Step {:?}", key); loop { match func() { Ok(()) => { - self.report.push_result(Some((key, true))); + self.report.push_result(Some((key, StepResult::Success))); break; } Err(e) if e.downcast_ref::().is_some() => { @@ -44,11 +48,19 @@ impl<'a> Runner<'a> { ctrlc::unset_interrupted(); } - let should_ask = interrupted || !self.ctx.config().no_retry(); + let ignore_failure = self.ctx.config().ignore_failure(step); + let should_ask = interrupted || !(self.ctx.config().no_retry() || ignore_failure); let should_retry = should_ask && should_retry(interrupted, key.as_ref())?; if !should_retry { - self.report.push_result(Some((key, false))); + self.report.push_result(Some(( + key, + if ignore_failure { + StepResult::Ignored + } else { + StepResult::Failure + }, + ))); break; } } diff --git a/src/terminal.rs b/src/terminal.rs index e78660ed..11cbe94b 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,3 +1,4 @@ +use crate::report::StepResult; #[cfg(target_os = "linux")] use crate::utils::which; use chrono::{Local, Timelike}; @@ -160,17 +161,17 @@ impl Terminal { .ok(); } - fn print_result>(&mut self, key: P, succeeded: bool) { + fn print_result>(&mut self, key: P, result: StepResult) { let key = key.as_ref(); self.term .write_fmt(format_args!( "{}: {}\n", key, - if succeeded { - style("OK").bold().green() - } else { - style("FAILED").bold().red() + match result { + StepResult::Success => style("OK").bold().green(), + StepResult::Failure => style("FAILED").bold().red(), + StepResult::Ignored => style("IGNORED").bold().yellow(), } )) .ok(); @@ -269,8 +270,8 @@ pub fn print_info>(message: P) { TERMINAL.lock().unwrap().print_info(message) } -pub fn print_result>(key: P, succeeded: bool) { - TERMINAL.lock().unwrap().print_result(key, succeeded) +pub fn print_result>(key: P, result: StepResult) { + TERMINAL.lock().unwrap().print_result(key, result) } /// Tells whether the terminal is dumb.