Compare commits

...

50 Commits

Author SHA1 Message Date
Thomas Schönauer
3a3f22b4e5 V10 3 2 bugfix + revert #347 (#382)
* Revert "run_custom_command: use interactive shell on unix (#347)"

This reverts commit d767ef31a5.

* v10-3-3 + revert of #347
2023-03-13 19:27:33 +00:00
Thomas de Queiroz Barros
a3628d0d49 Add sudo_command option (#379)
This allows the user to specify the preferred sudo command to be used
instead of the command chosen by Sudo::detect
2023-03-13 19:23:37 +00:00
Thomas Schönauer
462016e51e 10.3.2 patch (#378)
* 10.3.2 patch

* Clippy
2023-03-12 20:37:41 +00:00
Thomas Schönauer
199b81183b Update check-and-lint.yaml to use Rust version 1.68.0 2023-03-12 20:22:58 +00:00
Thomas Schönauer
342d7f7209 skip skip-notify warning on Win (#362) 2023-03-03 11:58:58 +00:00
Isaac Tay
9c2d121fc9 cargo: add cleanup step (using cargo-cache) (#371) 2023-03-03 11:58:15 +00:00
Roey Darwish Dror
7728819133 Support antidote (#368) 2023-02-26 21:45:43 +00:00
arctic-penguin
a5d5d987d2 pacdef: support new version 1.x (#364) 2023-02-23 22:01:53 +00:00
TGRCDev
fae5d80f0a pip3: Check for EXTERNALLY-MANAGED (PEP 668) (#367) 2023-02-23 22:01:26 +00:00
Thomas Schönauer
2369e371be apt: Recognise mist (#351) 2023-02-18 21:22:02 +00:00
Jason Stelzer
e3b71b647f Silence misleading warning on other platforms. (#353)

2023-02-07 17:21:15 +00:00
Guilherme Silva
e224ea38b3 CI: Update cross to v0.2.5 (#354) 2023-02-07 17:19:46 +00:00
Thomas Schönauer
8ec37bcd44 vim: Adds Astrovim support (#352) 2023-02-03 13:46:09 +00:00
Thomas Schönauer
6b7f6f4cc7 ruby_gems: Fixes asdf (#350) 2023-02-02 21:48:48 +00:00
Utkarsh Gupta
d767ef31a5 run_custom_command: use interactive shell on unix (#347) 2023-02-02 19:46:11 +00:00
Dan Sully
fcf776fe07 Add support for please (access elevation) (#310)
* Add support for please (access elevation)

Please is a sudo-like tool written in Rust.

https://gitlab.com/edneville/please

* Fixes code typo

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
Co-authored-by: Thomas Schönauer <t.schoenauer@hgs-wt.at>
2023-02-02 19:22:56 +00:00
edi
58060dda09 use documented way of updating (#344) 2023-01-31 22:19:01 +00:00
Thomas Schönauer
8cfc8d66be v10.3.1 patch (#342) 2023-01-30 21:24:06 +00:00
edi
9dcc8fdd0d (neo)vim: topgrade should only invoke plugin managers not plugins (#341)
* fix upgrade order of (n)vim plugins

* treesitter should use the synchronous cmd

* add lazy pkg manager for neovim

* fix lazy cmd

* change calls

* add autocmd, remove ts and coc

* fix vimscript err invalid range

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-01-30 18:42:13 +00:00
Thomas Schönauer
828477b255 Update README.md 2023-01-30 18:41:48 +00:00
arctic-penguin
4eae1fedf7 fix ignored config display_preamble = false (#340)
Bug was introduced in f1e4009. Fixes #337.
2023-01-30 18:41:00 +00:00
Thomas Schönauer
1051e4cf47 AM fix + version bump (#335) 2023-01-29 21:53:26 +00:00
Thomas Schönauer
80a95cb404 Clippy (#331) 2023-01-29 19:31:37 +00:00
Thomas Schönauer
ab630cfbc6 v10.2.5 release (#330)
* Don't show desktop notification on error (if `skip_notify = true`) (#275)

* Use ─ (U+2500) to draw borders (#282)

* Adds Pclinuxos support (#283)

* Add Devkitpro Pacman support (#291)

* Added support for Neovim package manager lazy.nvim (#293)

* Added support for lazy.nvim

From https://github.com/folke/lazy.nvim
Authored-by: Jacob Lane Ledbetter <jledbetter460@gmail.com>

* Make garuda-update update AUR packages by default (#296)

* fix(#298): Don't throw error if no Helm repository found (#305)

* Skip .NET when `dotnet tool list` is not successful (#302)

* feat(pacstall): add `-y` flag variant (#312)

* Add openSUSE MicroOS support (#315)

* Adds notify-send timeout of 10s (#318)

* Don't run yum when rpm-ostree is available (#313)

* don't run yum when rpm-ostree is available

* Clippy fix

* rpm-ostree: set default value to true

* Fixes if loop error

* Fixes gem update --system requires sudo now (#317)

* Fixes gem update --system requires sudo now

* rubygem: Adds arg -EH to sudo

* Use fixed nala path instead of which(nala) (#314)

* Adds notify-send bug warning when topgrade is run (#324)

* Adds notify-send bug warning when topgrade is run

* fix typo + clippy

* notify-send warning respects skip_notify flag

* nix: Adds additional arguments support (#325)

* Adds pip-review and pipupgrade support (#316)

* Adds pip-review and pipupgrade support

* Python: fixes pip_review and pipupgrade

* v10.2.5 patch (#329)

* WSL: Adds new wsl --update flags (#327)

* wsl: Updates available flags

* Clippy fix

* Add WslUpdate runner

* wsl: Code Typo

* wsl: Code Typos

* wsl: Code Typos

* wsl: Code Typo

* Adds AM Package Manager (#328)

* Adds AM Package Manager

* Clippy fixes

* Cargo fmt

* Moves am to linux only in main file

---------

Co-authored-by: Guilherme Silva <626206+guihkx@users.noreply.github.com>
Co-authored-by: Gabriel Augendre <gabriel@augendre.info>
Co-authored-by: Cat Core <34719527+arthurbambou@users.noreply.github.com>
Co-authored-by: Hugo Haas <hugoh@hugoh.net>
Co-authored-by: Baptiste <32563450+BapRx@users.noreply.github.com>
Co-authored-by: bbx0 <39773919+bbx0@users.noreply.github.com>
Co-authored-by: Sourajyoti Basak <wiz28@protonmail.com>
2023-01-29 19:19:27 +00:00
edi
c13e14080c Add Lazy, a Neovim plugin manager (#326)
* fix upgrade order of (n)vim plugins

* treesitter should use the synchronous cmd

* add lazy pkg manager for neovim

* fix lazy cmd

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-01-29 18:49:56 +00:00
edi
4abbee99cc fix upgrade order of (n)vim plugins (#322)
* fix upgrade order of (n)vim plugins

* treesitter should use the synchronous cmd

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-01-27 21:41:48 +00:00
Giovanni Merlino
45d935eda3 Fix missing separator for Pkgin (pkgsrc) (#307)
* Fix missing separator for Pkgin (pkgsrc)

* Fix whitespace (giving issues with cargo fmt?)

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-01-27 21:34:27 +00:00
pwygab
b4c5efde50 Fix small typo in warning when notify-send fails (#319)
Fix small typo
2023-01-27 20:52:20 +00:00
Roey Darwish Dror
cba9dc1c2c Fix windows build (#303) 2023-01-09 08:50:21 +01:00
dependabot[bot]
938647123c Bump tokio from 1.8.5 to 1.18.4 (#301) 2023-01-06 23:13:01 +00:00
Jacob Lane Ledbetter
9f24f6474e Add dnf config to config example (#292) 2022-12-30 22:52:04 +00:00
Thomas Schönauer
814e39644c fixes dotnet update + version bump 10.2.4 (#274) 2022-12-18 15:37:10 +00:00
Thomas Schönauer
eb51be0732 10.2.3 release (#272) 2022-12-18 14:20:00 +00:00
Thomas Schönauer
56a717dcc6 Fixes dotnet if dotnet tool is not available (#229)
* Fixes dotnet if dotnet tool si not available

* dotnet: multi-lang support

* Fixes dotnet for non-english terminals
2022-12-18 14:12:36 +00:00
Thomas Schönauer
ee353ccb66 adds fish_update_completions to fish step (#270) 2022-12-18 14:11:36 +00:00
Thomas Schönauer
33cea0e5b6 10.2.3 release (#271)
10.2.3
2022-12-18 14:07:31 +00:00
Thomas Schönauer
78a491a976 Fixes rubygems on .deb distros (#268)
Fixes rubygem on systems with the .deb ruby package
2022-12-15 20:27:27 +00:00
Thomas Schönauer
9553be04e4 Fixes vcpkg when installed as root (#266) 2022-12-15 11:39:25 +00:00
Thomas Schönauer
81928f55a2 Fix notify not available (#257) 2022-12-15 11:34:18 +00:00
Mark Nefedov
4e56bf07f3 Add options to disable Yarn and Pnpm individually. (#260)
* Add options to disable Yarn and Pnpm individualy.

* cargo fmt

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2022-12-14 21:42:48 +00:00
sandal
9f424f03c3 Added a step for fetching and building treesitter grammars for helix (#263) 2022-12-14 08:12:39 +00:00
Thomas Kosiewski
1e14b3bf28 Add helm (#255) (#256)
* Add `helm` (#255)

Signed-off-by: Thomas Kosiewski <thoma471@googlemail.com>

* Sorted steps alphabetically

Signed-off-by: Thomas Kosiewski <thoma471@googlemail.com>

Signed-off-by: Thomas Kosiewski <thoma471@googlemail.com>
2022-12-08 21:47:57 +00:00
Thomas Schönauer
51e7a31f48 10.2.2 (#253) 2022-12-06 19:48:46 +00:00
Thomas Schönauer
9847fd9d4d Merge branch 'master' into dev 2022-12-06 19:23:45 +00:00
Thomas Schönauer
24ef44291f 10.2.2 version bump (#252) 2022-12-06 19:22:06 +00:00
Julien ITARD
ebb0c5a6d8 Fix RubyGems step (#251)
* Bump tokio from 1.5.1 to 1.8.4 (#245)

Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.5.1 to 1.8.4.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.5.1...tokio-1.8.4)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix RubyGems step

* Fix style

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 19:13:07 +00:00
Armel Soro
1dee462175 Fix misleading log message when detecting rbenv (#249) 2022-12-05 17:42:26 +00:00
Armel Soro
dc82b8b766 Fix issue with RubyGems update command (#248) 2022-12-05 13:16:03 +00:00
dependabot[bot]
73888e7669 Bump tokio from 1.5.1 to 1.8.4 (#245)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.5.1 to 1.8.4.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.5.1...tokio-1.8.4)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-30 21:56:52 +00:00
dependabot[bot]
46a010cc8f Bump tokio from 1.5.1 to 1.8.4 (#147)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.5.1 to 1.8.4.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.5.1...tokio-1.8.4)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2022-11-30 21:29:39 +00:00
27 changed files with 882 additions and 401 deletions

View File

@@ -7,8 +7,8 @@ on:
name: CI
env:
RUST_VER: '1.60.0'
CROSS_VER: '0.2.4'
RUST_VER: '1.68.0'
CROSS_VER: '0.2.5'
CARGO_NET_RETRY: 3
jobs:

581
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,8 @@ description = "Upgrade all the things"
categories = ["os"]
keywords = ["upgrade", "update"]
license = "GPL-3.0"
# license-file = "LICENSE"
repository = "https://github.com/topgrade-rs/topgrade"
version = "10.2.1"
version = "10.3.3"
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
exclude = ["doc/screenshot.gif"]
edition = "2021"
@@ -39,14 +38,14 @@ strum = { version = "~0.24", features = ["derive"] }
thiserror = "~1.0"
tempfile = "~3.2"
cfg-if = "~1.0"
tokio = { version = "~1.5", features = ["process", "rt-multi-thread"] }
tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] }
futures = "~0.3"
regex = "~1.5"
semver = "~1.0"
shell-words = "~1.1"
color-eyre = "0.6.2"
tracing = { version = "0.1.37", features = ["attributes", "log"] }
tracing-subscriber = { version = "0.3.16", features = ["env-filter", "time"] }
color-eyre = "~0.6"
tracing = { version = "~0.1", features = ["attributes", "log"] }
tracing-subscriber = { version = "~0.3", features = ["env-filter", "time"] }
[target.'cfg(target_os = "macos")'.dependencies]
notify-rust = "~4.5"

View File

@@ -1,4 +0,0 @@
# Workaround for: https://github.com/cross-rs/cross/issues/1100
# TODO: Remove this file altogether once a new version of cross (after v0.2.4) is released.
[target.x86_64-unknown-freebsd.env]
passthrough = ["AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar"]

View File

@@ -80,8 +80,6 @@ Just fork the repository and start coding.
- Check if your code passes `cargo fmt` and `cargo clippy`.
- Check if your code is self explanatory, if not it should be documented by comments.
- Make a pull request to the `dev` branch for new features or to the `bug-fixes` branch for bug fixes.
## Roadmap
- [ ] Add a proper testing framework to the code base.

View File

@@ -13,6 +13,9 @@
# Do not ask to retry failed steps (default: false)
#no_retry = true
# Sudo command to be used
#sudo_command = "sudo"
# Run `sudo -v` to cache credentials at the start of the run; this avoids a
# blocking password prompt in the middle of a possibly-unattended run.
#pre_sudo = false
@@ -44,6 +47,9 @@
# Skip sending a notification at the end of a run
#skip_notify = true
# Skip the preamble displayed when topgrade is run
#display_preamble = false
[git]
#max_concurrency = 5
# Additional git repositories to pull
@@ -74,12 +80,15 @@
#autoremove = true
[linux]
# Arch Package Manager to use. Allowed values: autodetect, trizen, aura, paru, yay, pikaur, pacman, pamac.
# Arch Package Manager to use. Allowed values: autodetect, aura, garuda_update, pacman, pamac, paru, pikaur, trizen, yay.
#arch_package_manager = "pacman"
# Arguments to pass yay (or paru) when updating packages
#yay_arguments = "--nodevel"
# Arguments to pass dnf when updating packages
#dnf_arguments = "--refresh"
#aura_aur_arguments = "-kx"
#aura_pacman_arguments = ""
#garuda_update_arguments = ""
#show_arch_news = true
#trizen_arguments = "--devel"
#pikaur_arguments = ""
@@ -89,11 +98,18 @@
#emerge_update_flags = "-uDNa --with-bdeps=y world"
#redhat_distro_sync = false
#rpm_ostree = false
#nix_arguments = "--flake"
[python]
#enable_pip_review = true ###disabled by default
#enable_pipupgrade = true ###disabled by default
[windows]
# Manually select Windows updates
#accept_all_updates = false
#open_remotes_in_new_terminal = true
#wsl_update_pre_release = true
#wsl_update_use_web_download = true
# Causes Topgrade to rename itself during the run to allow package managers
# to upgrade it. Use this only if you installed Topgrade by using a package

View File

@@ -18,6 +18,7 @@ use tracing::debug;
use which_crate::which;
use crate::command::CommandExt;
use crate::sudo::SudoKind;
use super::utils::{editor, hostname};
@@ -71,12 +72,13 @@ type Commands = BTreeMap<String, String>;
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Step {
AM,
Asdf,
Atom,
Bin,
BrewCask,
BrewFormula,
Bun,
Bin,
Cargo,
Chezmoi,
Chocolatey,
@@ -87,8 +89,9 @@ pub enum Step {
Containers,
CustomCommands,
DebGet,
Distrobox,
Deno,
Distrobox,
DkpPacman,
Dotnet,
Emacs,
Firmware,
@@ -100,15 +103,17 @@ pub enum Step {
Ghcup,
GithubCliExtensions,
GitRepos,
GnomeShellExtensions,
Go,
Guix,
Haxelib,
GnomeShellExtensions,
Helm,
HomeManager,
Jetpack,
Julia,
Juliaup,
Kakoune,
Helix,
Krew,
Macports,
Mas,
@@ -120,10 +125,13 @@ pub enum Step {
Pacdef,
Pacstall,
Pearl,
Pipx,
Pip3,
PipReview,
Pipupgrade,
Pipx,
Pkg,
Pkgin,
Pnpm,
Powershell,
Protonup,
Raco,
@@ -151,7 +159,9 @@ pub enum Step {
Vim,
Winget,
Wsl,
WslUpdate,
Yadm,
Yarn,
}
#[derive(Deserialize, Default, Debug)]
@@ -178,6 +188,15 @@ pub struct Windows {
self_rename: Option<bool>,
open_remotes_in_new_terminal: Option<bool>,
enable_winget: Option<bool>,
wsl_update_pre_release: Option<bool>,
wsl_update_use_web_download: Option<bool>,
}
#[derive(Deserialize, Default, Debug)]
#[serde(deny_unknown_fields)]
pub struct Python {
enable_pip_review: Option<bool>,
enable_pipupgrade: Option<bool>,
}
#[derive(Deserialize, Default, Debug)]
@@ -226,15 +245,15 @@ pub struct Brew {
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ArchPackageManager {
GarudaUpdate,
Autodetect,
Trizen,
Paru,
Yay,
Pacman,
Pikaur,
Pamac,
Aura,
GarudaUpdate,
Pacman,
Pamac,
Paru,
Pikaur,
Trizen,
Yay,
}
#[derive(Deserialize, Default, Debug)]
@@ -245,10 +264,12 @@ pub struct Linux {
aura_pacman_arguments: Option<String>,
arch_package_manager: Option<ArchPackageManager>,
show_arch_news: Option<bool>,
garuda_update_arguments: Option<String>,
trizen_arguments: Option<String>,
pikaur_arguments: Option<String>,
pamac_arguments: Option<String>,
dnf_arguments: Option<String>,
nix_arguments: Option<String>,
apt_arguments: Option<String>,
enable_tlmgr: Option<bool>,
redhat_distro_sync: Option<bool>,
@@ -273,6 +294,7 @@ pub struct Vim {
#[serde(deny_unknown_fields)]
/// Configuration file
pub struct ConfigFile {
sudo_command: Option<SudoKind>,
pre_sudo: Option<bool>,
pre_commands: Option<Commands>,
post_commands: Option<Commands>,
@@ -288,10 +310,12 @@ pub struct ConfigFile {
tmux_arguments: Option<String>,
set_title: Option<bool>,
display_time: Option<bool>,
display_preamble: Option<bool>,
assume_yes: Option<bool>,
yay_arguments: Option<String>,
aura_aur_arguments: Option<String>,
aura_pacman_arguments: Option<String>,
python: Option<Python>,
no_retry: Option<bool>,
run_in_tmux: Option<bool>,
cleanup: Option<bool>,
@@ -735,6 +759,24 @@ impl Config {
.unwrap_or(false)
}
// 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)
}
/// Whether Brew cask should be greedy
pub fn brew_cask_greedy(&self) -> bool {
self.config_file
@@ -776,6 +818,15 @@ impl Config {
self.config_file.notify_each_step.unwrap_or(false)
}
/// 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("")
}
/// Extra trizen arguments
pub fn trizen_arguments(&self) -> &str {
self.config_file
@@ -863,6 +914,14 @@ impl Config {
.and_then(|linux| linux.dnf_arguments.as_deref())
}
/// Extra nix arguments
pub fn nix_arguments(&self) -> Option<&str> {
self.config_file
.linux
.as_ref()
.and_then(|linux| linux.nix_arguments.as_deref())
}
/// Distrobox use root
pub fn distrobox_root(&self) -> bool {
self.config_file
@@ -927,7 +986,7 @@ impl Config {
.linux
.as_ref()
.and_then(|linux| linux.rpm_ostree)
.unwrap_or(false)
.unwrap_or(true)
}
/// Should we ignore failures for this step
@@ -960,6 +1019,10 @@ impl Config {
.unwrap_or(false)
}
pub fn sudo_command(&self) -> Option<SudoKind> {
self.config_file.sudo_command
}
/// 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 {
@@ -1031,10 +1094,31 @@ impl Config {
.unwrap_or(false);
}
pub fn enable_pipupgrade(&self) -> bool {
return self
.config_file
.python
.as_ref()
.and_then(|python| python.enable_pipupgrade)
.unwrap_or(false);
}
pub fn enable_pip_review(&self) -> bool {
return self
.config_file
.python
.as_ref()
.and_then(|python| python.enable_pip_review)
.unwrap_or(false);
}
pub fn display_time(&self) -> bool {
self.config_file.display_time.unwrap_or(true)
}
pub fn display_preamble(&self) -> bool {
self.config_file.display_preamble.unwrap_or(true)
}
pub fn should_run_custom_command(&self, name: &str) -> bool {
if self.opt.custom_commands.is_empty() {
return true;

View File

@@ -3,6 +3,7 @@
use std::env;
use std::io;
use std::process::exit;
use std::time::Duration;
use clap::CommandFactory;
use clap::{crate_version, Parser};
@@ -85,6 +86,16 @@ fn run() -> Result<()> {
debug!("Binary path: {:?}", std::env::current_exe());
debug!("Self Update: {:?}", cfg!(feature = "self-update"));
#[cfg(target_os = "linux")]
{
if config.display_preamble() && !config.skip_notify() {
print_warning("Due to a design issue with notify-send it could be that topgrade hangs when it's finished.
If this is the case on your system add the --skip-notify flag to the topgrade command or set skip_notify = true in the config file.
If you don't want this message to appear any longer set display_preamble = false in the config file.
For more information about this issue see https://askubuntu.com/questions/110969/notify-send-ignores-timeout and https://github.com/topgrade-rs/topgrade/issues/288.");
}
}
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
#[cfg(unix)]
{
@@ -96,7 +107,7 @@ fn run() -> Result<()> {
let git = git::Git::new();
let mut git_repos = git::Repositories::new(&git);
let sudo = sudo::Sudo::detect();
let sudo = config.sudo_command().map_or_else(sudo::Sudo::detect, sudo::Sudo::new);
let run_type = executor::RunType::new(config.dry_run());
let ctx = execution_context::ExecutionContext::new(run_type, sudo, &git, &config, &base_dirs);
@@ -115,7 +126,7 @@ fn run() -> Result<()> {
return result;
}
}
print_warning(format!("Self update error: {}", e));
print_warning(format!("Self update error: {e}"));
}
}
}
@@ -145,9 +156,12 @@ fn run() -> Result<()> {
#[cfg(windows)]
runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(&ctx))?;
#[cfg(windows)]
runner.execute(Step::WslUpdate, "WSL", || windows::update_wsl(&ctx))?;
if let Some(topgrades) = config.remote_topgrades() {
for remote_topgrade in topgrades.iter().filter(|t| config.should_execute_remote(t)) {
runner.execute(Step::Remotes, format!("Remote ({})", remote_topgrade), || {
runner.execute(Step::Remotes, format!("Remote ({remote_topgrade})"), || {
remote::ssh::ssh_step(&ctx, remote_topgrade)
})?;
}
@@ -163,7 +177,7 @@ fn run() -> Result<()> {
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
}
Err(e) => {
println!("Error detecting current distribution: {}", e);
println!("Error detecting current distribution: {e}");
}
}
runner.execute(Step::ConfigUpdate, "config-update", || linux::run_config_update(&ctx))?;
@@ -305,6 +319,7 @@ fn run() -> Result<()> {
{
runner.execute(Step::Shell, "zr", || zsh::run_zr(&base_dirs, run_type))?;
runner.execute(Step::Shell, "antibody", || zsh::run_antibody(run_type))?;
runner.execute(Step::Shell, "antidote", || zsh::run_antidote(&ctx))?;
runner.execute(Step::Shell, "antigen", || zsh::run_antigen(&base_dirs, run_type))?;
runner.execute(Step::Shell, "zgenom", || zsh::run_zgenom(&base_dirs, run_type))?;
runner.execute(Step::Shell, "zplug", || zsh::run_zplug(&base_dirs, run_type))?;
@@ -348,10 +363,12 @@ fn run() -> Result<()> {
runner.execute(Step::Go, "gup", || go::run_go_gup(run_type))?;
runner.execute(Step::Emacs, "Emacs", || emacs.upgrade(&ctx))?;
runner.execute(Step::Opam, "opam", || generic::run_opam_update(&ctx))?;
runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(run_type))?;
runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(&ctx))?;
runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(run_type))?;
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(run_type))?;
runner.execute(Step::PipReview, "pip-review", || generic::run_pip_review_update(&ctx))?;
runner.execute(Step::Pipupgrade, "pipupgrade", || generic::run_pipupgrade_update(&ctx))?;
runner.execute(Step::Ghcup, "ghcup", || generic::run_ghcup_update(run_type))?;
runner.execute(Step::Stack, "stack", || generic::run_stack_update(run_type))?;
runner.execute(Step::Tlmgr, "tlmgr", || generic::run_tlmgr_update(&ctx))?;
@@ -367,17 +384,17 @@ fn run() -> Result<()> {
runner.execute(Step::Vim, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(&ctx))?;
runner.execute(Step::Vim, "voom", || vim::run_voom(&base_dirs, run_type))?;
runner.execute(Step::Kakoune, "Kakoune", || kakoune::upgrade_kak_plug(&ctx))?;
runner.execute(Step::Helix, "helix", || generic::run_helix_grammars(&ctx))?;
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
runner.execute(Step::Node, "yarn", || node::run_yarn_upgrade(&ctx))?;
runner.execute(Step::Node, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
runner.execute(Step::Yarn, "yarn", || node::run_yarn_upgrade(&ctx))?;
runner.execute(Step::Pnpm, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
runner.execute(Step::Krew, "krew", || generic::run_krew_upgrade(run_type))?;
runner.execute(Step::Helm, "helm", || generic::run_helm_repo_update(run_type))?;
runner.execute(Step::Gem, "gem", || generic::run_gem(&base_dirs, run_type))?;
runner.execute(Step::RubyGems, "rubygems", || {
generic::run_rubygems(&base_dirs, run_type)
})?;
runner.execute(Step::RubyGems, "rubygems", || generic::run_rubygems(&ctx))?;
runner.execute(Step::Julia, "julia", || generic::update_julia_packages(&ctx))?;
runner.execute(Step::Haxelib, "haxelib", || generic::run_haxelib_update(&ctx))?;
runner.execute(Step::Sheldon, "sheldon", || generic::run_sheldon(&ctx))?;
@@ -395,6 +412,7 @@ fn run() -> Result<()> {
#[cfg(target_os = "linux")]
{
runner.execute(Step::AM, "am", || linux::update_am(&ctx))?;
runner.execute(Step::DebGet, "deb-get", || linux::run_deb_get(&ctx))?;
runner.execute(Step::Toolbx, "toolbx", || toolbx::run_toolbx(&ctx))?;
runner.execute(Step::Flatpak, "Flatpak", || linux::flatpak_update(&ctx))?;
@@ -403,6 +421,7 @@ fn run() -> Result<()> {
runner.execute(Step::Pacdef, "pacdef", || linux::run_pacdef(&ctx))?;
runner.execute(Step::Protonup, "protonup", || linux::run_protonup_update(&ctx))?;
runner.execute(Step::Distrobox, "distrobox", || linux::run_distrobox_update(&ctx))?;
runner.execute(Step::DkpPacman, "dkp-pacman", || linux::run_dkp_pacman_update(&ctx))?;
}
if let Some(commands) = config.commands() {
@@ -514,8 +533,8 @@ fn run() -> Result<()> {
"Topgrade finished {}",
if failed { "with errors" } else { "successfully" }
),
None,
);
Some(Duration::from_secs(10)),
)
}
if failed {
@@ -548,7 +567,7 @@ fn main() {
// The `Debug` implementation of `eyre::Result` prints a multi-line
// error message that includes all the 'causes' added with
// `.with_context(...)` calls.
println!("Error: {:?}", error);
println!("Error: {error:?}");
}
exit(1);
}

View File

@@ -34,7 +34,7 @@ impl<'a> Report<'a> {
if let Some((key, success)) = result {
let key = key.into();
debug_assert!(!self.data.iter().any(|(k, _)| k == &key), "{} already reported", key);
debug_assert!(!self.data.iter().any(|(k, _)| k == &key), "{key} already reported");
self.data.push((key, success));
}
}

View File

@@ -31,7 +31,7 @@ pub fn self_update() -> Result<()> {
if let UpdateStatus::Updated(release) = &result {
println!("\nTopgrade upgraded to {}:\n", release.version);
if let Some(body) = &release.body {
println!("{}", body);
println!("{body}");
}
} else {
println!("Topgrade is up-to-date");

View File

@@ -10,15 +10,15 @@ use color_eyre::eyre::Context;
use color_eyre::eyre::Result;
use directories::BaseDirs;
use tempfile::tempfile_in;
use tracing::debug;
use tracing::{debug, error};
use crate::command::{CommandExt, Utf8Output};
use crate::execution_context::ExecutionContext;
use crate::executor::{ExecutorOutput, RunType};
use crate::terminal::{print_separator, shell};
use crate::utils::{self, require_option, PathExt};
use crate::utils::{self, require, require_option, which, PathExt};
use crate::{
error::{SkipStep, TopgradeError},
error::{SkipStep, StepFailed, TopgradeError},
terminal::print_warning,
};
@@ -56,7 +56,24 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type()
.execute(cargo_update)
.args(["install-update", "--git", "--all"])
.status_checked()
.status_checked()?;
if ctx.config().cleanup() {
let cargo_cache = utils::require("cargo-cache")
.ok()
.or_else(|| cargo_dir.join("bin/cargo-cache").if_exists());
match cargo_cache {
Some(e) => {
ctx.run_type().execute(e).args(["-a"]).status_checked()?;
}
None => {
let message = String::from("cargo-cache isn't installed so Topgrade can't cleanup cargo packages.\nInstall cargo-cache by running `cargo install cargo-cache`");
print_warning(message);
}
}
}
Ok(())
}
pub fn run_flutter_upgrade(run_type: RunType) -> Result<()> {
@@ -83,21 +100,30 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
command.status_checked()
}
pub fn run_rubygems(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
let gem = utils::require("gem")?;
base_dirs.home_dir().join(".gem").require()?;
pub fn run_rubygems(ctx: &ExecutionContext) -> Result<()> {
ctx.base_dirs().home_dir().join(".gem").require()?;
let gem = require("gem")?;
print_separator("RubyGems");
let mut command = run_type.execute(gem);
command.arg("update --system");
if env::var_os("RBENV_SHELL").is_none() {
debug!("Detected rbenv. Avoiding --user-install");
command.arg("--user-install");
let gem_path_str = gem.as_os_str();
if gem_path_str.to_str().unwrap().contains("asdf") {
ctx.run_type()
.execute(gem)
.args(["update", "--system"])
.status_checked()?;
} else if let Some(sudo) = &ctx.sudo() {
if !std::path::Path::new("/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb").exists() {
ctx.run_type()
.execute(sudo)
.arg("-EH")
.arg(gem)
.args(["update", "--system"])
.status_checked()?;
}
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
command.status_checked()
Ok(())
}
pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
@@ -268,14 +294,27 @@ pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
pub fn run_vcpkg_update(run_type: RunType) -> Result<()> {
pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
let vcpkg = utils::require("vcpkg")?;
print_separator("vcpkg");
run_type
.execute(vcpkg)
.args(["upgrade", "--no-dry-run"])
.status_checked()
#[cfg(unix)]
let is_root_install = !&vcpkg.starts_with("/home");
#[cfg(not(unix))]
let is_root_install = false;
let mut command = if is_root_install {
ctx.run_type().execute(&vcpkg)
} else {
let mut c = ctx
.run_type()
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?);
c.arg(&vcpkg);
c
};
command.args(["upgrade", "--no-dry-run"]).status_checked()
}
pub fn run_pipx_update(run_type: RunType) -> Result<()> {
@@ -311,6 +350,21 @@ pub fn run_pip3_update(run_type: RunType) -> Result<()> {
.output_checked_utf8()
.map_err(|_| SkipStep("pip does not exists".to_string()))?;
let check_externally_managed = "import sysconfig; from os import path; print('Y') if path.isfile(path.join(sysconfig.get_path('stdlib'), 'EXTERNALLY-MANAGED')) else print('N')";
Command::new(&python3)
.args(["-c", check_externally_managed])
.output_checked_utf8()
.map_err(|_| SkipStep("pip may be externally managed".to_string()))
.and_then(|output| match output.stdout.trim() {
"N" => Ok(()),
"Y" => Err(SkipStep("pip is externally managed".to_string())),
_ => {
print_warning("Unexpected output when checking EXTERNALLY-MANAGED");
print_warning(output.stdout.trim());
Err(SkipStep("pip may be externally managed".to_string()))
}
})?;
print_separator("pip3");
if std::env::var("VIRTUAL_ENV").is_ok() {
print_warning("This step is will be skipped when running inside a virtual environment");
@@ -323,6 +377,39 @@ pub fn run_pip3_update(run_type: RunType) -> Result<()> {
.status_checked()
}
pub fn run_pip_review_update(ctx: &ExecutionContext) -> Result<()> {
let pip_review = require("pip-review")?;
print_separator("pip-review");
if !ctx.config().enable_pip_review() {
print_warning(
"Pip-review is disabled by default. Enable it by setting enable_pip_review=true in the configuration.",
);
return Err(SkipStep(String::from("Pip-review is disabled by default")).into());
}
ctx.run_type()
.execute(pip_review)
.arg("--auto")
.status_checked_with_codes(&[1])?;
Ok(())
}
pub fn run_pipupgrade_update(ctx: &ExecutionContext) -> Result<()> {
let pipupgrade = require("pipupgrade")?;
print_separator("Pipupgrade");
if !ctx.config().enable_pip_review() {
print_warning(
"Pipupgrade is disabled by default. Enable it by setting enable_pipupgrade=true in the configuration.",
);
return Err(SkipStep(String::from("Pipupgrade is disabled by default")).into());
}
ctx.run_type().execute(pipupgrade).status_checked()?;
Ok(())
}
pub fn run_stack_update(run_type: RunType) -> Result<()> {
if utils::require("ghcup").is_ok() {
// `ghcup` is present and probably(?) being used to install `stack`.
@@ -426,7 +513,7 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
let composer_home = Command::new(&composer)
.args(["global", "config", "--absolute", "--quiet", "home"])
.output_checked_utf8()
.map_err(|e| (SkipStep(format!("Error getting the composer directory: {}", e))))
.map_err(|e| (SkipStep(format!("Error getting the composer directory: {e}"))))
.map(|s| PathBuf::from(s.stdout.trim()))?
.require()?;
@@ -479,9 +566,21 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
let dotnet = utils::require("dotnet")?;
let output = Command::new(dotnet)
//Skip when the `dotnet tool list` subcommand fails. (This is expected when a dotnet runtime is installed but no SDK.)
let output = match ctx
.run_type()
.execute(&dotnet)
.args(["tool", "list", "--global"])
.output_checked_utf8()?;
.output_checked_utf8()
{
Ok(output) => output,
Err(_) => {
return Err(SkipStep(String::from(
"Error running `dotnet tool list`. This is expected when a dotnet runtime is installed but no SDK.",
))
.into())
}
};
if !output.stdout.starts_with("Package Id") {
return Err(SkipStep(String::from("dotnet did not output packages")).into());
@@ -498,7 +597,7 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
for package in packages {
let package_name = package.split_whitespace().next().unwrap();
ctx.run_type()
.execute("dotnet")
.execute(&dotnet)
.args(["tool", "update", package_name, "--global"])
.status_checked()
.with_context(|| format!("Failed to update .NET package {package_name}"))?;
@@ -507,6 +606,26 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
pub fn run_helix_grammars(ctx: &ExecutionContext) -> Result<()> {
utils::require("helix")?;
print_separator("Helix");
ctx.run_type()
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?)
.args(["helix", "--grammar", "fetch"])
.status_checked()
.with_context(|| "Failed to download helix grammars!")?;
ctx.run_type()
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?)
.args(["helix", "--grammar", "build"])
.status_checked()
.with_context(|| "Failed to build helix grammars!")?;
Ok(())
}
pub fn run_raco_update(run_type: RunType) -> Result<()> {
let raco = utils::require("raco")?;
@@ -554,3 +673,29 @@ pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
.args(["-e", "using Pkg; Pkg.update()"])
.status_checked()
}
pub fn run_helm_repo_update(run_type: RunType) -> Result<()> {
let helm = utils::require("helm")?;
print_separator("Helm");
let no_repo = "no repositories found";
let mut success = true;
let mut exec = run_type.execute(helm);
if let Err(e) = exec.arg("repo").arg("update").status_checked() {
error!("Updating repositories failed: {}", e);
success = match exec.output_checked_utf8() {
Ok(s) => s.stdout.contains(no_repo) || s.stderr.contains(no_repo),
Err(e) => match e.downcast_ref::<TopgradeError>() {
Some(TopgradeError::ProcessFailedWithOutput(_, _, stderr)) => stderr.contains(no_repo),
_ => false,
},
};
}
if success {
Ok(())
} else {
Err(eyre!(StepFailed))
}
}

View File

@@ -71,7 +71,7 @@ async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) -
if let Err(message) = &result {
println!("{} pulling {}", style("Failed").red().bold(), &repo);
print!("{}", message);
print!("{message}");
} else {
let after_revision = get_head_revision(git, &repo);
@@ -87,7 +87,7 @@ async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) -
"log",
"--no-decorate",
"--oneline",
&format!("{}..{}", before, after),
&format!("{before}..{after}"),
])
.status_checked()?;
println!();
@@ -187,7 +187,7 @@ impl Git {
repositories
.bad_patterns
.iter()
.for_each(|pattern| print_warning(format!("Path {} did not contain any git repositories", pattern)));
.for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories")));
self.multi_pull(repositories, ctx)
}

View File

@@ -26,7 +26,7 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type().execute(&pkg).arg("clean").status_checked()?;
let apt = require("apt")?;
let mut command = ctx.run_type().execute(&apt);
let mut command = ctx.run_type().execute(apt);
command.arg("autoremove");
if ctx.config().yes(Step::System) {
command.arg("-y");

View File

@@ -80,8 +80,18 @@ pub struct GarudaUpdate {
impl ArchPackageManager for GarudaUpdate {
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
let mut command = ctx.run_type().execute(&self.executable);
command.env("PATH", get_execution_path());
command
.env("PATH", get_execution_path())
.env("UPDATE_AUR", "1")
.env("SKIP_MIRRORLIST", "1");
if ctx.config().yes(Step::System) {
command.env("PACMAN_NOCONFIRM", "1");
}
command.args(ctx.config().garuda_update_arguments().split_whitespace());
command.status_checked()?;
Ok(())
}
}

View File

@@ -29,7 +29,9 @@ pub enum Distribution {
Debian,
Gentoo,
OpenMandriva,
PCLinuxOS,
Suse,
SuseMicro,
Void,
Solus,
Exherbo,
@@ -55,8 +57,10 @@ impl Distribution {
Some("gentoo") => Distribution::Gentoo,
Some("exherbo") => Distribution::Exherbo,
Some("nixos") => Distribution::NixOS,
Some("opensuse-microos") => Distribution::SuseMicro,
Some("neon") => Distribution::KDENeon,
Some("openmandriva") => Distribution::OpenMandriva,
Some("pclinuxos") => Distribution::PCLinuxOS,
_ => {
if let Some(id_like) = id_like {
if id_like.contains(&"debian") || id_like.contains(&"ubuntu") {
@@ -103,6 +107,7 @@ impl Distribution {
Distribution::Debian => upgrade_debian(ctx),
Distribution::Gentoo => upgrade_gentoo(ctx),
Distribution::Suse => upgrade_suse(ctx),
Distribution::SuseMicro => upgrade_suse_micro(ctx),
Distribution::Void => upgrade_void(ctx),
Distribution::Solus => upgrade_solus(ctx),
Distribution::Exherbo => upgrade_exherbo(ctx),
@@ -110,6 +115,7 @@ impl Distribution {
Distribution::KDENeon => upgrade_neon(ctx),
Distribution::Bedrock => update_bedrock(ctx),
Distribution::OpenMandriva => upgrade_openmandriva(ctx),
Distribution::PCLinuxOS => upgrade_pclinuxos(ctx),
}
}
@@ -193,7 +199,6 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> {
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(())
}
@@ -224,6 +229,18 @@ fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() {
ctx.run_type()
.execute(sudo)
.args(["transactional-update", "dup"])
.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(())
}
fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() {
@@ -246,6 +263,33 @@ fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() {
let mut command_update = ctx.run_type().execute(sudo);
command_update.arg(&which("apt-get").unwrap()).arg("update");
if let Some(args) = ctx.config().dnf_arguments() {
command_update.args(args.split_whitespace());
}
if ctx.config().yes(Step::System) {
command_update.arg("-y");
}
command_update.status_checked()?;
ctx.run_type()
.execute(sudo)
.arg(&which("apt-get").unwrap())
.arg("dist-upgrade")
.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(())
}
fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() {
@@ -317,7 +361,20 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() {
let apt = which("apt-fast")
.or_else(|| which("nala"))
.or_else(|| {
if which("mist").is_some() {
Some(PathBuf::from("mist"))
} else {
None
}
})
.or_else(|| {
if Path::new("/usr/bin/nala").exists() {
Some(Path::new("/usr/bin/nala").to_path_buf())
} else {
None
}
})
.unwrap_or_else(|| PathBuf::from("apt-get"));
let is_nala = apt.ends_with("nala");
@@ -385,15 +442,44 @@ fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
pub fn update_am(ctx: &ExecutionContext) -> Result<()> {
let am = require("am")?;
if let Some(sudo) = ctx.sudo() {
ctx.run_type().execute(sudo).arg(am).arg("-u").status_checked()?;
} else {
print_warning("No sudo detected. Skipping AM Step");
}
Ok(())
}
pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> {
let pacdef = require("pacdef")?;
print_separator("pacdef");
ctx.run_type().execute(&pacdef).arg("sync").status_checked()?;
let output = ctx.run_type().execute(&pacdef).arg("version").output_checked()?;
let string = String::from_utf8(output.stdout)?;
let new_version = string.contains("version: 1");
println!();
ctx.run_type().execute(&pacdef).arg("review").status_checked()
if new_version {
ctx.run_type()
.execute(&pacdef)
.args(["package", "sync"])
.status_checked()?;
println!();
ctx.run_type()
.execute(&pacdef)
.args(["package", "review"])
.status_checked()?;
} else {
ctx.run_type().execute(&pacdef).arg("sync").status_checked()?;
println!();
ctx.run_type().execute(&pacdef).arg("review").status_checked()?;
}
Ok(())
}
pub fn run_pacstall(ctx: &ExecutionContext) -> Result<()> {
@@ -401,8 +487,16 @@ pub fn run_pacstall(ctx: &ExecutionContext) -> Result<()> {
print_separator("Pacstall");
ctx.run_type().execute(&pacstall).arg("-U").status_checked()?;
ctx.run_type().execute(pacstall).arg("-Up").status_checked()
let mut update_cmd = ctx.run_type().execute(&pacstall);
let mut upgrade_cmd = ctx.run_type().execute(pacstall);
if ctx.config().yes(Step::Pacstall) {
update_cmd.arg("-P");
upgrade_cmd.arg("-P");
}
update_cmd.arg("-U").status_checked()?;
upgrade_cmd.arg("-Up").status_checked()
}
fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
@@ -452,10 +546,13 @@ fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> {
fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() {
ctx.run_type()
.execute(sudo)
.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"])
.status_checked()?;
let mut command = ctx.run_type().execute(sudo);
command.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]);
if let Some(args) = ctx.config().nix_arguments() {
command.args(args.split_whitespace());
}
command.status_checked()?;
if ctx.config().cleanup() {
ctx.run_type()
@@ -660,6 +757,29 @@ pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> {
.status_checked()
}
pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?;
let dkp_pacman = require("dkp-pacman")?;
print_separator("Devkitpro pacman");
ctx.run_type()
.execute(sudo)
.arg(&dkp_pacman)
.arg("-Syu")
.status_checked()?;
if ctx.config().cleanup() {
ctx.run_type()
.execute(sudo)
.arg(&dkp_pacman)
.arg("-Scc")
.status_checked()?;
}
Ok(())
}
pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?;
if ctx.config().yes(Step::ConfigUpdate) {

View File

@@ -0,0 +1,9 @@
NAME="PCLinuxOS"
VERSION="2022"
ID=pclinuxos
VERSION_ID=2022
ID_LIKE="mandriva"
PRETTY_NAME="PCLinuxOS 2022"
ANSI_COLOR="1;37"
HOME_URL="http://www.pclinuxos.com/"
SUPPORT_URL="http://www.pclinuxos.com/"

View File

@@ -102,6 +102,12 @@ pub fn run_fisher(run_type: RunType) -> Result<()> {
.and_then(|output| Path::new(&output.stdout.trim()).require().map(|_| ()))
.map_err(|err| SkipStep(format!("`fish_plugins` path doesn't exist: {err}")))?;
Command::new(&fish)
.args(["-c", "fish_update_completions"])
.output_checked_utf8()
.map(|_| ())
.map_err(|_| SkipStep("`fish_update_completions` is not available".to_owned()))?;
print_separator("Fisher");
let version_str = run_type
@@ -146,6 +152,8 @@ pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> {
pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> {
let pkgin = require("pkgin")?;
print_separator("Pkgin");
let mut command = ctx.run_type().execute(ctx.sudo().as_ref().unwrap());
command.arg(&pkgin).arg("update");
if ctx.config().yes(Step::Pkgin) {

View File

@@ -48,7 +48,7 @@ pub fn run_winget(ctx: &ExecutionContext) -> Result<()> {
}
ctx.run_type()
.execute(&winget)
.execute(winget)
.args(["upgrade", "--all"])
.status_checked()
}
@@ -68,6 +68,25 @@ pub fn run_scoop(cleanup: bool, run_type: RunType) -> Result<()> {
Ok(())
}
pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> {
let wsl = require("wsl")?;
print_separator("Update WSL");
let mut wsl_command = ctx.run_type().execute(wsl);
wsl_command.args(["--update"]);
if ctx.config().wsl_update_pre_release() {
wsl_command.args(["--pre-release"]);
}
if ctx.config().wsl_update_use_web_download() {
wsl_command.args(["--web-download"]);
}
wsl_command.status_checked()?;
Ok(())
}
fn get_wsl_distributions(wsl: &Path) -> Result<Vec<String>> {
let output = Command::new(wsl).args(["--list", "-q"]).output_checked_utf8()?.stdout;
Ok(output
@@ -86,7 +105,7 @@ fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> R
let mut command = ctx.run_type().execute(wsl);
command
.args(["-d", dist, "bash", "-c"])
.arg(format!("TOPGRADE_PREFIX={} exec {}", dist, topgrade));
.arg(format!("TOPGRADE_PREFIX={dist} exec {topgrade}"));
if ctx.config().yes(Step::Wsl) {
command.arg("-y");

View File

@@ -50,7 +50,7 @@ impl Powershell {
.args([
"-NoProfile",
"-Command",
&format!("Get-Module -ListAvailable {}", command),
&format!("Get-Module -ListAvailable {command}"),
])
.output_checked_utf8()
.map(|result| !result.stdout.is_empty())

View File

@@ -19,7 +19,7 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> {
args.extend(ssh_arguments.split_whitespace());
}
let env = format!("TOPGRADE_PREFIX={}", hostname);
let env = format!("TOPGRADE_PREFIX={hostname}");
args.extend(["env", &env, "$SHELL", "-lc", topgrade]);
if ctx.config().run_in_tmux() && !ctx.run_type().dry() {
@@ -43,11 +43,11 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> {
args.extend(ssh_arguments.split_whitespace());
}
let env = format!("TOPGRADE_PREFIX={}", hostname);
let env = format!("TOPGRADE_PREFIX={hostname}");
args.extend(["env", &env, "$SHELL", "-lc", topgrade]);
print_separator(format!("Remote ({})", hostname));
println!("Connecting to {}...", hostname);
print_separator(format!("Remote ({hostname})"));
println!("Connecting to {hostname}...");
ctx.run_type().execute(ssh).args(&args).status_checked()
}

View File

@@ -183,7 +183,7 @@ pub fn topgrade_vagrant_box(ctx: &ExecutionContext, vagrant_box: &VagrantBox) ->
let mut _poweron = None;
if !vagrant_box.initial_status.powered_on() {
if !(ctx.config().vagrant_power_on().unwrap_or(true)) {
return Err(SkipStep(format!("Skipping powered off box {}", vagrant_box)).into());
return Err(SkipStep(format!("Skipping powered off box {vagrant_box}")).into());
} else {
print_separator(seperator);
_poweron = Some(vagrant.temporary_power_on(vagrant_box, ctx)?);

View File

@@ -42,7 +42,7 @@ pub fn run_toolbx(ctx: &ExecutionContext) -> Result<()> {
let topgrade_path = topgrade_path.to_str().unwrap();
for tb in toolboxes.iter() {
let topgrade_prefix = format!("TOPGRADE_PREFIX='Toolbx {}'", tb);
let topgrade_prefix = format!("TOPGRADE_PREFIX='Toolbx {tb}'");
let mut args = vec![
"run",
"-c",

View File

@@ -33,23 +33,20 @@ if exists(":PaqUpdate")
PaqUpdate
endif
if exists(":CocUpdateSync")
echo "CocUpdateSync"
CocUpdateSync
if exists(":Lazy")
echo "Lazy Update"
Lazy! sync | qa
endif
" TODO: Should this be after `PackerSync`?
" Not sure how to sequence this after Packer without doing something weird
" with that `PackerComplete` autocommand.
if exists(":TSUpdate")
echo "TreeSitter Update"
TSUpdate
if exists(":AstroUpdate")
echo "AstroUpdate"
AstroUpdate
endif
if exists(':PackerSync')
echo "Packer"
autocmd User PackerComplete quitall
PackerSync
echo "Packer"
autocmd User PackerComplete quitall
PackerSync
else
quitall
quitall
endif

View File

@@ -1,5 +1,5 @@
use std::env;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process::Command;
use color_eyre::eyre::Result;
@@ -25,10 +25,28 @@ pub fn run_zr(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked()
}
pub fn zshrc(base_dirs: &BaseDirs) -> PathBuf {
fn zdotdir(base_dirs: &BaseDirs) -> PathBuf {
env::var("ZDOTDIR")
.map(|p| Path::new(&p).join(".zshrc"))
.unwrap_or_else(|_| base_dirs.home_dir().join(".zshrc"))
.map(PathBuf::from)
.unwrap_or_else(|_| base_dirs.home_dir().to_path_buf())
}
pub fn zshrc(base_dirs: &BaseDirs) -> PathBuf {
zdotdir(base_dirs).join(".zshrc")
}
pub fn run_antidote(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?;
let mut antidote = zdotdir(ctx.base_dirs()).join(".antidote").require()?;
antidote.push("antidote.zsh");
print_separator("antidote");
ctx.run_type()
.execute(zsh)
.arg("-c")
.arg(format!("source {} && antidote update", antidote.display()))
.status_checked()
}
pub fn run_antibody(run_type: RunType) -> Result<()> {

View File

@@ -4,6 +4,8 @@ use std::path::PathBuf;
use color_eyre::eyre::Context;
use color_eyre::eyre::Result;
use serde::Deserialize;
use strum::AsRefStr;
use crate::command::CommandExt;
use crate::execution_context::ExecutionContext;
@@ -24,12 +26,18 @@ impl Sudo {
pub fn detect() -> Option<Self> {
which("doas")
.map(|p| (p, SudoKind::Doas))
.or_else(|| which("please").map(|p| (p, SudoKind::Please)))
.or_else(|| which("sudo").map(|p| (p, SudoKind::Sudo)))
.or_else(|| which("gsudo").map(|p| (p, SudoKind::Gsudo)))
.or_else(|| which("pkexec").map(|p| (p, SudoKind::Pkexec)))
.map(|(path, kind)| Self { path, kind })
}
/// Create Sudo from SudoKind, if found in the system
pub fn new(kind: SudoKind) -> Option<Self> {
which(kind.as_ref()).map(|path| Self { path, kind })
}
/// Elevate permissions with `sudo`.
///
/// This helps prevent blocking `sudo` prompts from stopping the run in the middle of a
@@ -47,6 +55,12 @@ impl Sudo {
// See: https://man.openbsd.org/doas
cmd.arg("echo");
}
SudoKind::Please => {
// From `man please`
// -w, --warm
// Warm the access token and exit.
cmd.arg("-w");
}
SudoKind::Sudo => {
// From `man sudo` on macOS:
// -v, --validate
@@ -93,9 +107,12 @@ impl Sudo {
}
}
#[derive(Clone, Copy, Debug)]
enum SudoKind {
#[derive(Clone, Copy, Debug, Deserialize, AsRefStr)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum SudoKind {
Doas,
Please,
Sudo,
Gsudo,
Pkexec,

View File

@@ -21,8 +21,9 @@ use which_crate::which;
use crate::command::CommandExt;
use crate::report::StepResult;
#[cfg(target_os = "linux")]
use crate::terminal;
#[cfg(target_os = "linux")]
use crate::utils::which;
lazy_static! {
static ref TERMINAL: Mutex<Terminal> = Mutex::new(Terminal::new());
}
@@ -59,7 +60,7 @@ impl Terminal {
width: term.size_checked().map(|(_, w)| w),
term,
prefix: env::var("TOPGRADE_PREFIX")
.map(|prefix| format!("({}) ", prefix))
.map(|prefix| format!("({prefix}) "))
.unwrap_or_else(|_| String::new()),
set_title: true,
display_time: true,
@@ -105,7 +106,7 @@ impl Terminal {
command.args(["-a", "Topgrade", "Topgrade"]);
command.arg(message.as_ref());
if let Err(err) = command.output_checked() {
tracing::error!("{err:?}");
terminal::print_warning("Sending notification failed with {err:?}");
}
}
}
@@ -142,7 +143,7 @@ impl Terminal {
.write_fmt(format_args!(
"{}\n",
style(format_args!(
"\n―― {} {:^border$}",
"\n── {} {:^border$}",
message,
"",
border = max(
@@ -158,7 +159,7 @@ impl Terminal {
.ok();
}
None => {
self.term.write_fmt(format_args!("―― {} ――\n", message)).ok();
self.term.write_fmt(format_args!("―― {message} ――\n")).ok();
}
}
}
@@ -170,7 +171,7 @@ impl Terminal {
self.term
.write_fmt(format_args!(
"{} {}",
style(format!("{} failed:", key)).red().bold(),
style(format!("{key} failed:")).red().bold(),
message
))
.ok();
@@ -214,7 +215,7 @@ impl Terminal {
self.term
.write_fmt(format_args!(
"{}",
style(format!("{} (y)es/(N)o", question,)).yellow().bold()
style(format!("{question} (y)es/(N)o",)).yellow().bold()
))
.ok();
@@ -236,13 +237,15 @@ impl Terminal {
self.term.set_title("Topgrade - Awaiting user");
}
self.notify_desktop(format!("{} failed", step_name), None);
if self.desktop_notification {
self.notify_desktop(format!("{step_name} failed"), None);
}
let prompt_inner = style(format!("{}Retry? (y)es/(N)o/(s)hell/(q)uit", self.prefix))
.yellow()
.bold();
self.term.write_fmt(format_args!("\n{}", prompt_inner)).ok();
self.term.write_fmt(format_args!("\n{prompt_inner}")).ok();
let answer = loop {
match self.term.read_key() {
@@ -250,7 +253,7 @@ impl Terminal {
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
println!("\n\nDropping you to shell. Fix what you need and then exit the shell.\n");
if let Err(err) = run_shell().context("Failed to run shell") {
self.term.write_fmt(format_args!("{err:?}\n{}", prompt_inner)).ok();
self.term.write_fmt(format_args!("{err:?}\n{prompt_inner}")).ok();
} else {
break Ok(true);
}

View File

@@ -149,6 +149,6 @@ pub fn hostname() -> Result<String> {
Command::new("hostname")
.output_checked_utf8()
.map_err(|err| SkipStep(format!("Failed to get hostname: {}", err)).into())
.map_err(|err| SkipStep(format!("Failed to get hostname: {err}")).into())
.map(|output| output.stdout.trim().to_owned())
}