Better error handling (fixes #15)

This commit is contained in:
Roey Darwish Dror
2018-06-06 15:32:38 +03:00
parent 3897b2ac76
commit ef69dc01ba
4 changed files with 254 additions and 142 deletions

View File

@@ -1,3 +1,4 @@
use super::Check;
use failure::Error; use failure::Error;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
@@ -38,17 +39,18 @@ impl Git {
None None
} }
pub fn pull<P: AsRef<Path>>(&self, path: P) -> Result<Option<bool>, Error> { pub fn pull<P: AsRef<Path>>(&self, path: P) -> Result<Option<()>, Error> {
if let Some(git) = &self.git { if let Some(git) = &self.git {
if let Ok(mut command) = Command::new(&git) Command::new(&git)
.arg("pull") .arg("pull")
.arg("--rebase") .arg("--rebase")
.arg("--autostash") .arg("--autostash")
.current_dir(path) .current_dir(path)
.spawn() .spawn()?
{ .wait()?
return Ok(Some(command.wait()?.success())); .check()?;
}
return Ok(Some(()));
} }
Ok(None) Ok(None)

View File

@@ -7,6 +7,7 @@ extern crate termion;
mod git; mod git;
mod report; mod report;
mod steps;
mod terminal; mod terminal;
use failure::Error; use failure::Error;
@@ -16,7 +17,8 @@ use report::{Report, Reporter};
use std::collections::HashSet; use std::collections::HashSet;
use std::env::home_dir; use std::env::home_dir;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{Command, ExitStatus}; use std::process::ExitStatus;
use steps::*;
use terminal::Terminal; use terminal::Terminal;
use which::which; use which::which;
@@ -38,8 +40,6 @@ impl Check for ExitStatus {
} }
} }
const EMACS_UPGRADE: &str = include_str!("emacs.el");
fn home_path(p: &str) -> PathBuf { fn home_path(p: &str) -> PathBuf {
let mut path = home_dir().unwrap(); let mut path = home_dir().unwrap();
path.push(p); path.push(p);
@@ -88,79 +88,45 @@ fn main() -> Result<(), Error> {
if cfg!(unix) { if cfg!(unix) {
if let Ok(zsh) = which("zsh") { if let Ok(zsh) = which("zsh") {
terminal.print_separator("zplug");
if home_path(".zplug").exists() { if home_path(".zplug").exists() {
Command::new(&zsh) terminal.print_separator("zplug");
.args(&["-c", "source ~/.zshrc && zplug update"]) run_zplug(&zsh).report("zplug", &mut reports);
.spawn()?
.wait()?
.report("zplug", &mut reports);
} }
} }
if let Some(tpm) = tpm() { if let Some(tpm) = tpm() {
terminal.print_separator("tmux plugins"); terminal.print_separator("tmux plugins");
Command::new(&tpm) run_tpm(&tpm).report("tmux", &mut reports);
.arg("all")
.spawn()?
.wait()?
.report("tmux", &mut reports);
} }
} }
let cargo_upgrade = home_path(".cargo/bin/cargo-install-update"); let cargo_upgrade = home_path(".cargo/bin/cargo-install-update");
if cargo_upgrade.exists() { if cargo_upgrade.exists() {
terminal.print_separator("Cargo"); terminal.print_separator("Cargo");
Command::new(&cargo_upgrade) run_cargo_update(&cargo_upgrade).report("Cargo", &mut reports);
.args(&["install-update", "--all"])
.spawn()?
.wait()?
.report("Cargo", &mut reports);
} }
if let Ok(emacs) = which("emacs") { if let Ok(emacs) = which("emacs") {
if home_path(".emacs.d").exists() { let init_file = home_path(".emacs.d/init.el");
if init_file.exists() {
terminal.print_separator("Emacs"); terminal.print_separator("Emacs");
Command::new(&emacs) run_emacs(&emacs, &home_path(".emacs.d/init.el")).report("Emacs", &mut reports);
.args(&[
"--batch",
"-l",
home_path(".emacs.d/init.el").to_str().unwrap(),
"--eval",
EMACS_UPGRADE,
])
.spawn()?
.wait()?
.report("Emacs", &mut reports);
} }
} }
if let Ok(gem) = which("gem") { if let Ok(gem) = which("gem") {
terminal.print_separator("RubyGems"); terminal.print_separator("RubyGems");
Command::new(&gem) run_gem(&gem).report("RubyGems", &mut reports);
.args(&["update"])
.spawn()?
.wait()?
.report("RubyGems", &mut reports);
} }
if let Ok(npm) = which("npm") { if let Ok(npm) = which("npm") {
terminal.print_separator("Node Package Manager"); terminal.print_separator("Node Package Manager");
Command::new(&npm) run_npm(&npm).report("Node Package Manager", &mut reports);
.args(&["update", "-g"])
.spawn()?
.wait()?
.report("Node Package Manager", &mut reports);
} }
if let Ok(apm) = which("apm") { if let Ok(apm) = which("apm") {
terminal.print_separator("Atom Package Manager"); terminal.print_separator("Atom Package Manager");
Command::new(&apm) run_apm(&apm).report("Atom Package Manager", &mut reports);
.args(&["upgrade", "--confirm=false"])
.spawn()?
.wait()
.map_err(Error::from)?
.report("Atom Package Manager", &mut reports);
} }
if cfg!(target_os = "linux") { if cfg!(target_os = "linux") {
@@ -168,87 +134,29 @@ fn main() -> Result<(), Error> {
terminal.print_separator("System update"); terminal.print_separator("System update");
match os_type::current_platform().os_type { match os_type::current_platform().os_type {
OSType::Arch => { OSType::Arch => Some(upgrade_arch_linux(&sudo, &terminal)),
if let Ok(yay) = which("yay") { OSType::CentOS | OSType::Redhat => Some(upgrade_redhat(&sudo, &terminal)),
Command::new(yay) OSType::Ubuntu | OSType::Debian => Some(upgrade_debian(&sudo, &terminal)),
.spawn()?
.wait()?
.report("System upgrade", &mut reports);
} else {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["pacman", "-Syu"])
.spawn()?
.wait()?
.report("System upgrade", &mut reports);
} else {
terminal.print_warning("No sudo or yay detected. Skipping system upgrade");
}
}
}
OSType::CentOS | OSType::Redhat => {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["yum", "upgrade"])
.spawn()?
.wait()?
.report("System upgrade", &mut reports);;
}
}
OSType::Ubuntu | OSType::Debian => {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["apt", "update"])
.spawn()?
.wait()?
.check()
.and_then(|()| {
Command::new(&sudo)
.args(&["apt", "dist-upgrade"])
.spawn()?
.wait()
.map_err(Error::from)
})?
.report("System upgrade", &mut reports);;
}
}
OSType::Unknown => { OSType::Unknown => {
terminal.print_warning( terminal.print_warning(
"Could not detect your Linux distribution. Do you have lsb-release installed?", "Could not detect your Linux distribution. Do you have lsb-release installed?",
); );
None
} }
_ => (), _ => None,
} }.report("System upgrade", &mut reports);
if let Ok(fwupdmgr) = which("fwupdmgr") { if let Ok(fwupdmgr) = which("fwupdmgr") {
terminal.print_separator("Firmware upgrades"); terminal.print_separator("Firmware upgrades");
Command::new(&fwupdmgr) run_fwupdmgr(&fwupdmgr).report("Firmware upgrade", &mut reports);
.arg("refresh")
.spawn()?
.wait()?
.check()
.and_then(|()| {
Command::new(&fwupdmgr)
.arg("get-updates")
.spawn()?
.wait()
.map_err(Error::from)
})?
.report("Firmware upgrade", &mut reports);
} }
if let Ok(sudo) = &sudo { if let Ok(sudo) = &sudo {
if let Ok(needrestart) = which("needrestart") { if let Ok(_) = which("needrestart") {
terminal.print_separator("Check for needed restarts"); terminal.print_separator("Check for needed restarts");
Command::new(&sudo) run_needrestart(&sudo).report("Restarts", &mut reports);
.arg(&needrestart)
.spawn()?
.wait()?
.report("Restarts", &mut reports);
} }
} }
} }
@@ -256,27 +164,11 @@ fn main() -> Result<(), Error> {
if cfg!(target_os = "macos") { if cfg!(target_os = "macos") {
if let Ok(brew) = which("brew") { if let Ok(brew) = which("brew") {
terminal.print_separator("Homebrew"); terminal.print_separator("Homebrew");
Command::new(&brew) run_homebrew(&brew).report("Homebrew", &mut reports);
.arg("update")
.spawn()?
.wait()?
.check()
.and_then(|()| {
Command::new(&brew)
.arg("upgrade")
.spawn()?
.wait()
.map_err(Error::from)
})?
.report("Homebrew", &mut reports);
} }
terminal.print_separator("System update"); terminal.print_separator("System update");
Command::new("softwareupdate") upgrade_macos().report("System upgrade", &mut reports);;
.args(&["--install", "--all"])
.spawn()?
.wait()?
.report("System upgrade", &mut reports);;
} }
let mut reports: Vec<_> = reports.into_iter().collect(); let mut reports: Vec<_> = reports.into_iter().collect();

View File

@@ -1,6 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::process::ExitStatus;
pub type Report = HashMap<String, bool>; pub type Report = HashMap<String, bool>;
@@ -8,9 +7,30 @@ pub trait Reporter {
fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report); fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report);
} }
impl Reporter for ExitStatus { impl<T, E> Reporter for Result<T, E>
where
T: Reporter,
{
fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report) { fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report) {
report.insert(key.into().into_owned(), self.success()); match self {
Err(_) => {
report.insert(key.into().into_owned(), false);
}
Ok(item) => {
item.report(key, report);
}
}
}
}
impl<T> Reporter for Option<T>
where
T: Reporter,
{
fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report) {
if let Some(item) = self {
item.report(key, report);
}
} }
} }
@@ -19,3 +39,9 @@ impl Reporter for bool {
report.insert(key.into().into_owned(), *self); report.insert(key.into().into_owned(), *self);
} }
} }
impl Reporter for () {
fn report<'a, M: Into<Cow<'a, str>>>(&self, key: M, report: &mut Report) {
report.insert(key.into().into_owned(), true);
}
}

192
src/steps.rs Normal file
View File

@@ -0,0 +1,192 @@
use super::terminal::Terminal;
use super::{which, Check};
use failure;
use std::path::PathBuf;
use std::process::Command;
const EMACS_UPGRADE: &str = include_str!("emacs.el");
pub fn run_zplug(zsh: &PathBuf) -> Result<(), failure::Error> {
Command::new(zsh)
.args(&["-c", "source ~/.zshrc && zplug update"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_tpm(tpm: &PathBuf) -> Result<(), failure::Error> {
Command::new(&tpm).arg("all").spawn()?.wait()?.check()?;
Ok(())
}
pub fn run_cargo_update(cargo_update: &PathBuf) -> Result<(), failure::Error> {
Command::new(&cargo_update)
.args(&["install-update", "--all"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_emacs(emacs: &PathBuf, init: &PathBuf) -> Result<(), failure::Error> {
Command::new(&emacs)
.args(&[
"--batch",
"-l",
init.to_str().unwrap(),
"--eval",
EMACS_UPGRADE,
])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_gem(gem: &PathBuf) -> Result<(), failure::Error> {
Command::new(&gem)
.args(&["update"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_npm(npm: &PathBuf) -> Result<(), failure::Error> {
Command::new(&npm)
.args(&["update", "-g"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_apm(apm: &PathBuf) -> Result<(), failure::Error> {
Command::new(&apm)
.args(&["upgrade", "--confirm=false"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_needrestart(sudo: &PathBuf) -> Result<(), failure::Error> {
Command::new(&sudo)
.arg("needrestart")
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_fwupdmgr(fwupdmgr: &PathBuf) -> Result<(), failure::Error> {
Command::new(&fwupdmgr)
.arg("refresh")
.spawn()?
.wait()?
.check()?;
Command::new(&fwupdmgr)
.arg("get-updates")
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn upgrade_macos() -> Result<(), failure::Error> {
Command::new("softwareupdate")
.args(&["--install", "--all"])
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn run_homebrew(homebrew: &PathBuf) -> Result<(), failure::Error> {
Command::new(&homebrew)
.arg("update")
.spawn()?
.wait()?
.check()?;
Command::new(&homebrew)
.arg("upgrade")
.spawn()?
.wait()?
.check()?;
Ok(())
}
pub fn upgrade_arch_linux(
sudo: &Result<PathBuf, which::Error>,
terminal: &Terminal,
) -> Result<(), failure::Error> {
if let Ok(yay) = which("yay") {
Command::new(yay).spawn()?.wait()?.check()?;
} else {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["pacman", "-Syu"])
.spawn()?
.wait()?
.check()?;
} else {
terminal.print_warning("No sudo or yay detected. Skipping system upgrade");
}
}
Ok(())
}
pub fn upgrade_redhat(
sudo: &Result<PathBuf, which::Error>,
terminal: &Terminal,
) -> Result<(), failure::Error> {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["yum", "upgrade"])
.spawn()?
.wait()?
.check()?;
} else {
terminal.print_warning("No sudo detected. Skipping system upgrade");
}
Ok(())
}
pub fn upgrade_debian(
sudo: &Result<PathBuf, which::Error>,
terminal: &Terminal,
) -> Result<(), failure::Error> {
if let Ok(sudo) = &sudo {
Command::new(&sudo)
.args(&["apt", "update"])
.spawn()?
.wait()?
.check()?;
Command::new(&sudo)
.args(&["apt", "upgrade"])
.spawn()?
.wait()?
.check()?;
} else {
terminal.print_warning("No sudo detected. Skipping system upgrade");
}
Ok(())
}