Files
topgrade/src/main.rs

415 lines
14 KiB
Rust
Raw Normal View History

#![allow(clippy::cognitive_complexity)]
2018-06-28 12:16:54 +03:00
mod config;
mod ctrlc;
2018-12-11 16:43:26 +02:00
mod error;
2020-02-08 22:13:56 +02:00
mod execution_context;
2018-08-26 16:12:59 +03:00
mod executor;
2018-06-03 18:04:58 +03:00
mod report;
2020-02-09 13:41:55 +02:00
mod runner;
#[cfg(windows)]
mod self_renamer;
2018-11-26 14:27:19 +02:00
#[cfg(feature = "self-update")]
mod self_update;
2018-12-15 21:52:21 +02:00
mod steps;
2018-05-31 16:00:01 +03:00
mod terminal;
2018-06-17 11:43:25 +03:00
mod utils;
2018-05-30 07:53:19 +03:00
use self::config::{CommandLineArgs, Config, Step};
2020-02-09 13:41:55 +02:00
use self::error::StepFailed;
#[cfg(all(windows, feature = "self-update"))]
use self::error::Upgraded;
2020-02-09 13:41:55 +02:00
2020-08-22 14:46:17 +03:00
use self::steps::{remote::*, *};
2018-12-09 10:30:41 +02:00
use self::terminal::*;
use anyhow::{anyhow, Result};
2020-02-26 15:33:06 +02:00
use log::debug;
2020-07-30 10:18:37 +03:00
2018-06-20 21:05:49 +03:00
use std::env;
2018-12-11 16:43:26 +02:00
use std::io;
2018-06-17 11:43:25 +03:00
use std::process::exit;
use structopt::clap::crate_version;
use structopt::StructOpt;
2018-05-29 23:48:30 +03:00
fn run() -> Result<()> {
ctrlc::set_handler();
let base_dirs = directories::BaseDirs::new().ok_or_else(|| anyhow!("No base directories"))?;
2018-09-06 14:42:56 +03:00
let opt = CommandLineArgs::from_args();
if opt.edit_config() {
Config::edit(&base_dirs)?;
return Ok(());
};
if opt.show_config_reference() {
print!("{}", crate::config::EXAMPLE_CONFIG);
return Ok(());
}
let config = Config::load(&base_dirs, opt)?;
terminal::set_title(config.set_title());
terminal::set_desktop_notifications(config.notify_each_step());
debug!("Version: {}", crate_version!());
debug!("OS: {}", env!("TARGET"));
debug!("{:?}", std::env::args());
debug!("Binary path: {:?}", std::env::current_exe());
debug!("Self Update: {:?}", cfg!(feature = "self-update"));
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
2018-06-27 23:04:39 +03:00
#[cfg(unix)]
{
tmux::run_in_tmux(config.tmux_arguments());
2018-06-20 21:05:49 +03:00
}
}
2018-12-15 21:52:21 +02:00
let git = git::Git::new();
let mut git_repos = git::Repositories::new(&git);
2018-11-07 14:31:44 +02:00
let sudo = utils::sudo();
let run_type = executor::RunType::new(config.dry_run());
let ctx = execution_context::ExecutionContext::new(run_type, &sudo, &git, &config, &base_dirs);
2020-02-08 22:13:56 +02:00
2020-02-09 13:41:55 +02:00
let mut runner = runner::Runner::new(&ctx);
#[cfg(feature = "self-update")]
{
2020-07-30 10:18:37 +03:00
#[cfg(target_os = "linux")]
openssl_probe::init_ssl_cert_env_vars();
2018-12-31 14:05:15 +02:00
if !run_type.dry() && env::var("TOPGRADE_NO_SELF_UPGRADE").is_err() {
let result = self_update::self_update();
if let Err(e) = &result {
#[cfg(windows)]
{
if e.downcast_ref::<Upgraded>().is_some() {
return result;
}
}
2018-12-05 11:34:08 +02:00
print_warning(format!("Self update error: {}", e));
}
}
}
2018-11-07 14:31:44 +02:00
#[cfg(windows)]
let _self_rename = if config.self_rename() {
Some(crate::self_renamer::SelfRenamer::create()?)
} else {
None
};
2018-06-20 20:26:08 +03:00
if let Some(commands) = config.pre_commands() {
for (name, command) in commands {
2020-02-08 22:13:56 +02:00
generic::run_custom_command(&name, &command, &ctx)?;
2018-06-20 20:26:08 +03:00
}
}
2019-08-14 21:36:57 +03:00
let powershell = powershell::Powershell::new();
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
2019-02-27 09:47:20 +02:00
#[cfg(windows)]
2020-08-21 21:10:54 +03:00
runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(&ctx))?;
2019-06-05 14:15:45 +03:00
if let Some(topgrades) = config.remote_topgrades() {
for remote_topgrade in topgrades.iter().filter(|t| config.should_execute_remote(t)) {
runner.execute(Step::Remotes, format!("Remote ({})", remote_topgrade), || {
2020-08-22 14:46:17 +03:00
remote::ssh::ssh_step(&ctx, remote_topgrade)
})?;
2019-06-05 14:15:45 +03:00
}
}
#[cfg(target_os = "linux")]
let distribution = linux::Distribution::detect();
2018-08-22 22:18:48 +03:00
#[cfg(target_os = "linux")]
{
match &distribution {
Ok(distribution) => {
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
}
Err(e) => {
println!("Error detecting current distribution: {}", e);
}
2018-08-22 22:18:48 +03:00
}
runner.execute(Step::System, "etc-update", || {
linux::run_etc_update(sudo.as_ref(), run_type)
})?;
2018-06-28 07:47:51 +03:00
}
2018-08-22 22:18:48 +03:00
#[cfg(windows)]
{
runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?;
runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?;
}
2018-10-17 13:57:30 +03:00
2018-08-19 14:45:23 +03:00
#[cfg(unix)]
{
runner.execute(Step::Brew, "Brew", || unix::run_brew(&ctx))?;
#[cfg(target_os = "macos")]
{
runner.execute(Step::MacPorts, "MacPorts", || macos::run_macports(&ctx))?;
runner.execute(Step::MicrosoftAutoUpdate, "Microsoft AutoUpdate", || {
macos::run_msupdate(&ctx)
})?;
}
2020-07-11 08:00:35 +03:00
runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?;
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))?;
}
2019-06-25 22:47:36 -07:00
#[cfg(target_os = "dragonfly")]
runner.execute(Step::Pkg, "DragonFly BSD Packages", || {
dragonfly::upgrade_packages(sudo.as_ref(), run_type)
})?;
2018-11-12 11:13:43 +02:00
#[cfg(target_os = "freebsd")]
runner.execute(Step::Pkg, "FreeBSD Packages", || {
freebsd::upgrade_packages(sudo.as_ref(), run_type)
})?;
2018-08-19 14:45:23 +03:00
let emacs = emacs::Emacs::new(&base_dirs);
2020-01-28 21:06:33 +02:00
if config.use_predefined_git_repos() {
if config.should_run(Step::Emacs) {
2020-01-29 21:31:14 +02:00
if !emacs.is_doom() {
if let Some(directory) = emacs.directory() {
git_repos.insert_if_repo(directory);
2020-01-29 21:31:14 +02:00
}
}
git_repos.insert_if_repo(base_dirs.home_dir().join(".doom.d"));
}
2018-09-05 11:17:15 +03:00
if config.should_run(Step::Vim) {
git_repos.insert_if_repo(base_dirs.home_dir().join(".vim"));
git_repos.insert_if_repo(base_dirs.home_dir().join(".config/nvim"));
}
2018-05-30 07:53:19 +03:00
#[cfg(unix)]
{
git_repos.insert_if_repo(zsh::zshrc(&base_dirs));
if config.should_run(Step::Tmux) {
git_repos.insert_if_repo(base_dirs.home_dir().join(".tmux"));
}
git_repos.insert_if_repo(base_dirs.home_dir().join(".config/fish"));
git_repos.insert_if_repo(base_dirs.config_dir().join("openbox"));
git_repos.insert_if_repo(base_dirs.config_dir().join("bspwm"));
git_repos.insert_if_repo(base_dirs.config_dir().join("i3"));
git_repos.insert_if_repo(base_dirs.config_dir().join("sway"));
}
2018-05-30 07:53:19 +03:00
#[cfg(windows)]
git_repos.insert_if_repo(
base_dirs
.data_local_dir()
.join("Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState"),
);
if let Some(profile) = powershell.profile() {
git_repos.insert_if_repo(profile);
}
2018-08-23 22:08:04 +03:00
}
if config.should_run(Step::GitRepos) {
if let Some(custom_git_repos) = config.git_repos() {
for git_repo in custom_git_repos {
git_repos.glob_insert(git_repo);
}
2018-05-30 07:53:19 +03:00
}
runner.execute(Step::GitRepos, "Git repositories", || {
git.multi_pull_step(&git_repos, &ctx)
})?;
2018-05-30 07:53:19 +03:00
}
2019-08-14 21:36:57 +03:00
if should_run_powershell {
runner.execute(Step::Powershell, "Powershell Modules Update", || {
2020-07-15 08:47:15 +03:00
powershell.update_modules(&ctx)
})?;
}
2019-02-19 08:47:01 +02:00
#[cfg(unix)]
{
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))?;
2020-09-03 09:32:45 +03:00
runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?;
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)
})?;
}
2018-10-29 14:32:33 +02:00
#[cfg(not(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly"
)))]
runner.execute(Step::Atom, "apm", || generic::run_apm(run_type))?;
runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&base_dirs, run_type))?;
2020-08-28 16:16:23 +03:00
runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?;
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, "jetpack", || 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))?;
2020-07-30 06:27:29 +03:00
runner.execute(Step::Sheldon, "sheldon", || generic::run_sheldon(&ctx))?;
runner.execute(Step::Rtcl, "rtcl", || generic::run_rtcl(&ctx))?;
2018-06-27 23:04:39 +03:00
#[cfg(target_os = "linux")]
{
runner.execute(Step::Flatpak, "Flatpak", || linux::flatpak_update(run_type))?;
runner.execute(Step::Snap, "snap", || linux::run_snap(sudo.as_ref(), run_type))?;
2018-06-14 13:24:52 +03:00
}
if let Some(commands) = config.commands() {
for (name, command) in commands {
runner.execute(Step::CustomCommands, name, || {
generic::run_custom_command(&name, &command, &ctx)
})?;
}
}
2018-06-27 23:04:39 +03:00
#[cfg(target_os = "linux")]
{
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)
})?;
2018-05-31 09:19:27 +03:00
}
2018-06-27 23:04:39 +03:00
#[cfg(target_os = "macos")]
{
runner.execute(Step::System, "App Store", || macos::run_mas(run_type))?;
runner.execute(Step::System, "System upgrade", || macos::upgrade_macos(&ctx))?;
2018-06-03 18:04:58 +03:00
}
2018-11-12 11:13:43 +02:00
#[cfg(target_os = "freebsd")]
runner.execute(Step::System, "FreeBSD Upgrade", || {
freebsd::upgrade_freebsd(sudo.as_ref(), run_type)
})?;
2018-11-12 11:13:43 +02:00
2018-08-22 22:18:48 +03:00
#[cfg(windows)]
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
2020-06-10 11:51:52 +03:00
if config.should_run(Step::Vagrant) {
if let Ok(boxes) = vagrant::collect_boxes(&ctx) {
for vagrant_box in boxes {
runner.execute(Step::Vagrant, format!("Vagrant ({})", vagrant_box.smart_name()), || {
vagrant::topgrade_vagrant_box(&ctx, &vagrant_box)
})?;
}
}
2020-06-10 11:51:52 +03:00
}
2020-02-09 13:41:55 +02:00
if !runner.report().data().is_empty() {
2018-12-05 11:34:08 +02:00
print_separator("Summary");
2018-06-03 18:04:58 +03:00
for (key, result) in runner.report().data() {
2020-08-21 23:04:36 +03:00
print_result(key, result);
2018-06-03 18:04:58 +03:00
}
#[cfg(target_os = "linux")]
{
if let Ok(distribution) = &distribution {
distribution.show_summary();
}
}
2018-11-15 11:37:08 +02:00
#[cfg(target_os = "freebsd")]
2018-11-15 15:54:24 +02:00
freebsd::audit_packages(&sudo).ok();
2019-06-25 22:47:36 -07:00
#[cfg(target_os = "dragonfly")]
dragonfly::audit_packages(&sudo).ok();
2018-05-29 23:48:30 +03:00
}
2020-08-30 07:40:06 +03:00
let mut post_command_failed = false;
if let Some(commands) = config.post_commands() {
for (name, command) in commands {
if generic::run_custom_command(&name, &command, &ctx).is_err() {
post_command_failed = true;
}
}
}
2019-06-16 09:09:05 +03:00
if config.keep_at_end() {
2019-08-04 09:25:35 +03:00
print_info("\n(R)eboot\n(S)hell\n(Q)uit");
loop {
match get_char() {
's' | 'S' => {
run_shell();
}
'r' | 'R' => {
reboot();
}
'q' | 'Q' => (),
_ => {
continue;
}
2019-06-13 22:05:18 +03:00
}
2019-08-04 09:25:35 +03:00
break;
2019-06-13 22:05:18 +03:00
}
}
2020-08-30 07:40:06 +03:00
if post_command_failed || runner.report().data().iter().any(|(_, result)| result.failed()) {
Err(StepFailed.into())
} else {
Ok(())
}
}
fn main() {
match run() {
Ok(()) => {
exit(0);
}
Err(error) => {
#[cfg(all(windows, feature = "self-update"))]
{
if let Some(Upgraded(status)) = error.downcast_ref::<Upgraded>() {
exit(status.code().unwrap());
}
}
let skip_print = (error.downcast_ref::<StepFailed>().is_some())
|| (error
.downcast_ref::<io::Error>()
2018-12-11 16:43:26 +02:00
.filter(|io_error| io_error.kind() == io::ErrorKind::Interrupted)
.is_some());
2018-12-11 16:43:26 +02:00
if !skip_print {
2018-12-11 16:43:26 +02:00
println!("Error: {}", error);
}
exit(1);
}
}
2018-05-29 23:48:30 +03:00
}