2022-04-23 12:35:06 +02:00
#![ allow(dead_code) ]
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 ;
2024-09-01 03:35:23 -04:00
use std ::{ env , fmt , fs } ;
2022-11-08 05:54:35 -05:00
2023-11-24 07:50:41 +08:00
use clap ::{ Parser , ValueEnum } ;
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 ;
2025-06-17 05:58:23 +02:00
use indexmap ::IndexMap ;
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 ;
2025-07-16 10:16:27 +01:00
use strum ::IntoEnumIterator ;
2025-11-02 14:06:35 +00:00
use tracing ::{ debug , error } ;
2020-06-24 08:59:06 +03:00
use which_crate ::which ;
2018-06-08 18:19:07 +03:00
2023-11-24 07:50:41 +08:00
use super ::utils ::editor ;
2022-11-08 05:54:35 -05:00
use crate ::command ::CommandExt ;
2025-11-02 14:06:35 +00:00
use crate ::execution_context ::RunType ;
2025-07-16 10:16:27 +01:00
use crate ::step ::Step ;
2023-03-13 19:23:37 +00:00
use crate ::sudo ::SudoKind ;
2024-04-08 13:43:32 +02:00
use crate ::utils ::string_prepend_str ;
2021-12-06 13:31:05 +02:00
2024-10-03 12:47:35 +02:00
// TODO: Add i18n to this. Tracking issue: https://github.com/topgrade-rs/topgrade/issues/859
2020-06-30 10:01:22 +03:00
pub static EXAMPLE_CONFIG : & str = include_str! ( " ../config.example.toml " ) ;
2023-10-17 11:19:47 +08:00
/// Topgrade's default log level.
pub const DEFAULT_LOG_LEVEL : & str = " warn " ;
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 ( ) )
}
} ;
}
2025-06-17 05:58:23 +02:00
pub type Commands = IndexMap < String , String > ;
2018-06-20 20:26:08 +03: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
}
2023-11-24 09:44:52 +01:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Containers {
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
ignored_containers : Option < Vec < String > > ,
2024-09-01 03:35:23 -04:00
runtime : Option < ContainerRuntime > ,
2023-11-24 09:44:52 +01:00
}
2025-09-14 03:08:52 -03:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Mandb {
enable : 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-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) ]
2023-11-22 02:04:19 +01: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
}
2025-07-14 00:57:00 -07:00
#[ derive(Deserialize, Default, Debug, Copy, Clone) ]
#[ serde(rename_all = " snake_case " ) ]
pub enum UpdatesAutoReboot {
Yes ,
#[ default ]
No ,
Ask ,
}
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 > ,
2025-07-14 00:57:00 -07:00
updates_auto_reboot : Option < UpdatesAutoReboot > ,
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 > ,
2023-01-29 19:19:27 +00:00
wsl_update_pre_release : Option < bool > ,
wsl_update_use_web_download : Option < bool > ,
2025-03-30 09:11:04 -04:00
winget_silent_install : Option < bool > ,
2025-07-15 09:12:55 +02:00
winget_use_sudo : Option < bool > ,
2023-01-29 19:19:27 +00:00
}
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 > ,
2024-12-07 09:09:52 +02:00
poetry_force_self_update : Option < bool > ,
2020-06-06 17:34:27 +03:00
}
2025-07-15 06:15:42 -05:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Conda {
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
env_names : Option < Vec < String > > ,
#[ merge(strategy = crate::utils::merge_strategies::vec_prepend_opt) ]
env_paths : Option < Vec < String > > ,
}
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 > ,
}
2024-10-29 11:09:47 +01:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Deno {
version : Option < String > ,
}
2025-11-11 01:57:40 +05:45
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Chezmoi {
exclude_encrypted : 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 > ,
}
2025-07-14 09:51:02 +02:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
#[ allow(clippy::upper_case_acronyms) ]
pub struct Pixi {
include_release_notes : 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 > ,
2024-02-17 00:45:57 -03:00
greedy_latest : Option < bool > ,
2024-09-26 12:29:11 +02:00
greedy_auto_updates : Option < bool > ,
2022-10-10 20:17:50 +00:00
autoremove : Option < bool > ,
2024-02-06 10:17:27 +02:00
fetch_head : 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
}
2024-09-01 03:35:23 -04:00
#[ derive(Clone, Copy, Debug, Deserialize) ]
#[ serde(rename_all = " snake_case " ) ]
pub enum ContainerRuntime {
Docker ,
Podman ,
}
impl fmt ::Display for ContainerRuntime {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
ContainerRuntime ::Docker = > write! ( f , " docker " ) ,
ContainerRuntime ::Podman = > write! ( f , " podman " ) ,
}
}
}
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
2023-09-21 03:05:03 +02:00
#[ merge(strategy = crate::utils::merge_strategies::string_append_opt) ]
nix_env_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 > ,
2024-11-19 00:07:12 -03:00
bootc : 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 {
2025-09-26 14:49:12 +02:00
allow_root : Option < bool > ,
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) ]
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-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
2019-10-10 11:26:00 +03:00
no_retry : Option < bool > ,
2023-05-25 12:22:11 +02:00
2025-08-29 06:01:40 -03:00
show_skipped : Option < bool > ,
2019-10-10 11:26:00 +03:00
run_in_tmux : Option < bool > ,
2023-05-25 12:22:11 +02:00
2024-09-17 20:06:39 +07:00
tmux_session_mode : Option < TmuxSessionMode > ,
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
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 > ,
2023-09-17 03:04:46 -04:00
log_filters : Option < Vec < String > > ,
2025-11-08 10:58:36 +01:00
show_distribution_summary : Option < bool > ,
2023-05-25 12:22:11 +02:00
}
2024-09-17 20:06:39 +07:00
#[ derive(Clone, Copy, Debug, Deserialize, ValueEnum) ]
#[ clap(rename_all = " snake_case " ) ]
#[ serde(rename_all = " snake_case " ) ]
pub enum TmuxSessionMode {
AttachIfNotInSession ,
AttachAlways ,
}
pub struct TmuxConfig {
pub args : Vec < String > ,
pub session_mode : TmuxSessionMode ,
}
2024-06-30 22:41:09 +08:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Lensfun {
use_sudo : Option < bool > ,
}
2024-11-19 01:17:51 +00:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct JuliaConfig {
startup_file : Option < bool > ,
}
2025-02-08 06:25:10 +00:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Zigup {
target_versions : Option < Vec < String > > ,
install_dir : Option < String > ,
path_link : Option < String > ,
cleanup : Option < bool > ,
}
2025-02-11 01:51:52 -03:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct VscodeConfig {
profile : Option < String > ,
}
2025-11-08 11:03:08 +01:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct DoomConfig {
aot : Option < bool > ,
}
2025-11-06 11:04:00 +01:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Rustup {
channels : Option < Vec < String > > ,
}
2025-11-10 14:11:03 +01:00
#[ derive(Deserialize, Default, Debug, Merge) ]
#[ serde(deny_unknown_fields) ]
pub struct Pkgfile {
enable : Option < bool > ,
}
2023-05-25 12:22:11 +02:00
#[ 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 > ,
2025-07-15 06:15:42 -05:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
conda : Option < Conda > ,
2023-05-25 12:22:11 +02:00
#[ 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
2025-09-14 03:08:52 -03:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
mandb : Option < Mandb > ,
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
2023-11-24 09:44:52 +01:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
containers : Option < Containers > ,
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
2025-11-11 01:57:40 +05:45
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
chezmoi : Option < Chezmoi > ,
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
2024-10-29 11:09:47 +01:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
deno : Option < Deno > ,
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
2025-07-14 09:51:02 +02:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
pixi : Option < Pixi > ,
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 > ,
2024-06-30 22:41:09 +08:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
lensfun : Option < Lensfun > ,
2024-11-19 01:17:51 +00:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
julia : Option < JuliaConfig > ,
2025-02-08 06:25:10 +00:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
zigup : Option < Zigup > ,
2025-02-11 01:51:52 -03:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
vscode : Option < VscodeConfig > ,
2025-11-06 11:04:00 +01:00
2025-11-08 11:03:08 +01:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
doom : Option < DoomConfig > ,
2025-11-06 11:04:00 +01:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
rustup : Option < Rustup > ,
2025-11-10 14:11:03 +01:00
#[ merge(strategy = crate::utils::merge_strategies::inner_merge_opt) ]
pkgfile : Option < Pkgfile > ,
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
2024-03-24 11:24:39 +08:00
let possible_config_paths = [
2023-05-25 12:22:11 +02:00
config_directory . join ( " topgrade.toml " ) ,
config_directory . join ( " topgrade/topgrade.toml " ) ,
] ;
// Search for the main config file
2025-02-02 19:24:57 -08:00
for path in & possible_config_paths {
2023-05-25 12:22:11 +02:00
if path . exists ( ) {
debug! ( " Configuration at {} " , path . display ( ) ) ;
2024-05-06 20:37:55 +08:00
res . 0. clone_from ( path ) ;
2023-05-25 12:22:11 +02:00
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 ( ) {
2024-05-06 20:37:55 +08:00
res . 0. clone_from ( & possible_config_paths [ 0 ] ) ;
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 ? ;
2024-07-07 14:47:53 +09:00
// Use `Path::is_file()` here to traverse symbolic links.
// `DirEntry::file_type()` and `FileType::is_file()` will not traverse symbolic links.
if entry . path ( ) . is_file ( ) {
2023-05-25 12:22:11 +02:00
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 {
2024-07-22 01:33:42 +02:00
let include_contents = fs ::read_to_string ( & include ) . inspect_err ( | _ | {
2023-10-17 11:19:47 +08:00
error! ( " Unable to read {} " , include . display ( ) ) ;
2023-05-25 12:22:11 +02:00
} ) ? ;
2024-07-22 01:33:42 +02:00
let include_contents_parsed = toml ::from_str ( include_contents . as_str ( ) ) . inspect_err ( | _ | {
2023-10-17 11:19:47 +08:00
error! ( " Failed to deserialize {} " , include . display ( ) ) ;
2023-05-25 12:22:11 +02:00
} ) ? ;
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 ) ;
}
2024-07-22 01:33:42 +02:00
let mut contents_non_split = fs ::read_to_string ( & config_path ) . inspect_err ( | _ | {
2023-10-17 11:19:47 +08:00
error! ( " Unable to read {} " , config_path . display ( ) ) ;
2019-12-11 23:05:38 +02:00
} ) ? ;
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 {
2024-07-22 01:33:42 +02:00
let config_file_include_only : ConfigFileIncludeOnly = toml ::from_str ( contents ) . inspect_err ( | _ | {
2023-10-17 11:19:47 +08:00
error! ( " Failed to deserialize an include section of {} " , config_path . display ( ) ) ;
2023-05-25 12:22:11 +02:00
} ) ? ;
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 ) = > {
2024-10-03 12:47:35 +02:00
error! ( " Unable to read {}: {e} " , include_path . display ( ) , ) ;
2023-06-01 03:15:49 -04:00
continue ;
}
} ;
match toml ::from_str ::< Self > ( & include_contents ) {
Ok ( include_parsed ) = > result . merge ( include_parsed ) ,
Err ( e ) = > {
2024-10-03 12:47:35 +02:00
error! ( " Failed to deserialize {}: {e} " , include_path . display ( ) , ) ;
2023-06-01 03:15:49 -04:00
continue ;
}
} ;
}
2023-05-25 12:22:11 +02:00
}
}
match toml ::from_str ::< Self > ( contents ) {
Ok ( contents ) = > result . merge ( contents ) ,
2024-10-03 12:47:35 +02:00
Err ( e ) = > error! ( " Failed to deserialize {}: {e} " , config_path . display ( ) , ) ,
2023-05-25 12:22:11 +02:00
}
}
2023-11-22 02:04:19 +01: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
2024-10-03 12:47:35 +02:00
// TODO: i18n of clap currently not easily possible. Waiting for https://github.com/clap-rs/clap/issues/380
// Tracking issue for i18n: https://github.com/topgrade-rs/topgrade/issues/859
2022-04-23 12:35:06 +02:00
#[ derive(Parser, Debug) ]
2024-07-01 11:06:04 +02:00
#[ command(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(short = 't', long = " tmux " ) ]
2019-01-22 22:37:32 +02:00
run_in_tmux : bool ,
2018-09-06 14:42:56 +03:00
2025-09-18 09:01:19 -03:00
/// Don't run inside tmux
#[ arg(long = " no-tmux " ) ]
no_tmux : bool ,
2018-12-05 13:29:31 +02:00
/// Cleanup temporary or old files
2024-07-01 11:06:04 +02:00
#[ arg(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
2025-11-02 14:06:35 +00:00
///
/// Alias for --run-type dry
2024-07-01 11:06:04 +02:00
#[ arg(short = 'n', long = " dry-run " ) ]
2019-01-22 22:37:32 +02:00
dry_run : bool ,
2018-12-05 13:38:18 +02:00
2025-11-02 14:06:35 +00:00
/// Pick between just running commands, running and logging commands, and just logging commands
#[ arg(short = 'r', long = " run-type " , value_enum, default_value_t) ]
run_type : RunType ,
2018-12-05 13:38:18 +02:00
/// Do not ask to retry failed steps
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(long = " disable " , value_name = " STEP " , value_enum, num_args = 1..) ]
2019-01-22 22:37:32 +02:00
disable : Vec < Step > ,
2019-06-05 11:44:28 +03:00
2024-09-29 09:03:26 +08:00
/// Perform only the specified steps
2024-07-01 11:06:04 +02:00
#[ arg(long = " only " , value_name = " STEP " , value_enum, num_args = 1..) ]
2019-09-28 15:17:06 +03:00
only : Vec < Step > ,
2022-05-07 15:25:51 +03:00
/// Run only specific custom commands
2024-07-01 11:06:04 +02:00
#[ arg(long = " custom-commands " , value_name = " NAME " , num_args = 1..) ]
2022-05-07 15:25:51 +03:00
custom_commands : Vec < String > ,
2022-05-07 08:11:53 +03:00
/// Set environment variables
2024-07-01 11:06:04 +02:00
#[ arg(long = " env " , value_name = " NAME=VALUE " , num_args = 1..) ]
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`.
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(
2022-11-06 13:54:38 +00:00
short = 'y' ,
long = " yes " ,
value_name = " STEP " ,
2023-11-24 07:50:41 +08:00
value_enum ,
num_args = 0 .. ,
2022-11-06 13:54:38 +00:00
) ]
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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(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
2024-07-01 11:06:04 +02:00
#[ arg(long = " show-skipped " ) ]
2020-08-21 23:04:36 +03:00
show_skipped : bool ,
2022-11-16 13:43:57 -05:00
2025-09-26 14:49:12 +02:00
/// Suppress warning and confirmation prompt if running as root
#[ arg(long = " allow-root " ) ]
allow_root : bool ,
2022-11-16 13:43:57 -05:00
/// Tracing filter directives.
///
2025-03-17 06:07:40 +00:00
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
2024-07-01 11:06:04 +02:00
#[ arg(long, default_value = DEFAULT_LOG_LEVEL) ]
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
2024-07-01 11:06:04 +02:00
#[ arg(long, value_enum, hide = true) ]
2022-11-26 20:42:35 +01:00
pub gen_completion : Option < Shell > ,
/// Print roff manpage and exit
2024-07-01 11:06:04 +02:00
#[ arg(long, hide = true) ]
2022-11-26 20:42:35 +01:00
pub gen_manpage : bool ,
2023-03-22 22:05:21 +01:00
/// Don't update Topgrade
2024-07-01 11:06:04 +02:00
#[ arg(long = " no-self-update " ) ]
2023-03-22 22:05:21 +01:00
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
}
2023-10-17 11:19:47 +08:00
/// In Topgrade, filter directives come from 3 places:
/// 1. CLI option `--log-filter`
/// 2. Config file
/// 3. `debug` if the `--verbose` option is present
///
/// Before loading the configuration file, we need our logger to work, so this
/// function will return directives coming from part 1 and 2.
///
///
/// When the configuration file is loaded, `Config::tracing_filter_directives()`
/// will return all the 3 parts.
pub fn tracing_filter_directives ( & self ) -> String {
let mut ret = self . log_filter . clone ( ) ;
if self . verbose {
ret . push ( ',' ) ;
ret . push_str ( " debug " ) ;
}
ret
}
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.
2023-10-17 11:19:47 +08:00
#[ derive(Debug) ]
2019-01-22 22:37:32 +02:00
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
2024-10-03 12:47:35 +02:00
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
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
}
2025-07-15 06:15:42 -05:00
/// The list of additional named conda environments.
pub fn conda_env_names ( & self ) -> Option < & Vec < String > > {
self . config_file
. conda
. as_ref ( )
. and_then ( | conda | conda . env_names . as_ref ( ) )
}
/// The list of additional conda environment paths.
pub fn conda_env_paths ( & self ) -> Option < & Vec < String > > {
self . config_file
. conda
. as_ref ( )
. and_then ( | conda | conda . env_paths . as_ref ( ) )
}
2023-10-13 11:01:35 +02:00
/// The list of additional git repositories to pull.
2023-11-22 09:34:21 +08:00
pub fn git_repos ( & self ) -> Option < & Vec < String > > {
self . config_file . git . as_ref ( ) . and_then ( | git | git . repos . as_ref ( ) )
2023-10-13 11:01:35 +02:00
}
2019-01-22 22:37:32 +02:00
2023-11-24 09:44:52 +01:00
/// The list of docker/podman containers to ignore.
pub fn containers_ignored_tags ( & self ) -> Option < & Vec < String > > {
self . config_file
. containers
. as_ref ( )
. and_then ( | containers | containers . ignored_containers . as_ref ( ) )
}
2024-09-01 03:35:23 -04:00
/// The preferred runtime for container updates (podman / docker).
pub fn containers_runtime ( & self ) -> ContainerRuntime {
self . config_file
. containers
. as_ref ( )
2024-09-03 15:26:41 +08:00
. and_then ( | containers | containers . runtime )
2024-09-01 03:35:23 -04:00
. unwrap_or ( ContainerRuntime ::Docker ) // defaults to a popular choice
}
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 > {
2025-11-08 11:12:25 +01:00
// The enabled steps are
2021-12-06 14:12:20 +02:00
let mut enabled_steps : Vec < Step > = Vec ::new ( ) ;
2025-11-08 11:12:25 +01:00
// Any steps that are passed with `--only`
2021-12-06 14:12:20 +02:00
enabled_steps . extend ( & opt . only ) ;
2019-10-10 11:26:00 +03:00
2025-11-08 11:12:25 +01:00
// Plus any steps in the config file's `misc.only`
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
}
2025-11-08 11:12:25 +01:00
// If neither of those contain anything
2021-12-12 13:52:07 +02:00
if enabled_steps . is_empty ( ) {
2025-11-08 11:12:25 +01:00
// All steps are enabled
2021-12-12 13:52:07 +02:00
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
2025-11-08 11:12:25 +01:00
// All steps that are disabled are not enabled, except ones that are passed to `--only`
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 {
2025-09-18 09:01:19 -03:00
! self . opt . no_tmux
& & ( 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
}
2024-09-17 20:06:39 +07:00
/// The preferred way to run the new tmux session.
fn tmux_session_mode ( & self ) -> TmuxSessionMode {
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . tmux_session_mode )
. unwrap_or ( TmuxSessionMode ::AttachIfNotInSession )
}
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
}
2025-11-02 14:06:35 +00:00
/// Get the [RunType] for the current execution
pub fn run_type ( & self ) -> RunType {
if self . opt . dry_run {
RunType ::Dry
} else {
self . opt . run_type
}
2019-01-22 22:37:32 +02:00
}
/// 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
}
2023-11-22 02:04:19 +01:00
/// Extra Git arguments
2023-11-22 09:34:21 +08:00
pub fn git_arguments ( & self ) -> Option < & String > {
self . config_file . git . as_ref ( ) . and_then ( | git | git . arguments . as_ref ( ) )
2019-09-04 21:31:23 +03:00
}
2024-09-17 20:06:39 +07:00
pub fn tmux_config ( & self ) -> Result < TmuxConfig > {
let args = self . tmux_arguments ( ) ? ;
Ok ( TmuxConfig {
args ,
session_mode : self . tmux_session_mode ( ) ,
} )
}
2019-11-06 07:27:01 +02:00
/// Extra Tmux arguments
2024-09-17 20:06:39 +07:00
fn tmux_arguments ( & self ) -> Result < Vec < String > > {
2023-05-25 12:22:11 +02:00
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-10-18 11:13:14 +08:00
self . config_file
. windows
. as_ref ( )
. and_then ( | windows | windows . accept_all_updates )
. unwrap_or ( true )
2020-01-30 20:42:49 +02:00
}
2025-07-14 00:57:00 -07:00
/// Whether to auto reboot for Windows updates that require it
pub fn windows_updates_auto_reboot ( & self ) -> UpdatesAutoReboot {
self . config_file
. windows
. as_ref ( )
. and_then ( | windows | windows . updates_auto_reboot )
. unwrap_or_default ( )
}
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 )
}
2025-07-15 09:12:55 +02:00
/// Should use sudo for Winget
pub fn winget_use_sudo ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | w | w . winget_use_sudo )
. 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 )
}
2024-02-17 00:45:57 -03:00
/// Whether Brew cask should be greedy_latest
pub fn brew_greedy_latest ( & self ) -> bool {
self . config_file
. brew
. as_ref ( )
. and_then ( | c | c . greedy_latest )
. unwrap_or ( false )
}
2024-09-26 12:29:11 +02:00
/// Whether Brew cask should be auto_updates
pub fn brew_greedy_auto_updates ( & self ) -> bool {
self . config_file
. brew
. as_ref ( )
. and_then ( | c | c . greedy_auto_updates )
. 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 )
}
2024-02-06 10:17:27 +02:00
/// Whether Brew should upgrade formulae built from the HEAD branch
pub fn brew_fetch_head ( & self ) -> bool {
self . config_file
. brew
. as_ref ( )
. and_then ( | c | c . fetch_head )
. 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 ( " " )
}
2025-07-14 09:51:02 +02:00
/// Show release notes of latest pixi release
pub fn show_pixi_release_notes ( & self ) -> bool {
self . config_file
. pixi
. as_ref ( )
. and_then ( | s | s . include_release_notes )
. unwrap_or ( false )
}
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-09-21 03:05:03 +02:00
/// Extra nix-env arguments
pub fn nix_env_arguments ( & self ) -> Option < & str > {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . nix_env_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
}
2024-11-19 00:07:12 -03:00
/// Use bootc in *when bootc is detected* (default: false)
pub fn bootc ( & self ) -> bool {
self . config_file
. linux
. as_ref ( )
. and_then ( | linux | linux . bootc )
. unwrap_or ( false )
}
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 ( ) )
2025-02-02 19:24:57 -08:00
. is_some_and ( | v | v . contains ( & step ) )
2020-07-02 11:15:56 +03:00
}
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-10-18 11:13:14 +08:00
& & self
. config_file
. git
. as_ref ( )
. and_then ( | git | git . pull_predefined )
. unwrap_or ( true )
2020-01-28 21:04:59 +02:00
}
2020-06-19 10:34:45 +03:00
2025-11-06 11:04:00 +01:00
pub fn rustup_channels ( & self ) -> Vec < String > {
self . config_file
. rustup
. as_ref ( )
. and_then ( | rustup | rustup . channels . clone ( ) )
. unwrap_or_default ( )
}
2020-06-28 08:33:40 +03:00
pub fn verbose ( & self ) -> bool {
self . opt . verbose
}
2023-10-17 11:19:47 +08:00
/// After loading the config file, filter directives consist of 3 parts:
///
/// 1. directives from the configuration file
/// 2. directives from the CLI options `--log-filter`
/// 3. `debug`, which would be enabled if the `--verbose` option is present
///
/// Previous directive will be overwritten if a directive with the same target
/// appear later.
2023-09-17 03:04:46 -04:00
pub fn tracing_filter_directives ( & self ) -> String {
let mut ret = String ::new ( ) ;
if let Some ( directives ) = self . config_file . misc . as_ref ( ) . and_then ( | m | m . log_filters . as_ref ( ) ) {
ret . push_str ( & directives . join ( " , " ) ) ;
}
ret . push ( ',' ) ;
ret . push_str ( & self . opt . log_filter ) ;
if self . verbose ( ) {
ret . push_str ( " ,debug " ) ;
}
ret
}
2020-08-21 23:04:36 +03:00
pub fn show_skipped ( & self ) -> bool {
self . opt . show_skipped
2025-08-29 06:01:40 -03:00
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . show_skipped )
. unwrap_or ( false )
2020-08-21 23:04:36 +03:00
}
2025-09-14 03:08:52 -03:00
pub fn enable_mandb ( & self ) -> bool {
self . config_file
. mandb
. as_ref ( )
. and_then ( | mandb | mandb . enable )
. unwrap_or ( false )
}
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 )
}
2025-03-30 09:11:04 -04:00
pub fn winget_silent_install ( & self ) -> bool {
self . config_file
. windows
. as_ref ( )
. and_then ( | windows | windows . winget_silent_install )
. unwrap_or ( true )
}
2025-09-26 14:49:12 +02:00
pub fn allow_root ( & self ) -> bool {
self . opt . allow_root
| | self
. config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . allow_root )
. 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
}
2025-11-11 17:17:00 +02:00
/// If `true`, `sudo -v` should be called to cache credentials at the start of the run
2022-11-24 14:15:43 -05:00
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
}
2025-11-08 10:58:36 +01:00
pub fn show_distribution_summary ( & self ) -> bool {
self . config_file
. misc
. as_ref ( )
. and_then ( | misc | misc . show_distribution_summary )
. unwrap_or ( true )
}
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
2024-10-29 11:09:47 +01:00
pub fn deno_version ( & self ) -> Option < & str > {
self . config_file . deno . as_ref ( ) . and_then ( | deno | deno . version . as_deref ( ) )
}
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
2024-04-08 13:43:32 +02:00
pub fn should_execute_remote ( & self , hostname : Result < String > , remote : & str ) -> bool {
let remote_host = remote . split_once ( '@' ) . map_or ( remote , | ( _ , host ) | host ) ;
if let Ok ( hostname ) = hostname {
if remote_host = = hostname {
2021-12-06 13:31:05 +02:00
return false ;
}
}
2024-04-08 13:43:32 +02:00
if let Some ( limit ) = & self . opt . remote_host_limit . as_ref ( ) {
return limit . is_match ( remote_host ) ;
2021-12-06 13:31:05 +02:00
}
true
2020-08-02 06:29:16 +03:00
}
2022-03-05 21:58:36 +02:00
2023-01-29 19:19:27 +00:00
pub fn enable_pipupgrade ( & self ) -> bool {
2024-10-15 11:56:03 +02:00
self . config_file
2023-01-29 19:19:27 +00:00
. python
. as_ref ( )
. and_then ( | python | python . enable_pipupgrade )
2024-10-15 11:56:03 +02:00
. unwrap_or ( false )
2023-01-29 19:19:27 +00:00
}
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 {
2024-10-15 11:56:03 +02:00
self . config_file
2023-01-29 19:19:27 +00:00
. python
. as_ref ( )
. and_then ( | python | python . enable_pip_review )
2024-10-15 11:56:03 +02:00
. unwrap_or ( false )
2023-01-29 19:19:27 +00:00
}
2023-05-20 23:03:59 +05:30
pub fn enable_pip_review_local ( & self ) -> bool {
2024-10-15 11:56:03 +02:00
self . config_file
2023-05-20 23:03:59 +05:30
. python
. as_ref ( )
. and_then ( | python | python . enable_pip_review_local )
2024-10-15 11:56:03 +02:00
. unwrap_or ( false )
2023-05-20 23:03:59 +05:30
}
2024-12-07 09:09:52 +02:00
pub fn poetry_force_self_update ( & self ) -> bool {
self . config_file
. python
. as_ref ( )
. and_then ( | python | python . poetry_force_self_update )
. 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 )
}
2024-06-30 22:41:09 +08:00
pub fn lensfun_use_sudo ( & self ) -> bool {
self . config_file
. lensfun
. as_ref ( )
. and_then ( | lensfun | lensfun . use_sudo )
. unwrap_or ( false )
}
2024-11-19 01:17:51 +00:00
pub fn julia_use_startup_file ( & self ) -> bool {
self . config_file
. julia
. as_ref ( )
. and_then ( | julia | julia . startup_file )
. unwrap_or ( true )
}
2025-02-08 06:25:10 +00:00
pub fn zigup_target_versions ( & self ) -> Vec < String > {
self . config_file
. zigup
. as_ref ( )
. and_then ( | zigup | zigup . target_versions . clone ( ) )
. unwrap_or ( vec! [ " master " . to_owned ( ) ] )
}
pub fn zigup_install_dir ( & self ) -> Option < & str > {
self . config_file
. zigup
. as_ref ( )
. and_then ( | zigup | zigup . install_dir . as_deref ( ) )
}
pub fn zigup_path_link ( & self ) -> Option < & str > {
self . config_file
. zigup
. as_ref ( )
. and_then ( | zigup | zigup . path_link . as_deref ( ) )
}
pub fn zigup_cleanup ( & self ) -> bool {
self . config_file
. zigup
. as_ref ( )
. and_then ( | zigup | zigup . cleanup )
. unwrap_or ( false )
}
2025-02-11 01:51:52 -03:00
2025-11-11 01:57:40 +05:45
pub fn chezmoi_exclude_encrypted ( & self ) -> bool {
self . config_file
. chezmoi
. as_ref ( )
. and_then ( | chezmoi | chezmoi . exclude_encrypted )
. unwrap_or ( false )
}
2025-02-11 01:51:52 -03:00
pub fn vscode_profile ( & self ) -> Option < & str > {
let vscode_cfg = self . config_file . vscode . as_ref ( ) ? ;
let profile = vscode_cfg . profile . as_ref ( ) ? ;
if profile . is_empty ( ) {
None
} else {
Some ( profile . as_str ( ) )
}
}
2025-11-08 11:03:08 +01:00
pub fn doom_aot ( & self ) -> bool {
self . config_file
. doom
. as_ref ( )
. and_then ( | doom | doom . aot )
. unwrap_or ( false )
}
2025-11-10 14:11:03 +01:00
pub fn enable_pkgfile ( & self ) -> bool {
self . config_file
. pkgfile
. as_ref ( )
. and_then ( | pkgfile | pkgfile . enable )
. unwrap_or ( false )
}
2018-09-06 14:42:56 +03:00
}
2023-06-23 17:04:05 +08:00
#[ cfg(test) ]
mod test {
2024-04-08 13:43:32 +02:00
use crate ::config ::* ;
use color_eyre ::eyre ::eyre ;
2023-06-23 17:04:05 +08:00
/// 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 ( ) ) ;
}
2024-04-08 13:43:32 +02:00
fn config ( ) -> Config {
Config {
opt : CommandLineArgs ::parse_from ::< _ , String > ( [ ] ) ,
config_file : ConfigFile ::default ( ) ,
allowed_steps : Vec ::new ( ) ,
}
}
#[ test ]
fn test_should_execute_remote_different_hostname ( ) {
2025-02-02 19:24:57 -08:00
assert! ( config ( ) . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " remote_hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_execute_remote_different_hostname_with_user ( ) {
2025-02-02 19:24:57 -08:00
assert! ( config ( ) . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " user@remote_hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_execute_remote_unknown_hostname ( ) {
2025-02-02 19:24:57 -08:00
assert! ( config ( ) . should_execute_remote ( Err ( eyre! ( " failed to get hostname " ) ) , " remote_hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_not_execute_remote_same_hostname ( ) {
2025-02-02 19:24:57 -08:00
assert! ( ! config ( ) . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_not_execute_remote_same_hostname_with_user ( ) {
2025-02-02 19:24:57 -08:00
assert! ( ! config ( ) . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " user@hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_execute_remote_matching_limit ( ) {
let mut config = config ( ) ;
config . opt = CommandLineArgs ::parse_from ( [ " topgrade " , " --remote-host-limit " , " remote_hostname " ] ) ;
2025-02-02 19:24:57 -08:00
assert! ( config . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " user@remote_hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
#[ test ]
fn test_should_not_execute_remote_not_matching_limit ( ) {
let mut config = config ( ) ;
config . opt = CommandLineArgs ::parse_from ( [ " topgrade " , " --remote-host-limit " , " other_hostname " ] ) ;
2025-02-02 19:24:57 -08:00
assert! ( ! config . should_execute_remote ( Ok ( " hostname " . to_string ( ) ) , " user@remote_hostname " ) ) ;
2024-04-08 13:43:32 +02:00
}
2023-06-23 17:04:05 +08:00
}