Add --show-skipped (fix #501) (#502)

This commit is contained in:
Roey Darwish Dror
2020-08-21 23:04:36 +03:00
committed by GitHub
parent d48182e6bd
commit 417ca1257a
18 changed files with 73 additions and 54 deletions

View File

@@ -337,6 +337,10 @@ pub struct CommandLineArgs {
/// A regular expression for restricting remote host execution /// A regular expression for restricting remote host execution
#[structopt(long = "remote-host-limit", parse(try_from_str))] #[structopt(long = "remote-host-limit", parse(try_from_str))]
remote_host_limit: Option<Regex>, remote_host_limit: Option<Regex>,
/// Show the reason for skipped steps
#[structopt(long = "show-skipped")]
show_skipped: bool,
} }
impl CommandLineArgs { impl CommandLineArgs {
@@ -649,6 +653,10 @@ impl Config {
self.opt.verbose self.opt.verbose
} }
pub fn show_skipped(&self) -> bool {
self.opt.show_skipped
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
str_value!(linux, emerge_sync_flags); str_value!(linux, emerge_sync_flags);

View File

@@ -23,8 +23,8 @@ pub enum TopgradeError {
pub struct StepFailed; pub struct StepFailed;
#[derive(Error, Debug)] #[derive(Error, Debug)]
#[error("A step should be skipped")] #[error("{0}")]
pub struct SkipStep; pub struct SkipStep(pub String);
#[cfg(all(windows, feature = "self-update"))] #[cfg(all(windows, feature = "self-update"))]
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@@ -332,7 +332,7 @@ fn run() -> Result<()> {
print_separator("Summary"); print_separator("Summary");
for (key, result) in runner.report().data() { for (key, result) in runner.report().data() {
print_result(key, *result); print_result(key, result);
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]

View File

@@ -1,16 +1,16 @@
use std::borrow::Cow; use std::borrow::Cow;
#[derive(Clone, Copy)]
pub enum StepResult { pub enum StepResult {
Success, Success,
Failure, Failure,
Ignored, Ignored,
Skipped(String),
} }
impl StepResult { impl StepResult {
pub fn failed(self) -> bool { pub fn failed(&self) -> bool {
match self { match self {
StepResult::Success | StepResult::Ignored => false, StepResult::Success | StepResult::Ignored | StepResult::Skipped(_) => false,
StepResult::Failure => true, StepResult::Failure => true,
} }
} }

View File

@@ -40,6 +40,9 @@ impl<'a> Runner<'a> {
break; break;
} }
Err(e) if e.downcast_ref::<SkipStep>().is_some() => { Err(e) if e.downcast_ref::<SkipStep>().is_some() => {
if self.ctx.config().verbose() || self.ctx.config().show_skipped() {
self.report.push_result(Some((key, StepResult::Skipped(e.to_string()))));
}
break; break;
} }
Err(_) => { Err(_) => {

View File

@@ -67,7 +67,9 @@ impl Emacs {
pub fn upgrade(&self, run_type: RunType) -> Result<()> { pub fn upgrade(&self, run_type: RunType) -> Result<()> {
let emacs = require("emacs")?; let emacs = require("emacs")?;
let init_file = require_option(self.directory.as_ref())?.join("init.el").require()?; let init_file = require_option(self.directory.as_ref(), String::from("Emacs directory does not exist"))?
.join("init.el")
.require()?;
if let Some(doom) = &self.doom { if let Some(doom) = &self.doom {
return Emacs::update_doom(doom, run_type); return Emacs::update_doom(doom, run_type);

View File

@@ -145,7 +145,7 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] { if #[cfg(target_os = "linux")] {
if !ctx.config().enable_tlmgr_linux() { if !ctx.config().enable_tlmgr_linux() {
return Err(SkipStep.into()); return Err(SkipStep(String::from("tlmgr must be explicity enabled in the configuration to run in Linux")).into());
} }
} }
} }
@@ -216,12 +216,16 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
let composer_home = Command::new(&composer) let composer_home = Command::new(&composer)
.args(&["global", "config", "--absolute", "--quiet", "home"]) .args(&["global", "config", "--absolute", "--quiet", "home"])
.check_output() .check_output()
.map_err(|_| (SkipStep)) .map_err(|e| (SkipStep(format!("Error getting the composer directory: {}", e))))
.map(|s| PathBuf::from(s.trim()))? .map(|s| PathBuf::from(s.trim()))?
.require()?; .require()?;
if !composer_home.is_descendant_of(ctx.base_dirs().home_dir()) { if !composer_home.is_descendant_of(ctx.base_dirs().home_dir()) {
return Err(SkipStep.into()); return Err(SkipStep(format!(
"Composer directory {} isn't a decandent of the user's home directory",
composer_home.display()
))
.into());
} }
print_separator("Composer"); print_separator("Composer");
@@ -275,7 +279,7 @@ pub fn run_remote_topgrade(ctx: &ExecutionContext, hostname: &str) -> Result<()>
#[cfg(unix)] #[cfg(unix)]
{ {
crate::tmux::run_remote_topgrade(hostname, &ssh, topgrade, ctx.config().tmux_arguments())?; crate::tmux::run_remote_topgrade(hostname, &ssh, topgrade, ctx.config().tmux_arguments())?;
Err(SkipStep.into()) Err(SkipStep(String::from("Remote Topgrade launched in Tmux")).into())
} }
#[cfg(not(unix))] #[cfg(not(unix))]

View File

@@ -173,7 +173,7 @@ impl Git {
} }
pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> {
if repositories.repositories.is_empty() { if repositories.repositories.is_empty() {
return Err(SkipStep.into()); return Err(SkipStep(String::from("No repositories to pull")).into());
} }
print_separator("Git repositories"); print_separator("Git repositories");

View File

@@ -42,7 +42,11 @@ pub fn run_npm_upgrade(_base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
{ {
let npm_root = npm.root()?; let npm_root = npm.root()?;
if !npm_root.is_descendant_of(_base_dirs.home_dir()) { if !npm_root.is_descendant_of(_base_dirs.home_dir()) {
return Err(SkipStep.into()); return Err(SkipStep(format!(
"NPM root at {} isn't a decandent of the user's home directory",
npm_root.display()
))
.into());
} }
} }
@@ -55,8 +59,7 @@ pub fn yarn_global_update(run_type: RunType) -> Result<()> {
let output = Command::new(&yarn).arg("--version").string_output()?; let output = Command::new(&yarn).arg("--version").string_output()?;
if output.contains("Hadoop") { if output.contains("Hadoop") {
debug!("Yarn is Hadoop yarn"); return Err(SkipStep(String::from("Installed yarn is Hadoop's yarn")).into());
return Err(SkipStep.into());
} }
print_separator("Yarn"); print_separator("Yarn");

View File

@@ -455,13 +455,12 @@ fn upgrade_nixos(sudo: &Option<PathBuf>, cleanup: bool, run_type: RunType) -> Re
} }
pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo)?; let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let needrestart = require("needrestart")?; let needrestart = require("needrestart")?;
let distribution = Distribution::detect()?; let distribution = Distribution::detect()?;
if distribution.redhat_based() { if distribution.redhat_based() {
debug!("Skipping needrestart on Redhat based distributions"); return Err(SkipStep(String::from("needrestart will be ran by the package manager")).into());
return Err(SkipStep.into());
} }
print_separator("Check for needed restarts"); print_separator("Check for needed restarts");
@@ -475,7 +474,7 @@ pub fn run_fwupdmgr(run_type: RunType) -> Result<()> {
let fwupdmgr = require("fwupdmgr")?; let fwupdmgr = require("fwupdmgr")?;
if is_wsl()? { if is_wsl()? {
return Err(SkipStep.into()); return Err(SkipStep(String::from("Should not run in WSL")).into());
} }
print_separator("Firmware upgrades"); print_separator("Firmware upgrades");
@@ -508,11 +507,11 @@ pub fn flatpak_update(run_type: RunType) -> Result<()> {
} }
pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo)?; let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let snap = require("snap")?; let snap = require("snap")?;
if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() { if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() {
return Err(SkipStep.into()); return Err(SkipStep(String::from("Snapd socket does not exist")).into());
} }
print_separator("snap"); print_separator("snap");
@@ -520,7 +519,7 @@ pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
} }
pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo)?; let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let pihole = require("pihole")?; let pihole = require("pihole")?;
Path::new("/opt/pihole/update.sh").require()?; Path::new("/opt/pihole/update.sh").require()?;
@@ -530,7 +529,7 @@ pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()
} }
pub fn run_etc_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn run_etc_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo)?; let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let etc_update = require("etc-update")?; let etc_update = require("etc-update")?;
print_separator("etc-update"); print_separator("etc-update");

View File

@@ -2,7 +2,7 @@ use crate::execution_context::ExecutionContext;
use crate::executor::{CommandExt, RunType}; use crate::executor::{CommandExt, RunType};
use crate::terminal::{print_separator, prompt_yesno}; use crate::terminal::{print_separator, prompt_yesno};
use crate::{ use crate::{
error::{SkipStep, TopgradeError}, error::TopgradeError,
utils::{require, PathExt}, utils::{require, PathExt},
}; };
use anyhow::Result; use anyhow::Result;
@@ -88,7 +88,7 @@ pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {
if system_update_available()? { if system_update_available()? {
let answer = prompt_yesno("A system update is available. Do you wish to install it?")?; let answer = prompt_yesno("A system update is available. Do you wish to install it?")?;
if !answer { if !answer {
return Err(SkipStep.into()); return Ok(());
} }
println!(); println!();
} else { } else {

View File

@@ -56,8 +56,7 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
use super::linux::Distribution; use super::linux::Distribution;
if let Ok(Distribution::NixOS) = Distribution::detect() { if let Ok(Distribution::NixOS) = Distribution::detect() {
debug!("Nix on NixOS must be upgraded via 'nixos-rebuild switch', skipping."); return Err(SkipStep(String::from("Nix on NixOS must be upgraded via nixos-rebuild switch")).into());
return Err(SkipStep.into());
} }
} }

View File

@@ -52,7 +52,7 @@ pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> {
let topgrade = Command::new(&wsl) let topgrade = Command::new(&wsl)
.args(&["which", "topgrade"]) .args(&["which", "topgrade"])
.check_output() .check_output()
.map_err(|_| SkipStep)?; .map_err(|_| SkipStep(String::from("Could not find Topgrade installed in WSL")))?;
let mut command = ctx.run_type().execute(&wsl); let mut command = ctx.run_type().execute(&wsl);
command command

View File

@@ -53,7 +53,7 @@ impl Powershell {
} }
pub fn update_modules(&self, ctx: &ExecutionContext) -> Result<()> { pub fn update_modules(&self, ctx: &ExecutionContext) -> Result<()> {
let powershell = require_option(self.path.as_ref())?; let powershell = require_option(self.path.as_ref(), String::from("Powershell is not installed"))?;
print_separator("Powershell Modules Update"); print_separator("Powershell Modules Update");
@@ -77,7 +77,7 @@ impl Powershell {
#[cfg(windows)] #[cfg(windows)]
pub fn windows_update(&self, ctx: &ExecutionContext) -> Result<()> { pub fn windows_update(&self, ctx: &ExecutionContext) -> Result<()> {
let powershell = require_option(self.path.as_ref())?; let powershell = require_option(self.path.as_ref(), String::from("Powershell is not installed"))?;
debug_assert!(self.supports_windows_update()); debug_assert!(self.supports_windows_update());

View File

@@ -148,7 +148,10 @@ impl<'a> Drop for TemporaryPowerOn<'a> {
} }
pub fn collect_boxes(ctx: &ExecutionContext) -> Result<Vec<VagrantBox>> { pub fn collect_boxes(ctx: &ExecutionContext) -> Result<Vec<VagrantBox>> {
let directories = utils::require_option(ctx.config().vagrant_directories())?; let directories = utils::require_option(
ctx.config().vagrant_directories(),
String::from("No Vagrant directories were specified in the configuration file"),
)?;
let vagrant = Vagrant { let vagrant = Vagrant {
path: utils::require("vagrant")?, path: utils::require("vagrant")?,
}; };
@@ -179,8 +182,7 @@ pub fn topgrade_vagrant_box(ctx: &ExecutionContext, vagrant_box: &VagrantBox) ->
let mut _poweron = None; let mut _poweron = None;
if !vagrant_box.initial_status.powered_on() { if !vagrant_box.initial_status.powered_on() {
if !(ctx.config().vagrant_power_on().unwrap_or(true)) { if !(ctx.config().vagrant_power_on().unwrap_or(true)) {
debug!("Skipping powered off box {}", vagrant_box); return Err(SkipStep(format!("Skipping powered off box {}", vagrant_box)).into());
return Err(SkipStep.into());
} else { } else {
print_separator(seperator); print_separator(seperator);
_poweron = Some(vagrant.temporary_power_on(&vagrant_box, ctx)?); _poweron = Some(vagrant.temporary_power_on(&vagrant_box, ctx)?);

View File

@@ -5,7 +5,7 @@ use crate::executor::{CommandExt, ExecutorOutput, RunType};
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::{ use crate::{
execution_context::ExecutionContext, execution_context::ExecutionContext,
utils::{require, require_option, PathExt}, utils::{require, PathExt},
}; };
use directories::BaseDirs; use directories::BaseDirs;
use log::debug; use log::debug;
@@ -17,20 +17,20 @@ use std::{
const UPGRADE_VIM: &str = include_str!("upgrade.vim"); const UPGRADE_VIM: &str = include_str!("upgrade.vim");
pub fn vimrc(base_dirs: &BaseDirs) -> Option<PathBuf> { pub fn vimrc(base_dirs: &BaseDirs) -> Result<PathBuf> {
base_dirs base_dirs
.home_dir() .home_dir()
.join(".vimrc") .join(".vimrc")
.if_exists() .require()
.or_else(|| base_dirs.home_dir().join(".vim/vimrc").if_exists()) .or_else(|_| base_dirs.home_dir().join(".vim/vimrc").require())
} }
fn nvimrc(base_dirs: &BaseDirs) -> Option<PathBuf> { fn nvimrc(base_dirs: &BaseDirs) -> Result<PathBuf> {
#[cfg(unix)] #[cfg(unix)]
return base_dirs.home_dir().join(".config/nvim/init.vim").if_exists(); return base_dirs.home_dir().join(".config/nvim/init.vim").require();
#[cfg(windows)] #[cfg(windows)]
return base_dirs.cache_dir().join("nvim/init.vim").if_exists(); return base_dirs.cache_dir().join("nvim/init.vim").require();
} }
fn upgrade(vim: &PathBuf, vimrc: &PathBuf, ctx: &ExecutionContext) -> Result<()> { fn upgrade(vim: &PathBuf, vimrc: &PathBuf, ctx: &ExecutionContext) -> Result<()> {
@@ -70,10 +70,10 @@ pub fn upgrade_vim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> {
let output = Command::new(&vim).arg("--version").check_output()?; let output = Command::new(&vim).arg("--version").check_output()?;
if !output.starts_with("VIM") { if !output.starts_with("VIM") {
return Err(SkipStep.into()); return Err(SkipStep(String::from("vim binary might by actually nvim")).into());
} }
let vimrc = require_option(vimrc(&base_dirs))?; let vimrc = vimrc(&base_dirs)?;
print_separator("Vim"); print_separator("Vim");
upgrade(&vim, &vimrc, ctx) upgrade(&vim, &vimrc, ctx)
@@ -81,7 +81,7 @@ pub fn upgrade_vim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> {
pub fn upgrade_neovim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> { pub fn upgrade_neovim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> {
let nvim = require("nvim")?; let nvim = require("nvim")?;
let nvimrc = require_option(nvimrc(&base_dirs))?; let nvimrc = nvimrc(&base_dirs)?;
print_separator("Neovim"); print_separator("Neovim");
upgrade(&nvim, &nvimrc, ctx) upgrade(&nvim, &nvimrc, ctx)

View File

@@ -161,7 +161,7 @@ impl Terminal {
.ok(); .ok();
} }
fn print_result<P: AsRef<str>>(&mut self, key: P, result: StepResult) { fn print_result<P: AsRef<str>>(&mut self, key: P, result: &StepResult) {
let key = key.as_ref(); let key = key.as_ref();
self.term self.term
@@ -169,9 +169,10 @@ impl Terminal {
"{}: {}\n", "{}: {}\n",
key, key,
match result { match result {
StepResult::Success => style("OK").bold().green(), StepResult::Success => format!("{}", style("OK").bold().green()),
StepResult::Failure => style("FAILED").bold().red(), StepResult::Failure => format!("{}", style("FAILED").bold().red()),
StepResult::Ignored => style("IGNORED").bold().yellow(), StepResult::Ignored => format!("{}", style("IGNORED").bold().yellow()),
StepResult::Skipped(reason) => format!("{}: {}", style("SKIPPED").bold().blue(), reason),
} }
)) ))
.ok(); .ok();
@@ -270,7 +271,7 @@ pub fn print_info<P: AsRef<str>>(message: P) {
TERMINAL.lock().unwrap().print_info(message) TERMINAL.lock().unwrap().print_info(message)
} }
pub fn print_result<P: AsRef<str>>(key: P, result: StepResult) { pub fn print_result<P: AsRef<str>>(key: P, result: &StepResult) {
TERMINAL.lock().unwrap().print_result(key, result) TERMINAL.lock().unwrap().print_result(key, result)
} }

View File

@@ -62,8 +62,7 @@ where
debug!("Path {:?} exists", self.as_ref()); debug!("Path {:?} exists", self.as_ref());
Ok(self) Ok(self)
} else { } else {
debug!("Path {:?} doesn't exist", self.as_ref()); Err(SkipStep(format!("Path {:?} doesn't exist", self.as_ref())).into())
Err(SkipStep.into())
} }
} }
} }
@@ -109,8 +108,7 @@ pub fn require<T: AsRef<OsStr> + Debug>(binary_name: T) -> Result<PathBuf> {
} }
Err(e) => match e { Err(e) => match e {
which_crate::Error::CannotFindBinaryPath => { which_crate::Error::CannotFindBinaryPath => {
debug!("Cannot find {:?}", &binary_name); Err(SkipStep(format!("Cannot find {:?} in PATH", &binary_name)).into())
Err(SkipStep.into())
} }
_ => { _ => {
panic!("Detecting {:?} failed: {}", &binary_name, e); panic!("Detecting {:?} failed: {}", &binary_name, e);
@@ -120,10 +118,10 @@ pub fn require<T: AsRef<OsStr> + Debug>(binary_name: T) -> Result<PathBuf> {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn require_option<T>(option: Option<T>) -> Result<T> { pub fn require_option<T>(option: Option<T>, cause: String) -> Result<T> {
if let Some(value) = option { if let Some(value) = option {
Ok(value) Ok(value)
} else { } else {
Err(SkipStep.into()) Err(SkipStep(cause).into())
} }
} }