2022-04-23 12:35:06 +02:00
#![ allow(dead_code) ]
2023-05-25 12:22:11 +02:00
2022-11-08 05:54:35 -05:00
use std ::collections ::BTreeMap ;
2023-05-25 12:22:11 +02:00
use std ::fs ::{ write , File } ;
use std ::io ::Write ;
use std ::path ::{ Path , PathBuf } ;
2022-11-08 05:54:35 -05:00
use std ::process ::Command ;
use std ::{ env , fs } ;
2022-11-23 15:23:00 +00:00
use clap ::{ ArgEnum , Parser } ;
2022-11-26 20:42:35 +01:00
use clap_complete ::Shell ;
2022-11-11 09:39:29 -05:00
use color_eyre ::eyre ::Context ;
use color_eyre ::eyre ::Result ;
2023-05-01 00:02:13 +05:30
use etcetera ::base_strategy ::BaseStrategy ;
2023-05-25 12:22:11 +02:00
use merge ::Merge ;
2022-11-23 15:23:00 +00:00
use regex ::Regex ;
2023-05-25 12:22:11 +02:00
use regex_split ::RegexSplit ;
2022-11-23 15:23:00 +00:00
use serde ::Deserialize ;
2022-04-23 12:35:06 +02:00
use strum ::{ EnumIter , EnumString , EnumVariantNames , IntoEnumIterator } ;
2022-11-16 13:43:57 -05:00
use tracing ::debug ;
2020-06-24 08:59:06 +03:00
use which_crate ::which ;
2018-06-08 18:19:07 +03:00
2022-11-08 05:54:35 -05:00
use crate ::command ::CommandExt ;
2023-03-13 19:23:37 +00:00
use crate ::sudo ::SudoKind ;
2023-05-25 12:22:11 +02:00
use crate ::utils ::string_prepend_str ;
2022-11-08 05:54:35 -05:00
2022-11-16 20:05:20 -03:00
use super ::utils ::{ editor , hostname } ;
2021-12-06 13:31:05 +02:00
2020-06-30 10:01:22 +03:00
pub static EXAMPLE_CONFIG : & str = include_str! ( " ../config.example.toml " ) ;
2020-06-19 10:34:45 +03:00
#[ allow(unused_macros) ]
macro_rules ! str_value {
( $section :ident , $value :ident ) = > {
pub fn $value ( & self ) -> Option < & str > {
self . config_file
. $section
. as_ref ( )
. and_then ( | section | section . $value . as_deref ( ) )
}
} ;
}
2020-06-07 07:11:44 +03:00
macro_rules ! check_deprecated {
( $config :expr , $old :ident , $section :ident , $new :ident ) = > {
if $config . $old . is_some ( ) {
println! ( concat! (
" ' " ,
stringify! ( $old ) ,
" ' configuration option is deprecated. Rename it to ' " ,
stringify! ( $new ) ,
" ' and put it under the section [ " ,
stringify! ( $section ) ,
" ] " ,
) ) ;
}
} ;
}
2023-05-25 12:22:11 +02:00
/// Get a deprecated option moved from a section to another
macro_rules ! get_deprecated_moved_opt {
( $old_section :expr , $old :ident , $new_section :expr , $new :ident ) = > { {
if let Some ( old_section ) = & $old_section {
if old_section . $old . is_some ( ) {
return & old_section . $old ;
2020-06-07 07:11:44 +03:00
}
}
2023-05-25 12:22:11 +02:00
if let Some ( new_section ) = & $new_section {
return & new_section . $new ;
}
return & None ;
} } ;
}
macro_rules ! get_deprecated_moved_or_default_to {
( $old_section :expr , $old :ident , $new_section :expr , $new :ident , $default_ret :ident ) = > { {
if let Some ( old_section ) = & $old_section {
if let Some ( old ) = old_section . $old {
return old ;
}
}
if let Some ( new_section ) = & $new_section {
if let Some ( new ) = new_section . $new {
return new ;
}
}
return $default_ret ;
} } ;
2020-06-07 07:11:44 +03:00
}
2023-05-25 12:22:11 +02:00
pub type Commands = BTreeMap < String , String > ;
2018-06-20 20:26:08 +03:00
2022-11-02 15:28:47 +00:00
#[ derive(ArgEnum, EnumString, EnumVariantNames, Debug, Clone, PartialEq, Eq, Deserialize, EnumIter, Copy) ]
2022-04-23 12:35:06 +02:00
#[ clap(rename_all = " snake_case " ) ]
2020-06-30 11:36:34 +03:00
#[ serde(rename_all = " snake_case " ) ]
2019-09-28 14:41:06 +03:00
#[ strum(serialize_all = " snake_case " ) ]
2019-01-16 08:51:43 +00:00
pub enum Step {
2023-01-29 19:19:27 +00:00
AM ,
2023-05-09 14:03:06 +08:00
AppMan ,
2020-11-16 14:28:29 +02:00
Asdf ,
Atom ,
2022-12-08 22:47:57 +01:00
Bin ,
2023-06-23 17:03:57 +08:00
Bob ,
2020-12-30 10:35:50 +02:00
BrewCask ,
BrewFormula ,
2022-10-10 20:16:13 +00:00
Bun ,
2020-11-16 14:28:29 +02:00
Cargo ,
2021-07-19 07:29:34 +02:00
Chezmoi ,
2020-11-16 14:28:29 +02:00
Chocolatey ,
Choosenim ,
Composer ,
2022-01-15 06:50:42 +02:00
Conda ,
2022-01-26 22:52:52 +02:00
ConfigUpdate ,
2022-03-05 20:59:19 +01:00
Containers ,
2020-11-16 14:28:29 +02:00
CustomCommands ,
2022-05-07 09:14:20 +03:00
DebGet ,
2020-11-16 14:28:29 +02:00
Deno ,
2022-12-08 22:47:57 +01:00
Distrobox ,
2023-01-29 19:19:27 +00:00
DkpPacman ,
2020-12-26 06:43:45 +02:00
Dotnet ,
2019-01-16 08:51:43 +00:00
Emacs ,
2020-11-16 14:28:29 +02:00
Firmware ,
Flatpak ,
Flutter ,
2021-02-17 16:46:29 +02:00
Fossil ,
2020-11-16 14:28:29 +02:00
Gcloud ,
2019-01-16 08:51:43 +00:00
Gem ,
2022-10-30 12:34:14 -04:00
Ghcup ,
2022-03-08 00:16:52 +01:00
GithubCliExtensions ,
2020-11-16 14:28:29 +02:00
GitRepos ,
2022-12-08 22:47:57 +01:00
GnomeShellExtensions ,
2022-01-16 16:24:57 -04:00
Go ,
2022-10-10 18:03:34 +00:00
Guix ,
2021-10-25 12:54:27 -06:00
Haxelib ,
2022-12-08 22:47:57 +01:00
Helm ,
2020-11-16 14:28:29 +02:00
HomeManager ,
Jetpack ,
2022-10-10 18:29:56 +00:00
Julia ,
2022-11-24 14:17:58 -05:00
Juliaup ,
2022-02-06 18:48:06 -03:00
Kakoune ,
2022-12-14 09:12:39 +01:00
Helix ,
2020-06-29 05:13:31 +02:00
Krew ,
2021-05-04 11:27:19 +03:00
Macports ,
2023-04-17 16:19:59 +02:00
Mamba ,
2023-08-13 04:05:07 +02:00
Miktex ,
2021-04-06 09:52:55 +03:00
Mas ,
2023-05-18 01:18:03 +08:00
Maza ,
2021-02-15 20:43:36 +01:00
Micro ,
2020-11-16 14:28:29 +02:00
Myrepos ,
Nix ,
Node ,
Opam ,
2022-10-10 20:09:24 +00:00
Pacdef ,
2021-10-25 21:33:58 +03:00
Pacstall ,
2020-11-16 14:28:29 +02:00
Pearl ,
2021-03-02 10:39:49 +01:00
Pip3 ,
2023-01-29 19:19:27 +00:00
PipReview ,
2023-05-20 23:03:59 +05:30
PipReviewLocal ,
2023-01-29 19:19:27 +00:00
Pipupgrade ,
2022-12-08 22:47:57 +01:00
Pipx ,
2020-11-16 14:28:29 +02:00
Pkg ,
2021-09-02 06:14:56 +03:00
Pkgin ,
2022-12-15 00:42:48 +03:00
Pnpm ,
2020-11-16 14:28:29 +02:00
Powershell ,
2022-10-14 16:46:21 +02:00
Protonup ,
2021-03-14 13:12:38 +03:00
Raco ,
2022-10-31 02:05:12 -04:00
Rcm ,
2019-07-01 08:53:53 +03:00
Remotes ,
2020-11-16 14:28:29 +02:00
Restarts ,
Rtcl ,
2022-11-24 20:21:03 +01:00
RubyGems ,
2019-08-19 20:15:01 +03:00
Rustup ,
2020-11-16 14:28:29 +02:00
Scoop ,
Sdkman ,
Sheldon ,
2019-09-05 13:38:45 -04:00
Shell ,
2020-11-16 14:28:29 +02:00
Snap ,
2022-06-17 11:10:21 +03:00
Sparkle ,
2021-11-15 07:09:02 +01:00
Spicetify ,
2019-11-04 23:04:35 +02:00
Stack ,
2023-05-05 16:17:42 +08:00
Stew ,
2020-11-16 14:28:29 +02:00
System ,
2020-01-05 22:56:18 +02:00
Tldr ,
2020-11-16 14:28:29 +02:00
Tlmgr ,
2020-05-12 08:51:42 +03:00
Tmux ,
2022-01-24 11:10:21 +01:00
Toolbx ,
2020-06-10 11:51:52 +03:00
Vagrant ,
2020-11-16 14:28:29 +02:00
Vcpkg ,
Vim ,
2021-06-03 13:08:17 +03:00
Winget ,
2020-11-16 14:28:29 +02:00
Wsl ,
2023-01-29 19:19:27 +00:00
WslUpdate ,
2020-07-11 08:00:35 +03:00
Yadm ,
2022-12-15 00:42:48 +03:00
Yarn ,
2019-01-16 08:51:43 +00:00
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Include {
2023-06-01 03:15:49 -04:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
paths : Option < Vec < String > > ,
2023-05-25 12:22:11 +02:00
}
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-06-06 17:34:27 +03:00
pub struct Git {
max_concurrency : Option < usize > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-06-07 07:11:44 +03:00
arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2020-06-07 07:11:44 +03:00
repos : Option < Vec < String > > ,
2023-05-25 12:22:11 +02:00
2020-06-07 07:11:44 +03:00
pull_predefined : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-06-10 11:51:52 +03:00
pub struct Vagrant {
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2020-06-10 11:51:52 +03:00
directories : Option < Vec < String > > ,
2023-05-25 12:22:11 +02:00
2020-06-10 11:51:52 +03:00
power_on : Option < bool > ,
2020-06-13 07:44:53 +03:00
always_suspend : Option < bool > ,
2020-06-10 11:51:52 +03:00
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-06-07 07:11:44 +03:00
pub struct Windows {
accept_all_updates : Option < bool > ,
2020-07-01 21:03:19 +03:00
self_rename : Option < bool > ,
2020-08-23 09:12:40 +03:00
open_remotes_in_new_terminal : Option < bool > ,
2022-03-05 21:58:36 +02:00
enable_winget : Option < bool > ,
2023-01-29 19:19:27 +00:00
wsl_update_pre_release : Option < bool > ,
wsl_update_use_web_download : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2023-01-29 19:19:27 +00:00
#[ serde(deny_unknown_fields) ]
pub struct Python {
enable_pip_review : Option < bool > ,
2023-05-20 23:03:59 +05:30
enable_pip_review_local : Option < bool > ,
2023-01-29 19:19:27 +00:00
enable_pipupgrade : Option < bool > ,
2023-05-30 03:04:23 -04:00
pipupgrade_arguments : Option < String > ,
2020-06-06 17:34:27 +03:00
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2022-10-28 00:29:35 +02:00
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Distrobox {
use_root : Option < bool > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2022-10-28 00:29:35 +02:00
containers : Option < Vec < String > > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2022-10-10 20:08:11 +00:00
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Yarn {
use_sudo : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2021-06-09 10:52:48 +03:00
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct NPM {
use_sudo : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2021-06-30 11:15:18 +02:00
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Firmware {
upgrade : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2021-06-30 12:37:41 +02:00
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Flatpak {
use_sudo : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-05-12 09:07:54 +03:00
pub struct Brew {
greedy_cask : Option < bool > ,
2022-10-10 20:17:50 +00:00
autoremove : Option < bool > ,
2020-05-12 09:07:54 +03:00
}
2021-10-25 22:03:57 +03:00
#[ derive(Debug, Deserialize, Clone, Copy) ]
2021-10-28 18:46:59 +02:00
#[ serde(rename_all = " snake_case " ) ]
2021-10-25 22:03:57 +03:00
pub enum ArchPackageManager {
Autodetect ,
2023-01-29 19:19:27 +00:00
Aura ,
GarudaUpdate ,
2021-10-25 22:03:57 +03:00
Pacman ,
2022-06-18 04:22:23 -07:00
Pamac ,
2023-01-29 19:19:27 +00:00
Paru ,
Pikaur ,
Trizen ,
Yay ,
2021-10-25 22:03:57 +03:00
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-05-14 09:22:14 +03:00
pub struct Linux {
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-05-14 09:22:14 +03:00
yay_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-10-23 21:29:25 +00:00
aura_aur_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-10-23 21:29:25 +00:00
aura_pacman_arguments : Option < String > ,
2021-10-25 22:03:57 +03:00
arch_package_manager : Option < ArchPackageManager > ,
2021-10-29 09:53:30 +03:00
show_arch_news : Option < bool > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2023-01-29 19:19:27 +00:00
garuda_update_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-05-15 21:10:01 +03:00
trizen_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-04-22 23:28:01 +03:00
pikaur_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-06-18 04:22:23 -07:00
pamac_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-05-14 09:22:14 +03:00
dnf_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2023-01-29 19:19:27 +00:00
nix_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2021-04-06 10:54:23 +03:00
apt_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
2020-06-03 22:12:27 +03:00
enable_tlmgr : Option < bool > ,
2020-12-26 09:17:54 +02:00
redhat_distro_sync : Option < bool > ,
2023-05-05 08:24:01 +00:00
suse_dup : Option < bool > ,
2021-05-13 14:57:29 -04:00
rpm_ostree : Option < bool > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-06-19 10:34:45 +03:00
emerge_sync_flags : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2020-06-19 10:34:45 +03:00
emerge_update_flags : Option < String > ,
2023-07-24 13:07:55 +08:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
home_manager_arguments : Option < Vec < String > > ,
2020-05-14 09:22:14 +03:00
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2020-06-19 22:29:42 +03:00
#[ serde(deny_unknown_fields) ]
2020-03-08 21:38:25 +02:00
pub struct Composer {
self_update : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2021-11-06 06:06:10 +02:00
#[ serde(deny_unknown_fields) ]
pub struct Vim {
force_plug_update : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
2019-08-22 21:45:42 +03:00
#[ serde(deny_unknown_fields) ]
2023-05-25 12:22:11 +02:00
pub struct Misc {
2022-11-24 14:15:43 -05:00
pre_sudo : Option < bool > ,
2023-05-25 12:22:11 +02:00
2023-07-01 21:58:39 +08:00
sudo_command : Option < SudoKind > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2018-06-08 18:19:07 +03:00
git_repos : Option < Vec < String > > ,
2023-05-25 12:22:11 +02:00
2020-01-28 21:04:59 +02:00
predefined_git_repos : Option < bool > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2019-01-22 22:37:32 +02:00
disable : Option < Vec < Step > > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2020-07-02 11:15:56 +03:00
ignore_failures : Option < Vec < Step > > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2019-06-05 14:15:45 +03:00
remote_topgrades : Option < Vec < String > > ,
2023-05-25 12:22:11 +02:00
2020-07-10 02:45:15 -05:00
remote_topgrade_path : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2019-09-01 20:45:44 +02:00
ssh_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2019-09-04 21:31:23 +03:00
git_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2019-11-06 07:27:01 +02:00
tmux_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
2019-09-05 20:52:51 +03:00
set_title : Option < bool > ,
2023-05-25 12:22:11 +02:00
2022-03-29 03:42:47 +03:00
display_time : Option < bool > ,
2023-05-25 12:22:11 +02:00
2019-09-28 15:45:05 +03:00
assume_yes : Option < bool > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2019-10-03 08:12:43 +03:00
yay_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-10-23 21:29:25 +00:00
aura_aur_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
2022-10-23 21:29:25 +00:00
aura_pacman_arguments : Option < String > ,
2023-05-25 12:22:11 +02:00
2019-10-10 11:26:00 +03:00
no_retry : Option < bool > ,
2023-05-25 12:22:11 +02:00
2019-10-10 11:26:00 +03:00
run_in_tmux : Option < bool > ,
2023-05-25 12:22:11 +02:00
2019-10-10 11:26:00 +03:00
cleanup : Option < bool > ,
2023-05-25 12:22:11 +02:00
2020-02-27 13:30:55 +02:00
notify_each_step : Option < bool > ,
2023-05-25 12:22:11 +02:00
2020-01-30 20:42:49 +02:00
accept_all_windows_updates : Option < bool > ,
2023-05-25 12:22:11 +02:00
2022-10-10 20:21:20 +00:00
skip_notify : Option < bool > ,
2023-05-25 12:22:11 +02:00
2021-02-10 08:58:03 +02:00
bashit_branch : Option < String > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
2019-10-10 11:26:00 +03:00
only : Option < Vec < Step > > ,
2023-05-25 12:22:11 +02:00
no_self_update : Option < bool > ,
}
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
/// Configuration file
pub struct ConfigFile {
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
include : Option < Include > ,
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
misc : Option < Misc > ,
#[ merge(strategy = crate::utils::merge_strategies::commands_merge_opt) ]
pre_commands : Option < Commands > ,
#[ merge(strategy = crate::utils::merge_strategies::commands_merge_opt) ]
post_commands : Option < Commands > ,
#[ merge(strategy = crate::utils::merge_strategies::commands_merge_opt) ]
commands : Option < Commands > ,
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
python : Option < Python > ,
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-03-08 21:38:25 +02:00
composer : Option < Composer > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-05-12 09:07:54 +03:00
brew : Option < Brew > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-05-14 09:22:14 +03:00
linux : Option < Linux > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-06-06 17:34:27 +03:00
git : Option < Git > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-06-07 07:11:44 +03:00
windows : Option < Windows > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2021-06-09 10:52:48 +03:00
npm : Option < NPM > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2022-10-10 20:08:11 +00:00
yarn : Option < Yarn > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2021-11-06 06:06:10 +02:00
vim : Option < Vim > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2021-06-30 11:15:18 +02:00
firmware : Option < Firmware > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2020-06-10 11:51:52 +03:00
vagrant : Option < Vagrant > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2021-06-30 12:37:41 +02:00
flatpak : Option < Flatpak > ,
2023-05-25 12:22:11 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
2022-10-28 00:29:35 +02:00
distrobox : Option < Distrobox > ,
2018-06-08 18:19:07 +03:00
}
2023-05-01 00:02:13 +05:30
fn config_directory ( ) -> PathBuf {
#[ cfg(unix) ]
return crate ::XDG_DIRS . config_dir ( ) ;
2020-06-17 09:56:52 +03:00
2023-05-01 00:02:13 +05:30
#[ cfg(windows) ]
return crate ::WINDOWS_DIRS . config_dir ( ) ;
2020-06-17 09:56:52 +03:00
}
2023-05-25 12:22:11 +02:00
/// The only purpose of this struct is to deserialize only the `include` field of the config file.
#[ derive(Deserialize, Default, Debug) ]
struct ConfigFileIncludeOnly {
include : Option < Include > ,
}
2019-01-22 22:37:32 +02:00
impl ConfigFile {
2023-05-25 12:22:11 +02:00
/// Returns the main config file and any additional config files
/// 0 = main config file
/// 1 = additional config files coming from topgrade.d
fn ensure ( ) -> Result < ( PathBuf , Vec < PathBuf > ) > {
let mut res = ( PathBuf ::new ( ) , Vec ::new ( ) ) ;
2023-05-01 00:02:13 +05:30
let config_directory = config_directory ( ) ;
2020-01-28 20:48:39 +02:00
2023-05-25 12:22:11 +02:00
let possible_config_paths = vec! [
config_directory . join ( " topgrade.toml " ) ,
config_directory . join ( " topgrade/topgrade.toml " ) ,
] ;
// Search for the main config file
for path in possible_config_paths . iter ( ) {
if path . exists ( ) {
debug! ( " Configuration at {} " , path . display ( ) ) ;
res . 0 = path . clone ( ) ;
break ;
}
}
2020-01-28 20:48:39 +02:00
2023-05-25 12:22:11 +02:00
res . 1 = Self ::ensure_topgrade_d ( & config_directory ) ? ;
// If no config file exists, create a default one in the config directory
if ! res . 0. exists ( ) & & res . 1. is_empty ( ) {
2023-05-30 03:07:02 -04:00
res . 0 = possible_config_paths [ 0 ] . clone ( ) ;
2019-08-22 22:08:28 +03:00
debug! ( " No configuration exists " ) ;
2023-05-25 12:22:11 +02:00
write ( & res . 0 , EXAMPLE_CONFIG ) . map_err ( | e | {
2019-12-11 23:05:38 +02:00
debug! (
" Unable to write the example configuration file to {}: {}. Using blank config. " ,
2023-05-25 12:22:11 +02:00
& res . 0. display ( ) ,
2019-12-11 23:05:38 +02:00
e
) ;
e
} ) ? ;
2018-06-08 18:19:07 +03:00
}
2023-05-25 12:22:11 +02:00
Ok ( res )
}
/// Searches topgrade.d for additional config files
fn ensure_topgrade_d ( config_directory : & Path ) -> Result < Vec < PathBuf > > {
let mut res = Vec ::new ( ) ;
let dir_to_search = config_directory . join ( " topgrade.d " ) ;
if dir_to_search . exists ( ) {
for entry in fs ::read_dir ( dir_to_search ) ? {
let entry = entry ? ;
if entry . file_type ( ) ? . is_file ( ) {
debug! (
" Found additional (directory) configuration file at {} " ,
entry . path ( ) . display ( )
) ;
res . push ( entry . path ( ) ) ;
}
}
res . sort ( ) ;
} else {
debug! ( " No additional configuration directory exists, creating one " ) ;
fs ::create_dir_all ( & dir_to_search ) ? ;
}
Ok ( res )
2019-08-22 22:29:31 +03:00
}
/// Read the configuration file.
///
2023-05-25 12:22:11 +02:00
/// If the configuration file does not exist, the function returns the default ConfigFile.
2023-05-01 00:02:13 +05:30
fn read ( config_path : Option < PathBuf > ) -> Result < ConfigFile > {
2023-05-25 12:22:11 +02:00
let mut result = Self ::default ( ) ;
2020-05-12 10:14:21 +03:00
let config_path = if let Some ( path ) = config_path {
path
} else {
2023-05-25 12:22:11 +02:00
let ( path , dir_include ) = Self ::ensure ( ) ? ;
/*
The Function was called without a config_path , we need
to read the include directory before returning the main config path
* /
for include in dir_include {
let include_contents = fs ::read_to_string ( & include ) . map_err ( | e | {
tracing ::error! ( " Unable to read {} " , include . display ( ) ) ;
e
} ) ? ;
let include_contents_parsed = toml ::from_str ( include_contents . as_str ( ) ) . map_err ( | e | {
tracing ::error! ( " Failed to deserialize {} " , include . display ( ) ) ;
e
} ) ? ;
result . merge ( include_contents_parsed ) ;
}
path
2020-05-12 10:14:21 +03:00
} ;
2019-12-05 19:16:28 +01:00
2023-06-13 10:17:27 -04:00
if config_path = = PathBuf ::default ( ) {
// Here we expect topgrade.d and consequently result is not empty.
// If empty, Self:: ensure() would have created the default config.
return Ok ( result ) ;
}
2023-05-25 12:22:11 +02:00
let mut contents_non_split = fs ::read_to_string ( & config_path ) . map_err ( | e | {
2022-11-16 13:43:57 -05:00
tracing ::error! ( " Unable to read {} " , config_path . display ( ) ) ;
2019-12-11 23:05:38 +02:00
e
} ) ? ;
2019-12-05 19:16:28 +01:00
2023-05-25 12:22:11 +02:00
Self ::ensure_misc_is_present ( & mut contents_non_split , & config_path ) ;
2018-06-11 13:56:57 +03:00
2023-05-25 12:22:11 +02:00
// To parse [include] sections in the order as they are written,
// we split the file and parse each part as a separate file
2023-06-01 03:15:49 -04:00
let regex_match_include = Regex ::new ( r "^\s*\[include]" ) . expect ( " Failed to compile regex " ) ;
2023-05-25 12:22:11 +02:00
let contents_split = regex_match_include . split_inclusive_left ( contents_non_split . as_str ( ) ) ;
for contents in contents_split {
let config_file_include_only : ConfigFileIncludeOnly = toml ::from_str ( contents ) . map_err ( | e | {
tracing ::error! ( " Failed to deserialize an include section of {} " , config_path . display ( ) ) ;
e
} ) ? ;
if let Some ( includes ) = & config_file_include_only . include {
// Parses the [include] section present in the slice
2023-06-01 03:15:49 -04:00
if let Some ( ref paths ) = includes . paths {
for include in paths . iter ( ) . rev ( ) {
let include_path = shellexpand ::tilde ::< & str > ( & include . as_ref ( ) ) . into_owned ( ) ;
let include_path = PathBuf ::from ( include_path ) ;
let include_contents = match fs ::read_to_string ( & include_path ) {
Ok ( c ) = > c ,
Err ( e ) = > {
tracing ::error! ( " Unable to read {}: {} " , include_path . display ( ) , e ) ;
continue ;
}
} ;
match toml ::from_str ::< Self > ( & include_contents ) {
Ok ( include_parsed ) = > result . merge ( include_parsed ) ,
Err ( e ) = > {
tracing ::error! ( " Failed to deserialize {}: {} " , include_path . display ( ) , e ) ;
continue ;
}
} ;
debug! ( " Configuration include found: {} " , include_path . display ( ) ) ;
}
} else {
debug! ( " No include paths found in {} " , config_path . display ( ) ) ;
2023-05-25 12:22:11 +02:00
}
}
match toml ::from_str ::< Self > ( contents ) {
Ok ( contents ) = > result . merge ( contents ) ,
Err ( e ) = > tracing ::error! ( " Failed to deserialize {}: {} " , config_path . display ( ) , e ) ,
}
}
if let Some ( misc ) = & mut result . misc {
if let Some ( ref mut paths ) = & mut misc . git_repos {
for path in paths . iter_mut ( ) {
let expanded = shellexpand ::tilde ::< & str > ( & path . as_ref ( ) ) . into_owned ( ) ;
debug! ( " Path {} expanded to {} " , path , expanded ) ;
* path = expanded ;
}
2020-06-11 15:19:18 +03:00
}
}
if let Some ( paths ) = result . git . as_mut ( ) . and_then ( | git | git . repos . as_mut ( ) ) {
2021-10-28 22:05:35 +03:00
for path in paths . iter_mut ( ) {
2020-06-11 15:19:18 +03:00
let expanded = shellexpand ::tilde ::< & str > ( & path . as_ref ( ) ) . into_owned ( ) ;
debug! ( " Path {} expanded to {} " , path , expanded ) ;
* path = expanded ;
2018-06-11 13:56:57 +03:00
}
}
2019-08-22 22:08:28 +03:00
debug! ( " Loaded configuration: {:?} " , result ) ;
2018-06-11 13:56:57 +03:00
Ok ( result )
2018-06-08 18:19:07 +03:00
}
2019-08-22 22:29:31 +03:00
2023-05-01 00:02:13 +05:30
fn edit ( ) -> Result < ( ) > {
2023-05-25 12:22:11 +02:00
let config_path = Self ::ensure ( ) ? . 0 ;
2019-08-22 22:29:31 +03:00
let editor = editor ( ) ;
2022-03-29 03:42:47 +03:00
debug! ( " Editor: {:?} " , editor ) ;
2019-08-22 22:29:31 +03:00
2020-06-24 08:59:06 +03:00
let command = which ( & editor [ 0 ] ) ? ;
let args : Vec < & String > = editor . iter ( ) . skip ( 1 ) . collect ( ) ;
Command ::new ( command )
. args ( args )
2019-08-22 22:29:31 +03:00
. arg ( config_path )
2022-11-08 05:54:35 -05:00
. status_checked ( )
. context ( " Failed to open configuration file editor " )
2019-08-22 22:29:31 +03:00
}
2023-05-25 12:22:11 +02:00
/// [Misc] was added later, here we check if it is present in the config file and add it if not
fn ensure_misc_is_present ( contents : & mut String , path : & PathBuf ) {
if ! contents . contains ( " [misc] " ) {
debug! ( " Adding [misc] section to {} " , path . display ( ) ) ;
string_prepend_str ( contents , " [misc] \n " ) ;
File ::create ( path )
. and_then ( | mut f | f . write_all ( contents . as_bytes ( ) ) )
. expect ( " Tried to auto-migrate the config file, unable to write to config file. \n Please add \" [misc] \" section manually to the first line of the file. \n Error " ) ;
}
}
2018-06-08 18:19:07 +03:00
}
2018-09-06 14:42:56 +03:00
2022-04-23 12:35:06 +02:00
// Command line arguments
#[ derive(Parser, Debug) ]
2022-11-02 15:28:47 +00:00
#[ clap(name = " Topgrade " , version) ]
2019-01-22 22:37:32 +02:00
pub struct CommandLineArgs {
2019-08-22 22:29:31 +03:00
/// Edit the configuration file
2022-11-02 15:28:47 +00:00
#[ clap(long = " edit-config " ) ]
2019-08-22 22:29:31 +03:00
edit_config : bool ,
2020-06-30 10:01:22 +03:00
/// Show config reference
2022-11-02 15:28:47 +00:00
#[ clap(long = " config-reference " ) ]
2020-06-30 10:01:22 +03:00
show_config_reference : bool ,
2018-12-05 13:29:31 +02:00
/// Run inside tmux
2022-11-02 15:28:47 +00:00
#[ clap(short = 't', long = " tmux " ) ]
2019-01-22 22:37:32 +02:00
run_in_tmux : bool ,
2018-09-06 14:42:56 +03:00
2018-12-05 13:29:31 +02:00
/// Cleanup temporary or old files
2022-11-02 15:28:47 +00:00
#[ clap(short = 'c', long = " cleanup " ) ]
2019-01-22 22:37:32 +02:00
cleanup : bool ,
2018-11-17 19:09:46 +01:00
2018-12-05 13:29:31 +02:00
/// Print what would be done
2022-11-02 15:28:47 +00:00
#[ clap(short = 'n', long = " dry-run " ) ]
2019-01-22 22:37:32 +02:00
dry_run : bool ,
2018-12-05 13:38:18 +02:00
/// Do not ask to retry failed steps
2022-11-02 15:28:47 +00:00
#[ clap(long = " no-retry " ) ]
2019-01-22 22:37:32 +02:00
no_retry : bool ,
2019-01-16 08:51:43 +00:00
/// Do not perform upgrades for the given steps
2022-11-06 13:54:38 +00:00
#[ clap(long = " disable " , value_name = " STEP " , arg_enum, multiple_values = true) ]
2019-01-22 22:37:32 +02:00
disable : Vec < Step > ,
2019-06-05 11:44:28 +03:00
2019-09-28 15:17:06 +03:00
/// Perform only the specified steps (experimental)
2022-11-06 13:54:38 +00:00
#[ clap(long = " only " , value_name = " STEP " , arg_enum, multiple_values = true) ]
2019-09-28 15:17:06 +03:00
only : Vec < Step > ,
2022-05-07 15:25:51 +03:00
/// Run only specific custom commands
2022-11-06 13:54:38 +00:00
#[ clap(long = " custom-commands " , value_name = " NAME " , multiple_values = true) ]
2022-05-07 15:25:51 +03:00
custom_commands : Vec < String > ,
2022-05-07 08:11:53 +03:00
/// Set environment variables
2022-11-06 13:54:38 +00:00
#[ clap(long = " env " , value_name = " NAME=VALUE " , multiple_values = true) ]
2022-05-07 08:11:53 +03:00
env : Vec < String > ,
2022-11-16 13:43:57 -05:00
/// Output debug logs. Alias for `--log-filter debug`.
2022-11-02 15:28:47 +00:00
#[ clap(short = 'v', long = " verbose " ) ]
2022-03-29 03:42:47 +03:00
pub verbose : bool ,
2019-06-16 09:09:05 +03:00
2019-09-01 13:44:20 -05:00
/// Prompt for a key before exiting
2022-11-02 15:28:47 +00:00
#[ clap(short = 'k', long = " keep " ) ]
2019-06-16 09:09:05 +03:00
keep_at_end : bool ,
2019-09-28 15:45:05 +03:00
2022-10-10 20:21:20 +00:00
/// Skip sending a notification at the end of a run
2022-11-02 15:28:47 +00:00
#[ clap(long = " skip-notify " ) ]
2022-10-10 20:21:20 +00:00
skip_notify : bool ,
2021-12-06 14:44:20 +02:00
/// Say yes to package manager's prompt
2022-11-06 13:54:38 +00:00
#[ clap(
short = 'y' ,
long = " yes " ,
value_name = " STEP " ,
arg_enum ,
multiple_values = true ,
min_values = 0
) ]
2021-12-06 14:44:20 +02:00
yes : Option < Vec < Step > > ,
2020-01-28 21:04:59 +02:00
2020-01-28 21:59:55 +02:00
/// Don't pull the predefined git repos
2022-11-02 15:28:47 +00:00
#[ clap(long = " disable-predefined-git-repos " ) ]
2020-01-28 21:04:59 +02:00
disable_predefined_git_repos : bool ,
2020-05-08 06:30:20 +03:00
/// Alternative configuration file
2022-11-06 13:54:38 +00:00
#[ clap(long = " config " , value_name = " PATH " ) ]
2020-05-08 06:30:20 +03:00
config : Option < PathBuf > ,
2020-08-02 06:29:16 +03:00
/// A regular expression for restricting remote host execution
2022-11-06 13:54:38 +00:00
#[ clap(long = " remote-host-limit " , value_name = " REGEX " ) ]
2020-08-02 06:29:16 +03:00
remote_host_limit : Option < Regex > ,
2020-08-21 23:04:36 +03:00
/// Show the reason for skipped steps
2022-11-02 15:28:47 +00:00
#[ clap(long = " show-skipped " ) ]
2020-08-21 23:04:36 +03:00
show_skipped : bool ,
2022-11-16 13:43:57 -05:00
/// Tracing filter directives.
///
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
2023-05-26 10:07:14 +02:00
#[ clap(long, default_value = " warn " ) ]
2022-11-16 13:43:57 -05:00
pub log_filter : String ,
2022-11-26 20:42:35 +01:00
/// Print completion script for the given shell and exit
#[ clap(long, arg_enum, hide = true) ]
pub gen_completion : Option < Shell > ,
/// Print roff manpage and exit
#[ clap(long, hide = true) ]
pub gen_manpage : bool ,
2023-03-22 22:05:21 +01:00
/// Don't update Topgrade
#[ clap(long = " no-self-update " ) ]
pub no_self_update : bool ,
2019-01-22 22:37:32 +02:00
}
2019-11-04 22:55:06 +02:00
impl CommandLineArgs {
pub fn edit_config ( & self ) -> bool {
self . edit_config
}
2020-06-30 10:01:22 +03:00
pub fn show_config_reference ( & self ) -> bool {
self . show_config_reference
}
2022-05-07 08:11:53 +03:00
pub fn env_variables ( & self ) -> & Vec < String > {
& self . env
}
2022-11-16 13:43:57 -05:00
pub fn tracing_filter_directives ( & self ) -> String {
if self . verbose {
" debug " . into ( )
} else {
self . log_filter . clone ( )
}
}
2019-11-04 22:55:06 +02:00
}
2019-01-22 22:37:32 +02:00
/// Represents the application configuration
///
/// The struct holds the loaded configuration file, as well as the arguments parsed from the command line.
2023-05-25 12:22:11 +02:00
/// Its provided methods decide the appropriate options based on combining the configuration file and the
2019-01-22 22:37:32 +02:00
/// command line arguments.
pub struct Config {
opt : CommandLineArgs ,
config_file : ConfigFile ,
2019-10-10 11:26:00 +03:00
allowed_steps : Vec < Step > ,
2019-01-22 22:37:32 +02:00
}
impl Config {
/// Load the configuration.
///
2023-05-25 12:22:11 +02:00
/// The function parses the command line arguments and reads the configuration file.
2023-05-01 00:02:13 +05:30
pub fn load ( opt : CommandLineArgs ) -> Result < Self > {
let config_directory = config_directory ( ) ;
2020-06-17 09:56:52 +03:00
let config_file = if config_directory . is_dir ( ) {
2023-05-01 00:02:13 +05:30
ConfigFile ::read ( opt . config . clone ( ) ) . unwrap_or_else ( | e | {
2020-06-17 09:56:52 +03:00
// Inform the user about errors when loading the configuration,
// but fallback to the default config to at least attempt to do something
2022-11-16 13:43:57 -05:00
tracing ::error! ( " failed to load configuration: {} " , e ) ;
2020-06-17 09:56:52 +03:00
ConfigFile ::default ( )
} )
} else {
2023-05-25 12:22:11 +02:00
debug! ( " Configuration directory {} does not exist " , config_directory . display ( ) ) ;
2019-12-05 19:16:28 +01:00
ConfigFile ::default ( )
2020-06-17 09:56:52 +03:00
} ;
2019-10-20 14:11:57 +03:00
2023-05-25 12:22:11 +02:00
if let Some ( misc ) = & config_file . misc {
check_deprecated! ( misc , git_arguments , git , arguments ) ;
check_deprecated! ( misc , git_repos , git , repos ) ;
check_deprecated! ( misc , predefined_git_repos , git , pull_predefined ) ;
check_deprecated! ( misc , yay_arguments , linux , yay_arguments ) ;
check_deprecated! ( misc , accept_all_windows_updates , windows , accept_all_updates ) ;
}
2020-06-07 07:11:44 +03:00
2019-10-20 14:11:57 +03:00
let allowed_steps = Self ::allowed_steps ( & opt , & config_file ) ;
2019-01-22 22:37:32 +02:00
Ok ( Self {
2019-08-22 22:08:28 +03:00
opt ,
2019-10-10 11:26:00 +03:00
config_file ,
allowed_steps ,
2019-01-22 22:37:32 +02:00
} )
}
2019-08-22 22:29:31 +03:00
/// Launch an editor to edit the configuration
2023-05-01 00:02:13 +05:30
pub fn edit ( ) -> Result < ( ) > {
ConfigFile ::edit ( )
2019-08-22 22:29:31 +03:00
}
2019-01-22 22:37:32 +02:00
/// The list of commands to run before performing any step.
pub fn pre_commands ( & self ) -> & Option < Commands > {
& self . config_file . pre_commands
}
2020-08-30 07:40:06 +03:00
/// The list of commands to run at the end of all steps
pub fn post_commands ( & self ) -> & Option < Commands > {
& self . config_file . post_commands
}
2019-01-22 22:37:32 +02:00
/// The list of custom steps.
pub fn commands ( & self ) -> & Option < Commands > {
& self . config_file . commands
}
/// The list of additional git repositories to pull.
pub fn git_repos ( & self ) -> & Option < Vec < String > > {
2023-05-25 12:22:11 +02:00
get_deprecated_moved_opt! ( & self . config_file . misc , git_repos , & self . config_file . git , repos )
2019-01-22 22:37:32 +02:00
}
/// Tell whether the specified step should run.
///
/// If the step appears either in the `--disable` command line argument
/// or the `disable` option in the configuration, the function returns false.
pub fn should_run ( & self , step : Step ) -> bool {
2019-10-10 11:26:00 +03:00
self . allowed_steps . contains ( & step )
}
2019-09-28 15:17:06 +03:00
2019-10-10 11:26:00 +03:00
fn allowed_steps ( opt : & CommandLineArgs , config_file : & ConfigFile ) -> Vec < Step > {
2021-12-06 14:12:20 +02:00
let mut enabled_steps : Vec < Step > = Vec ::new ( ) ;
enabled_steps . extend ( & opt . only ) ;
2019-10-10 11:26:00 +03:00
2023-05-25 12:22:11 +02:00
if let Some ( misc ) = config_file . misc . as_ref ( ) {
if let Some ( only ) = misc . only . as_ref ( ) {
enabled_steps . extend ( only ) ;
}
2021-12-06 14:12:20 +02:00
}
2021-12-12 13:52:07 +02:00
if enabled_steps . is_empty ( ) {
enabled_steps . extend ( Step ::iter ( ) ) ;
}
2021-12-06 14:12:20 +02:00
let mut disabled_steps : Vec < Step > = Vec ::new ( ) ;
disabled_steps . extend ( & opt . disable ) ;
2023-05-25 12:22:11 +02:00
if let Some ( misc ) = config_file . misc . as_ref ( ) {
if let Some ( disabled ) = misc . disable . as_ref ( ) {
disabled_steps . extend ( disabled ) ;
}
2021-12-06 14:12:20 +02:00
}
2019-10-10 11:26:00 +03:00
2020-10-02 23:18:14 +03:00
enabled_steps . retain ( | e | ! disabled_steps . contains ( e ) | | opt . only . contains ( e ) ) ;
2019-10-10 11:26:00 +03:00
enabled_steps
2019-01-22 22:37:32 +02:00
}
2023-05-25 12:22:11 +02:00
/// Tell whether we should run a self-update.
2023-03-22 22:05:21 +01:00
pub fn no_self_update ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . opt . no_self_update
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . no_self_update )
. unwrap_or ( false )
2023-03-22 22:05:21 +01:00
}
2019-01-22 22:37:32 +02:00
/// Tell whether we should run in tmux.
pub fn run_in_tmux ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . opt . run_in_tmux
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . run_in_tmux )
. unwrap_or ( false )
2019-01-22 22:37:32 +02:00
}
/// Tell whether we should perform cleanup steps.
pub fn cleanup ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . opt . cleanup
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . cleanup )
. unwrap_or ( false )
2019-01-22 22:37:32 +02:00
}
/// Tell whether we are dry-running.
pub fn dry_run ( & self ) -> bool {
self . opt . dry_run
}
/// Tell whether we should not attempt to retry anything.
pub fn no_retry ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . opt . no_retry
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . no_retry )
. unwrap_or ( false )
2019-01-22 22:37:32 +02:00
}
2019-06-05 11:44:28 +03:00
2019-06-05 14:15:45 +03:00
/// List of remote hosts to run Topgrade in
2023-05-25 12:22:11 +02:00
pub fn remote_topgrades ( & self ) -> Option < & Vec < String > > {
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . remote_topgrades . as_ref ( ) )
2019-06-05 14:15:45 +03:00
}
2019-06-16 09:09:05 +03:00
2020-07-10 02:45:15 -05:00
/// Path to Topgrade executable used for all remote hosts
pub fn remote_topgrade_path ( & self ) -> & str {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . remote_topgrade_path . as_deref ( ) )
. unwrap_or ( " topgrade " )
2020-07-10 02:45:15 -05:00
}
2019-09-01 20:45:44 +02:00
/// Extra SSH arguments
2023-05-25 12:22:11 +02:00
pub fn ssh_arguments ( & self ) -> Option < & String > {
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . ssh_arguments . as_ref ( ) )
2019-09-01 20:45:44 +02:00
}
2019-09-04 21:31:23 +03:00
/// Extra Git arguments
pub fn git_arguments ( & self ) -> & Option < String > {
2023-05-25 12:22:11 +02:00
get_deprecated_moved_opt! ( & self . config_file . misc , git_arguments , & self . config_file . git , arguments )
2019-09-04 21:31:23 +03:00
}
2019-11-06 07:27:01 +02:00
/// Extra Tmux arguments
2023-05-25 12:22:11 +02:00
pub fn tmux_arguments ( & self ) -> Result < Vec < String > > {
let args = & self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . tmux_arguments . as_ref ( ) )
. map ( String ::to_owned )
. unwrap_or_default ( ) ;
2022-11-03 12:46:43 -04:00
shell_words ::split ( args )
// The only time the parse failed is in case of a missing close quote.
// The error message looks like this:
// Error: Failed to parse `tmux_arguments`: `'foo`
//
// Caused by:
// missing closing quote
. with_context ( | | format! ( " Failed to parse `tmux_arguments`: ` {args} ` " ) )
2019-11-06 07:27:01 +02:00
}
2019-06-16 09:09:05 +03:00
/// Prompt for a key before exiting
pub fn keep_at_end ( & self ) -> bool {
self . opt . keep_at_end | | env ::var ( " TOPGRADE_KEEP_END " ) . is_ok ( )
}
2019-08-22 22:29:31 +03:00
2022-10-10 20:21:20 +00:00
/// Skip sending a notification at the end of a run
pub fn skip_notify ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
if let Some ( yes ) = self . config_file . misc . as_ref ( ) . and_then ( | misc | misc . skip_notify ) {
2022-10-10 20:21:20 +00:00
return yes ;
}
self . opt . skip_notify
}
2019-09-05 20:52:51 +03:00
/// Whether to set the terminal title
pub fn set_title ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . set_title )
. unwrap_or ( true )
2019-09-05 20:52:51 +03:00
}
2019-09-28 15:45:05 +03:00
/// Whether to say yes to package managers
2021-12-06 14:44:20 +02:00
pub fn yes ( & self , step : Step ) -> bool {
2023-05-25 12:22:11 +02:00
if let Some ( yes ) = self . config_file . misc . as_ref ( ) . and_then ( | misc | misc . assume_yes ) {
2021-12-06 14:44:20 +02:00
return yes ;
}
if let Some ( yes_list ) = & self . opt . yes {
if yes_list . is_empty ( ) {
return true ;
}
return yes_list . contains ( & step ) ;
}
false
2019-09-28 15:45:05 +03:00
}
2019-10-03 08:12:43 +03:00
2021-02-10 08:58:03 +02:00
/// Bash-it branch
pub fn bashit_branch ( & self ) -> & str {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . bashit_branch . as_deref ( ) )
. unwrap_or ( " stable " )
2021-02-10 08:58:03 +02:00
}
2020-01-30 20:42:49 +02:00
/// Whether to accept all Windows updates
pub fn accept_all_windows_updates ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
get_deprecated_moved_or_default_to! (
& self . config_file . misc ,
2020-06-07 07:11:44 +03:00
accept_all_windows_updates ,
2023-05-25 12:22:11 +02:00
& self . config_file . windows ,
accept_all_updates ,
true
2020-06-07 07:11:44 +03:00
)
2020-01-30 20:42:49 +02:00
}
2020-07-01 21:03:19 +03:00
/// Whether to self rename the Topgrade executable during the run
pub fn self_rename ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | w | w . self_rename )
. unwrap_or ( false )
}
2023-01-29 19:19:27 +00:00
// Should wsl --update should use the --pre-release flag
pub fn wsl_update_pre_release ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | w | w . wsl_update_pre_release )
. unwrap_or ( false )
}
// Should wsl --update use the --web-download flag
pub fn wsl_update_use_web_download ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | w | w . wsl_update_use_web_download )
. unwrap_or ( false )
}
2020-05-12 09:07:54 +03:00
/// Whether Brew cask should be greedy
pub fn brew_cask_greedy ( & self ) -> bool {
self . config_file
. brew
. as_ref ( )
. and_then ( | c | c . greedy_cask )
. unwrap_or ( false )
}
2022-10-10 20:17:50 +00:00
/// Whether Brew should autoremove
pub fn brew_autoremove ( & self ) -> bool {
self . config_file
. brew
. as_ref ( )
. and_then ( | c | c . autoremove )
. unwrap_or ( false )
}
2020-03-08 21:38:25 +02:00
/// Whether Composer should update itself
pub fn composer_self_update ( & self ) -> bool {
self . config_file
. composer
. as_ref ( )
. and_then ( | c | c . self_update )
. unwrap_or ( false )
}
2021-11-06 06:06:10 +02:00
/// Whether to force plug update in Vim
pub fn force_vim_plug_update ( & self ) -> bool {
self . config_file
. vim
. as_ref ( )
. and_then ( | c | c . force_plug_update )
. unwrap_or_default ( )
}
2020-02-27 13:30:55 +02:00
/// Whether to send a desktop notification at the beginning of every step
pub fn notify_each_step ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . notify_each_step )
. unwrap_or ( false )
2020-02-27 13:30:55 +02:00
}
2023-01-29 19:19:27 +00:00
/// Extra garuda-update arguments
pub fn garuda_update_arguments ( & self ) -> & str {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . garuda_update_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
2020-05-15 21:10:01 +03:00
/// Extra trizen arguments
pub fn trizen_arguments ( & self ) -> & str {
2021-09-02 07:27:09 +03:00
self . config_file
2020-05-15 21:10:01 +03:00
. linux
. as_ref ( )
. and_then ( | s | s . trizen_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
2022-04-22 23:28:01 +03:00
/// Extra Pikaur arguments
#[ allow(dead_code) ]
pub fn pikaur_arguments ( & self ) -> & str {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . pikaur_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
2022-06-18 04:22:23 -07:00
/// Extra Pamac arguments
pub fn pamac_arguments ( & self ) -> & str {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . pamac_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
2021-10-29 09:53:30 +03:00
/// Show news on Arch Linux
pub fn show_arch_news ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . show_arch_news )
. unwrap_or ( true )
}
2022-05-01 06:01:09 +02:00
/// Get the package manager of an Arch Linux system
2021-10-25 22:03:57 +03:00
pub fn arch_package_manager ( & self ) -> ArchPackageManager {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . arch_package_manager )
. unwrap_or ( ArchPackageManager ::Autodetect )
}
2019-10-03 08:12:43 +03:00
/// Extra yay arguments
pub fn yay_arguments ( & self ) -> & str {
2022-10-23 21:29:25 +00:00
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . yay_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
/// Extra aura arguments for AUR and pacman
pub fn aura_aur_arguments ( & self ) -> & str {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . aura_aur_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
pub fn aura_pacman_arguments ( & self ) -> & str {
self . config_file
. linux
. as_ref ( )
. and_then ( | s | s . aura_pacman_arguments . as_deref ( ) )
. unwrap_or ( " " )
2020-05-14 09:22:14 +03:00
}
2021-04-06 10:54:23 +03:00
/// Extra apt arguments
pub fn apt_arguments ( & self ) -> Option < & str > {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . apt_arguments . as_deref ( ) )
}
/// Extra dnf arguments
2020-05-14 09:22:14 +03:00
pub fn dnf_arguments ( & self ) -> Option < & str > {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . dnf_arguments . as_deref ( ) )
2019-10-03 08:12:43 +03:00
}
2020-01-28 21:04:59 +02:00
2023-01-29 19:19:27 +00:00
/// Extra nix arguments
pub fn nix_arguments ( & self ) -> Option < & str > {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . nix_arguments . as_deref ( ) )
}
2023-07-24 13:07:55 +08:00
/// Extra Home Manager arguments
pub fn home_manager ( & self ) -> Option < & Vec < String > > {
self . config_file
. linux
. as_ref ( )
. and_then ( | misc | misc . home_manager_arguments . as_ref ( ) )
}
2022-10-28 00:29:35 +02:00
/// Distrobox use root
pub fn distrobox_root ( & self ) -> bool {
self . config_file
. distrobox
. as_ref ( )
. and_then ( | r | r . use_root )
. unwrap_or ( false )
}
/// Distrobox containers
pub fn distrobox_containers ( & self ) -> Option < & Vec < String > > {
self . config_file . distrobox . as_ref ( ) . and_then ( | r | r . containers . as_ref ( ) )
}
2020-06-06 17:34:27 +03:00
/// Concurrency limit for git
pub fn git_concurrency_limit ( & self ) -> Option < usize > {
self . config_file . git . as_ref ( ) . and_then ( | git | git . max_concurrency )
}
2023-05-25 12:22:11 +02:00
/// Determine whether we should power on vagrant boxes
2020-06-10 11:51:52 +03:00
pub fn vagrant_power_on ( & self ) -> Option < bool > {
self . config_file . vagrant . as_ref ( ) . and_then ( | vagrant | vagrant . power_on )
}
/// Vagrant directories
pub fn vagrant_directories ( & self ) -> Option < & Vec < String > > {
self . config_file
. vagrant
. as_ref ( )
. and_then ( | vagrant | vagrant . directories . as_ref ( ) )
}
2020-06-13 07:44:53 +03:00
/// Always suspend vagrant boxes instead of powering off
pub fn vagrant_always_suspend ( & self ) -> Option < bool > {
self . config_file
. vagrant
. as_ref ( )
. and_then ( | vagrant | vagrant . always_suspend )
}
2020-12-26 09:17:54 +02:00
/// Enable tlmgr on Linux
2020-06-03 22:12:27 +03:00
pub fn enable_tlmgr_linux ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . enable_tlmgr )
. unwrap_or ( false )
}
2023-05-25 12:22:11 +02:00
/// Use distro-sync in Red Hat based distributions
2020-12-26 09:17:54 +02:00
pub fn redhat_distro_sync ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . redhat_distro_sync )
. unwrap_or ( false )
}
2023-05-05 08:24:01 +00:00
/// Use zypper dist-upgrade (same as distro-sync on RH) instead of update (default: false on SLE/Leap, ignored on Tumbleweed (dup is always ran))
pub fn suse_dup ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . suse_dup )
. unwrap_or ( false )
}
2021-05-13 14:57:29 -04:00
/// Use rpm-ostree in *when rpm-ostree is detected* (default: true)
pub fn rpm_ostree ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . rpm_ostree )
2023-04-30 18:22:08 +00:00
. unwrap_or ( false )
2021-05-13 14:57:29 -04:00
}
2023-05-25 12:22:11 +02:00
/// Determine if we should ignore failures for this step
2020-07-02 11:15:56 +03:00
pub fn ignore_failure ( & self , step : Step ) -> bool {
self . config_file
2023-05-25 12:22:11 +02:00
. misc
2020-07-02 11:15:56 +03:00
. as_ref ( )
2023-05-25 12:22:11 +02:00
. and_then ( | misc | misc . ignore_failures . as_ref ( ) )
2020-07-02 11:15:56 +03:00
. map ( | v | v . contains ( & step ) )
. unwrap_or ( false )
}
2020-01-28 21:04:59 +02:00
pub fn use_predefined_git_repos ( & self ) -> bool {
2020-06-07 07:11:44 +03:00
! self . opt . disable_predefined_git_repos
2023-05-25 12:22:11 +02:00
& & get_deprecated_moved_or_default_to! (
& self . config_file . misc ,
predefined_git_repos ,
& self . config_file . git ,
pull_predefined ,
true
)
2020-01-28 21:04:59 +02:00
}
2020-06-19 10:34:45 +03:00
2020-06-28 08:33:40 +03:00
pub fn verbose ( & self ) -> bool {
self . opt . verbose
}
2020-08-21 23:04:36 +03:00
pub fn show_skipped ( & self ) -> bool {
self . opt . show_skipped
}
2020-08-23 09:12:40 +03:00
pub fn open_remotes_in_new_terminal ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | windows | windows . open_remotes_in_new_terminal )
. unwrap_or ( false )
}
2023-03-13 19:23:37 +00:00
pub fn sudo_command ( & self ) -> Option < SudoKind > {
2023-07-01 21:58:39 +08:00
self . config_file . misc . as_ref ( ) . and_then ( | misc | misc . sudo_command )
2023-03-13 19:23:37 +00:00
}
2022-11-24 14:15:43 -05:00
/// If `true`, `sudo` should be called after `pre_commands` in order to elevate at the
/// start of the session (and not in the middle).
pub fn pre_sudo ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . pre_sudo )
. unwrap_or ( false )
2022-11-24 14:15:43 -05:00
}
2021-06-09 10:52:48 +03:00
#[ cfg(target_os = " linux " ) ]
pub fn npm_use_sudo ( & self ) -> bool {
self . config_file
. npm
. as_ref ( )
. and_then ( | npm | npm . use_sudo )
. unwrap_or ( false )
}
2022-10-10 20:08:11 +00:00
#[ cfg(target_os = " linux " ) ]
pub fn yarn_use_sudo ( & self ) -> bool {
self . config_file
. yarn
. as_ref ( )
. and_then ( | yarn | yarn . use_sudo )
. unwrap_or ( false )
}
2021-06-09 10:52:48 +03:00
2021-06-30 11:15:18 +02:00
#[ cfg(target_os = " linux " ) ]
pub fn firmware_upgrade ( & self ) -> bool {
self . config_file
. firmware
. as_ref ( )
. and_then ( | firmware | firmware . upgrade )
. unwrap_or ( false )
}
2021-06-30 12:37:41 +02:00
#[ cfg(target_os = " linux " ) ]
pub fn flatpak_use_sudo ( & self ) -> bool {
self . config_file
. flatpak
. as_ref ( )
. and_then ( | flatpak | flatpak . use_sudo )
. unwrap_or ( false )
}
2020-06-19 10:34:45 +03:00
#[ cfg(target_os = " linux " ) ]
str_value! ( linux , emerge_sync_flags ) ;
#[ cfg(target_os = " linux " ) ]
str_value! ( linux , emerge_update_flags ) ;
2020-08-02 06:29:16 +03:00
pub fn should_execute_remote ( & self , remote : & str ) -> bool {
2021-12-06 13:31:05 +02:00
if let Ok ( hostname ) = hostname ( ) {
if remote = = hostname {
return false ;
}
}
if let Some ( limit ) = self . opt . remote_host_limit . as_ref ( ) {
return limit . is_match ( remote ) ;
}
true
2020-08-02 06:29:16 +03:00
}
2022-03-05 21:58:36 +02:00
2022-03-23 22:34:39 +02:00
#[ cfg(windows) ]
2022-03-05 21:58:36 +02:00
pub fn enable_winget ( & self ) -> bool {
return self
. config_file
. windows
. as_ref ( )
. and_then ( | w | w . enable_winget )
. unwrap_or ( false ) ;
}
2022-03-29 03:42:47 +03:00
2023-01-29 19:19:27 +00:00
pub fn enable_pipupgrade ( & self ) -> bool {
return self
. config_file
. python
. as_ref ( )
. and_then ( | python | python . enable_pipupgrade )
. unwrap_or ( false ) ;
}
2023-05-30 03:04:23 -04:00
pub fn pipupgrade_arguments ( & self ) -> & str {
self . config_file
. python
. as_ref ( )
. and_then ( | s | s . pipupgrade_arguments . as_deref ( ) )
. unwrap_or ( " " )
}
2023-01-29 19:19:27 +00:00
pub fn enable_pip_review ( & self ) -> bool {
return self
. config_file
. python
. as_ref ( )
. and_then ( | python | python . enable_pip_review )
. unwrap_or ( false ) ;
}
2023-05-20 23:03:59 +05:30
pub fn enable_pip_review_local ( & self ) -> bool {
return self
. config_file
. python
. as_ref ( )
. and_then ( | python | python . enable_pip_review_local )
. unwrap_or ( false ) ;
}
2023-01-29 19:19:27 +00:00
2022-03-29 03:42:47 +03:00
pub fn display_time ( & self ) -> bool {
2023-05-25 12:22:11 +02:00
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . display_time )
. unwrap_or ( true )
2022-03-29 03:42:47 +03:00
}
2022-05-07 15:25:51 +03:00
pub fn should_run_custom_command ( & self , name : & str ) -> bool {
if self . opt . custom_commands . is_empty ( ) {
return true ;
}
self . opt . custom_commands . iter ( ) . any ( | s | s = = name )
}
2018-09-06 14:42:56 +03:00
}
2023-06-23 17:04:05 +08:00
#[ cfg(test) ]
mod test {
use crate ::config ::ConfigFile ;
/// Test the default configuration in `config.example.toml` is valid.
#[ test ]
fn test_default_config ( ) {
let str = include_str! ( " ../config.example.toml " ) ;
assert! ( toml ::from_str ::< ConfigFile > ( str ) . is_ok ( ) ) ;
}
}