2020-12-01 08:59:59 +02:00
|
|
|
#![allow(clippy::cognitive_complexity)]
|
2021-12-18 20:39:57 +02:00
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::process::exit;
|
|
|
|
|
|
2022-11-26 20:42:35 +01:00
|
|
|
use clap::CommandFactory;
|
2022-04-23 12:35:06 +02:00
|
|
|
use clap::{crate_version, Parser};
|
2022-11-11 09:39:29 -05:00
|
|
|
use color_eyre::eyre::Context;
|
|
|
|
|
use color_eyre::eyre::{eyre, Result};
|
2021-12-18 20:39:57 +02:00
|
|
|
use console::Key;
|
2022-11-16 13:43:57 -05:00
|
|
|
use tracing::debug;
|
2021-12-18 20:39:57 +02:00
|
|
|
|
|
|
|
|
use self::config::{CommandLineArgs, Config, Step};
|
|
|
|
|
use self::error::StepFailed;
|
|
|
|
|
#[cfg(all(windows, feature = "self-update"))]
|
|
|
|
|
use self::error::Upgraded;
|
|
|
|
|
use self::steps::{remote::*, *};
|
|
|
|
|
use self::terminal::*;
|
|
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
mod command;
|
2018-06-28 12:16:54 +03:00
|
|
|
mod config;
|
2018-10-17 14:07:58 +03:00
|
|
|
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;
|
2020-07-01 21:03:19 +03:00
|
|
|
#[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;
|
2022-11-24 14:15:43 -05:00
|
|
|
mod sudo;
|
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
|
|
|
|
2019-12-11 23:05:38 +02:00
|
|
|
fn run() -> Result<()> {
|
2022-11-11 09:39:29 -05:00
|
|
|
color_eyre::install()?;
|
2018-10-17 14:07:58 +03:00
|
|
|
ctrlc::set_handler();
|
|
|
|
|
|
2022-11-11 09:39:29 -05:00
|
|
|
let base_dirs = directories::BaseDirs::new().ok_or_else(|| eyre!("No base directories"))?;
|
2018-09-06 14:42:56 +03:00
|
|
|
|
2022-04-23 12:35:06 +02:00
|
|
|
let opt = CommandLineArgs::parse();
|
2022-05-07 08:11:53 +03:00
|
|
|
|
2022-11-26 20:42:35 +01:00
|
|
|
if let Some(shell) = opt.gen_completion {
|
|
|
|
|
let cmd = &mut CommandLineArgs::command();
|
|
|
|
|
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut std::io::stdout());
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opt.gen_manpage {
|
|
|
|
|
let man = clap_mangen::Man::new(CommandLineArgs::command());
|
|
|
|
|
man.render(&mut std::io::stdout())?;
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 13:43:57 -05:00
|
|
|
install_tracing(&opt.tracing_filter_directives())?;
|
|
|
|
|
|
2022-05-07 08:11:53 +03:00
|
|
|
for env in opt.env_variables() {
|
|
|
|
|
let mut splitted = env.split('=');
|
|
|
|
|
let var = splitted.next().unwrap();
|
|
|
|
|
let value = splitted.next().unwrap();
|
|
|
|
|
env::set_var(var, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 22:55:06 +02:00
|
|
|
if opt.edit_config() {
|
2019-08-22 22:29:31 +03:00
|
|
|
Config::edit(&base_dirs)?;
|
|
|
|
|
return Ok(());
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-30 10:01:22 +03:00
|
|
|
if opt.show_config_reference() {
|
|
|
|
|
print!("{}", crate::config::EXAMPLE_CONFIG);
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 22:55:06 +02:00
|
|
|
let config = Config::load(&base_dirs, opt)?;
|
|
|
|
|
terminal::set_title(config.set_title());
|
2022-03-29 03:42:47 +03:00
|
|
|
terminal::display_time(config.display_time());
|
2020-02-27 13:30:55 +02:00
|
|
|
terminal::set_desktop_notifications(config.notify_each_step());
|
2019-11-04 22:55:06 +02:00
|
|
|
|
2020-02-26 12:27:58 +02:00
|
|
|
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"));
|
|
|
|
|
|
2019-06-13 09:21:39 +03:00
|
|
|
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2022-11-15 10:30:26 -05:00
|
|
|
tmux::run_in_tmux(config.tmux_arguments()?)?;
|
|
|
|
|
return Ok(());
|
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
|
|
|
|
2022-11-25 17:19:32 -05:00
|
|
|
let sudo = sudo::Sudo::detect();
|
2019-01-22 22:37:32 +02:00
|
|
|
let run_type = executor::RunType::new(config.dry_run());
|
2018-06-11 08:29:40 +03:00
|
|
|
|
2022-11-25 17:19:32 -05:00
|
|
|
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);
|
|
|
|
|
|
2018-11-12 21:27:49 +02:00
|
|
|
#[cfg(feature = "self-update")]
|
|
|
|
|
{
|
2018-12-31 14:05:15 +02:00
|
|
|
if !run_type.dry() && env::var("TOPGRADE_NO_SELF_UPGRADE").is_err() {
|
2019-06-03 09:41:25 +03:00
|
|
|
let result = self_update::self_update();
|
|
|
|
|
|
2019-12-11 23:05:38 +02:00
|
|
|
if let Err(e) = &result {
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
{
|
|
|
|
|
if e.downcast_ref::<Upgraded>().is_some() {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-06-03 09:41:25 +03:00
|
|
|
}
|
2018-12-05 11:34:08 +02:00
|
|
|
print_warning(format!("Self update error: {}", e));
|
2018-11-12 21:27:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-07 14:31:44 +02:00
|
|
|
|
2020-07-01 21:03:19 +03: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 {
|
2021-09-02 06:18:01 +03:00
|
|
|
generic::run_custom_command(name, command, &ctx)?;
|
2018-06-20 20:26:08 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-24 14:15:43 -05:00
|
|
|
if config.pre_sudo() {
|
2022-11-25 17:19:32 -05:00
|
|
|
if let Some(sudo) = ctx.sudo() {
|
|
|
|
|
sudo.elevate(&ctx)?;
|
|
|
|
|
}
|
2022-11-24 14:15:43 -05:00
|
|
|
}
|
|
|
|
|
|
2019-08-14 21:36:57 +03:00
|
|
|
let powershell = powershell::Powershell::new();
|
2020-07-02 11:15:56 +03:00
|
|
|
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
|
2019-02-27 09:47:20 +02:00
|
|
|
|
2019-06-04 09:35:29 +03:00
|
|
|
#[cfg(windows)]
|
2020-08-21 21:10:54 +03:00
|
|
|
runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(&ctx))?;
|
2019-06-04 09:35:29 +03:00
|
|
|
|
2019-06-05 14:15:45 +03:00
|
|
|
if let Some(topgrades) = config.remote_topgrades() {
|
2020-08-02 06:29:16 +03:00
|
|
|
for remote_topgrade in topgrades.iter().filter(|t| config.should_execute_remote(t)) {
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Remotes, format!("Remote ({})", remote_topgrade), || {
|
2020-08-22 14:46:17 +03:00
|
|
|
remote::ssh::ssh_step(&ctx, remote_topgrade)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2019-06-05 14:15:45 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 10:46:38 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
let distribution = linux::Distribution::detect();
|
|
|
|
|
|
2022-10-23 11:34:30 +00:00
|
|
|
#[cfg(target_os = r#"linux"#)]
|
2018-08-22 22:18:48 +03:00
|
|
|
{
|
2020-07-02 11:15:56 +03:00
|
|
|
match &distribution {
|
|
|
|
|
Ok(distribution) => {
|
|
|
|
|
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
println!("Error detecting current distribution: {}", e);
|
2018-10-02 10:46:38 +03:00
|
|
|
}
|
2018-08-22 22:18:48 +03:00
|
|
|
}
|
2022-01-26 22:52:52 +02:00
|
|
|
runner.execute(Step::ConfigUpdate, "config-update", || linux::run_config_update(&ctx))?;
|
2020-12-16 13:43:38 +02:00
|
|
|
|
2020-12-30 10:35:50 +02:00
|
|
|
runner.execute(Step::BrewFormula, "Brew", || {
|
2022-05-13 20:59:13 +03:00
|
|
|
unix::run_brew_formula(&ctx, unix::BrewVariant::Path)
|
2020-12-30 10:35:50 +02:00
|
|
|
})?;
|
2018-06-28 07:47:51 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-22 22:18:48 +03:00
|
|
|
#[cfg(windows)]
|
2019-09-28 14:49:24 +03:00
|
|
|
{
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?;
|
|
|
|
|
runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?;
|
2021-06-03 13:08:17 +03:00
|
|
|
runner.execute(Step::Winget, "Winget", || windows::run_winget(&ctx))?;
|
2019-09-28 14:49:24 +03:00
|
|
|
}
|
2018-10-17 13:57:30 +03:00
|
|
|
|
2020-12-16 13:43:38 +02:00
|
|
|
#[cfg(target_os = "macos")]
|
2019-09-28 14:49:24 +03:00
|
|
|
{
|
2020-12-30 10:35:50 +02:00
|
|
|
runner.execute(Step::BrewFormula, "Brew (ARM)", || {
|
|
|
|
|
unix::run_brew_formula(&ctx, unix::BrewVariant::MacArm)
|
|
|
|
|
})?;
|
|
|
|
|
runner.execute(Step::BrewFormula, "Brew (Intel)", || {
|
|
|
|
|
unix::run_brew_formula(&ctx, unix::BrewVariant::MacIntel)
|
|
|
|
|
})?;
|
2022-05-13 20:59:13 +03:00
|
|
|
runner.execute(Step::BrewFormula, "Brew", || {
|
|
|
|
|
unix::run_brew_formula(&ctx, unix::BrewVariant::Path)
|
|
|
|
|
})?;
|
2020-12-30 10:35:50 +02:00
|
|
|
runner.execute(Step::BrewCask, "Brew Cask (ARM)", || {
|
|
|
|
|
unix::run_brew_cask(&ctx, unix::BrewVariant::MacArm)
|
2020-12-16 13:43:38 +02:00
|
|
|
})?;
|
2020-12-30 10:35:50 +02:00
|
|
|
runner.execute(Step::BrewCask, "Brew Cask (Intel)", || {
|
|
|
|
|
unix::run_brew_cask(&ctx, unix::BrewVariant::MacIntel)
|
2020-12-16 13:43:38 +02:00
|
|
|
})?;
|
2022-05-13 20:59:13 +03:00
|
|
|
runner.execute(Step::BrewCask, "Brew Cask", || {
|
|
|
|
|
unix::run_brew_cask(&ctx, unix::BrewVariant::Path)
|
|
|
|
|
})?;
|
2021-05-04 11:27:19 +03:00
|
|
|
runner.execute(Step::Macports, "MacPorts", || macos::run_macports(&ctx))?;
|
2020-12-16 13:43:38 +02:00
|
|
|
}
|
2020-06-24 14:30:24 +03:00
|
|
|
|
2020-12-16 13:43:38 +02:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2020-07-11 08:00:35 +03:00
|
|
|
runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?;
|
2022-10-10 18:03:34 +00:00
|
|
|
runner.execute(Step::Guix, "guix", || unix::run_guix(&ctx))?;
|
|
|
|
|
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(run_type))?;
|
|
|
|
|
runner.execute(Step::Asdf, "asdf", || unix::run_asdf(run_type))?;
|
2021-09-02 06:14:56 +03:00
|
|
|
runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?;
|
2022-10-10 20:16:13 +00:00
|
|
|
runner.execute(Step::Bun, "bun", || unix::run_bun(&ctx))?;
|
2019-09-28 14:49:24 +03:00
|
|
|
}
|
|
|
|
|
|
2019-06-25 22:47:36 -07:00
|
|
|
#[cfg(target_os = "dragonfly")]
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Pkg, "DragonFly BSD Packages", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
dragonfly::upgrade_packages(ctx.sudo().as_ref(), run_type)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2019-09-28 14:49:24 +03:00
|
|
|
|
2018-11-12 11:13:43 +02:00
|
|
|
#[cfg(target_os = "freebsd")]
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Pkg, "FreeBSD Packages", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
freebsd::upgrade_packages(&ctx, ctx.sudo().as_ref(), run_type)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2018-08-19 14:45:23 +03:00
|
|
|
|
2022-11-02 11:00:10 +01:00
|
|
|
#[cfg(target_os = "openbsd")]
|
|
|
|
|
runner.execute(Step::Pkg, "OpenBSD Packages", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
openbsd::upgrade_packages(ctx.sudo().as_ref(), run_type)
|
2022-11-02 11:00:10 +01:00
|
|
|
})?;
|
|
|
|
|
|
2021-09-02 16:54:31 +00:00
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
|
runner.execute(Step::Pkg, "Termux Packages", || android::upgrade_packages(&ctx))?;
|
|
|
|
|
|
2019-03-12 08:54:19 +02:00
|
|
|
let emacs = emacs::Emacs::new(&base_dirs);
|
2020-01-28 21:06:33 +02:00
|
|
|
if config.use_predefined_git_repos() {
|
2020-01-28 21:04:59 +02:00
|
|
|
if config.should_run(Step::Emacs) {
|
2020-01-29 21:31:14 +02:00
|
|
|
if !emacs.is_doom() {
|
|
|
|
|
if let Some(directory) = emacs.directory() {
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(directory);
|
2020-01-29 21:31:14 +02:00
|
|
|
}
|
2020-01-28 21:04:59 +02:00
|
|
|
}
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".doom.d"));
|
2019-01-27 20:04:46 +02:00
|
|
|
}
|
2018-09-05 11:17:15 +03:00
|
|
|
|
2020-01-28 21:04:59 +02:00
|
|
|
if config.should_run(Step::Vim) {
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".vim"));
|
|
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".config/nvim"));
|
2020-01-28 21:04:59 +02:00
|
|
|
}
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2021-05-30 15:50:21 +03:00
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".ideavimrc"));
|
|
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".intellimacs"));
|
|
|
|
|
|
2022-10-31 02:05:12 -04:00
|
|
|
if config.should_run(Step::Rcm) {
|
|
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".dotfiles"));
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-28 21:04:59 +02:00
|
|
|
#[cfg(unix)]
|
|
|
|
|
{
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(zsh::zshrc(&base_dirs));
|
2020-05-12 08:51:42 +03:00
|
|
|
if config.should_run(Step::Tmux) {
|
|
|
|
|
git_repos.insert_if_repo(base_dirs.home_dir().join(".tmux"));
|
|
|
|
|
}
|
2020-03-04 10:32:58 +02:00
|
|
|
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"));
|
2020-01-28 21:04:59 +02:00
|
|
|
}
|
2018-05-30 07:53:19 +03:00
|
|
|
|
2020-01-28 21:04:59 +02:00
|
|
|
#[cfg(windows)]
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(
|
2020-01-28 21:04:59 +02:00
|
|
|
base_dirs
|
|
|
|
|
.data_local_dir()
|
|
|
|
|
.join("Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState"),
|
|
|
|
|
);
|
|
|
|
|
|
2021-02-02 22:28:22 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
|
windows::insert_startup_scripts(&ctx, &mut git_repos).ok();
|
|
|
|
|
|
2020-01-28 21:04:59 +02:00
|
|
|
if let Some(profile) = powershell.profile() {
|
2020-03-04 10:32:58 +02:00
|
|
|
git_repos.insert_if_repo(profile);
|
2020-01-28 21:04:59 +02:00
|
|
|
}
|
2018-08-23 22:08:04 +03:00
|
|
|
}
|
|
|
|
|
|
2019-01-22 22:37:32 +02:00
|
|
|
if config.should_run(Step::GitRepos) {
|
2018-09-03 11:51:09 -04:00
|
|
|
if let Some(custom_git_repos) = config.git_repos() {
|
|
|
|
|
for git_repo in custom_git_repos {
|
2019-08-11 12:41:09 +03:00
|
|
|
git_repos.glob_insert(git_repo);
|
2018-09-03 11:51:09 -04:00
|
|
|
}
|
2018-05-30 07:53:19 +03:00
|
|
|
}
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::GitRepos, "Git repositories", || {
|
|
|
|
|
git.multi_pull_step(&git_repos, &ctx)
|
|
|
|
|
})?;
|
2018-05-30 07:53:19 +03:00
|
|
|
}
|
2018-05-31 15:59:45 +03:00
|
|
|
|
2019-08-14 21:36:57 +03:00
|
|
|
if should_run_powershell {
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Powershell, "Powershell Modules Update", || {
|
2020-07-15 08:47:15 +03:00
|
|
|
powershell.update_modules(&ctx)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2019-11-04 23:04:35 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-19 08:47:01 +02:00
|
|
|
#[cfg(unix)]
|
2019-11-04 23:04:35 +02:00
|
|
|
{
|
2020-07-02 11:15:56 +03:00
|
|
|
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))?;
|
2022-02-18 15:01:34 +01:00
|
|
|
runner.execute(Step::Shell, "zgenom", || zsh::run_zgenom(&base_dirs, run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Shell, "zplug", || zsh::run_zplug(&base_dirs, run_type))?;
|
|
|
|
|
runner.execute(Step::Shell, "zinit", || zsh::run_zinit(&base_dirs, run_type))?;
|
2022-05-07 00:37:28 +09:00
|
|
|
runner.execute(Step::Shell, "zi", || zsh::run_zi(&base_dirs, run_type))?;
|
2020-10-24 14:46:38 -05:00
|
|
|
runner.execute(Step::Shell, "zim", || zsh::run_zim(&base_dirs, run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?;
|
2022-11-14 02:49:46 -05:00
|
|
|
runner.execute(Step::Shell, "fisher", || unix::run_fisher(run_type))?;
|
2021-02-10 08:58:03 +02:00
|
|
|
runner.execute(Step::Shell, "bash-it", || unix::run_bashit(&ctx))?;
|
2020-09-03 09:32:45 +03:00
|
|
|
runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?;
|
2021-04-12 04:14:14 -05:00
|
|
|
runner.execute(Step::Shell, "fish-plug", || unix::run_fish_plug(&ctx))?;
|
2022-10-31 02:05:43 -04:00
|
|
|
runner.execute(Step::Shell, "fundle", || unix::run_fundle(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
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))?;
|
2022-04-23 01:50:17 +05:30
|
|
|
#[cfg(not(any(target_os = "macos", target_os = "android")))]
|
2021-10-25 21:31:13 +03:00
|
|
|
runner.execute(Step::GnomeShellExtensions, "Gnome Shell Extensions", || {
|
|
|
|
|
unix::upgrade_gnome_extensions(&ctx)
|
|
|
|
|
})?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Sdkman, "SDKMAN!", || {
|
|
|
|
|
unix::run_sdkman(&base_dirs, config.cleanup(), run_type)
|
|
|
|
|
})?;
|
2022-10-31 02:05:12 -04:00
|
|
|
runner.execute(Step::Rcm, "rcm", || unix::run_rcm(&ctx))?;
|
2020-06-29 05:13:31 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-29 14:32:33 +02:00
|
|
|
#[cfg(not(any(
|
|
|
|
|
target_os = "freebsd",
|
|
|
|
|
target_os = "openbsd",
|
|
|
|
|
target_os = "netbsd",
|
|
|
|
|
target_os = "dragonfly"
|
|
|
|
|
)))]
|
2021-02-17 19:41:52 +02:00
|
|
|
runner.execute(Step::Atom, "apm", || generic::run_apm(run_type))?;
|
2021-02-17 16:46:29 +02:00
|
|
|
runner.execute(Step::Fossil, "fossil", || generic::run_fossil(run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&base_dirs, run_type))?;
|
2022-11-24 14:17:58 -05:00
|
|
|
runner.execute(Step::Juliaup, "juliaup", || generic::run_juliaup(&base_dirs, run_type))?;
|
2020-12-26 09:21:14 +02:00
|
|
|
runner.execute(Step::Dotnet, ".NET", || generic::run_dotnet_upgrade(&ctx))?;
|
2020-08-28 16:16:23 +03:00
|
|
|
runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?;
|
2021-03-22 09:00:56 +02:00
|
|
|
runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(run_type))?;
|
2022-11-19 19:04:33 -05:00
|
|
|
runner.execute(Step::Go, "go-global-update", || go::run_go_global_update(run_type))?;
|
|
|
|
|
runner.execute(Step::Go, "gup", || go::run_go_gup(run_type))?;
|
2021-11-18 20:45:34 +02:00
|
|
|
runner.execute(Step::Emacs, "Emacs", || emacs.upgrade(&ctx))?;
|
2022-10-10 20:24:41 +00:00
|
|
|
runner.execute(Step::Opam, "opam", || generic::run_opam_update(&ctx))?;
|
2022-12-15 11:39:25 +00:00
|
|
|
runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(run_type))?;
|
2022-01-15 06:50:42 +02:00
|
|
|
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
|
2021-04-12 11:04:51 +03:00
|
|
|
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(run_type))?;
|
2022-10-30 12:34:14 -04:00
|
|
|
runner.execute(Step::Ghcup, "ghcup", || generic::run_ghcup_update(run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
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)
|
|
|
|
|
})?;
|
2021-07-19 07:29:34 +02:00
|
|
|
runner.execute(Step::Chezmoi, "chezmoi", || {
|
|
|
|
|
generic::run_chezmoi_update(&base_dirs, run_type)
|
|
|
|
|
})?;
|
2020-09-23 18:31:11 +08:00
|
|
|
runner.execute(Step::Jetpack, "jetpack", || generic::run_jetpack(run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Vim, "vim", || vim::upgrade_vim(&base_dirs, &ctx))?;
|
|
|
|
|
runner.execute(Step::Vim, "Neovim", || vim::upgrade_neovim(&base_dirs, &ctx))?;
|
2022-04-23 15:39:58 +03:00
|
|
|
runner.execute(Step::Vim, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Vim, "voom", || vim::run_voom(&base_dirs, run_type))?;
|
2022-02-06 18:48:06 -03:00
|
|
|
runner.execute(Step::Kakoune, "Kakoune", || kakoune::upgrade_kak_plug(&ctx))?;
|
2022-12-14 09:12:39 +01:00
|
|
|
runner.execute(Step::Helix, "helix", || generic::run_helix_grammars(&ctx))?;
|
2021-06-09 10:52:48 +03:00
|
|
|
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
|
2022-12-15 00:42:48 +03:00
|
|
|
runner.execute(Step::Yarn, "yarn", || node::run_yarn_upgrade(&ctx))?;
|
|
|
|
|
runner.execute(Step::Pnpm, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
|
2022-03-05 20:59:19 +01:00
|
|
|
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
|
2020-10-01 15:59:06 -04:00
|
|
|
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
|
|
|
|
|
runner.execute(Step::Krew, "krew", || generic::run_krew_upgrade(run_type))?;
|
2022-12-08 22:47:57 +01:00
|
|
|
runner.execute(Step::Helm, "helm", || generic::run_helm_repo_update(run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Gem, "gem", || generic::run_gem(&base_dirs, run_type))?;
|
2022-11-24 20:21:03 +01:00
|
|
|
runner.execute(Step::RubyGems, "rubygems", || {
|
|
|
|
|
generic::run_rubygems(&base_dirs, run_type)
|
|
|
|
|
})?;
|
2022-10-10 18:29:56 +00:00
|
|
|
runner.execute(Step::Julia, "julia", || generic::update_julia_packages(&ctx))?;
|
2021-10-25 12:54:27 -06:00
|
|
|
runner.execute(Step::Haxelib, "haxelib", || generic::run_haxelib_update(&ctx))?;
|
2020-07-30 06:27:29 +03:00
|
|
|
runner.execute(Step::Sheldon, "sheldon", || generic::run_sheldon(&ctx))?;
|
2020-09-25 01:00:06 +08:00
|
|
|
runner.execute(Step::Rtcl, "rtcl", || generic::run_rtcl(&ctx))?;
|
2021-05-29 01:59:27 -03:00
|
|
|
runner.execute(Step::Bin, "bin", || generic::bin_update(&ctx))?;
|
2020-11-15 20:07:42 -08:00
|
|
|
runner.execute(Step::Gcloud, "gcloud", || {
|
|
|
|
|
generic::run_gcloud_components_update(run_type)
|
|
|
|
|
})?;
|
2021-02-15 20:43:36 +01:00
|
|
|
runner.execute(Step::Micro, "micro", || generic::run_micro(run_type))?;
|
2021-03-14 13:12:38 +03:00
|
|
|
runner.execute(Step::Raco, "raco", || generic::run_raco_update(run_type))?;
|
2021-11-15 07:09:02 +01:00
|
|
|
runner.execute(Step::Spicetify, "spicetify", || generic::spicetify_upgrade(&ctx))?;
|
2022-04-03 17:25:07 -04:00
|
|
|
runner.execute(Step::GithubCliExtensions, "GitHub CLI Extensions", || {
|
2022-03-08 00:16:52 +01:00
|
|
|
generic::run_ghcli_extensions_upgrade(&ctx)
|
|
|
|
|
})?;
|
2018-06-06 11:27:43 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2022-05-07 09:14:20 +03:00
|
|
|
runner.execute(Step::DebGet, "deb-get", || linux::run_deb_get(&ctx))?;
|
2022-01-24 11:10:21 +01:00
|
|
|
runner.execute(Step::Toolbx, "toolbx", || toolbx::run_toolbx(&ctx))?;
|
2021-06-30 12:37:41 +02:00
|
|
|
runner.execute(Step::Flatpak, "Flatpak", || linux::flatpak_update(&ctx))?;
|
2022-11-25 17:19:32 -05:00
|
|
|
runner.execute(Step::Snap, "snap", || linux::run_snap(ctx.sudo().as_ref(), run_type))?;
|
2021-10-25 21:33:58 +03:00
|
|
|
runner.execute(Step::Pacstall, "pacstall", || linux::run_pacstall(&ctx))?;
|
2022-10-10 20:09:24 +00:00
|
|
|
runner.execute(Step::Pacdef, "pacdef", || linux::run_pacdef(&ctx))?;
|
2022-10-14 16:46:21 +02:00
|
|
|
runner.execute(Step::Protonup, "protonup", || linux::run_protonup_update(&ctx))?;
|
2022-10-27 00:12:38 +02:00
|
|
|
runner.execute(Step::Distrobox, "distrobox", || linux::run_distrobox_update(&ctx))?;
|
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 {
|
2022-05-07 15:25:51 +03:00
|
|
|
if config.should_run_custom_command(name) {
|
|
|
|
|
runner.execute(Step::CustomCommands, name, || {
|
|
|
|
|
generic::run_custom_command(name, command, &ctx)
|
|
|
|
|
})?;
|
|
|
|
|
}
|
2018-06-11 08:38:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
{
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::System, "pihole", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
linux::run_pihole_update(ctx.sudo().as_ref(), run_type)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2021-03-23 06:33:30 +02:00
|
|
|
runner.execute(Step::Firmware, "Firmware upgrades", || linux::run_fwupdmgr(&ctx))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Restarts, "Restarts", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
linux::run_needrestart(ctx.sudo().as_ref(), run_type)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2018-05-31 09:19:27 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
{
|
2022-06-17 11:10:21 +03:00
|
|
|
runner.execute(Step::Sparkle, "Sparkle", || macos::run_sparkle(&ctx))?;
|
2021-04-06 09:52:55 +03:00
|
|
|
runner.execute(Step::Mas, "App Store", || macos::run_mas(run_type))?;
|
2020-07-02 11:15:56 +03:00
|
|
|
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")]
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::System, "FreeBSD Upgrade", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
freebsd::upgrade_freebsd(ctx.sudo().as_ref(), run_type)
|
2020-07-02 11:15:56 +03:00
|
|
|
})?;
|
2018-11-12 11:13:43 +02:00
|
|
|
|
2022-11-02 11:00:10 +01:00
|
|
|
#[cfg(target_os = "openbsd")]
|
|
|
|
|
runner.execute(Step::System, "OpenBSD Upgrade", || {
|
2022-11-25 17:19:32 -05:00
|
|
|
openbsd::upgrade_openbsd(ctx.sudo().as_ref(), run_type)
|
2022-11-02 11:00:10 +01:00
|
|
|
})?;
|
|
|
|
|
|
2018-08-22 22:18:48 +03:00
|
|
|
#[cfg(windows)]
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
|
2019-06-13 12:19:47 +02:00
|
|
|
|
2020-06-10 11:51:52 +03:00
|
|
|
if config.should_run(Step::Vagrant) {
|
2020-06-16 21:02:50 +03:00
|
|
|
if let Ok(boxes) = vagrant::collect_boxes(&ctx) {
|
|
|
|
|
for vagrant_box in boxes {
|
2020-07-02 11:15:56 +03:00
|
|
|
runner.execute(Step::Vagrant, format!("Vagrant ({})", vagrant_box.smart_name()), || {
|
2020-06-16 21:02:50 +03:00
|
|
|
vagrant::topgrade_vagrant_box(&ctx, &vagrant_box)
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-10 11:51:52 +03:00
|
|
|
}
|
2022-04-23 15:09:54 +03:00
|
|
|
runner.execute(Step::Vagrant, "Vagrant boxes", || vagrant::upgrade_vagrant_boxes(&ctx))?;
|
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
|
|
|
|
2020-07-02 11:15:56 +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
|
|
|
}
|
2018-10-02 11:36:10 +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")]
|
2022-11-25 17:19:32 -05:00
|
|
|
freebsd::audit_packages(ctx.sudo().as_ref()).ok();
|
2019-06-25 22:47:36 -07:00
|
|
|
|
|
|
|
|
#[cfg(target_os = "dragonfly")]
|
2022-11-25 17:19:32 -05:00
|
|
|
dragonfly::audit_packages(ctx.sudo().as_ref()).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 {
|
2021-09-02 06:18:01 +03:00
|
|
|
if generic::run_custom_command(name, command, &ctx).is_err() {
|
2020-08-30 07:40:06 +03:00
|
|
|
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 {
|
2021-02-13 06:26:50 +02:00
|
|
|
match get_key() {
|
2021-10-28 22:05:35 +03:00
|
|
|
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
|
2022-11-08 05:54:35 -05:00
|
|
|
run_shell().context("Failed to execute shell")?;
|
2021-10-28 22:05:35 +03:00
|
|
|
}
|
|
|
|
|
Ok(Key::Char('r')) | Ok(Key::Char('R')) => {
|
2022-11-08 05:54:35 -05:00
|
|
|
reboot().context("Failed to reboot")?;
|
2021-10-28 22:05:35 +03:00
|
|
|
}
|
|
|
|
|
Ok(Key::Char('q')) | Ok(Key::Char('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
|
|
|
}
|
2019-06-13 09:21:39 +03:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 11:31:09 +02:00
|
|
|
let failed = post_command_failed || runner.report().data().iter().any(|(_, result)| result.failed());
|
2022-10-10 20:21:20 +00:00
|
|
|
|
|
|
|
|
if !config.skip_notify() {
|
|
|
|
|
terminal::notify_desktop(
|
|
|
|
|
format!(
|
|
|
|
|
"Topgrade finished {}",
|
|
|
|
|
if failed { "with errors" } else { "successfully" }
|
|
|
|
|
),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-04 11:31:09 +02:00
|
|
|
if failed {
|
2019-12-11 23:05:38 +02:00
|
|
|
Err(StepFailed.into())
|
2020-07-02 11:15:56 +03:00
|
|
|
} else {
|
|
|
|
|
Ok(())
|
2018-06-11 08:57:55 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
match run() {
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
Err(error) => {
|
2019-06-03 09:41:25 +03:00
|
|
|
#[cfg(all(windows, feature = "self-update"))]
|
|
|
|
|
{
|
2019-12-11 23:05:38 +02:00
|
|
|
if let Some(Upgraded(status)) = error.downcast_ref::<Upgraded>() {
|
2019-06-03 09:41:25 +03:00
|
|
|
exit(status.code().unwrap());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 23:05:38 +02:00
|
|
|
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)
|
2019-12-11 23:05:38 +02:00
|
|
|
.is_some());
|
2018-12-11 16:43:26 +02:00
|
|
|
|
2019-12-11 23:05:38 +02:00
|
|
|
if !skip_print {
|
2022-11-11 09:39:29 -05:00
|
|
|
// The `Debug` implementation of `eyre::Result` prints a multi-line
|
2022-11-03 12:46:43 -04:00
|
|
|
// error message that includes all the 'causes' added with
|
|
|
|
|
// `.with_context(...)` calls.
|
|
|
|
|
println!("Error: {:?}", error);
|
2018-10-17 14:07:58 +03:00
|
|
|
}
|
2018-06-11 08:57:55 +03:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-29 23:48:30 +03:00
|
|
|
}
|
2022-11-16 13:43:57 -05:00
|
|
|
|
|
|
|
|
pub fn install_tracing(filter_directives: &str) -> Result<()> {
|
|
|
|
|
use tracing_subscriber::fmt;
|
|
|
|
|
use tracing_subscriber::fmt::format::FmtSpan;
|
|
|
|
|
use tracing_subscriber::layer::SubscriberExt;
|
|
|
|
|
use tracing_subscriber::util::SubscriberInitExt;
|
|
|
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
|
|
|
|
|
|
let env_filter = EnvFilter::try_new(filter_directives)
|
|
|
|
|
.or_else(|_| EnvFilter::try_from_default_env())
|
|
|
|
|
.or_else(|_| EnvFilter::try_new("info"))?;
|
|
|
|
|
|
|
|
|
|
let fmt_layer = fmt::layer()
|
|
|
|
|
.with_target(false)
|
|
|
|
|
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
|
|
|
|
.without_time();
|
|
|
|
|
|
|
|
|
|
let registry = tracing_subscriber::registry();
|
|
|
|
|
|
|
|
|
|
registry.with(env_filter).with(fmt_layer).init();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|