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;
|
|
|
|
|
mod git;
|
2018-06-09 20:39:13 +03:00
|
|
|
mod npm;
|
2018-06-03 18:04:58 +03:00
|
|
|
mod report;
|
2018-06-06 15:32:38 +03:00
|
|
|
mod steps;
|
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};
|
|
|
|
|
use self::report::{Report, Reporter};
|
|
|
|
|
use self::steps::*;
|
|
|
|
|
use self::terminal::Terminal;
|
|
|
|
|
use self::utils::PathExt;
|
2018-06-20 21:05:49 +03:00
|
|
|
use clap::{App, Arg};
|
2018-06-04 22:33:39 +03:00
|
|
|
use failure::Error;
|
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-07-10 07:29:41 +03:00
|
|
|
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
|
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-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-06-27 18:06:24 +03:00
|
|
|
let mut reports = Report::new();
|
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 {
|
|
|
|
|
terminal.print_separator(name);
|
|
|
|
|
run_custom_command(&command)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 14:39:29 +03:00
|
|
|
if !(matches.is_present("no_system")) {
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
|
|
|
|
terminal.print_separator("System update");
|
|
|
|
|
match linux::Distribution::detect() {
|
|
|
|
|
Ok(distribution) => {
|
|
|
|
|
match distribution {
|
|
|
|
|
linux::Distribution::Arch => linux::upgrade_arch_linux(&sudo, &mut terminal),
|
|
|
|
|
linux::Distribution::CentOS => linux::upgrade_redhat(&sudo, &mut terminal),
|
|
|
|
|
linux::Distribution::Fedora => linux::upgrade_fedora(&sudo, &mut terminal),
|
|
|
|
|
linux::Distribution::Ubuntu | linux::Distribution::Debian => {
|
|
|
|
|
linux::upgrade_debian(&sudo, &mut terminal)
|
|
|
|
|
}
|
|
|
|
|
}.report("System upgrade", &mut reports);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
println!("Error detecting current distribution: {}", e);
|
|
|
|
|
}
|
2018-06-11 08:29:40 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 14:39:29 +03:00
|
|
|
if let Some(brew) = utils::which("brew") {
|
|
|
|
|
terminal.print_separator("Brew");
|
|
|
|
|
run_homebrew(&brew).report("Brew", &mut reports);
|
|
|
|
|
}
|
2018-06-11 08:29:40 +03:00
|
|
|
|
2018-08-13 14:39:29 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
|
{
|
|
|
|
|
if let Some(choco) = utils::which("choco") {
|
|
|
|
|
terminal.print_separator("Chocolatey");
|
|
|
|
|
windows::run_chocolatey(&choco).report("Chocolatey", &mut reports);
|
|
|
|
|
}
|
2018-06-28 07:47:51 +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-06-08 18:19:07 +03:00
|
|
|
}
|
2018-05-30 07:53:19 +03:00
|
|
|
|
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-05-31 16:00:01 +03:00
|
|
|
terminal.print_separator(format!("Pulling {}", repo));
|
2018-07-10 07:29:41 +03:00
|
|
|
git.pull(&repo).report(format!("git: {}", repo), &mut reports);
|
2018-05-31 15:59:45 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(zsh) = utils::which("zsh") {
|
2018-07-07 02:18:19 +03:00
|
|
|
if base_dirs.home_dir().join(".zplug").exists() {
|
2018-06-06 15:32:38 +03:00
|
|
|
terminal.print_separator("zplug");
|
2018-06-28 12:16:54 +03:00
|
|
|
unix::run_zplug(&zsh).report("zplug", &mut reports);
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-03 14:31:25 +03:00
|
|
|
if let Some(fish) = utils::which("fish") {
|
2018-07-07 02:18:19 +03:00
|
|
|
if base_dirs.home_dir().join(".config/fish/functions/fisher.fish").exists() {
|
2018-07-03 14:31:25 +03:00
|
|
|
terminal.print_separator("fisherman");
|
|
|
|
|
unix::run_fisherman(&fish).report("fisherman", &mut reports);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 09:18:53 +03:00
|
|
|
if let Some(tpm) = unix::tpm_path(&base_dirs) {
|
2018-05-31 16:00:01 +03:00
|
|
|
terminal.print_separator("tmux plugins");
|
2018-06-28 12:16:54 +03:00
|
|
|
unix::run_tpm(&tpm).report("tmux", &mut reports);
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(rustup) = utils::which("rustup") {
|
2018-06-07 22:36:32 +03:00
|
|
|
terminal.print_separator("rustup");
|
2018-07-07 09:18:53 +03:00
|
|
|
run_rustup(&rustup, &base_dirs).report("rustup", &mut reports);
|
2018-06-07 22:36:32 +03:00
|
|
|
}
|
|
|
|
|
|
2018-07-07 09:18:53 +03:00
|
|
|
if let Some(cargo_upgrade) = base_dirs.home_dir().join(".cargo/bin/cargo-install-update").if_exists() {
|
2018-05-31 16:17:22 +03:00
|
|
|
terminal.print_separator("Cargo");
|
2018-06-06 15:32:38 +03:00
|
|
|
run_cargo_update(&cargo_upgrade).report("Cargo", &mut reports);
|
2018-05-31 16:17:22 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(emacs) = utils::which("emacs") {
|
2018-07-07 09:18:53 +03:00
|
|
|
if let Some(init_file) = base_dirs.home_dir().join(".emacs.d/init.el").if_exists() {
|
2018-06-01 14:46:33 +03:00
|
|
|
terminal.print_separator("Emacs");
|
2018-06-07 08:51:16 +03:00
|
|
|
run_emacs(&emacs, &init_file).report("Emacs", &mut reports);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(vim) = utils::which("vim") {
|
2018-07-07 02:18:19 +03:00
|
|
|
if let Some(vimrc) = vim::vimrc(&base_dirs) {
|
2018-06-07 08:51:16 +03:00
|
|
|
if let Some(plugin_framework) = vim::PluginFramework::detect(&vimrc) {
|
2018-07-04 14:22:34 +03:00
|
|
|
terminal.print_separator(&format!("Vim ({:?})", plugin_framework));
|
2018-07-14 22:19:03 +03:00
|
|
|
vim::upgrade(&vim, &vimrc, &plugin_framework).report("Vim", &mut reports);
|
2018-06-07 08:51:16 +03:00
|
|
|
}
|
2018-05-31 16:17:28 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 09:51:19 +02:00
|
|
|
if let Some(nvim) = utils::which("nvim") {
|
2018-07-07 02:18:19 +03:00
|
|
|
if let Some(nvimrc) = vim::nvimrc(&base_dirs) {
|
2018-07-04 09:51:19 +02:00
|
|
|
if let Some(plugin_framework) = vim::PluginFramework::detect(&nvimrc) {
|
2018-07-04 14:22:34 +03:00
|
|
|
terminal.print_separator(&format!("Neovim ({:?})", plugin_framework));
|
2018-07-14 22:19:03 +03:00
|
|
|
vim::upgrade(&nvim, &nvimrc, &plugin_framework).report("Neovim", &mut reports);
|
2018-07-04 09:51:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(npm) = utils::which("npm").map(npm::NPM::new) {
|
2018-06-09 20:39:13 +03:00
|
|
|
if let Ok(npm_root) = npm.root() {
|
2018-07-07 09:18:53 +03:00
|
|
|
if npm_root.is_descendant_of(base_dirs.home_dir()) {
|
2018-06-09 20:39:13 +03:00
|
|
|
terminal.print_separator("Node Package Manager");
|
|
|
|
|
npm.upgrade().report("Node Package Manager", &mut reports);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-06 11:27:43 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-07 13:43:24 +03:00
|
|
|
if let Some(yarn) = utils::which("yarn") {
|
|
|
|
|
terminal.print_separator("Yarn");
|
|
|
|
|
yarn_global_update(&yarn).report("Yarn", &mut reports);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(apm) = utils::which("apm") {
|
2018-06-06 11:27:43 +03:00
|
|
|
terminal.print_separator("Atom Package Manager");
|
2018-06-06 15:32:38 +03:00
|
|
|
run_apm(&apm).report("Atom Package Manager", &mut reports);
|
2018-06-06 11:27:43 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(flatpak) = utils::which("flatpak") {
|
2018-06-14 13:24:52 +03:00
|
|
|
terminal.print_separator("Flatpak");
|
2018-06-28 12:16:54 +03:00
|
|
|
linux::run_flatpak(&flatpak).report("Flatpak", &mut reports);
|
2018-06-14 13:24:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(sudo) = &sudo {
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(snap) = utils::which("snap") {
|
2018-06-14 13:24:52 +03:00
|
|
|
terminal.print_separator("snap");
|
2018-06-28 12:16:54 +03:00
|
|
|
linux::run_snap(&sudo, &snap).report("snap", &mut reports);
|
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 {
|
|
|
|
|
terminal.print_separator(name);
|
2018-06-27 18:06:24 +03:00
|
|
|
run_custom_command(&command).report(name.as_str(), &mut reports);
|
2018-06-11 08:38:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2018-06-17 14:17:36 +03:00
|
|
|
if let Some(fwupdmgr) = utils::which("fwupdmgr") {
|
2018-06-03 16:12:16 +03:00
|
|
|
terminal.print_separator("Firmware upgrades");
|
2018-06-28 12:16:54 +03:00
|
|
|
linux::run_fwupdmgr(&fwupdmgr).report("Firmware upgrade", &mut reports);
|
2018-06-03 16:12:16 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-11 08:29:40 +03:00
|
|
|
if let Some(sudo) = &sudo {
|
2018-07-01 20:24:01 +03:00
|
|
|
if let Some(needrestart) = utils::which("needrestart") {
|
2018-05-31 16:00:01 +03:00
|
|
|
terminal.print_separator("Check for needed restarts");
|
2018-07-01 20:24:01 +03:00
|
|
|
linux::run_needrestart(&sudo, &needrestart).report("Restarts", &mut reports);
|
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")) {
|
|
|
|
|
terminal.print_separator("App Store");
|
|
|
|
|
macos::upgrade_macos().report("App Store", &mut reports);
|
|
|
|
|
}
|
2018-06-03 18:04:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !reports.is_empty() {
|
|
|
|
|
terminal.print_separator("Summary");
|
|
|
|
|
|
2018-06-11 08:57:55 +03:00
|
|
|
for (key, succeeded) in &reports {
|
|
|
|
|
terminal.print_result(key, *succeeded);
|
2018-06-03 18:04:58 +03:00
|
|
|
}
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-11 08:57:55 +03:00
|
|
|
if reports.iter().all(|(_, succeeded)| *succeeded) {
|
|
|
|
|
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
|
|
|
}
|