2018-06-08 18:19:07 +03:00
|
|
|
extern crate directories;
|
2018-06-04 22:33:39 +03:00
|
|
|
extern crate failure;
|
2018-05-29 23:48:30 +03:00
|
|
|
extern crate which;
|
|
|
|
|
#[macro_use]
|
2018-06-04 22:33:39 +03:00
|
|
|
extern crate failure_derive;
|
2018-06-08 18:19:07 +03:00
|
|
|
extern crate toml;
|
|
|
|
|
#[macro_use]
|
|
|
|
|
extern crate serde_derive;
|
2018-06-12 11:30:03 +03:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate clap;
|
2018-06-08 18:19:07 +03:00
|
|
|
extern crate serde;
|
2018-06-11 13:56:57 +03:00
|
|
|
extern crate shellexpand;
|
2018-06-17 14:17:36 +03:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate log;
|
|
|
|
|
extern crate env_logger;
|
2018-06-27 22:19:36 +03:00
|
|
|
extern crate term_size;
|
|
|
|
|
extern crate termcolor;
|
2018-05-29 23:48:30 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
2018-06-07 16:19:11 +03:00
|
|
|
mod linux;
|
2018-06-28 12:16:54 +03:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
mod macos;
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
mod unix;
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
mod windows;
|
|
|
|
|
|
|
|
|
|
mod config;
|
2018-08-26 16:12:59 +03:00
|
|
|
mod executor;
|
2018-08-19 14:45:23 +03:00
|
|
|
mod generic;
|
2018-06-28 12:16:54 +03:00
|
|
|
mod git;
|
2018-08-19 14:45:23 +03:00
|
|
|
mod node;
|
2018-06-03 18:04:58 +03:00
|
|
|
mod report;
|
2018-05-31 16:00:01 +03:00
|
|
|
mod terminal;
|
2018-06-17 11:43:25 +03:00
|
|
|
mod utils;
|
2018-06-07 08:51:16 +03:00
|
|
|
mod vim;
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2018-08-22 10:43:32 +03:00
|
|
|
use self::config::Config;
|
|
|
|
|
use self::git::{Git, Repositories};
|
2018-08-24 21:52:17 +03:00
|
|
|
use self::report::Report;
|
2018-08-22 10:43:32 +03:00
|
|
|
use self::terminal::Terminal;
|
2018-06-20 21:05:49 +03:00
|
|
|
use clap::{App, Arg};
|
2018-06-04 22:33:39 +03:00
|
|
|
use failure::Error;
|
2018-08-25 22:19:38 +03:00
|
|
|
use std::borrow::Cow;
|
2018-06-20 21:05:49 +03:00
|
|
|
use std::env;
|
2018-06-17 11:43:25 +03:00
|
|
|
use std::process::exit;
|
2018-05-29 23:48:30 +03:00
|
|
|
|
2018-06-11 08:57:55 +03:00
|
|
|
#[derive(Fail, Debug)]
|
|
|
|
|
#[fail(display = "A step failed")]
|
|
|
|
|
struct StepFailed;
|
|
|
|
|
|
2018-07-07 02:18:19 +03:00
|
|
|
#[derive(Fail, Debug)]
|
|
|
|
|
#[fail(display = "Cannot find the user base directories")]
|
|
|
|
|
struct NoBaseDirectories;
|
|
|
|
|
|
2018-08-25 22:19:38 +03:00
|
|
|
fn execute<'a, F, M>(func: F, terminal: &mut Terminal) -> Option<(M, bool)>
|
|
|
|
|
where
|
|
|
|
|
M: Into<Cow<'a, str>>,
|
|
|
|
|
F: Fn(&mut Terminal) -> Option<(M, bool)>,
|
|
|
|
|
{
|
|
|
|
|
while let Some((key, success)) = func(terminal) {
|
|
|
|
|
if success {
|
|
|
|
|
return Some((key, success));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !terminal.should_retry() {
|
|
|
|
|
return Some((key, success));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 08:57:55 +03:00
|
|
|
fn run() -> Result<(), Error> {
|
2018-06-20 21:05:49 +03:00
|
|
|
let matches = App::new("Topgrade")
|
2018-06-12 11:30:03 +03:00
|
|
|
.version(crate_version!())
|
|
|
|
|
.about("Upgrade all the things")
|
2018-06-20 21:05:49 +03:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("tmux")
|
|
|
|
|
.help("Invoke inside tmux")
|
|
|
|
|
.short("t")
|
|
|
|
|
.long("tmux"),
|
|
|
|
|
)
|
2018-08-13 14:39:29 +03:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("no_system")
|
|
|
|
|
.help("Don't perform system upgrade")
|
|
|
|
|
.long("no-system"),
|
|
|
|
|
)
|
2018-08-26 16:12:59 +03:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("dry_run")
|
|
|
|
|
.help("Print what would be done")
|
|
|
|
|
.short("n")
|
|
|
|
|
.long("dry-run"),
|
|
|
|
|
)
|
2018-06-12 11:30:03 +03:00
|
|
|
.get_matches();
|
|
|
|
|
|
2018-07-10 07:29:41 +03:00
|
|
|
if matches.is_present("tmux") && env::var("TMUX").is_err() {
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2018-06-28 12:16:54 +03:00
|
|
|
unix::run_in_tmux();
|
2018-06-20 21:05:49 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
env_logger::init();
|
2018-07-07 02:18:19 +03:00
|
|
|
let base_dirs = directories::BaseDirs::new().ok_or(NoBaseDirectories)?;
|
2018-05-30 07:53:19 +03:00
|
|
|
let git = Git::new();
|
2018-06-11 08:21:39 +03:00
|
|
|
let mut git_repos = Repositories::new(&git);
|
2018-06-27 22:19:36 +03:00
|
|
|
let mut terminal = Terminal::new();
|
2018-07-07 02:18:19 +03:00
|
|
|
let config = Config::read(&base_dirs)?;
|
2018-08-24 21:52:17 +03:00
|
|
|
let mut report = Report::new();
|
2018-08-26 16:12:59 +03:00
|
|
|
let dry_run = matches.is_present("dry_run");
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
let sudo = utils::which("sudo");
|
2018-06-11 08:29:40 +03:00
|
|
|
|
2018-06-20 20:26:08 +03:00
|
|
|
if let Some(commands) = config.pre_commands() {
|
|
|
|
|
for (name, command) in commands {
|
2018-08-26 16:12:59 +03:00
|
|
|
generic::run_custom_command(&name, &command, &mut terminal, dry_run)?;
|
2018-06-20 20:26:08 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 22:01:06 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
|
let powershell = windows::Powershell::new();
|
|
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| powershell.update_modules(terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-22 22:01:06 +03:00
|
|
|
|
2018-08-22 22:18:48 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
|
|
|
|
if !(matches.is_present("no_system")) {
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| linux::upgrade(&sudo, terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-22 22:18:48 +03:00
|
|
|
}
|
2018-06-28 07:47:51 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-22 22:18:48 +03:00
|
|
|
#[cfg(windows)]
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| windows::run_chocolatey(terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-22 22:18:48 +03:00
|
|
|
|
2018-08-19 14:45:23 +03:00
|
|
|
#[cfg(unix)]
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(|terminal| unix::run_homebrew(terminal, dry_run), &mut terminal));
|
2018-08-19 14:45:23 +03:00
|
|
|
|
2018-07-07 02:18:19 +03:00
|
|
|
git_repos.insert(base_dirs.home_dir().join(".emacs.d"));
|
|
|
|
|
git_repos.insert(base_dirs.home_dir().join(".vim"));
|
|
|
|
|
git_repos.insert(base_dirs.home_dir().join(".config/nvim"));
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2018-07-07 02:18:19 +03:00
|
|
|
git_repos.insert(base_dirs.home_dir().join(".zshrc"));
|
|
|
|
|
git_repos.insert(base_dirs.home_dir().join(".oh-my-zsh"));
|
|
|
|
|
git_repos.insert(base_dirs.home_dir().join(".tmux"));
|
|
|
|
|
git_repos.insert(base_dirs.home_dir().join(".config/fish"));
|
2018-08-27 15:22:44 +03:00
|
|
|
git_repos.insert(base_dirs.config_dir().join("openbox"));
|
2018-06-08 18:19:07 +03:00
|
|
|
}
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2018-08-23 22:08:04 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
|
{
|
|
|
|
|
if let Some(profile) = powershell.profile() {
|
|
|
|
|
git_repos.insert(profile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-08 18:19:07 +03:00
|
|
|
if let Some(custom_git_repos) = config.git_repos() {
|
|
|
|
|
for git_repo in custom_git_repos {
|
2018-06-11 08:21:39 +03:00
|
|
|
git_repos.insert(git_repo);
|
2018-05-30 07:53:19 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 08:21:39 +03:00
|
|
|
for repo in git_repos.repositories() {
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(|terminal| git.pull(&repo, terminal, dry_run), &mut terminal));
|
2018-05-31 15:59:45 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2018-08-25 22:19:38 +03:00
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| unix::run_zplug(&base_dirs, terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| unix::run_fisherman(&base_dirs, terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| unix::run_tpm(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-05-31 16:17:22 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-25 22:19:38 +03:00
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| generic::run_rustup(&base_dirs, terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| generic::run_cargo_update(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| generic::run_emacs(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| vim::upgrade_vim(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| vim::upgrade_neovim(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| node::run_npm_upgrade(&base_dirs, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| node::yarn_global_update(terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(|terminal| generic::run_apm(terminal, dry_run), &mut terminal));
|
2018-06-06 11:27:43 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(|terminal| linux::run_flatpak(terminal, dry_run), &mut terminal));
|
|
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| linux::run_snap(&sudo, terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-06-14 13:24:52 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-11 08:38:29 +03:00
|
|
|
if let Some(commands) = config.commands() {
|
|
|
|
|
for (name, command) in commands {
|
2018-08-25 22:19:38 +03:00
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| {
|
|
|
|
|
Some((
|
|
|
|
|
name,
|
|
|
|
|
generic::run_custom_command(&name, &command, terminal, dry_run).is_ok(),
|
|
|
|
|
))
|
|
|
|
|
},
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-06-11 08:38:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2018-08-25 22:19:38 +03:00
|
|
|
report.push_result(execute(
|
2018-08-26 16:12:59 +03:00
|
|
|
|terminal| linux::run_fwupdmgr(terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
|
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| linux::run_needrestart(&sudo, terminal, dry_run),
|
2018-08-25 22:19:38 +03:00
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-05-31 09:19:27 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
{
|
2018-08-13 14:39:29 +03:00
|
|
|
if !(matches.is_present("no_system")) {
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| macos::upgrade_macos(terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-13 14:39:29 +03:00
|
|
|
}
|
2018-06-03 18:04:58 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-22 22:18:48 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
|
{
|
|
|
|
|
if !(matches.is_present("no_system")) {
|
2018-08-26 16:12:59 +03:00
|
|
|
report.push_result(execute(
|
|
|
|
|
|terminal| powershell.windows_update(terminal, dry_run),
|
|
|
|
|
&mut terminal,
|
|
|
|
|
));
|
2018-08-22 22:18:48 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 21:52:17 +03:00
|
|
|
if !report.data().is_empty() {
|
2018-06-03 18:04:58 +03:00
|
|
|
terminal.print_separator("Summary");
|
|
|
|
|
|
2018-08-24 21:52:17 +03:00
|
|
|
for (key, succeeded) in report.data() {
|
2018-06-11 08:57:55 +03:00
|
|
|
terminal.print_result(key, *succeeded);
|
2018-06-03 18:04:58 +03:00
|
|
|
}
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-24 21:52:17 +03:00
|
|
|
if report.data().iter().all(|(_, succeeded)| *succeeded) {
|
2018-06-11 08:57:55 +03:00
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
Err(StepFailed.into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
match run() {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
Err(error) => {
|
|
|
|
|
match error.downcast::<StepFailed>() {
|
|
|
|
|
Ok(_) => (),
|
|
|
|
|
Err(error) => println!("ERROR: {}", error),
|
|
|
|
|
};
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|