Compare commits

...

45 Commits

Author SHA1 Message Date
Thomas Schönauer
b974938a33 v12 Cargo files update (#441) 2023-06-27 10:02:27 +00:00
SteveLauC
06cb88a1a1 test: test for config file creation and default config file parsing (#459) 2023-06-23 09:04:05 +00:00
SteveLauC
a6195d284c feat: support Bob (#461) 2023-06-23 09:03:57 +00:00
SteveLauC
5b8850e8a3 chore: update bug report issue template (#474) 2023-06-23 09:03:29 +00:00
SteveLauC
57546a07fc fix(pip3): prefer python when available (#471) 2023-06-23 09:02:58 +00:00
slowsage
d7709490ce fix: Run AstroUpdate before Lazy sync (#473) 2023-06-23 09:01:55 +00:00
slowsage
3e6c6e513b fix: handle no topgrade.toml but files in topgrade.d (#460) 2023-06-13 14:17:27 +00:00
SteveLauC
30858780cf refactor: unify the behavior of the steps that require sudo (#467) 2023-06-13 14:15:57 +00:00
SteveLauC
a7ddf4575a fix: fix Mist (#466) 2023-06-05 06:38:14 +00:00
Thomas Schönauer
470231c9d1 Revert "fix: fix mist" (#465)
Revert "fix: fix mist (#464)"

This reverts commit 282e336ac4.
2023-06-03 21:22:23 +00:00
SteveLauC
282e336ac4 fix: fix mist (#464) 2023-06-03 21:20:57 +00:00
SteveLauC
658829e4ff refactor: make update fn take &ExectionContext & put update fn together (#457) 2023-06-02 20:20:42 +00:00
SteveLauC
a0ff565220 docs: update CONTRIBUTING.md & config.example.toml (#458) 2023-06-01 11:02:39 +00:00
SteveLauC
7e48c5dedc fix: warn user about bad pattern paths before skipping step git (#456) 2023-06-01 07:16:01 +00:00
slowsage
03436b7f8f fix: Handle '# [include]'. Update default config (#450) 2023-06-01 07:15:49 +00:00
SteveLauC
3f5eedb83d fix: run AM without sudo (#454) 2023-05-31 07:01:45 +00:00
SteveLauC
234ad4bdd7 docs: add config-related CONTRIBUTING doc (#452) 2023-05-30 10:03:22 +00:00
slowsage
c7923393be fix: Write to correct config path when none exists. (#449) 2023-05-30 07:07:02 +00:00
slowsage
d4548b2f9a feat: Add arguments to pipupgrade and fix enable_pipupgrade check (#448) 2023-05-30 07:04:23 +00:00
SteveLauC
f6e8af186c feat: support Vanilla Linux (#447) 2023-05-29 11:45:11 +00:00
SteveLauC
58153635da refactor: remove Anarchy and Antergos as they are discontinued (#446) 2023-05-28 12:44:49 +00:00
SteveLauC
5358509825 fix: fix panic during container update (#445) 2023-05-27 14:12:45 +00:00
SteveLauC
1ab0232d96 feat: support deepin OS (#444) 2023-05-27 09:41:51 +00:00
SteveLauC
66860f1848 refactor: remove unnecessary qualification (#443) 2023-05-27 09:41:42 +00:00
SteveLauC
625f823f46 refactor: rename update fn name & some cleanup (#442) 2023-05-27 09:37:51 +00:00
Thomas Schönauer
6263ab7e10 Allow apt-get update to continue to apt-get upgrade with error code 100 (#440)
Allow apt-get update to continue with error code 100
2023-05-26 19:57:05 +00:00
Kevin Gavrois
7db991db9d Merge code for desktop notification between MacOS and Linux (#438) 2023-05-26 10:07:14 +02:00
SteveLauC
d75782892e docs: CONTRIBUTING.md (#439) 2023-05-26 09:34:20 +02:00
PolpOnline
cb7adc8ced Added ability to include directories as an extension of the config file (#421) 2023-05-25 12:22:11 +02:00
SteveLauC
7c3ba80270 fix: fix .NET language issue (#437)
Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-05-25 09:24:53 +02:00
SteveLauC
76c39edc8b refactor: make all step functions take &ExectutionContext (#436) 2023-05-25 09:09:23 +02:00
SteveLauC
c20a300eea fix: use --platform opt when pulling containers (#435) 2023-05-23 08:47:47 +02:00
SteveLauC
de3902a9c9 fix: use env ZSH to compose oh-my-zsh install dir (#434) 2023-05-22 14:06:19 +02:00
SteveLauC
8bca671e9f fix: run deb-get without sudo (#430) 2023-05-20 19:35:17 +02:00
MonstrousOgre
54301a6a17 Adding local pip-review (#433) 2023-05-20 19:33:59 +02:00
Cat Core
f06b7c0807 Differentiate NPM and PNPM steps in name (#431) 2023-05-20 11:33:41 +02:00
SteveLauC
43c02cf7a7 feat: support maza (#427) 2023-05-17 19:18:03 +02:00
SteveLauC
3a1568e884 feat: support oh-my-bash (#425) 2023-05-17 19:17:37 +02:00
SteveLauC
14753a14e7 feat: support AppMan (#423) 2023-05-09 08:03:06 +02:00
Sourajyoti Basak
227e8dcc8d feat(shell): add packer.nu (#414)
* feat(shell): add `packer.nu`

* dependency update (#413)

* fix(main): move `packer.nu` step before linux package managers

---------

Co-authored-by: Thomas Schönauer <37108907+DottoDev@users.noreply.github.com>
2023-05-05 11:01:24 +02:00
signed-log
97fd2b2718 Make zypper dist-upgrade opt-in on SLE/Leap (#417)
Make zypper dist-upgrade opt-in on SLE/Leap

- Create a `suse_dup` config option
- Create a new `Distribution::OpenSuseTumbleweed` object along with `upgrade_opensuse_tumbleweed()`
    * The purpose of it is to ignore the config option on Tumblweed as
      zypper `dup` is the only way to update a Tumbleweed
2023-05-05 10:24:01 +02:00
SteveLauC
f30e36d7bb feat: support stew (#422) 2023-05-05 10:17:42 +02:00
SteveLauC
d640bc66f5 docs: update README for alternative config path (#419) 2023-05-04 08:36:36 +00:00
PolpOnline
a2331a2575 Add the ability to have the config file in $XDG_CONFIG_HOME/topgrade/topgrade.toml (#418) 2023-05-03 19:53:52 +00:00
Thomas Schönauer
26a2c3c266 v11.0.2 version bump (#416)
* dependency update

* Cargo.toml version bump
2023-05-01 18:26:18 +00:00
35 changed files with 2173 additions and 1306 deletions

View File

@@ -2,32 +2,79 @@
name: Bug report name: Bug report
about: Topgrade is misbehaving about: Topgrade is misbehaving
title: '' title: ''
labels: '' labels: 'bug'
assignees: '' assignees: ''
--- ---
<!-- If you're here to report about a "No asset found" error, please make sure that an hour has been passed since the last release was made. --> <!--
Thanks for taking the time to fill out this bug report!
Please make sure to
[search for existing issues](https://github.com/topgrade-rs/topgrade/issues)
before filing a new one!
## What did you expect to happen? Questions labeled with `Optional` can be skipped.
-->
<!--
If you're here to report about a "No asset found" error, please make sure that
an hour has been passed since the last release was made.
-->
## What actually happened? ## Erroneous Behavior
<!--
What actually happened?
-->
## Expected Behavior
<!--
Describe the expected behavior
-->
## Steps to reproduce
<!--
A minimal example to reproduce the issue
-->
## Possible Cause (Optional)
<!--
If you know the possible cause of the issue, please tell us.
-->
## Problem persists without calling from topgrade
<!--
Execute the erroneous command directly to see if the problem persists
-->
- [ ] Yes
## Configuration file (Optional)
<!--
Paste your configuration file inside the code block if you think this issue is
related to configuration.
-->
```toml
```
## Additional Details ## Additional Details
- Which operating system or Linux distribution are you using? - Operation System/Version
- How did you install Topgrade? <!-- For example, Fedora Linux 38 -->
- Which version are you running? <!-- Check with `topgrade -V` -->
<!-- - Installation
Run `topgrade --dry-run` to see which commands Topgrade is running. <!--
If the command seems wrong and you know why please tell us so. How did you install topgrade: build from repo / crates.io (cargo install topgrade)
If the command seems fine try to run it yourself and tell us if you got a different result from Topgrade. / package manager (which one) / other (describe)
-->
- Topgrade version (`topgrade -V`)
## Verbose Output (`topgrade -v`)
<!--
Paste the verbose output into the pre-tags
--> -->
<details> <details>
<!-- Paste the output of the problematic command with `-v` into the pre-tags -->
<pre> <pre>
</pre> </pre>

View File

@@ -1,6 +1,7 @@
## Standards checklist: ## Standards checklist:
- [ ] The PR title is descriptive. - [ ] The PR title is descriptive.
- [ ] I have read `CONTRIBUTING.md`
- [ ] The code compiles (`cargo build`) - [ ] The code compiles (`cargo build`)
- [ ] The code passes rustfmt (`cargo fmt`) - [ ] The code passes rustfmt (`cargo fmt`)
- [ ] The code passes clippy (`cargo clippy`) - [ ] The code passes clippy (`cargo clippy`)

View File

@@ -57,3 +57,10 @@ jobs:
# token: ${{ secrets.CODECOV_TOKEN }} # token: ${{ secrets.CODECOV_TOKEN }}
files: ./lcov.info files: ./lcov.info
fail_ci_if_error: true fail_ci_if_error: true
- name: Test creation of config file
run: |
CONFIG_PATH=~/.config/topgrade.toml;
if [ -f "$CONFIG_PATH" ]; then rm $CONFIG_PATH; fi
cargo build;
./target/debug/topgrade --dry-run --only system;
stat $CONFIG_PATH;

134
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,134 @@
## Contributing to `topgrade`
Thank you for your interest in contributing to `topgrade`! We welcome and encourage
contributions of all kinds, such as:
1. Issue reports or feature requests
2. Documentation improvements
3. Code (PR or PR Review)
## Adding a new `step`
In `topgrade`'s term, package manager is called `step`. To add a new `step` to
`topgrade`:
1. Add a new variant to
[`enum Step`](https://github.com/topgrade-rs/topgrade/blob/cb7adc8ced8a77addf2cb051d18bba9f202ab866/src/config.rs#L100)
```rust
pub enum Step {
// Existed steps
// ...
// Your new step here!
// You may want it to be sorted alphabetically because that looks great:)
Xxx,
}
```
2. Implement the update function
You need to find the appropriate location where this update function goes, it should be
a file under [`src/steps`](https://github.com/topgrade-rs/topgrade/tree/master/src/steps),
the file names are self-explanatory, for example, `step`s related to `zsh` are
placed in [`steps/zsh.rs`](https://github.com/topgrade-rs/topgrade/blob/master/src/steps/zsh.rs).
Then you implement the update function, and put it in the file where it belongs.
```rust
pub fn run_xxx(ctx: &ExecutionContext) -> Result<()> {
// Check if this step is installed, if not, then this update will be skipped.
let xxx = require("xxx")?;
// Print the separator
print_separator("xxx");
// Invoke the new step to get things updated!
ctx.run_type()
.execute("xxx")
.arg(/* args required by this step */)
.status_checked()
}
```
Such a update function would be conventionally named `run_xxx()`, where `xxx`
is the name of the new step, and it should take a argument of type
`&ExecutionContext`, this is adequate for most cases unless some extra stuff is
needed (You can find some examples where extra arguments are needed
[here](https://github.com/topgrade-rs/topgrade/blob/7e48c5dedcfd5d0124bb9f39079a03e27ed23886/src/main.rs#L201-L219)).
Update function would usually do 3 things:
1. Check if the step is installed
2. Output the Separator
3. Invoke the step
Still, this is sufficient for most tools, but you may need some extra stuff
with complicated `step`.
3. Finally, invoke that update function in `main.rs`
```rust
runner.execute(Step::Xxx, "xxx", || ItsModule::run_xxx(&ctx))?;
```
We use [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html)
to separate the steps, for example, for steps that are Linux-only, it goes
like this:
```
#[cfg(target_os = "linux")]
{
// Xxx is Linux-only
runner.execute(Step::Xxx, "xxx", || ItsModule::run_xxx(&ctx))?;
}
```
Congrats, you just added a new `step`:)
## Modification to the configuration entries
If your PR has the configuration options
(in [`src/config.rs`](https://github.com/topgrade-rs/topgrade/blob/master/src/config.rs))
modified:
1. Adding new options
2. Changing the existing options
Be sure to apply your changes to
[`config.example.toml`](https://github.com/topgrade-rs/topgrade/blob/master/config.example.toml),
and have some basic documentations guiding user how to use these options.
## Before you submit your PR
Make sure your patch passes the following tests on your host:
```shell
$ cargo build
$ cargo fmt
$ cargo clippy
$ cargo test
```
Don't worry about other platforms, we have most of them covered in our CI.
## Some tips
1. Locale
Some `step` respects locale, which means their output can be in language other
than English, we should not do check on it.
For example, one may want to check if a tool works by doing this:
```rust
let output = Command::new("xxx").arg("--help").output().unwrap();
let stdout = from_utf8(output.stdout).expect("Assume it is UTF-8 encoded");
if stdout.contains("help") {
// xxx works
}
```
If `xxx` respects locale, then the above code should work on English system,
on a system that does not use English, e.g., it uses Chinese, that `"help"` may be
translated to `"帮助"`, and the above code won't work.

724
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ categories = ["os"]
keywords = ["upgrade", "update"] keywords = ["upgrade", "update"]
license = "GPL-3.0" license = "GPL-3.0"
repository = "https://github.com/topgrade-rs/topgrade" repository = "https://github.com/topgrade-rs/topgrade"
version = "11.0.0" version = "12.0.0"
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"] authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
exclude = ["doc/screenshot.gif"] exclude = ["doc/screenshot.gif"]
edition = "2021" edition = "2021"
@@ -41,15 +41,15 @@ tempfile = "~3.2"
cfg-if = "~1.0" cfg-if = "~1.0"
tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] } tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] }
futures = "~0.3" futures = "~0.3"
regex = "~1.5" regex = "~1.7"
semver = "~1.0" semver = "~1.0"
shell-words = "~1.1" shell-words = "~1.1"
color-eyre = "~0.6" color-eyre = "~0.6"
tracing = { version = "~0.1", features = ["attributes", "log"] } tracing = { version = "~0.1", features = ["attributes", "log"] }
tracing-subscriber = { version = "~0.3", features = ["env-filter", "time"] } tracing-subscriber = { version = "~0.3", features = ["env-filter", "time"] }
merge = "~0.1"
[target.'cfg(target_os = "macos")'.dependencies] regex-split = "~0.1"
notify-rust = "~4.5" notify-rust = "~4.8"
[package.metadata.generate-rpm] [package.metadata.generate-rpm]
assets = [{source = "target/release/topgrade", dest="/usr/bin/topgrade"}] assets = [{source = "target/release/topgrade", dest="/usr/bin/topgrade"}]

View File

@@ -55,10 +55,18 @@ See `config.example.toml` for an example configuration file.
### Configuration Path ### Configuration Path
The configuration should be placed in the following paths depending on the operating system: #### `CONFIG_DIR` on each platform
- **Windows**: `%APPDATA%`
- **macOS** and **other Unix systems**: `${XDG_CONFIG_HOME:-~/.config}`
- **Windows** - `%APPDATA%/topgrade.toml` `topgrade` will look for the configuration file in the following places, in order of priority:
- **macOS** and **other Unix systems** - `${XDG_CONFIG_HOME:-~/.config}/topgrade.toml`
1. `CONFIG_DIR/topgrade.toml`
2. `CONFIG_DIR/topgrade/topgrade.toml`
If the file with higher priority is present, no matter it is valid or not, the other configuration files will be ignored.
On the first run(no configuration file exists), `topgrade` will create a configuration file at `CONFIG_DIR/topgrade.toml` for you.
### Custom Commands ### Custom Commands
@@ -92,8 +100,8 @@ Just fork the repository and start coding.
### Contribution Guidelines ### Contribution Guidelines
- Check if your code passes `cargo fmt` and `cargo clippy`. See [CONTRIBUTING.md](https://github.com/topgrade-rs/topgrade/blob/master/CONTRIBUTING.md)
- Check if your code is self explanatory, if not it should be documented by comments.
## Roadmap ## Roadmap
- [ ] Add a proper testing framework to the code base. - [ ] Add a proper testing framework to the code base.

View File

@@ -1,3 +1,10 @@
# Include any additional configuration file(s)
# [include] sections are processed in the order you write them
# Files in $CONFIG_DIR/topgrade/topgrade.d/ are automatically included at the beginning of this file
[include]
#paths = ["/etc/topgrade.toml"]
[misc]
# Don't ask for confirmations # Don't ask for confirmations
#assume_yes = true #assume_yes = true
@@ -47,9 +54,6 @@
# Skip sending a notification at the end of a run # Skip sending a notification at the end of a run
#skip_notify = true #skip_notify = true
# Skip the preamble displayed when topgrade is run
#display_preamble = false
# Whether to self update (this is ignored if the binary has been built without self update support, available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE) # Whether to self update (this is ignored if the binary has been built without self update support, available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE)
#no_self_update = true #no_self_update = true
@@ -101,12 +105,15 @@
#emerge_sync_flags = "-q" #emerge_sync_flags = "-q"
#emerge_update_flags = "-uDNa --with-bdeps=y world" #emerge_update_flags = "-uDNa --with-bdeps=y world"
#redhat_distro_sync = false #redhat_distro_sync = false
#suse_dup = false
#rpm_ostree = false #rpm_ostree = false
#nix_arguments = "--flake" #nix_arguments = "--flake"
[python] [python]
#enable_pip_review = true ###disabled by default #enable_pip_review = true ###disabled by default
#enable_pipupgrade = true ###disabled by default #enable_pip_review_local = true ###disabled by default
#enable_pipupgrade = true ###disabled by default
#pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default
[windows] [windows]
# Manually select Windows updates # Manually select Windows updates

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,6 @@ pub enum TopgradeError {
#[error("`{0}` failed: {1}")] #[error("`{0}` failed: {1}")]
ProcessFailedWithOutput(String, ExitStatus, String), ProcessFailedWithOutput(String, ExitStatus, String),
#[error("Sudo is required for this step")]
#[allow(dead_code)]
SudoRequired,
#[error("Unknown Linux Distribution")] #[error("Unknown Linux Distribution")]
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
UnknownLinuxDistribution, UnknownLinuxDistribution,

View File

@@ -2,7 +2,7 @@
use crate::executor::RunType; use crate::executor::RunType;
use crate::git::Git; use crate::git::Git;
use crate::sudo::Sudo; use crate::sudo::Sudo;
use crate::utils::require_option; use crate::utils::{require_option, REQUIRE_SUDO};
use crate::{config::Config, executor::Executor}; use crate::{config::Config, executor::Executor};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::path::Path; use std::path::Path;
@@ -31,7 +31,7 @@ impl<'a> ExecutionContext<'a> {
} }
pub fn execute_elevated(&self, command: &Path, interactive: bool) -> Result<Executor> { pub fn execute_elevated(&self, command: &Path, interactive: bool) -> Result<Executor> {
let sudo = require_option(self.sudo.clone(), "Sudo is required for this operation".into())?; let sudo = require_option(self.sudo.as_ref(), REQUIRE_SUDO.to_string())?;
Ok(sudo.execute_elevated(self, command, interactive)) Ok(sudo.execute_elevated(self, command, interactive))
} }

View File

@@ -3,7 +3,6 @@ use std::ffi::{OsStr, OsString};
use std::path::Path; use std::path::Path;
use std::process::{Child, Command, ExitStatus, Output}; use std::process::{Child, Command, ExitStatus, Output};
use color_eyre::eyre;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use tracing::debug; use tracing::debug;
@@ -238,7 +237,7 @@ impl CommandExt for Executor {
// TODO: It might be nice to make `output_checked_with` return something that has a // TODO: It might be nice to make `output_checked_with` return something that has a
// variant for wet/dry runs. // variant for wet/dry runs.
fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result<Output> { fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> Result<Output> {
match self { match self {
Executor::Wet(c) => c.output_checked_with(succeeded), Executor::Wet(c) => c.output_checked_with(succeeded),
Executor::Dry(c) => { Executor::Dry(c) => {
@@ -248,7 +247,7 @@ impl CommandExt for Executor {
} }
} }
fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> eyre::Result<()> { fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> Result<()> {
match self { match self {
Executor::Wet(c) => c.status_checked_with(succeeded), Executor::Wet(c) => c.status_checked_with(succeeded),
Executor::Dry(c) => { Executor::Dry(c) => {
@@ -258,7 +257,7 @@ impl CommandExt for Executor {
} }
} }
fn spawn_checked(&mut self) -> eyre::Result<Self::Child> { fn spawn_checked(&mut self) -> Result<Self::Child> {
self.spawn() self.spawn()
} }
} }

View File

@@ -54,13 +54,13 @@ fn run() -> Result<()> {
if let Some(shell) = opt.gen_completion { if let Some(shell) = opt.gen_completion {
let cmd = &mut CommandLineArgs::command(); let cmd = &mut CommandLineArgs::command();
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut std::io::stdout()); clap_complete::generate(shell, cmd, clap::crate_name!(), &mut io::stdout());
return Ok(()); return Ok(());
} }
if opt.gen_manpage { if opt.gen_manpage {
let man = clap_mangen::Man::new(CommandLineArgs::command()); let man = clap_mangen::Man::new(CommandLineArgs::command());
man.render(&mut std::io::stdout())?; man.render(&mut io::stdout())?;
return Ok(()); return Ok(());
} }
@@ -79,14 +79,14 @@ fn run() -> Result<()> {
}; };
if opt.show_config_reference() { if opt.show_config_reference() {
print!("{}", crate::config::EXAMPLE_CONFIG); print!("{}", config::EXAMPLE_CONFIG);
return Ok(()); return Ok(());
} }
let config = Config::load(opt)?; let config = Config::load(opt)?;
terminal::set_title(config.set_title()); set_title(config.set_title());
terminal::display_time(config.display_time()); display_time(config.display_time());
terminal::set_desktop_notifications(config.notify_each_step()); set_desktop_notifications(config.notify_each_step());
debug!("Version: {}", crate_version!()); debug!("Version: {}", crate_version!());
debug!("OS: {}", env!("TARGET")); debug!("OS: {}", env!("TARGET"));
@@ -94,16 +94,6 @@ fn run() -> Result<()> {
debug!("Binary path: {:?}", std::env::current_exe()); debug!("Binary path: {:?}", std::env::current_exe());
debug!("Self Update: {:?}", cfg!(feature = "self-update")); debug!("Self Update: {:?}", cfg!(feature = "self-update"));
#[cfg(target_os = "linux")]
{
if config.display_preamble() && terminal::supports_notify_send() && !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() { if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
#[cfg(unix)] #[cfg(unix)]
{ {
@@ -114,12 +104,15 @@ For more information about this issue see https://askubuntu.com/questions/110969
let git = git::Git::new(); let git = git::Git::new();
let mut git_repos = git::Repositories::new(&git); let mut git_repos = git::Repositories::new(&git);
let powershell = powershell::Powershell::new();
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
let emacs = emacs::Emacs::new();
#[cfg(target_os = "linux")]
let distribution = linux::Distribution::detect();
let sudo = config.sudo_command().map_or_else(sudo::Sudo::detect, sudo::Sudo::new); let sudo = config.sudo_command().map_or_else(sudo::Sudo::detect, sudo::Sudo::new);
let run_type = executor::RunType::new(config.dry_run()); let run_type = executor::RunType::new(config.dry_run());
let ctx = execution_context::ExecutionContext::new(run_type, sudo, &git, &config); let ctx = execution_context::ExecutionContext::new(run_type, sudo, &git, &config);
let mut runner = runner::Runner::new(&ctx); let mut runner = runner::Runner::new(&ctx);
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
@@ -160,28 +153,30 @@ For more information about this issue see https://askubuntu.com/questions/110969
} }
} }
let powershell = powershell::Powershell::new();
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
#[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() { if let Some(topgrades) = config.remote_topgrades() {
for remote_topgrade in topgrades.iter().filter(|t| config.should_execute_remote(t)) { 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) ssh::ssh_step(&ctx, remote_topgrade)
})?; })?;
} }
} }
#[cfg(target_os = "linux")] #[cfg(windows)]
let distribution = linux::Distribution::detect();
#[cfg(target_os = r#"linux"#)]
{ {
runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(&ctx))?;
runner.execute(Step::WslUpdate, "WSL", || windows::update_wsl(&ctx))?;
runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?;
runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(&ctx))?;
runner.execute(Step::Winget, "Winget", || windows::run_winget(&ctx))?;
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
}
#[cfg(target_os = "linux")]
{
// NOTE: Due to breaking `nu` updates, `packer.nu` needs to be updated before `nu` get updated
// by other package managers.
runner.execute(Step::Shell, "packer.nu", || linux::run_packer_nu(&ctx))?;
match &distribution { match &distribution {
Ok(distribution) => { Ok(distribution) => {
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?; runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
@@ -195,13 +190,21 @@ For more information about this issue see https://askubuntu.com/questions/110969
runner.execute(Step::BrewFormula, "Brew", || { runner.execute(Step::BrewFormula, "Brew", || {
unix::run_brew_formula(&ctx, unix::BrewVariant::Path) unix::run_brew_formula(&ctx, unix::BrewVariant::Path)
})?; })?;
}
#[cfg(windows)] runner.execute(Step::AM, "am", || linux::run_am(&ctx))?;
{ runner.execute(Step::AppMan, "appman", || linux::run_appman(&ctx))?;
runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?; runner.execute(Step::DebGet, "deb-get", || linux::run_deb_get(&ctx))?;
runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?; runner.execute(Step::Toolbx, "toolbx", || toolbx::run_toolbx(&ctx))?;
runner.execute(Step::Winget, "Winget", || windows::run_winget(&ctx))?; runner.execute(Step::Flatpak, "Flatpak", || linux::run_flatpak(&ctx))?;
runner.execute(Step::Snap, "snap", || linux::run_snap(&ctx))?;
runner.execute(Step::Pacstall, "pacstall", || linux::run_pacstall(&ctx))?;
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))?;
runner.execute(Step::System, "pihole", || linux::run_pihole_update(&ctx))?;
runner.execute(Step::Firmware, "Firmware upgrades", || linux::run_fwupdmgr(&ctx))?;
runner.execute(Step::Restarts, "Restarts", || linux::run_needrestart(&ctx))?;
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -225,6 +228,35 @@ For more information about this issue see https://askubuntu.com/questions/110969
unix::run_brew_cask(&ctx, unix::BrewVariant::Path) unix::run_brew_cask(&ctx, unix::BrewVariant::Path)
})?; })?;
runner.execute(Step::Macports, "MacPorts", || macos::run_macports(&ctx))?; runner.execute(Step::Macports, "MacPorts", || macos::run_macports(&ctx))?;
runner.execute(Step::Sparkle, "Sparkle", || macos::run_sparkle(&ctx))?;
runner.execute(Step::Mas, "App Store", || macos::run_mas(&ctx))?;
runner.execute(Step::System, "System upgrade", || macos::upgrade_macos(&ctx))?;
}
#[cfg(target_os = "dragonfly")]
{
runner.execute(Step::Pkg, "DragonFly BSD Packages", || {
dragonfly::upgrade_packages(&ctx)
})?;
dragonfly::audit_packages(&ctx)?;
}
#[cfg(target_os = "freebsd")]
{
runner.execute(Step::Pkg, "FreeBSD Packages", || freebsd::upgrade_packages(&ctx))?;
runner.execute(Step::System, "FreeBSD Upgrade", || freebsd::upgrade_freebsd(&ctx))?;
freebsd::audit_packages(&ctx)?;
}
#[cfg(target_os = "openbsd")]
{
runner.execute(Step::Pkg, "OpenBSD Packages", || openbsd::upgrade_packages(&ctx))?;
runner.execute(Step::System, "OpenBSD Upgrade", || openbsd::upgrade_openbsd(&ctx))?;
}
#[cfg(target_os = "android")]
{
runner.execute(Step::Pkg, "Termux Packages", || android::upgrade_packages(&ctx))?;
} }
#[cfg(unix)] #[cfg(unix)]
@@ -232,32 +264,107 @@ For more information about this issue see https://askubuntu.com/questions/110969
runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?; runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?;
runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?; runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?;
runner.execute(Step::Guix, "guix", || unix::run_guix(&ctx))?; runner.execute(Step::Guix, "guix", || unix::run_guix(&ctx))?;
runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(&ctx))?;
runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(run_type))?; runner.execute(Step::Asdf, "asdf", || unix::run_asdf(&ctx))?;
runner.execute(Step::Asdf, "asdf", || unix::run_asdf(run_type))?;
runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?; runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?;
runner.execute(Step::Bun, "bun", || unix::run_bun(&ctx))?; runner.execute(Step::Bun, "bun", || unix::run_bun(&ctx))?;
runner.execute(Step::Shell, "zr", || zsh::run_zr(&ctx))?;
runner.execute(Step::Shell, "antibody", || zsh::run_antibody(&ctx))?;
runner.execute(Step::Shell, "antidote", || zsh::run_antidote(&ctx))?;
runner.execute(Step::Shell, "antigen", || zsh::run_antigen(&ctx))?;
runner.execute(Step::Shell, "zgenom", || zsh::run_zgenom(&ctx))?;
runner.execute(Step::Shell, "zplug", || zsh::run_zplug(&ctx))?;
runner.execute(Step::Shell, "zinit", || zsh::run_zinit(&ctx))?;
runner.execute(Step::Shell, "zi", || zsh::run_zi(&ctx))?;
runner.execute(Step::Shell, "zim", || zsh::run_zim(&ctx))?;
runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?;
runner.execute(Step::Shell, "oh-my-bash", || unix::run_oh_my_bash(&ctx))?;
runner.execute(Step::Shell, "fisher", || unix::run_fisher(&ctx))?;
runner.execute(Step::Shell, "bash-it", || unix::run_bashit(&ctx))?;
runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?;
runner.execute(Step::Shell, "fish-plug", || unix::run_fish_plug(&ctx))?;
runner.execute(Step::Shell, "fundle", || unix::run_fundle(&ctx))?;
runner.execute(Step::Tmux, "tmux", || tmux::run_tpm(&ctx))?;
runner.execute(Step::Tldr, "TLDR", || unix::run_tldr(&ctx))?;
runner.execute(Step::Pearl, "pearl", || unix::run_pearl(&ctx))?;
#[cfg(not(any(target_os = "macos", target_os = "android")))]
runner.execute(Step::GnomeShellExtensions, "Gnome Shell Extensions", || {
unix::upgrade_gnome_extensions(&ctx)
})?;
runner.execute(Step::Sdkman, "SDKMAN!", || unix::run_sdkman(&ctx))?;
runner.execute(Step::Rcm, "rcm", || unix::run_rcm(&ctx))?;
runner.execute(Step::Maza, "maza", || unix::run_maza(&ctx))?;
} }
#[cfg(target_os = "dragonfly")] #[cfg(not(any(
runner.execute(Step::Pkg, "DragonFly BSD Packages", || { target_os = "freebsd",
dragonfly::upgrade_packages(ctx.sudo().as_ref(), run_type) target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly"
)))]
{
runner.execute(Step::Atom, "apm", || generic::run_apm(&ctx))?;
}
// The following update function should be executed on all OSes.
runner.execute(Step::Fossil, "fossil", || generic::run_fossil(&ctx))?;
runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&ctx))?;
runner.execute(Step::Juliaup, "juliaup", || generic::run_juliaup(&ctx))?;
runner.execute(Step::Dotnet, ".NET", || generic::run_dotnet_upgrade(&ctx))?;
runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?;
runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(&ctx))?;
runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(&ctx))?;
runner.execute(Step::Go, "go-global-update", || go::run_go_global_update(&ctx))?;
runner.execute(Step::Go, "gup", || go::run_go_gup(&ctx))?;
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(&ctx))?;
runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(&ctx))?;
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
runner.execute(Step::Mamba, "mamba", || generic::run_mamba_update(&ctx))?;
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(&ctx))?;
runner.execute(Step::PipReview, "pip-review", || generic::run_pip_review_update(&ctx))?;
runner.execute(Step::PipReviewLocal, "pip-review (local)", || {
generic::run_pip_review_local_update(&ctx)
})?; })?;
runner.execute(Step::Pipupgrade, "pipupgrade", || generic::run_pipupgrade_update(&ctx))?;
#[cfg(target_os = "freebsd")] runner.execute(Step::Ghcup, "ghcup", || generic::run_ghcup_update(&ctx))?;
runner.execute(Step::Pkg, "FreeBSD Packages", || { runner.execute(Step::Stack, "stack", || generic::run_stack_update(&ctx))?;
freebsd::upgrade_packages(&ctx, ctx.sudo().as_ref(), run_type) runner.execute(Step::Tlmgr, "tlmgr", || generic::run_tlmgr_update(&ctx))?;
runner.execute(Step::Myrepos, "myrepos", || generic::run_myrepos_update(&ctx))?;
runner.execute(Step::Chezmoi, "chezmoi", || generic::run_chezmoi_update(&ctx))?;
runner.execute(Step::Jetpack, "jetpack", || generic::run_jetpack(&ctx))?;
runner.execute(Step::Vim, "vim", || vim::upgrade_vim(&ctx))?;
runner.execute(Step::Vim, "Neovim", || vim::upgrade_neovim(&ctx))?;
runner.execute(Step::Vim, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(&ctx))?;
runner.execute(Step::Vim, "voom", || vim::run_voom(&ctx))?;
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::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(&ctx))?;
runner.execute(Step::Helm, "helm", || generic::run_helm_repo_update(&ctx))?;
runner.execute(Step::Gem, "gem", || generic::run_gem(&ctx))?;
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))?;
runner.execute(Step::Stew, "stew", || generic::run_stew(&ctx))?;
runner.execute(Step::Rtcl, "rtcl", || generic::run_rtcl(&ctx))?;
runner.execute(Step::Bin, "bin", || generic::bin_update(&ctx))?;
runner.execute(Step::Gcloud, "gcloud", || generic::run_gcloud_components_update(&ctx))?;
runner.execute(Step::Micro, "micro", || generic::run_micro(&ctx))?;
runner.execute(Step::Raco, "raco", || generic::run_raco_update(&ctx))?;
runner.execute(Step::Spicetify, "spicetify", || generic::spicetify_upgrade(&ctx))?;
runner.execute(Step::GithubCliExtensions, "GitHub CLI Extensions", || {
generic::run_ghcli_extensions_upgrade(&ctx)
})?; })?;
runner.execute(Step::Bob, "Bob", || generic::run_bob(&ctx))?;
#[cfg(target_os = "openbsd")]
runner.execute(Step::Pkg, "OpenBSD Packages", || {
openbsd::upgrade_packages(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "android")]
runner.execute(Step::Pkg, "Termux Packages", || android::upgrade_packages(&ctx))?;
let emacs = emacs::Emacs::new();
if config.use_predefined_git_repos() { if config.use_predefined_git_repos() {
if config.should_run(Step::Emacs) { if config.should_run(Step::Emacs) {
if !emacs.is_doom() { if !emacs.is_doom() {
@@ -325,110 +432,6 @@ For more information about this issue see https://askubuntu.com/questions/110969
})?; })?;
} }
#[cfg(unix)]
{
runner.execute(Step::Shell, "zr", || zsh::run_zr(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(run_type))?;
runner.execute(Step::Shell, "zgenom", || zsh::run_zgenom(run_type))?;
runner.execute(Step::Shell, "zplug", || zsh::run_zplug(run_type))?;
runner.execute(Step::Shell, "zinit", || zsh::run_zinit(run_type))?;
runner.execute(Step::Shell, "zi", || zsh::run_zi(run_type))?;
runner.execute(Step::Shell, "zim", || zsh::run_zim(run_type))?;
runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?;
runner.execute(Step::Shell, "fisher", || unix::run_fisher(run_type))?;
runner.execute(Step::Shell, "bash-it", || unix::run_bashit(&ctx))?;
runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?;
runner.execute(Step::Shell, "fish-plug", || unix::run_fish_plug(&ctx))?;
runner.execute(Step::Shell, "fundle", || unix::run_fundle(&ctx))?;
runner.execute(Step::Tmux, "tmux", || tmux::run_tpm(run_type))?;
runner.execute(Step::Tldr, "TLDR", || unix::run_tldr(run_type))?;
runner.execute(Step::Pearl, "pearl", || unix::run_pearl(run_type))?;
#[cfg(not(any(target_os = "macos", target_os = "android")))]
runner.execute(Step::GnomeShellExtensions, "Gnome Shell Extensions", || {
unix::upgrade_gnome_extensions(&ctx)
})?;
runner.execute(Step::Sdkman, "SDKMAN!", || unix::run_sdkman(config.cleanup(), run_type))?;
runner.execute(Step::Rcm, "rcm", || unix::run_rcm(&ctx))?;
}
#[cfg(not(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly"
)))]
runner.execute(Step::Atom, "apm", || generic::run_apm(run_type))?;
runner.execute(Step::Fossil, "fossil", || generic::run_fossil(run_type))?;
runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&ctx))?;
runner.execute(Step::Juliaup, "juliaup", || generic::run_juliaup(run_type))?;
runner.execute(Step::Dotnet, ".NET", || generic::run_dotnet_upgrade(&ctx))?;
runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?;
runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(&ctx))?;
runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(run_type))?;
runner.execute(Step::Go, "go-global-update", || go::run_go_global_update(run_type))?;
runner.execute(Step::Go, "gup", || go::run_go_gup(run_type))?;
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(&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::Mamba, "mamba", || generic::run_mamba_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))?;
runner.execute(Step::Myrepos, "myrepos", || generic::run_myrepos_update(run_type))?;
runner.execute(Step::Chezmoi, "chezmoi", || generic::run_chezmoi_update(run_type))?;
runner.execute(Step::Jetpack, "jetpack", || generic::run_jetpack(run_type))?;
runner.execute(Step::Vim, "vim", || vim::upgrade_vim(&ctx))?;
runner.execute(Step::Vim, "Neovim", || vim::upgrade_neovim(&ctx))?;
runner.execute(Step::Vim, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(&ctx))?;
runner.execute(Step::Vim, "voom", || vim::run_voom(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::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(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))?;
runner.execute(Step::Rtcl, "rtcl", || generic::run_rtcl(&ctx))?;
runner.execute(Step::Bin, "bin", || generic::bin_update(&ctx))?;
runner.execute(Step::Gcloud, "gcloud", || {
generic::run_gcloud_components_update(run_type)
})?;
runner.execute(Step::Micro, "micro", || generic::run_micro(run_type))?;
runner.execute(Step::Raco, "raco", || generic::run_raco_update(run_type))?;
runner.execute(Step::Spicetify, "spicetify", || generic::spicetify_upgrade(&ctx))?;
runner.execute(Step::GithubCliExtensions, "GitHub CLI Extensions", || {
generic::run_ghcli_extensions_upgrade(&ctx)
})?;
#[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))?;
runner.execute(Step::Snap, "snap", || linux::run_snap(ctx.sudo().as_ref(), run_type))?;
runner.execute(Step::Pacstall, "pacstall", || linux::run_pacstall(&ctx))?;
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() { if let Some(commands) = config.commands() {
for (name, command) in commands { for (name, command) in commands {
if config.should_run_custom_command(name) { if config.should_run_custom_command(name) {
@@ -439,37 +442,6 @@ For more information about this issue see https://askubuntu.com/questions/110969
} }
} }
#[cfg(target_os = "linux")]
{
runner.execute(Step::System, "pihole", || {
linux::run_pihole_update(ctx.sudo().as_ref(), run_type)
})?;
runner.execute(Step::Firmware, "Firmware upgrades", || linux::run_fwupdmgr(&ctx))?;
runner.execute(Step::Restarts, "Restarts", || {
linux::run_needrestart(ctx.sudo().as_ref(), run_type)
})?;
}
#[cfg(target_os = "macos")]
{
runner.execute(Step::Sparkle, "Sparkle", || macos::run_sparkle(&ctx))?;
runner.execute(Step::Mas, "App Store", || macos::run_mas(run_type))?;
runner.execute(Step::System, "System upgrade", || macos::upgrade_macos(&ctx))?;
}
#[cfg(target_os = "freebsd")]
runner.execute(Step::System, "FreeBSD Upgrade", || {
freebsd::upgrade_freebsd(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "openbsd")]
runner.execute(Step::System, "OpenBSD Upgrade", || {
openbsd::upgrade_openbsd(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(windows)]
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
if config.should_run(Step::Vagrant) { if config.should_run(Step::Vagrant) {
if let Ok(boxes) = vagrant::collect_boxes(&ctx) { if let Ok(boxes) = vagrant::collect_boxes(&ctx) {
for vagrant_box in boxes { for vagrant_box in boxes {
@@ -494,12 +466,6 @@ For more information about this issue see https://askubuntu.com/questions/110969
distribution.show_summary(); distribution.show_summary();
} }
} }
#[cfg(target_os = "freebsd")]
freebsd::audit_packages(ctx.sudo().as_ref()).ok();
#[cfg(target_os = "dragonfly")]
dragonfly::audit_packages(ctx.sudo().as_ref()).ok();
} }
let mut post_command_failed = false; let mut post_command_failed = false;
@@ -533,7 +499,7 @@ For more information about this issue see https://askubuntu.com/questions/110969
let failed = post_command_failed || runner.report().data().iter().any(|(_, result)| result.failed()); let failed = post_command_failed || runner.report().data().iter().any(|(_, result)| result.failed());
if !config.skip_notify() { if !config.skip_notify() {
terminal::notify_desktop( notify_desktop(
format!( format!(
"Topgrade finished {}", "Topgrade finished {}",
if failed { "with errors" } else { "successfully" } if failed { "with errors" } else { "successfully" }
@@ -579,7 +545,7 @@ fn main() {
} }
} }
pub fn install_tracing(filter_directives: &str) -> Result<()> { fn install_tracing(filter_directives: &str) -> Result<()> {
use tracing_subscriber::fmt; use tracing_subscriber::fmt;
use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::layer::SubscriberExt;

View File

@@ -1,3 +1,4 @@
use std::fmt::{Display, Formatter};
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
@@ -18,15 +19,42 @@ use crate::{execution_context::ExecutionContext, utils::require};
// themselves or when using docker-compose. // themselves or when using docker-compose.
const NONEXISTENT_REPO: &str = "repository does not exist"; const NONEXISTENT_REPO: &str = "repository does not exist";
/// Uniquely identifies a `Container`.
#[derive(Debug)]
struct Container {
/// `Repository` and `Tag`
///
/// format: `Repository:Tag`, e.g., `nixos/nix:latest`.
repo_tag: String,
/// Platform
///
/// format: `OS/Architecture`, e.g., `linux/amd64`.
platform: String,
}
impl Container {
/// Construct a new `Container`.
fn new(repo_tag: String, platform: String) -> Self {
Self { repo_tag, platform }
}
}
impl Display for Container {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// e.g., "`fedora:latest` for `linux/amd64`"
write!(f, "`{}` for `{}`", self.repo_tag, self.platform)
}
}
/// Returns a Vector of all containers, with Strings in the format /// Returns a Vector of all containers, with Strings in the format
/// "REGISTRY/[PATH/]CONTAINER_NAME:TAG" /// "REGISTRY/[PATH/]CONTAINER_NAME:TAG"
fn list_containers(crt: &Path) -> Result<Vec<String>> { fn list_containers(crt: &Path) -> Result<Vec<Container>> {
debug!( debug!(
"Querying '{} image ls --format \"{{{{.Repository}}}}:{{{{.Tag}}}}\"' for containers", "Querying '{} image ls --format \"{{{{.Repository}}}}:{{{{.Tag}}}}/{{{{.ID}}}}\"' for containers",
crt.display() crt.display()
); );
let output = Command::new(crt) let output = Command::new(crt)
.args(["image", "ls", "--format", "{{.Repository}}:{{.Tag}}"]) .args(["image", "ls", "--format", "{{.Repository}}:{{.Tag}} {{.ID}}"])
.output_checked_with_utf8(|_| Ok(()))?; .output_checked_with_utf8(|_| Ok(()))?;
let mut retval = vec![]; let mut retval = vec![];
@@ -49,7 +77,26 @@ fn list_containers(crt: &Path) -> Result<Vec<String>> {
} }
debug!("Using container '{}'", line); debug!("Using container '{}'", line);
retval.push(String::from(line));
// line is of format: `Repository:Tag ImageID`, e.g., `nixos/nix:latest d80fea9c32b4`
let split_res = line.split(' ').collect::<Vec<&str>>();
assert_eq!(split_res.len(), 2);
let (repo_tag, image_id) = (split_res[0], split_res[1]);
debug!(
"Querying '{} image inspect --format \"{{{{.Os}}}}/{{{{.Architecture}}}}\"' for container {}",
crt.display(),
image_id
);
let inspect_output = Command::new(crt)
.args(["image", "inspect", image_id, "--format", "{{.Os}}/{{.Architecture}}"])
.output_checked_with_utf8(|_| Ok(()))?;
let mut platform = inspect_output.stdout;
// truncate the tailing new line character
platform.truncate(platform.len() - 1);
assert!(platform.contains('/'));
retval.push(Container::new(repo_tag.to_string(), platform));
} }
Ok(retval) Ok(retval)
@@ -67,7 +114,12 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> {
for container in containers.iter() { for container in containers.iter() {
debug!("Pulling container '{}'", container); debug!("Pulling container '{}'", container);
let args = vec!["pull", &container[..]]; let args = vec![
"pull",
container.repo_tag.as_str(),
"--platform",
container.platform.as_str(),
];
let mut exec = ctx.run_type().execute(&crt); let mut exec = ctx.run_type().execute(&crt);
if let Err(e) = exec.args(&args).status_checked() { if let Err(e) = exec.args(&args).status_checked() {

View File

@@ -13,9 +13,9 @@ use tracing::{debug, error};
use crate::command::{CommandExt, Utf8Output}; use crate::command::{CommandExt, Utf8Output};
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::{ExecutorOutput, RunType}; use crate::executor::ExecutorOutput;
use crate::terminal::{print_separator, shell}; use crate::terminal::{print_separator, shell};
use crate::utils::{self, require, require_option, which, PathExt}; use crate::utils::{self, check_is_python_2_or_shim, require, require_option, which, PathExt, REQUIRE_SUDO};
use crate::Step; use crate::Step;
use crate::HOME_DIR; use crate::HOME_DIR;
use crate::{ use crate::{
@@ -28,7 +28,7 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or_else(|| HOME_DIR.join(".cargo")) .unwrap_or_else(|| HOME_DIR.join(".cargo"))
.require()?; .require()?;
utils::require("cargo").or_else(|_| { require("cargo").or_else(|_| {
require_option( require_option(
cargo_dir.join("bin/cargo").if_exists(), cargo_dir.join("bin/cargo").if_exists(),
String::from("No cargo detected"), String::from("No cargo detected"),
@@ -42,7 +42,7 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
} }
print_separator("Cargo"); print_separator("Cargo");
let cargo_update = utils::require("cargo-install-update") let cargo_update = require("cargo-install-update")
.ok() .ok()
.or_else(|| cargo_dir.join("bin/cargo-install-update").if_exists()); .or_else(|| cargo_dir.join("bin/cargo-install-update").if_exists());
let cargo_update = match cargo_update { let cargo_update = match cargo_update {
@@ -60,7 +60,7 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
.status_checked()?; .status_checked()?;
if ctx.config().cleanup() { if ctx.config().cleanup() {
let cargo_cache = utils::require("cargo-cache") let cargo_cache = require("cargo-cache")
.ok() .ok()
.or_else(|| cargo_dir.join("bin/cargo-cache").if_exists()); .or_else(|| cargo_dir.join("bin/cargo-cache").if_exists());
match cargo_cache { match cargo_cache {
@@ -77,20 +77,20 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
pub fn run_flutter_upgrade(run_type: RunType) -> Result<()> { pub fn run_flutter_upgrade(ctx: &ExecutionContext) -> Result<()> {
let flutter = utils::require("flutter")?; let flutter = require("flutter")?;
print_separator("Flutter"); print_separator("Flutter");
run_type.execute(flutter).arg("upgrade").status_checked() ctx.run_type().execute(flutter).arg("upgrade").status_checked()
} }
pub fn run_gem(run_type: RunType) -> Result<()> { pub fn run_gem(ctx: &ExecutionContext) -> Result<()> {
let gem = utils::require("gem")?; let gem = require("gem")?;
HOME_DIR.join(".gem").require()?; HOME_DIR.join(".gem").require()?;
print_separator("Gems"); print_separator("Gems");
let mut command = run_type.execute(gem); let mut command = ctx.run_type().execute(gem);
command.arg("update"); command.arg("update");
if env::var_os("RBENV_SHELL").is_none() { if env::var_os("RBENV_SHELL").is_none() {
@@ -112,8 +112,9 @@ pub fn run_rubygems(ctx: &ExecutionContext) -> Result<()> {
.execute(gem) .execute(gem)
.args(["update", "--system"]) .args(["update", "--system"])
.status_checked()?; .status_checked()?;
} else if let Some(sudo) = &ctx.sudo() { } else {
if !std::path::Path::new("/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb").exists() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
if !Path::new("/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb").exists() {
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.arg("-EH") .arg("-EH")
@@ -121,14 +122,13 @@ pub fn run_rubygems(ctx: &ExecutionContext) -> Result<()> {
.args(["update", "--system"]) .args(["update", "--system"])
.status_checked()?; .status_checked()?;
} }
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
Ok(()) Ok(())
} }
pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
let haxelib = utils::require("haxelib")?; let haxelib = require("haxelib")?;
let haxelib_dir = let haxelib_dir =
PathBuf::from(std::str::from_utf8(&Command::new(&haxelib).arg("config").output_checked()?.stdout)?.trim()) PathBuf::from(std::str::from_utf8(&Command::new(&haxelib).arg("config").output_checked()?.stdout)?.trim())
@@ -142,9 +142,8 @@ pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
let mut command = if directory_writable { let mut command = if directory_writable {
ctx.run_type().execute(&haxelib) ctx.run_type().execute(&haxelib)
} else { } else {
let mut c = ctx let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
.run_type() let mut c = ctx.run_type().execute(sudo);
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?);
c.arg(&haxelib); c.arg(&haxelib);
c c
}; };
@@ -153,7 +152,7 @@ pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_sheldon(ctx: &ExecutionContext) -> Result<()> { pub fn run_sheldon(ctx: &ExecutionContext) -> Result<()> {
let sheldon = utils::require("sheldon")?; let sheldon = require("sheldon")?;
print_separator("Sheldon"); print_separator("Sheldon");
@@ -163,20 +162,21 @@ pub fn run_sheldon(ctx: &ExecutionContext) -> Result<()> {
.status_checked() .status_checked()
} }
pub fn run_fossil(run_type: RunType) -> Result<()> { pub fn run_fossil(ctx: &ExecutionContext) -> Result<()> {
let fossil = utils::require("fossil")?; let fossil = require("fossil")?;
print_separator("Fossil"); print_separator("Fossil");
run_type.execute(fossil).args(["all", "sync"]).status_checked() ctx.run_type().execute(fossil).args(["all", "sync"]).status_checked()
} }
pub fn run_micro(run_type: RunType) -> Result<()> { pub fn run_micro(ctx: &ExecutionContext) -> Result<()> {
let micro = utils::require("micro")?; let micro = require("micro")?;
print_separator("micro"); print_separator("micro");
let stdout = run_type let stdout = ctx
.run_type()
.execute(micro) .execute(micro)
.args(["-plugin", "update"]) .args(["-plugin", "update"])
.output_checked_utf8()? .output_checked_utf8()?
@@ -196,38 +196,41 @@ pub fn run_micro(run_type: RunType) -> Result<()> {
target_os = "netbsd", target_os = "netbsd",
target_os = "dragonfly" target_os = "dragonfly"
)))] )))]
pub fn run_apm(run_type: RunType) -> Result<()> { pub fn run_apm(ctx: &ExecutionContext) -> Result<()> {
let apm = utils::require("apm")?; let apm = require("apm")?;
print_separator("Atom Package Manager"); print_separator("Atom Package Manager");
run_type ctx.run_type()
.execute(apm) .execute(apm)
.args(["upgrade", "--confirm=false"]) .args(["upgrade", "--confirm=false"])
.status_checked() .status_checked()
} }
pub fn run_rustup(ctx: &ExecutionContext) -> Result<()> { pub fn run_rustup(ctx: &ExecutionContext) -> Result<()> {
let rustup = utils::require("rustup")?; let rustup = require("rustup")?;
print_separator("rustup"); print_separator("rustup");
ctx.run_type().execute(rustup).arg("update").status_checked() ctx.run_type().execute(rustup).arg("update").status_checked()
} }
pub fn run_juliaup(run_type: RunType) -> Result<()> { pub fn run_juliaup(ctx: &ExecutionContext) -> Result<()> {
let juliaup = utils::require("juliaup")?; let juliaup = require("juliaup")?;
print_separator("juliaup"); print_separator("juliaup");
if juliaup.canonicalize()?.is_descendant_of(&HOME_DIR) { if juliaup.canonicalize()?.is_descendant_of(&HOME_DIR) {
run_type.execute(&juliaup).args(["self", "update"]).status_checked()?; ctx.run_type()
.execute(&juliaup)
.args(["self", "update"])
.status_checked()?;
} }
run_type.execute(&juliaup).arg("update").status_checked() ctx.run_type().execute(&juliaup).arg("update").status_checked()
} }
pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> { pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> {
let choosenim = utils::require("choosenim")?; let choosenim = require("choosenim")?;
print_separator("choosenim"); print_separator("choosenim");
let run_type = ctx.run_type(); let run_type = ctx.run_type();
@@ -236,39 +239,42 @@ pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> {
run_type.execute(&choosenim).args(["update", "stable"]).status_checked() run_type.execute(&choosenim).args(["update", "stable"]).status_checked()
} }
pub fn run_krew_upgrade(run_type: RunType) -> Result<()> { pub fn run_krew_upgrade(ctx: &ExecutionContext) -> Result<()> {
let krew = utils::require("kubectl-krew")?; let krew = require("kubectl-krew")?;
print_separator("Krew"); print_separator("Krew");
run_type.execute(krew).args(["upgrade"]).status_checked() ctx.run_type().execute(krew).args(["upgrade"]).status_checked()
} }
pub fn run_gcloud_components_update(run_type: RunType) -> Result<()> { pub fn run_gcloud_components_update(ctx: &ExecutionContext) -> Result<()> {
let gcloud = utils::require("gcloud")?; let gcloud = require("gcloud")?;
if gcloud.starts_with("/snap") { if gcloud.starts_with("/snap") {
Ok(()) Ok(())
} else { } else {
print_separator("gcloud"); print_separator("gcloud");
run_type ctx.run_type()
.execute(gcloud) .execute(gcloud)
.args(["components", "update", "--quiet"]) .args(["components", "update", "--quiet"])
.status_checked() .status_checked()
} }
} }
pub fn run_jetpack(run_type: RunType) -> Result<()> { pub fn run_jetpack(ctx: &ExecutionContext) -> Result<()> {
let jetpack = utils::require("jetpack")?; let jetpack = require("jetpack")?;
print_separator("Jetpack"); print_separator("Jetpack");
run_type.execute(jetpack).args(["global", "update"]).status_checked() ctx.run_type()
.execute(jetpack)
.args(["global", "update"])
.status_checked()
} }
pub fn run_rtcl(ctx: &ExecutionContext) -> Result<()> { pub fn run_rtcl(ctx: &ExecutionContext) -> Result<()> {
let rupdate = utils::require("rupdate")?; let rupdate = require("rupdate")?;
print_separator("rtcl"); print_separator("rtcl");
@@ -276,7 +282,7 @@ pub fn run_rtcl(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> {
let opam = utils::require("opam")?; let opam = require("opam")?;
print_separator("OCaml Package Manager"); print_separator("OCaml Package Manager");
@@ -291,7 +297,7 @@ pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
let vcpkg = utils::require("vcpkg")?; let vcpkg = require("vcpkg")?;
print_separator("vcpkg"); print_separator("vcpkg");
#[cfg(unix)] #[cfg(unix)]
@@ -303,9 +309,8 @@ pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
let mut command = if is_root_install { let mut command = if is_root_install {
ctx.run_type().execute(&vcpkg) ctx.run_type().execute(&vcpkg)
} else { } else {
let mut c = ctx let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
.run_type() let mut c = ctx.run_type().execute(sudo);
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?);
c.arg(&vcpkg); c.arg(&vcpkg);
c c
}; };
@@ -313,15 +318,15 @@ pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
command.args(["upgrade", "--no-dry-run"]).status_checked() command.args(["upgrade", "--no-dry-run"]).status_checked()
} }
pub fn run_pipx_update(run_type: RunType) -> Result<()> { pub fn run_pipx_update(ctx: &ExecutionContext) -> Result<()> {
let pipx = utils::require("pipx")?; let pipx = require("pipx")?;
print_separator("pipx"); print_separator("pipx");
run_type.execute(pipx).arg("upgrade-all").status_checked() ctx.run_type().execute(pipx).arg("upgrade-all").status_checked()
} }
pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
let conda = utils::require("conda")?; let conda = require("conda")?;
let output = Command::new("conda") let output = Command::new("conda")
.args(["config", "--show", "auto_activate_base"]) .args(["config", "--show", "auto_activate_base"])
@@ -342,7 +347,7 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
let mamba = utils::require("mamba")?; let mamba = require("mamba")?;
let output = Command::new("mamba") let output = Command::new("mamba")
.args(["config", "--show", "auto_activate_base"]) .args(["config", "--show", "auto_activate_base"])
@@ -362,8 +367,19 @@ pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
command.status_checked() command.status_checked()
} }
pub fn run_pip3_update(run_type: RunType) -> Result<()> { pub fn run_pip3_update(ctx: &ExecutionContext) -> Result<()> {
let python3 = utils::require("python3")?; let py = require("python").and_then(check_is_python_2_or_shim);
let py3 = require("python3").and_then(check_is_python_2_or_shim);
let python3 = match (py, py3) {
// prefer `python` if it is available and is a valid Python 3.
(Ok(py), _) => py,
(Err(_), Ok(py3)) => py3,
(Err(py_err), Err(py3_err)) => {
return Err(SkipStep(format!("Skip due to following reasons: {} {}", py_err, py3_err)).into());
}
};
Command::new(&python3) Command::new(&python3)
.args(["-m", "pip"]) .args(["-m", "pip"])
.output_checked_utf8() .output_checked_utf8()
@@ -385,12 +401,12 @@ pub fn run_pip3_update(run_type: RunType) -> Result<()> {
})?; })?;
print_separator("pip3"); print_separator("pip3");
if std::env::var("VIRTUAL_ENV").is_ok() { if env::var("VIRTUAL_ENV").is_ok() {
print_warning("This step is will be skipped when running inside a virtual environment"); print_warning("This step is will be skipped when running inside a virtual environment");
return Err(SkipStep("Does not run inside a virtual environment".to_string()).into()); return Err(SkipStep("Does not run inside a virtual environment".to_string()).into());
} }
run_type ctx.run_type()
.execute(&python3) .execute(&python3)
.args(["-m", "pip", "install", "--upgrade", "--user", "pip"]) .args(["-m", "pip", "install", "--upgrade", "--user", "pip"])
.status_checked() .status_checked()
@@ -414,40 +430,61 @@ pub fn run_pip_review_update(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
pub fn run_pip_review_local_update(ctx: &ExecutionContext) -> Result<()> {
let pip_review = require("pip-review")?;
print_separator("pip-review (local)");
if !ctx.config().enable_pip_review_local() {
print_warning(
"Pip-review (local) is disabled by default. Enable it by setting enable_pip_review_local=true in the configuration.",
);
return Err(SkipStep(String::from("Pip-review (local) is disabled by default")).into());
}
ctx.run_type()
.execute(pip_review)
.arg("--local")
.arg("--auto")
.status_checked_with_codes(&[1])?;
Ok(())
}
pub fn run_pipupgrade_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_pipupgrade_update(ctx: &ExecutionContext) -> Result<()> {
let pipupgrade = require("pipupgrade")?; let pipupgrade = require("pipupgrade")?;
print_separator("Pipupgrade"); print_separator("Pipupgrade");
if !ctx.config().enable_pip_review() { if !ctx.config().enable_pipupgrade() {
print_warning( print_warning(
"Pipupgrade is disabled by default. Enable it by setting enable_pipupgrade=true in the configuration.", "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()); return Err(SkipStep(String::from("Pipupgrade is disabled by default")).into());
} }
ctx.run_type().execute(pipupgrade).status_checked()?; ctx.run_type()
.execute(pipupgrade)
.args(ctx.config().pipupgrade_arguments().split_whitespace())
.status_checked()?;
Ok(()) Ok(())
} }
pub fn run_stack_update(ctx: &ExecutionContext) -> Result<()> {
pub fn run_stack_update(run_type: RunType) -> Result<()> { if require("ghcup").is_ok() {
if utils::require("ghcup").is_ok() {
// `ghcup` is present and probably(?) being used to install `stack`. // `ghcup` is present and probably(?) being used to install `stack`.
// Don't upgrade `stack`, let `ghcup` handle it. Per `ghcup install stack`: // Don't upgrade `stack`, let `ghcup` handle it. Per `ghcup install stack`:
// !!! Additionally, you should upgrade stack only through ghcup and not use 'stack upgrade' !!! // !!! Additionally, you should upgrade stack only through ghcup and not use 'stack upgrade' !!!
return Ok(()); return Ok(());
} }
let stack = utils::require("stack")?; let stack = require("stack")?;
print_separator("stack"); print_separator("stack");
run_type.execute(stack).arg("upgrade").status_checked() ctx.run_type().execute(stack).arg("upgrade").status_checked()
} }
pub fn run_ghcup_update(run_type: RunType) -> Result<()> { pub fn run_ghcup_update(ctx: &ExecutionContext) -> Result<()> {
let ghcup = utils::require("ghcup")?; let ghcup = require("ghcup")?;
print_separator("ghcup"); print_separator("ghcup");
run_type.execute(ghcup).arg("upgrade").status_checked() ctx.run_type().execute(ghcup).arg("upgrade").status_checked()
} }
pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
@@ -459,8 +496,8 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
} }
} }
let tlmgr = utils::require("tlmgr")?; let tlmgr = require("tlmgr")?;
let kpsewhich = utils::require("kpsewhich")?; let kpsewhich = require("kpsewhich")?;
let tlmgr_directory = { let tlmgr_directory = {
let mut d = PathBuf::from( let mut d = PathBuf::from(
&Command::new(kpsewhich) &Command::new(kpsewhich)
@@ -482,9 +519,8 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
let mut command = if directory_writable { let mut command = if directory_writable {
ctx.run_type().execute(&tlmgr) ctx.run_type().execute(&tlmgr)
} else { } else {
let mut c = ctx let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
.run_type() let mut c = ctx.run_type().execute(sudo);
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?);
c.arg(&tlmgr); c.arg(&tlmgr);
c c
}; };
@@ -493,28 +529,28 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
command.status_checked() command.status_checked()
} }
pub fn run_chezmoi_update(run_type: RunType) -> Result<()> { pub fn run_chezmoi_update(ctx: &ExecutionContext) -> Result<()> {
let chezmoi = utils::require("chezmoi")?; let chezmoi = require("chezmoi")?;
HOME_DIR.join(".local/share/chezmoi").require()?; HOME_DIR.join(".local/share/chezmoi").require()?;
print_separator("chezmoi"); print_separator("chezmoi");
run_type.execute(chezmoi).arg("update").status_checked() ctx.run_type().execute(chezmoi).arg("update").status_checked()
} }
pub fn run_myrepos_update(run_type: RunType) -> Result<()> { pub fn run_myrepos_update(ctx: &ExecutionContext) -> Result<()> {
let myrepos = utils::require("mr")?; let myrepos = require("mr")?;
HOME_DIR.join(".mrconfig").require()?; HOME_DIR.join(".mrconfig").require()?;
print_separator("myrepos"); print_separator("myrepos");
run_type ctx.run_type()
.execute(&myrepos) .execute(&myrepos)
.arg("--directory") .arg("--directory")
.arg(&*HOME_DIR) .arg(&*HOME_DIR)
.arg("checkout") .arg("checkout")
.status_checked()?; .status_checked()?;
run_type ctx.run_type()
.execute(&myrepos) .execute(&myrepos)
.arg("--directory") .arg("--directory")
.arg(&*HOME_DIR) .arg(&*HOME_DIR)
@@ -536,7 +572,7 @@ pub fn run_custom_command(name: &str, command: &str, ctx: &ExecutionContext) ->
} }
pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
let composer = utils::require("composer")?; let composer = require("composer")?;
let composer_home = Command::new(&composer) let composer_home = Command::new(&composer)
.args(["global", "config", "--absolute", "--quiet", "home"]) .args(["global", "config", "--absolute", "--quiet", "home"])
.output_checked_utf8() .output_checked_utf8()
@@ -564,8 +600,9 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
}; };
if has_update { if has_update {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(ctx.sudo().as_ref().unwrap()) .execute(sudo)
.arg(&composer) .arg(&composer)
.arg("self-update") .arg("self-update")
.status_checked()?; .status_checked()?;
@@ -581,7 +618,7 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
let output: Utf8Output = output.try_into()?; let output: Utf8Output = output.try_into()?;
print!("{}\n{}", output.stdout, output.stderr); print!("{}\n{}", output.stdout, output.stderr);
if output.stdout.contains("valet") || output.stderr.contains("valet") { if output.stdout.contains("valet") || output.stderr.contains("valet") {
if let Some(valet) = utils::which("valet") { if let Some(valet) = which("valet") {
ctx.run_type().execute(valet).arg("install").status_checked()?; ctx.run_type().execute(valet).arg("install").status_checked()?;
} }
} }
@@ -591,9 +628,10 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
let dotnet = utils::require("dotnet")?; let dotnet = require("dotnet")?;
//Skip when the `dotnet tool list` subcommand fails. (This is expected when a dotnet runtime is installed but no SDK.) // Skip when the `dotnet tool list` subcommand fails.
// (This is expected when a dotnet runtime is installed but no SDK.)
let output = match ctx let output = match ctx
.run_type() .run_type()
.execute(&dotnet) .execute(&dotnet)
@@ -609,11 +647,20 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
} }
}; };
if !output.stdout.starts_with("Package Id") { let mut packages = output
return Err(SkipStep(String::from("dotnet did not output packages")).into()); .stdout
} .lines()
// Skip the header:
let mut packages = output.stdout.lines().skip(2).filter(|line| !line.is_empty()).peekable(); //
// Package Id Version Commands
// -------------------------------------
//
// One thing to note is that .NET SDK respect locale, which means this
// header can be printed in languages other than English, do NOT use it
// to do any check.
.skip(2)
.filter(|line| !line.is_empty())
.peekable();
if packages.peek().is_none() { if packages.peek().is_none() {
return Err(SkipStep(String::from("No dotnet global tools installed")).into()); return Err(SkipStep(String::from("No dotnet global tools installed")).into());
@@ -634,18 +681,19 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_helix_grammars(ctx: &ExecutionContext) -> Result<()> { pub fn run_helix_grammars(ctx: &ExecutionContext) -> Result<()> {
utils::require("helix")?; require("helix")?;
print_separator("Helix"); print_separator("Helix");
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?) .execute(sudo)
.args(["helix", "--grammar", "fetch"]) .args(["helix", "--grammar", "fetch"])
.status_checked() .status_checked()
.with_context(|| "Failed to download helix grammars!")?; .with_context(|| "Failed to download helix grammars!")?;
ctx.run_type() ctx.run_type()
.execute(ctx.sudo().as_ref().ok_or(TopgradeError::SudoRequired)?) .execute(sudo)
.args(["helix", "--grammar", "build"]) .args(["helix", "--grammar", "build"])
.status_checked() .status_checked()
.with_context(|| "Failed to build helix grammars!")?; .with_context(|| "Failed to build helix grammars!")?;
@@ -653,30 +701,33 @@ pub fn run_helix_grammars(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
pub fn run_raco_update(run_type: RunType) -> Result<()> { pub fn run_raco_update(ctx: &ExecutionContext) -> Result<()> {
let raco = utils::require("raco")?; let raco = require("raco")?;
print_separator("Racket Package Manager"); print_separator("Racket Package Manager");
run_type.execute(raco).args(["pkg", "update", "--all"]).status_checked() ctx.run_type()
.execute(raco)
.args(["pkg", "update", "--all"])
.status_checked()
} }
pub fn bin_update(ctx: &ExecutionContext) -> Result<()> { pub fn bin_update(ctx: &ExecutionContext) -> Result<()> {
let bin = utils::require("bin")?; let bin = require("bin")?;
print_separator("Bin"); print_separator("Bin");
ctx.run_type().execute(bin).arg("update").status_checked() ctx.run_type().execute(bin).arg("update").status_checked()
} }
pub fn spicetify_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn spicetify_upgrade(ctx: &ExecutionContext) -> Result<()> {
let spicetify = utils::require("spicetify")?; let spicetify = require("spicetify")?;
print_separator("Spicetify"); print_separator("Spicetify");
ctx.run_type().execute(spicetify).arg("upgrade").status_checked() ctx.run_type().execute(spicetify).arg("upgrade").status_checked()
} }
pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> {
let gh = utils::require("gh")?; let gh = require("gh")?;
let result = Command::new(&gh).args(["extensions", "list"]).output_checked_utf8(); let result = Command::new(&gh).args(["extensions", "list"]).output_checked_utf8();
if result.is_err() { if result.is_err() {
debug!("GH result {:?}", result); debug!("GH result {:?}", result);
@@ -691,7 +742,7 @@ pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> { pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
let julia = utils::require("julia")?; let julia = require("julia")?;
print_separator("Julia Packages"); print_separator("Julia Packages");
@@ -701,14 +752,14 @@ pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
.status_checked() .status_checked()
} }
pub fn run_helm_repo_update(run_type: RunType) -> Result<()> { pub fn run_helm_repo_update(ctx: &ExecutionContext) -> Result<()> {
let helm = utils::require("helm")?; let helm = require("helm")?;
print_separator("Helm"); print_separator("Helm");
let no_repo = "no repositories found"; let no_repo = "no repositories found";
let mut success = true; let mut success = true;
let mut exec = run_type.execute(helm); let mut exec = ctx.run_type().execute(helm);
if let Err(e) = exec.arg("repo").arg("update").status_checked() { if let Err(e) = exec.arg("repo").arg("update").status_checked() {
error!("Updating repositories failed: {}", e); error!("Updating repositories failed: {}", e);
success = match exec.output_checked_utf8() { success = match exec.output_checked_utf8() {
@@ -726,3 +777,18 @@ pub fn run_helm_repo_update(run_type: RunType) -> Result<()> {
Err(eyre!(StepFailed)) Err(eyre!(StepFailed))
} }
} }
pub fn run_stew(ctx: &ExecutionContext) -> Result<()> {
let stew = require("stew")?;
print_separator("stew");
ctx.run_type().execute(stew).args(["upgrade", "--all"]).status_checked()
}
pub fn run_bob(ctx: &ExecutionContext) -> Result<()> {
let bob = require("bob")?;
print_separator("Bob");
ctx.run_type().execute(bob).args(["update", "--all"]).status_checked()
}

View File

@@ -14,7 +14,6 @@ use tracing::{debug, error};
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils::{which, PathExt}; use crate::utils::{which, PathExt};
use crate::{error::SkipStep, terminal::print_warning}; use crate::{error::SkipStep, terminal::print_warning};
@@ -179,22 +178,28 @@ impl Git {
None None
} }
pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> {
// Warn the user about the bad patterns.
//
// NOTE: this should be executed **before** skipping the Git step or the
// user won't receive this warning in the cases where all the paths configured
// are bad patterns.
repositories
.bad_patterns
.iter()
.for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories")));
if repositories.repositories.is_empty() { if repositories.repositories.is_empty() {
return Err(SkipStep(String::from("No repositories to pull")).into()); return Err(SkipStep(String::from("No repositories to pull")).into());
} }
print_separator("Git repositories"); print_separator("Git repositories");
repositories
.bad_patterns
.iter()
.for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories")));
self.multi_pull(repositories, ctx) self.multi_pull(repositories, ctx)
} }
pub fn multi_pull(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { pub fn multi_pull(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> {
let git = self.git.as_ref().unwrap(); let git = self.git.as_ref().unwrap();
if let RunType::Dry = ctx.run_type() { if ctx.run_type().dry() {
repositories repositories
.repositories .repositories
.iter() .iter()

View File

@@ -4,27 +4,27 @@ use std::process::Command;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::executor::RunType; use crate::execution_context::ExecutionContext;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils; use crate::utils;
use crate::utils::PathExt; use crate::utils::PathExt;
/// <https://github.com/Gelio/go-global-update> /// <https://github.com/Gelio/go-global-update>
pub fn run_go_global_update(run_type: RunType) -> Result<()> { pub fn run_go_global_update(ctx: &ExecutionContext) -> Result<()> {
let go_global_update = require_go_bin("go-global-update")?; let go_global_update = require_go_bin("go-global-update")?;
print_separator("go-global-update"); print_separator("go-global-update");
run_type.execute(go_global_update).status_checked() ctx.run_type().execute(go_global_update).status_checked()
} }
/// <https://github.com/nao1215/gup> /// <https://github.com/nao1215/gup>
pub fn run_go_gup(run_type: RunType) -> Result<()> { pub fn run_go_gup(ctx: &ExecutionContext) -> Result<()> {
let gup = require_go_bin("gup")?; let gup = require_go_bin("gup")?;
print_separator("gup"); print_separator("gup");
run_type.execute(gup).arg("update").status_checked() ctx.run_type().execute(gup).arg("update").status_checked()
} }
/// Get the path of a Go binary. /// Get the path of a Go binary.

View File

@@ -4,7 +4,7 @@ use std::os::unix::fs::MetadataExt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use crate::utils::require_option; use crate::utils::{require_option, REQUIRE_SUDO};
use crate::HOME_DIR; use crate::HOME_DIR;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@@ -92,7 +92,7 @@ impl NPM {
fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> { fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> {
let args = ["update", self.global_location_arg()]; let args = ["update", self.global_location_arg()];
if use_sudo { if use_sudo {
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().clone(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.arg(&self.command) .arg(&self.command)
@@ -156,7 +156,7 @@ impl Yarn {
let args = ["global", "upgrade"]; let args = ["global", "upgrade"];
if use_sudo { if use_sudo {
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().clone(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.arg(self.yarn.as_ref().unwrap_or(&self.command)) .arg(self.yarn.as_ref().unwrap_or(&self.command))
@@ -230,7 +230,7 @@ pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> {
let pnpm = require("pnpm").map(|b| NPM::new(b, NPMVariant::Pnpm))?; let pnpm = require("pnpm").map(|b| NPM::new(b, NPMVariant::Pnpm))?;
print_separator("Node Package Manager"); print_separator("Performant Node Package Manager");
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {

View File

@@ -1,26 +1,23 @@
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::executor::RunType; use crate::execution_context::ExecutionContext;
use crate::sudo::Sudo;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils::require_option; use crate::utils::{require_option, REQUIRE_SUDO};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::process::Command; use std::process::Command;
pub fn upgrade_packages(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("DragonFly BSD Packages"); print_separator("DragonFly BSD Packages");
run_type ctx.execute(sudo)
.execute(sudo)
.args(["/usr/local/sbin/pkg", "upgrade"]) .args(["/usr/local/sbin/pkg", "upgrade"])
.status_checked() .status_checked()
} }
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> { pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = sudo { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
println!(); println!();
Command::new(sudo) Command::new(sudo)
.args(["/usr/local/sbin/pkg", "audit", "-Fr"]) .args(["/usr/local/sbin/pkg", "audit", "-Fr"])
.status_checked()?; .status_checked()?;
}
Ok(()) Ok(())
} }

View File

@@ -1,27 +1,25 @@
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::sudo::Sudo;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils::require_option; use crate::utils::{require_option, REQUIRE_SUDO};
use crate::Step; use crate::Step;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::process::Command; use std::process::Command;
pub fn upgrade_freebsd(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn upgrade_freebsd(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("FreeBSD Update"); print_separator("FreeBSD Update");
run_type ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["/usr/sbin/freebsd-update", "fetch", "install"]) .args(["/usr/sbin/freebsd-update", "fetch", "install"])
.status_checked() .status_checked()
} }
pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("FreeBSD Packages"); print_separator("FreeBSD Packages");
let mut command = run_type.execute(sudo); let mut command = ctx.run_type().execute(sudo);
command.args(["/usr/sbin/pkg", "upgrade"]); command.args(["/usr/sbin/pkg", "upgrade"]);
if ctx.config().yes(Step::System) { if ctx.config().yes(Step::System) {
@@ -30,12 +28,11 @@ pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&Sudo>, run_type: R
command.status_checked() command.status_checked()
} }
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> { pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = sudo { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
println!(); println!();
Command::new(sudo) Command::new(sudo)
.args(["/usr/sbin/pkg", "audit", "-Fr"]) .args(["/usr/sbin/pkg", "audit", "-Fr"])
.status_checked()?; .status_checked()?;
}
Ok(()) Ok(())
} }

View File

@@ -8,12 +8,10 @@ use tracing::{debug, warn};
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::error::{SkipStep, TopgradeError}; use crate::error::{SkipStep, TopgradeError};
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::steps::os::archlinux; use crate::steps::os::archlinux;
use crate::sudo::Sudo; use crate::terminal::print_separator;
use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, require_option, which, PathExt, REQUIRE_SUDO};
use crate::utils::{require, require_option, which, PathExt}; use crate::{Step, HOME_DIR};
use crate::Step;
static OS_RELEASE_PATH: &str = "/etc/os-release"; static OS_RELEASE_PATH: &str = "/etc/os-release";
@@ -30,9 +28,11 @@ pub enum Distribution {
Debian, Debian,
Gentoo, Gentoo,
OpenMandriva, OpenMandriva,
OpenSuseTumbleweed,
PCLinuxOS, PCLinuxOS,
Suse, Suse,
SuseMicro, SuseMicro,
Vanilla,
Void, Void,
Solus, Solus,
Exherbo, Exherbo,
@@ -41,9 +41,10 @@ pub enum Distribution {
} }
impl Distribution { impl Distribution {
fn parse_os_release(os_release: &ini::Ini) -> Result<Self> { fn parse_os_release(os_release: &Ini) -> Result<Self> {
let section = os_release.general_section(); let section = os_release.general_section();
let id = section.get("ID"); let id = section.get("ID");
let name = section.get("NAME");
let variant: Option<Vec<&str>> = section.get("VARIANT").map(|s| s.split_whitespace().collect()); let variant: Option<Vec<&str>> = section.get("VARIANT").map(|s| s.split_whitespace().collect());
let id_like: Option<Vec<&str>> = section.get("ID_LIKE").map(|s| s.split_whitespace().collect()); let id_like: Option<Vec<&str>> = section.get("ID_LIKE").map(|s| s.split_whitespace().collect());
@@ -52,20 +53,20 @@ impl Distribution {
Some("centos") | Some("rhel") | Some("ol") => Distribution::CentOS, Some("centos") | Some("rhel") | Some("ol") => Distribution::CentOS,
Some("clear-linux-os") => Distribution::ClearLinux, Some("clear-linux-os") => Distribution::ClearLinux,
Some("fedora") | Some("nobara") => { Some("fedora") | Some("nobara") => {
if let Some(variant) = variant { return if let Some(variant) = variant {
if variant.contains(&"Silverblue") { if variant.contains(&"Silverblue") {
return Ok(Distribution::FedoraSilverblue); Ok(Distribution::FedoraSilverblue)
} else { } else {
return Ok(Distribution::Fedora); Ok(Distribution::Fedora)
}; }
} else { } else {
return Ok(Distribution::Fedora); Ok(Distribution::Fedora)
} }
} }
Some("void") => Distribution::Void, Some("void") => Distribution::Void,
Some("debian") | Some("pureos") => Distribution::Debian, Some("debian") | Some("pureos") | Some("Deepin") => Distribution::Debian,
Some("arch") | Some("anarchy") | Some("manjaro-arm") | Some("garuda") | Some("artix") => Distribution::Arch, Some("arch") | Some("manjaro-arm") | Some("garuda") | Some("artix") => Distribution::Arch,
Some("solus") => Distribution::Solus, Some("solus") => Distribution::Solus,
Some("gentoo") => Distribution::Gentoo, Some("gentoo") => Distribution::Gentoo,
Some("exherbo") => Distribution::Exherbo, Some("exherbo") => Distribution::Exherbo,
@@ -75,13 +76,23 @@ impl Distribution {
Some("openmandriva") => Distribution::OpenMandriva, Some("openmandriva") => Distribution::OpenMandriva,
Some("pclinuxos") => Distribution::PCLinuxOS, Some("pclinuxos") => Distribution::PCLinuxOS,
_ => { _ => {
if let Some(name) = name {
if name.contains("Vanilla") {
return Ok(Distribution::Vanilla);
}
}
if let Some(id_like) = id_like { if let Some(id_like) = id_like {
if id_like.contains(&"debian") || id_like.contains(&"ubuntu") { if id_like.contains(&"debian") || id_like.contains(&"ubuntu") {
return Ok(Distribution::Debian); return Ok(Distribution::Debian);
} else if id_like.contains(&"centos") { } else if id_like.contains(&"centos") {
return Ok(Distribution::CentOS); return Ok(Distribution::CentOS);
} else if id_like.contains(&"suse") { } else if id_like.contains(&"suse") {
return Ok(Distribution::Suse); let id_variant = id.unwrap_or_default();
return if id_variant.contains("tumbleweed") {
Ok(Distribution::OpenSuseTumbleweed)
} else {
Ok(Distribution::Suse)
};
} else if id_like.contains(&"arch") || id_like.contains(&"archlinux") { } else if id_like.contains(&"arch") || id_like.contains(&"archlinux") {
return Ok(Distribution::Arch); return Ok(Distribution::Arch);
} else if id_like.contains(&"alpine") { } else if id_like.contains(&"alpine") {
@@ -122,6 +133,8 @@ impl Distribution {
Distribution::Gentoo => upgrade_gentoo(ctx), Distribution::Gentoo => upgrade_gentoo(ctx),
Distribution::Suse => upgrade_suse(ctx), Distribution::Suse => upgrade_suse(ctx),
Distribution::SuseMicro => upgrade_suse_micro(ctx), Distribution::SuseMicro => upgrade_suse_micro(ctx),
Distribution::OpenSuseTumbleweed => upgrade_opensuse_tumbleweed(ctx),
Distribution::Vanilla => upgrade_vanilla(ctx),
Distribution::Void => upgrade_void(ctx), Distribution::Void => upgrade_void(ctx),
Distribution::Solus => upgrade_solus(ctx), Distribution::Solus => upgrade_solus(ctx),
Distribution::Exherbo => upgrade_exherbo(ctx), Distribution::Exherbo => upgrade_exherbo(ctx),
@@ -145,7 +158,7 @@ impl Distribution {
} }
fn update_bedrock(ctx: &ExecutionContext) -> Result<()> { fn update_bedrock(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), String::from("Sudo required"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type().execute(sudo).args(["brl", "update"]); ctx.run_type().execute(sudo).args(["brl", "update"]);
@@ -176,7 +189,7 @@ fn is_wsl() -> Result<bool> {
fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> { fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> {
let apk = require("apk")?; let apk = require("apk")?;
let sudo = ctx.sudo().as_ref().unwrap(); let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?; ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked() ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
@@ -191,28 +204,25 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> {
} }
}; };
if let Some(sudo) = &ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let mut command = ctx.run_type().execute(sudo); let mut command = ctx.run_type().execute(sudo);
command command
.arg(which("dnf").unwrap_or_else(|| Path::new("yum").to_path_buf())) .arg(which("dnf").unwrap_or_else(|| Path::new("yum").to_path_buf()))
.arg(if ctx.config().redhat_distro_sync() { .arg(if ctx.config().redhat_distro_sync() {
"distro-sync" "distro-sync"
} else { } else {
"upgrade" "upgrade"
}); });
if let Some(args) = ctx.config().dnf_arguments() { if let Some(args) = ctx.config().dnf_arguments() {
command.args(args.split_whitespace()); command.args(args.split_whitespace());
}
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
Ok(()) Ok(())
} }
@@ -225,112 +235,136 @@ fn upgrade_fedora_silverblue(ctx: &ExecutionContext) -> Result<()> {
} }
fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> { fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type().execute(sudo).args(["brl", "update"]).status_checked()?; ctx.run_type().execute(sudo).args(["brl", "update"]).status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(()) Ok(())
} }
fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> { fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["zypper", "refresh"]) .args(["zypper", "refresh"])
.status_checked()?; .status_checked()?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["zypper", "dist-upgrade"]) .arg("zypper")
.status_checked()?; .arg(if ctx.config().suse_dup() {
} else { "dist-upgrade"
print_warning("No sudo detected. Skipping system upgrade"); } else {
} "update"
})
.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_opensuse_tumbleweed(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type()
.execute(sudo)
.args(["zypper", "refresh"])
.status_checked()?;
ctx.run_type()
.execute(sudo)
.arg("zypper")
.arg("dist-upgrade")
.status_checked()?;
Ok(())
}
fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> { fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["transactional-update", "dup"]) .args(["transactional-update", "dup"])
.status_checked()?; .status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(()) Ok(())
} }
fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> { fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let mut command = ctx.run_type().execute(sudo); let mut command = ctx.run_type().execute(sudo);
command.arg(&which("dnf").unwrap()).arg("upgrade"); command.arg(&which("dnf").unwrap()).arg("upgrade");
if let Some(args) = ctx.config().dnf_arguments() { if let Some(args) = ctx.config().dnf_arguments() {
command.args(args.split_whitespace()); command.args(args.split_whitespace());
}
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> { fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let mut command_update = ctx.run_type().execute(sudo); let mut command_update = ctx.run_type().execute(sudo);
command_update.arg(&which("apt-get").unwrap()).arg("update"); command_update.arg(&which("apt-get").unwrap()).arg("update");
if let Some(args) = ctx.config().dnf_arguments() { if let Some(args) = ctx.config().dnf_arguments() {
command_update.args(args.split_whitespace()); 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");
} }
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()?;
Ok(())
}
fn upgrade_vanilla(ctx: &ExecutionContext) -> Result<()> {
let apx = require("apx")?;
let mut update = ctx.run_type().execute(&apx);
update.args(["update", "--all"]);
if ctx.config().yes(Step::System) {
update.arg("-y");
}
update.status_checked()?;
let mut upgrade = ctx.run_type().execute(&apx);
update.args(["upgrade", "--all"]);
if ctx.config().yes(Step::System) {
upgrade.arg("-y");
}
upgrade.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_void(ctx: &ExecutionContext) -> Result<()> { fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let mut command = ctx.run_type().execute(sudo); let mut command = ctx.run_type().execute(sudo);
command.args(["xbps-install", "-Su", "xbps"]); command.args(["xbps-install", "-Su", "xbps"]);
if ctx.config().yes(Step::System) { if ctx.config().yes(Step::System) {
command.arg("-y"); command.arg("-y");
}
command.status_checked()?;
let mut command = ctx.run_type().execute(sudo);
command.args(["xbps-install", "-u"]);
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
command.status_checked()?;
let mut command = ctx.run_type().execute(sudo);
command.args(["xbps-install", "-u"]);
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
Ok(()) Ok(())
} }
@@ -338,99 +372,109 @@ fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> { fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
let run_type = ctx.run_type(); let run_type = ctx.run_type();
if let Some(sudo) = &ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
if let Some(layman) = which("layman") { if let Some(layman) = which("layman") {
run_type
.execute(sudo)
.arg(layman)
.args(["-s", "ALL"])
.status_checked()?;
}
println!("Syncing portage");
run_type run_type
.execute(sudo) .execute(sudo)
.args(["emerge", "--sync"]) .arg(layman)
.args( .args(["-s", "ALL"])
ctx.config()
.emerge_sync_flags()
.map(|s| s.split_whitespace().collect())
.unwrap_or_else(|| vec!["-q"]),
)
.status_checked()?; .status_checked()?;
if let Some(eix_update) = which("eix-update") {
run_type.execute(sudo).arg(eix_update).status_checked()?;
}
run_type
.execute(sudo)
.arg("emerge")
.args(
ctx.config()
.emerge_update_flags()
.map(|s| s.split_whitespace().collect())
.unwrap_or_else(|| vec!["-uDNa", "--with-bdeps=y", "world"]),
)
.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
println!("Syncing portage");
run_type
.execute(sudo)
.args(["emerge", "--sync"])
.args(
ctx.config()
.emerge_sync_flags()
.map(|s| s.split_whitespace().collect())
.unwrap_or_else(|| vec!["-q"]),
)
.status_checked()?;
if let Some(eix_update) = which("eix-update") {
run_type.execute(sudo).arg(eix_update).status_checked()?;
}
run_type
.execute(sudo)
.arg("emerge")
.args(
ctx.config()
.emerge_update_flags()
.map(|s| s.split_whitespace().collect())
.unwrap_or_else(|| vec!["-uDNa", "--with-bdeps=y", "world"]),
)
.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> { fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() { let apt = which("apt-fast")
let apt = which("apt-fast") .or_else(|| {
.or_else(|| { if which("mist").is_some() {
if which("mist").is_some() { Some(PathBuf::from("mist"))
Some(PathBuf::from("mist")) } else {
} else { None
None }
} })
}) .or_else(|| {
.or_else(|| { if Path::new("/usr/bin/nala").exists() {
if Path::new("/usr/bin/nala").exists() { Some(Path::new("/usr/bin/nala").to_path_buf())
Some(Path::new("/usr/bin/nala").to_path_buf()) } else {
} else { None
None }
} })
}) .unwrap_or_else(|| PathBuf::from("apt-get"));
.unwrap_or_else(|| PathBuf::from("apt-get"));
let is_nala = apt.ends_with("nala"); let is_mist = apt.ends_with("mist");
if !is_nala { let is_nala = apt.ends_with("nala");
ctx.run_type().execute(sudo).arg(&apt).arg("update").status_checked()?;
} // MIST does not require `sudo`
if is_mist {
ctx.run_type().execute(&apt).arg("update").status_checked()?;
ctx.run_type().execute(&apt).arg("upgrade").status_checked()?;
// Simply return as MIST does not have `clean` and `autoremove`
// subcommands, neither the `-y` option (for now maybe?).
return Ok(());
}
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
if !is_nala {
ctx.run_type()
.execute(sudo)
.arg(&apt)
.arg("update")
.status_checked_with_codes(&[0, 100])?;
}
let mut command = ctx.run_type().execute(sudo);
command.arg(&apt);
if is_nala {
command.arg("upgrade");
} else {
command.arg("dist-upgrade");
};
if ctx.config().yes(Step::System) {
command.arg("-y");
}
if let Some(args) = ctx.config().apt_arguments() {
command.args(args.split_whitespace());
}
command.status_checked()?;
if ctx.config().cleanup() {
ctx.run_type().execute(sudo).arg(&apt).arg("clean").status_checked()?;
let mut command = ctx.run_type().execute(sudo); let mut command = ctx.run_type().execute(sudo);
command.arg(&apt); command.arg(&apt).arg("autoremove");
if is_nala {
command.arg("upgrade");
} else {
command.arg("dist-upgrade");
};
if ctx.config().yes(Step::System) { if ctx.config().yes(Step::System) {
command.arg("-y"); command.arg("-y");
} }
if let Some(args) = ctx.config().apt_arguments() {
command.args(args.split_whitespace());
}
command.status_checked()?; command.status_checked()?;
if ctx.config().cleanup() {
ctx.run_type().execute(sudo).arg(&apt).arg("clean").status_checked()?;
let mut command = ctx.run_type().execute(sudo);
command.arg(&apt).arg("autoremove");
if ctx.config().yes(Step::System) {
command.arg("-y");
}
command.status_checked()?;
}
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
Ok(()) Ok(())
@@ -441,38 +485,48 @@ pub fn run_deb_get(ctx: &ExecutionContext) -> Result<()> {
print_separator("deb-get"); print_separator("deb-get");
ctx.execute_elevated(&deb_get, false)?.arg("update").status_checked()?; ctx.run_type().execute(&deb_get).arg("update").status_checked()?;
ctx.execute_elevated(&deb_get, false)?.arg("upgrade").status_checked()?; ctx.run_type().execute(&deb_get).arg("upgrade").status_checked()?;
if ctx.config().cleanup() { if ctx.config().cleanup() {
ctx.execute_elevated(&deb_get, false)?.arg("clean").status_checked()?; ctx.run_type().execute(&deb_get).arg("clean").status_checked()?;
} }
Ok(()) Ok(())
} }
fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> { fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["eopkg", "upgrade"]) .args(["eopkg", "upgrade"])
.status_checked()?; .status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(()) Ok(())
} }
pub fn update_am(ctx: &ExecutionContext) -> Result<()> { pub fn run_am(ctx: &ExecutionContext) -> Result<()> {
let am = require("am")?; let am = require("am")?;
if let Some(sudo) = ctx.sudo() {
ctx.run_type().execute(sudo).arg(am).arg("-u").status_checked()?; print_separator("AM");
let mut am = ctx.run_type().execute(am);
if ctx.config().yes(Step::AM) {
am.arg("-U");
} else { } else {
print_warning("No sudo detected. Skipping AM Step"); am.arg("-u");
} }
Ok(()) am.status_checked()
}
pub fn run_appman(ctx: &ExecutionContext) -> Result<()> {
let appman = require("appman")?;
print_separator("appman");
ctx.run_type().execute(appman).arg("-u").status_checked()
} }
pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> { pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> {
@@ -521,69 +575,79 @@ pub fn run_pacstall(ctx: &ExecutionContext) -> Result<()> {
upgrade_cmd.arg("-Up").status_checked() upgrade_cmd.arg("-Up").status_checked()
} }
pub fn run_packer_nu(ctx: &ExecutionContext) -> Result<()> {
let nu = require("nu")?;
let packer_home = HOME_DIR.join(".local/share/nushell/packer");
packer_home.clone().require()?;
print_separator("packer.nu");
ctx.run_type()
.execute(nu)
.env("PWD", "/")
.env("NU_PACKER_HOME", packer_home)
.args([
"-c",
"use ~/.local/share/nushell/packer/start/packer.nu/api_layer/packer.nu; packer update",
])
.status_checked()
}
fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> { fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = &ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["swupd", "update"]) .args(["swupd", "update"])
.status_checked()?; .status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
}
Ok(()) Ok(())
} }
fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> { fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type().execute(sudo).args(["cave", "sync"]).status_checked()?; ctx.run_type().execute(sudo).args(["cave", "sync"]).status_checked()?;
ctx.run_type()
.execute(sudo)
.args(["cave", "resolve", "world", "-c1", "-Cs", "-km", "-Km", "-x"])
.status_checked()?;
if ctx.config().cleanup() {
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["cave", "resolve", "world", "-c1", "-Cs", "-km", "-Km", "-x"]) .args(["cave", "purge", "-x"])
.status_checked()?; .status_checked()?;
if ctx.config().cleanup() {
ctx.run_type()
.execute(sudo)
.args(["cave", "purge", "-x"])
.status_checked()?;
}
ctx.run_type()
.execute(sudo)
.args(["cave", "fix-linkage", "-x", "--", "-Cs"])
.status_checked()?;
ctx.run_type()
.execute(sudo)
.args(["eclectic", "config", "interactive"])
.status_checked()?;
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
ctx.run_type()
.execute(sudo)
.args(["cave", "fix-linkage", "-x", "--", "-Cs"])
.status_checked()?;
ctx.run_type()
.execute(sudo)
.args(["eclectic", "config", "interactive"])
.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> { fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> {
if let Some(sudo) = ctx.sudo() { let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let mut command = ctx.run_type().execute(sudo); let mut command = ctx.run_type().execute(sudo);
command.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]); command.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]);
if let Some(args) = ctx.config().nix_arguments() { if let Some(args) = ctx.config().nix_arguments() {
command.args(args.split_whitespace()); command.args(args.split_whitespace());
} }
command.status_checked()?; command.status_checked()?;
if ctx.config().cleanup() { if ctx.config().cleanup() {
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
.args(["/run/current-system/sw/bin/nix-collect-garbage", "-d"]) .args(["/run/current-system/sw/bin/nix-collect-garbage", "-d"])
.status_checked()?; .status_checked()?;
}
} else {
print_warning("No sudo detected. Skipping system upgrade");
} }
Ok(()) Ok(())
@@ -595,31 +659,31 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> {
// in theory rpm based distributions use pkcon as well, though that // in theory rpm based distributions use pkcon as well, though that
// seems rare // seems rare
// if that comes up we need to create a Distribution::PackageKit or some such // if that comes up we need to create a Distribution::PackageKit or some such
if let Some(sudo) = &ctx.sudo() {
let pkcon = which("pkcon").unwrap(); let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
// pkcon ignores update with update and refresh provided together let pkcon = which("pkcon").unwrap();
ctx.run_type() // pkcon ignores update with update and refresh provided together
.execute(sudo) ctx.run_type()
.arg(&pkcon) .execute(sudo)
.arg("refresh") .arg(&pkcon)
.status_checked()?; .arg("refresh")
let mut exe = ctx.run_type().execute(sudo); .status_checked()?;
let cmd = exe.arg(&pkcon).arg("update"); let mut exe = ctx.run_type().execute(sudo);
if ctx.config().yes(Step::System) { let cmd = exe.arg(&pkcon).arg("update");
cmd.arg("-y"); if ctx.config().yes(Step::System) {
} cmd.arg("-y");
if ctx.config().cleanup() {
cmd.arg("--autoremove");
}
// from pkcon man, exit code 5 is 'Nothing useful was done.'
cmd.status_checked_with_codes(&[5])?;
} }
if ctx.config().cleanup() {
cmd.arg("--autoremove");
}
// from pkcon man, exit code 5 is 'Nothing useful was done.'
cmd.status_checked_with_codes(&[5])?;
Ok(()) Ok(())
} }
pub fn run_needrestart(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn run_needrestart(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let needrestart = require("needrestart")?; let needrestart = require("needrestart")?;
let distribution = Distribution::detect()?; let distribution = Distribution::detect()?;
@@ -629,7 +693,7 @@ pub fn run_needrestart(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
print_separator("Check for needed restarts"); print_separator("Check for needed restarts");
run_type.execute(sudo).arg(needrestart).status_checked()?; ctx.run_type().execute(sudo).arg(needrestart).status_checked()?;
Ok(()) Ok(())
} }
@@ -661,9 +725,9 @@ pub fn run_fwupdmgr(ctx: &ExecutionContext) -> Result<()> {
updmgr.status_checked_with_codes(&[2]) updmgr.status_checked_with_codes(&[2])
} }
pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_flatpak(ctx: &ExecutionContext) -> Result<()> {
let flatpak = require("flatpak")?; let flatpak = require("flatpak")?;
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let cleanup = ctx.config().cleanup(); let cleanup = ctx.config().cleanup();
let yes = ctx.config().yes(Step::Flatpak); let yes = ctx.config().yes(Step::Flatpak);
let run_type = ctx.run_type(); let run_type = ctx.run_type();
@@ -723,8 +787,8 @@ pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
pub fn run_snap(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn run_snap(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let snap = require("snap")?; let snap = require("snap")?;
if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() { if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() {
@@ -732,17 +796,17 @@ pub fn run_snap(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
} }
print_separator("snap"); print_separator("snap");
run_type.execute(sudo).arg(snap).arg("refresh").status_checked() ctx.run_type().execute(sudo).arg(snap).arg("refresh").status_checked()
} }
pub fn run_pihole_update(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> { pub fn run_pihole_update(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let pihole = require("pihole")?; let pihole = require("pihole")?;
Path::new("/opt/pihole/update.sh").require()?; Path::new("/opt/pihole/update.sh").require()?;
print_separator("pihole"); print_separator("pihole");
run_type.execute(sudo).arg(pihole).arg("-up").status_checked() ctx.run_type().execute(sudo).arg(pihole).arg("-up").status_checked()
} }
pub fn run_protonup_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_protonup_update(ctx: &ExecutionContext) -> Result<()> {
@@ -780,7 +844,7 @@ pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
let dkp_pacman = require("dkp-pacman")?; let dkp_pacman = require("dkp-pacman")?;
print_separator("Devkitpro pacman"); print_separator("Devkitpro pacman");
@@ -803,7 +867,7 @@ pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
if ctx.config().yes(Step::ConfigUpdate) { if ctx.config().yes(Step::ConfigUpdate) {
return Err(SkipStep("Skipped in --yes".to_string()).into()); return Err(SkipStep("Skipped in --yes".to_string()).into());
} }
@@ -886,11 +950,6 @@ mod tests {
test_template(include_str!("os_release/fedora"), Distribution::Fedora); test_template(include_str!("os_release/fedora"), Distribution::Fedora);
} }
#[test]
fn test_antergos() {
test_template(include_str!("os_release/antergos"), Distribution::Arch);
}
#[test] #[test]
fn test_manjaro() { fn test_manjaro() {
test_template(include_str!("os_release/manjaro"), Distribution::Arch); test_template(include_str!("os_release/manjaro"), Distribution::Arch);
@@ -901,11 +960,6 @@ mod tests {
test_template(include_str!("os_release/manjaro-arm"), Distribution::Arch); test_template(include_str!("os_release/manjaro-arm"), Distribution::Arch);
} }
#[test]
fn test_anarchy() {
test_template(include_str!("os_release/anarchy"), Distribution::Arch);
}
#[test] #[test]
fn test_gentoo() { fn test_gentoo() {
test_template(include_str!("os_release/gentoo"), Distribution::Gentoo); test_template(include_str!("os_release/gentoo"), Distribution::Gentoo);
@@ -950,4 +1004,14 @@ mod tests {
fn test_pureos() { fn test_pureos() {
test_template(include_str!("os_release/pureos"), Distribution::Debian); test_template(include_str!("os_release/pureos"), Distribution::Debian);
} }
#[test]
fn test_deepin() {
test_template(include_str!("os_release/deepin"), Distribution::Debian);
}
#[test]
fn test_vanilla() {
test_template(include_str!("os_release/vanilla"), Distribution::Vanilla);
}
} }

View File

@@ -1,7 +1,7 @@
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::terminal::{print_separator, prompt_yesno}; use crate::terminal::{print_separator, prompt_yesno};
use crate::utils::{require_option, REQUIRE_SUDO};
use crate::{utils::require, Step}; use crate::{utils::require, Step};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::fs; use std::fs;
@@ -10,7 +10,8 @@ use tracing::debug;
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> { pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
require("port")?; require("port")?;
let sudo = ctx.sudo().as_ref().unwrap(); let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("MacPorts"); print_separator("MacPorts");
ctx.run_type() ctx.run_type()
.execute(sudo) .execute(sudo)
@@ -30,11 +31,11 @@ pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
pub fn run_mas(run_type: RunType) -> Result<()> { pub fn run_mas(ctx: &ExecutionContext) -> Result<()> {
let mas = require("mas")?; let mas = require("mas")?;
print_separator("macOS App Store"); print_separator("macOS App Store");
run_type.execute(mas).arg("upgrade").status_checked() ctx.run_type().execute(mas).arg("upgrade").status_checked()
} }
pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> { pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {

View File

@@ -1,22 +1,22 @@
use crate::executor::RunType; use crate::execution_context::ExecutionContext;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils::require_option; use crate::utils::{require_option, REQUIRE_SUDO};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::path::PathBuf; use std::path::PathBuf;
pub fn upgrade_openbsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("OpenBSD Update"); print_separator("OpenBSD Update");
run_type ctx.run_type()
.execute(sudo) .execute(sudo)
.args(&["/usr/sbin/sysupgrade", "-n"]) .args(&["/usr/sbin/sysupgrade", "-n"])
.status_checked() .status_checked()
} }
pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("OpenBSD Packages"); print_separator("OpenBSD Packages");
run_type ctx.run_type()
.execute(sudo) .execute(sudo)
.args(&["/usr/sbin/pkg_add", "-u"]) .args(&["/usr/sbin/pkg_add", "-u"])
.status_checked() .status_checked()

View File

@@ -1,6 +0,0 @@
NAME="Anarchy Linux"
PRETTY_NAME="Anarchy Linux"
ID=anarchy
ID_LIKE=anarchylinux
ANSI_COLOR="0;36"
HOME_URL="https://anarchylinux.org/"

View File

@@ -1,10 +0,0 @@
NAME="Antergos Linux"
VERSION="18.7-ISO-Rolling"
ID="antergos"
ID_LIKE="arch"
PRETTY_NAME="Antergos Linux"
CPE_NAME="cpe:/o:antergosproject:antergos:18.7"
ANSI_COLOR="1;34;40"
HOME_URL="antergos.com"
SUPPORT_URL="forum.antergos.com"
BUG_REPORT_URL="@antergos"

View File

@@ -0,0 +1,8 @@
PRETTY_NAME="Deepin 20.9"
NAME="Deepin"
VERSION_ID="20.9"
VERSION="20.9"
VERSION_CODENAME="apricot"
ID=Deepin
HOME_URL="https://www.deepin.org/"
BUG_REPORT_URL="https://bbs.deepin.org/"

View File

@@ -0,0 +1,12 @@
PRETTY_NAME="VanillaOS 22.10 all"
NAME="VanillaOS"
VERSION_ID="22.10"
VERSION="22.10 all"
VERSION_CODENAME="kinetic"
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://github.com/vanilla-os"
SUPPORT_URL="https://github.com/vanilla-os"
BUG_REPORT_URL="https://github.com/vanilla-os"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME="kinetic"

View File

@@ -2,7 +2,7 @@ use std::fs;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::{env, path::Path}; use std::{env::var, path::Path};
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::{Step, HOME_DIR}; use crate::{Step, HOME_DIR};
@@ -15,11 +15,10 @@ use crate::error::SkipStep;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use crate::executor::Executor; use crate::executor::Executor;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use crate::executor::RunType; use crate::executor::RunType;
use crate::terminal::print_separator; use crate::terminal::print_separator;
#[cfg(not(any(target_os = "android", target_os = "macos")))] use crate::utils::{require, require_option, PathExt, REQUIRE_SUDO};
use crate::utils::require_option;
use crate::utils::{require, PathExt};
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
const INTEL_BREW: &str = "/usr/local/bin/brew"; const INTEL_BREW: &str = "/usr/local/bin/brew";
@@ -86,7 +85,7 @@ impl BrewVariant {
} }
} }
pub fn run_fisher(run_type: RunType) -> Result<()> { pub fn run_fisher(ctx: &ExecutionContext) -> Result<()> {
let fish = require("fish")?; let fish = require("fish")?;
Command::new(&fish) Command::new(&fish)
@@ -109,7 +108,8 @@ pub fn run_fisher(run_type: RunType) -> Result<()> {
print_separator("Fisher"); print_separator("Fisher");
let version_str = run_type let version_str = ctx
.run_type()
.execute(&fish) .execute(&fish)
.args(["-c", "fisher --version"]) .args(["-c", "fisher --version"])
.output_checked_utf8()? .output_checked_utf8()?
@@ -118,10 +118,13 @@ pub fn run_fisher(run_type: RunType) -> Result<()> {
if version_str.starts_with("fisher version 3.") { if version_str.starts_with("fisher version 3.") {
// v3 - see https://github.com/topgrade-rs/topgrade/pull/37#issuecomment-1283844506 // v3 - see https://github.com/topgrade-rs/topgrade/pull/37#issuecomment-1283844506
run_type.execute(&fish).args(["-c", "fisher"]).status_checked() ctx.run_type().execute(&fish).args(["-c", "fisher"]).status_checked()
} else { } else {
// v4 // v4
run_type.execute(&fish).args(["-c", "fisher update"]).status_checked() ctx.run_type()
.execute(&fish)
.args(["-c", "fisher update"])
.status_checked()
} }
} }
@@ -136,6 +139,27 @@ pub fn run_bashit(ctx: &ExecutionContext) -> Result<()> {
.status_checked() .status_checked()
} }
pub fn run_oh_my_bash(ctx: &ExecutionContext) -> Result<()> {
require("bash")?;
let oh_my_bash = var("OSH")
// default to `~/.oh-my-bash`
.unwrap_or(
HOME_DIR
.join(".oh-my-bash")
.to_str()
.expect("should be UTF-8 encoded")
.to_string(),
)
.require()?;
print_separator("oh-my-bash");
let mut update_script = oh_my_bash;
update_script.push_str("tools/upgrade.sh");
ctx.run_type().execute("bash").arg(update_script).status_checked()
}
pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> { pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> {
let fish = require("fish")?; let fish = require("fish")?;
HOME_DIR.join(".local/share/omf/pkg/omf/functions/omf.fish").require()?; HOME_DIR.join(".local/share/omf/pkg/omf/functions/omf.fish").require()?;
@@ -147,17 +171,18 @@ pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> {
pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> { pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> {
let pkgin = require("pkgin")?; let pkgin = require("pkgin")?;
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("Pkgin"); print_separator("Pkgin");
let mut command = ctx.run_type().execute(ctx.sudo().as_ref().unwrap()); let mut command = ctx.run_type().execute(sudo);
command.arg(&pkgin).arg("update"); command.arg(&pkgin).arg("update");
if ctx.config().yes(Step::Pkgin) { if ctx.config().yes(Step::Pkgin) {
command.arg("-y"); command.arg("-y");
} }
command.status_checked()?; command.status_checked()?;
let mut command = ctx.run_type().execute(ctx.sudo().as_ref().unwrap()); let mut command = ctx.run_type().execute(sudo);
command.arg(&pkgin).arg("upgrade"); command.arg(&pkgin).arg("upgrade");
if ctx.config().yes(Step::Pkgin) { if ctx.config().yes(Step::Pkgin) {
command.arg("-y"); command.arg("-y");
@@ -200,7 +225,7 @@ pub fn run_fundle(ctx: &ExecutionContext) -> Result<()> {
pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> { pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> {
let gdbus = require("gdbus")?; let gdbus = require("gdbus")?;
require_option( require_option(
env::var("XDG_CURRENT_DESKTOP").ok().filter(|p| p.contains("GNOME")), var("XDG_CURRENT_DESKTOP").ok().filter(|p| p.contains("GNOME")),
"Desktop doest not appear to be gnome".to_string(), "Desktop doest not appear to be gnome".to_string(),
)?; )?;
let output = Command::new("gdbus") let output = Command::new("gdbus")
@@ -378,7 +403,7 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
run_type.execute(nix_channel).arg("--update").status_checked()?; run_type.execute(nix_channel).arg("--update").status_checked()?;
if std::path::Path::new(&manifest_json_path).exists() { if Path::new(&manifest_json_path).exists() {
run_type run_type
.execute(&nix) .execute(&nix)
.arg("profile") .arg("profile")
@@ -398,43 +423,46 @@ pub fn run_yadm(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type().execute(yadm).arg("pull").status_checked() ctx.run_type().execute(yadm).arg("pull").status_checked()
} }
pub fn run_asdf(run_type: RunType) -> Result<()> { pub fn run_asdf(ctx: &ExecutionContext) -> Result<()> {
let asdf = require("asdf")?; let asdf = require("asdf")?;
print_separator("asdf"); print_separator("asdf");
run_type.execute(&asdf).arg("update").status_checked_with_codes(&[42])?; ctx.run_type()
.execute(&asdf)
.arg("update")
.status_checked_with_codes(&[42])?;
run_type ctx.run_type()
.execute(&asdf) .execute(&asdf)
.args(["plugin", "update", "--all"]) .args(["plugin", "update", "--all"])
.status_checked() .status_checked()
} }
pub fn run_home_manager(run_type: RunType) -> Result<()> { pub fn run_home_manager(ctx: &ExecutionContext) -> Result<()> {
let home_manager = require("home-manager")?; let home_manager = require("home-manager")?;
print_separator("home-manager"); print_separator("home-manager");
run_type.execute(home_manager).arg("switch").status_checked() ctx.run_type().execute(home_manager).arg("switch").status_checked()
} }
pub fn run_tldr(run_type: RunType) -> Result<()> { pub fn run_tldr(ctx: &ExecutionContext) -> Result<()> {
let tldr = require("tldr")?; let tldr = require("tldr")?;
print_separator("TLDR"); print_separator("TLDR");
run_type.execute(tldr).arg("--update").status_checked() ctx.run_type().execute(tldr).arg("--update").status_checked()
} }
pub fn run_pearl(run_type: RunType) -> Result<()> { pub fn run_pearl(ctx: &ExecutionContext) -> Result<()> {
let pearl = require("pearl")?; let pearl = require("pearl")?;
print_separator("pearl"); print_separator("pearl");
run_type.execute(pearl).arg("update").status_checked() ctx.run_type().execute(pearl).arg("update").status_checked()
} }
pub fn run_sdkman(cleanup: bool, run_type: RunType) -> Result<()> { pub fn run_sdkman(ctx: &ExecutionContext) -> Result<()> {
let bash = require("bash")?; let bash = require("bash")?;
let sdkman_init_path = env::var("SDKMAN_DIR") let sdkman_init_path = var("SDKMAN_DIR")
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".sdkman")) .unwrap_or_else(|_| HOME_DIR.join(".sdkman"))
.join("bin") .join("bin")
@@ -444,7 +472,7 @@ pub fn run_sdkman(cleanup: bool, run_type: RunType) -> Result<()> {
print_separator("SDKMAN!"); print_separator("SDKMAN!");
let sdkman_config_path = env::var("SDKMAN_DIR") let sdkman_config_path = var("SDKMAN_DIR")
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".sdkman")) .unwrap_or_else(|_| HOME_DIR.join(".sdkman"))
.join("etc") .join("etc")
@@ -459,33 +487,33 @@ pub fn run_sdkman(cleanup: bool, run_type: RunType) -> Result<()> {
if selfupdate_enabled == "true" { if selfupdate_enabled == "true" {
let cmd_selfupdate = format!("source {} && sdk selfupdate", &sdkman_init_path); let cmd_selfupdate = format!("source {} && sdk selfupdate", &sdkman_init_path);
run_type ctx.run_type()
.execute(&bash) .execute(&bash)
.args(["-c", cmd_selfupdate.as_str()]) .args(["-c", cmd_selfupdate.as_str()])
.status_checked()?; .status_checked()?;
} }
let cmd_update = format!("source {} && sdk update", &sdkman_init_path); let cmd_update = format!("source {} && sdk update", &sdkman_init_path);
run_type ctx.run_type()
.execute(&bash) .execute(&bash)
.args(["-c", cmd_update.as_str()]) .args(["-c", cmd_update.as_str()])
.status_checked()?; .status_checked()?;
let cmd_upgrade = format!("source {} && sdk upgrade", &sdkman_init_path); let cmd_upgrade = format!("source {} && sdk upgrade", &sdkman_init_path);
run_type ctx.run_type()
.execute(&bash) .execute(&bash)
.args(["-c", cmd_upgrade.as_str()]) .args(["-c", cmd_upgrade.as_str()])
.status_checked()?; .status_checked()?;
if cleanup { if ctx.config().cleanup() {
let cmd_flush_archives = format!("source {} && sdk flush archives", &sdkman_init_path); let cmd_flush_archives = format!("source {} && sdk flush archives", &sdkman_init_path);
run_type ctx.run_type()
.execute(&bash) .execute(&bash)
.args(["-c", cmd_flush_archives.as_str()]) .args(["-c", cmd_flush_archives.as_str()])
.status_checked()?; .status_checked()?;
let cmd_flush_temp = format!("source {} && sdk flush temp", &sdkman_init_path); let cmd_flush_temp = format!("source {} && sdk flush temp", &sdkman_init_path);
run_type ctx.run_type()
.execute(&bash) .execute(&bash)
.args(["-c", cmd_flush_temp.as_str()]) .args(["-c", cmd_flush_temp.as_str()])
.status_checked()?; .status_checked()?;
@@ -512,6 +540,13 @@ pub fn run_rcm(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type().execute(rcup).arg("-v").status_checked() ctx.run_type().execute(rcup).arg("-v").status_checked()
} }
pub fn run_maza(ctx: &ExecutionContext) -> Result<()> {
let maza = require("maza")?;
print_separator("maza");
ctx.run_type().execute(maza).arg("update").status_checked()
}
pub fn reboot() -> Result<()> { pub fn reboot() -> Result<()> {
print!("Rebooting..."); print!("Rebooting...");
Command::new("sudo").arg("reboot").status_checked() Command::new("sudo").arg("reboot").status_checked()

View File

@@ -8,7 +8,6 @@ use tracing::debug;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::terminal::{print_separator, print_warning}; use crate::terminal::{print_separator, print_warning};
use crate::utils::require; use crate::utils::require;
use crate::{error::SkipStep, steps::git::Repositories}; use crate::{error::SkipStep, steps::git::Repositories};
@@ -54,16 +53,16 @@ pub fn run_winget(ctx: &ExecutionContext) -> Result<()> {
.status_checked() .status_checked()
} }
pub fn run_scoop(cleanup: bool, run_type: RunType) -> Result<()> { pub fn run_scoop(ctx: &ExecutionContext) -> Result<()> {
let scoop = require("scoop")?; let scoop = require("scoop")?;
print_separator("Scoop"); print_separator("Scoop");
run_type.execute(&scoop).args(["update"]).status_checked()?; ctx.run_type().execute(&scoop).args(["update"]).status_checked()?;
run_type.execute(&scoop).args(["update", "*"]).status_checked()?; ctx.run_type().execute(&scoop).args(["update", "*"]).status_checked()?;
if cleanup { if ctx.config().cleanup() {
run_type.execute(&scoop).args(["cleanup", "*"]).status_checked()?; ctx.run_type().execute(&scoop).args(["cleanup", "*"]).status_checked()?;
} }
Ok(()) Ok(())

View File

@@ -7,7 +7,6 @@ use color_eyre::eyre::Context;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::executor::RunType;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::HOME_DIR; use crate::HOME_DIR;
use crate::{ use crate::{
@@ -18,12 +17,12 @@ use crate::{
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::process::CommandExt as _; use std::os::unix::process::CommandExt as _;
pub fn run_tpm(run_type: RunType) -> Result<()> { pub fn run_tpm(ctx: &ExecutionContext) -> Result<()> {
let tpm = HOME_DIR.join(".tmux/plugins/tpm/bin/update_plugins").require()?; let tpm = HOME_DIR.join(".tmux/plugins/tpm/bin/update_plugins").require()?;
print_separator("tmux plugins"); print_separator("tmux plugins");
run_type.execute(tpm).arg("all").status_checked() ctx.run_type().execute(tpm).arg("all").status_checked()
} }
struct Tmux { struct Tmux {

View File

@@ -1,3 +1,14 @@
" AstroUpdate calls a plugin manager - Lazy as of this writing. So we check for it before
" others. Add to init.lua:
" updater = {
" skip_prompts = true,
" },
if exists(":AstroUpdate")
echo "AstroUpdate"
AstroUpdate
quitall
endif
if exists(":NeoBundleUpdate") if exists(":NeoBundleUpdate")
echo "NeoBundle" echo "NeoBundle"
NeoBundleUpdate NeoBundleUpdate
@@ -38,11 +49,6 @@ if exists(":Lazy")
Lazy! sync | qa Lazy! sync | qa
endif endif
if exists(":AstroUpdate")
echo "AstroUpdate"
AstroUpdate
endif
if exists(':PackerSync') if exists(':PackerSync')
echo "Packer" echo "Packer"
autocmd User PackerComplete quitall autocmd User PackerComplete quitall

View File

@@ -4,7 +4,7 @@ use crate::HOME_DIR;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use etcetera::base_strategy::BaseStrategy; use etcetera::base_strategy::BaseStrategy;
use crate::executor::{Executor, ExecutorOutput, RunType}; use crate::executor::{Executor, ExecutorOutput};
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::{ use crate::{
execution_context::ExecutionContext, execution_context::ExecutionContext,
@@ -141,10 +141,10 @@ pub fn upgrade_neovim(ctx: &ExecutionContext) -> Result<()> {
) )
} }
pub fn run_voom(run_type: RunType) -> Result<()> { pub fn run_voom(ctx: &ExecutionContext) -> Result<()> {
let voom = require("voom")?; let voom = require("voom")?;
print_separator("voom"); print_separator("voom");
run_type.execute(voom).arg("update").status_checked() ctx.run_type().execute(voom).arg("update").status_checked()
} }

View File

@@ -8,13 +8,12 @@ use walkdir::WalkDir;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::git::Repositories; use crate::git::Repositories;
use crate::terminal::print_separator; use crate::terminal::print_separator;
use crate::utils::{require, PathExt}; use crate::utils::{require, PathExt};
use crate::HOME_DIR; use crate::HOME_DIR;
pub fn run_zr(run_type: RunType) -> Result<()> { pub fn run_zr(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
require("zr")?; require("zr")?;
@@ -22,7 +21,10 @@ pub fn run_zr(run_type: RunType) -> Result<()> {
print_separator("zr"); print_separator("zr");
let cmd = format!("source {} && zr --update", zshrc().display()); let cmd = format!("source {} && zr --update", zshrc().display());
run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() ctx.run_type()
.execute(zsh)
.args(["-l", "-c", cmd.as_str()])
.status_checked()
} }
fn zdotdir() -> PathBuf { fn zdotdir() -> PathBuf {
@@ -49,16 +51,16 @@ pub fn run_antidote(ctx: &ExecutionContext) -> Result<()> {
.status_checked() .status_checked()
} }
pub fn run_antibody(run_type: RunType) -> Result<()> { pub fn run_antibody(ctx: &ExecutionContext) -> Result<()> {
require("zsh")?; require("zsh")?;
let antibody = require("antibody")?; let antibody = require("antibody")?;
print_separator("antibody"); print_separator("antibody");
run_type.execute(antibody).arg("update").status_checked() ctx.run_type().execute(antibody).arg("update").status_checked()
} }
pub fn run_antigen(run_type: RunType) -> Result<()> { pub fn run_antigen(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
let zshrc = zshrc().require()?; let zshrc = zshrc().require()?;
env::var("ADOTDIR") env::var("ADOTDIR")
@@ -69,10 +71,13 @@ pub fn run_antigen(run_type: RunType) -> Result<()> {
print_separator("antigen"); print_separator("antigen");
let cmd = format!("source {} && (antigen selfupdate ; antigen update)", zshrc.display()); let cmd = format!("source {} && (antigen selfupdate ; antigen update)", zshrc.display());
run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() ctx.run_type()
.execute(zsh)
.args(["-l", "-c", cmd.as_str()])
.status_checked()
} }
pub fn run_zgenom(run_type: RunType) -> Result<()> { pub fn run_zgenom(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
let zshrc = zshrc().require()?; let zshrc = zshrc().require()?;
env::var("ZGEN_SOURCE") env::var("ZGEN_SOURCE")
@@ -83,10 +88,13 @@ pub fn run_zgenom(run_type: RunType) -> Result<()> {
print_separator("zgenom"); print_separator("zgenom");
let cmd = format!("source {} && zgenom selfupdate && zgenom update", zshrc.display()); let cmd = format!("source {} && zgenom selfupdate && zgenom update", zshrc.display());
run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() ctx.run_type()
.execute(zsh)
.args(["-l", "-c", cmd.as_str()])
.status_checked()
} }
pub fn run_zplug(run_type: RunType) -> Result<()> { pub fn run_zplug(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
zshrc().require()?; zshrc().require()?;
@@ -97,13 +105,13 @@ pub fn run_zplug(run_type: RunType) -> Result<()> {
print_separator("zplug"); print_separator("zplug");
run_type ctx.run_type()
.execute(zsh) .execute(zsh)
.args(["-i", "-c", "zplug update"]) .args(["-i", "-c", "zplug update"])
.status_checked() .status_checked()
} }
pub fn run_zinit(run_type: RunType) -> Result<()> { pub fn run_zinit(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
let zshrc = zshrc().require()?; let zshrc = zshrc().require()?;
@@ -115,10 +123,13 @@ pub fn run_zinit(run_type: RunType) -> Result<()> {
print_separator("zinit"); print_separator("zinit");
let cmd = format!("source {} && zinit self-update && zinit update --all", zshrc.display(),); let cmd = format!("source {} && zinit self-update && zinit update --all", zshrc.display(),);
run_type.execute(zsh).args(["-i", "-c", cmd.as_str()]).status_checked() ctx.run_type()
.execute(zsh)
.args(["-i", "-c", cmd.as_str()])
.status_checked()
} }
pub fn run_zi(run_type: RunType) -> Result<()> { pub fn run_zi(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
let zshrc = zshrc().require()?; let zshrc = zshrc().require()?;
@@ -127,10 +138,10 @@ pub fn run_zi(run_type: RunType) -> Result<()> {
print_separator("zi"); print_separator("zi");
let cmd = format!("source {} && zi self-update && zi update --all", zshrc.display(),); let cmd = format!("source {} && zi self-update && zi update --all", zshrc.display(),);
run_type.execute(zsh).args(["-i", "-c", &cmd]).status_checked() ctx.run_type().execute(zsh).args(["-i", "-c", &cmd]).status_checked()
} }
pub fn run_zim(run_type: RunType) -> Result<()> { pub fn run_zim(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?; let zsh = require("zsh")?;
env::var("ZIM_HOME") env::var("ZIM_HOME")
.or_else(|_| { .or_else(|_| {
@@ -146,7 +157,7 @@ pub fn run_zim(run_type: RunType) -> Result<()> {
print_separator("zim"); print_separator("zim");
run_type ctx.run_type()
.execute(zsh) .execute(zsh)
.args(["-i", "-c", "zimfw upgrade && zimfw update"]) .args(["-i", "-c", "zimfw upgrade && zimfw update"])
.status_checked() .status_checked()
@@ -154,7 +165,11 @@ pub fn run_zim(run_type: RunType) -> Result<()> {
pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> {
require("zsh")?; require("zsh")?;
let oh_my_zsh = HOME_DIR.join(".oh-my-zsh").require()?; let oh_my_zsh = env::var("ZSH")
.map(PathBuf::from)
// default to `~/.oh-my-zsh`
.unwrap_or(HOME_DIR.join(".oh-my-zsh"))
.require()?;
print_separator("oh-my-zsh"); print_separator("oh-my-zsh");
@@ -194,7 +209,10 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type() ctx.run_type()
.execute("zsh") .execute("zsh")
.env("ZSH", &oh_my_zsh)
.arg(&oh_my_zsh.join("tools/upgrade.sh")) .arg(&oh_my_zsh.join("tools/upgrade.sh"))
// oh-my-zsh returns 80 when it is already updated and no changes pulled
// in this update.
// See this comment: https://github.com/r-darwish/topgrade/issues/569#issuecomment-736756731
// for more information.
.status_checked_with_codes(&[80]) .status_checked_with_codes(&[80])
} }

View File

@@ -1,8 +1,6 @@
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::env; use std::env;
use std::io::{self, Write}; use std::io::{self, Write};
#[cfg(target_os = "linux")]
use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
@@ -12,7 +10,6 @@ use color_eyre::eyre;
use color_eyre::eyre::Context; use color_eyre::eyre::Context;
use console::{style, Key, Term}; use console::{style, Key, Term};
use lazy_static::lazy_static; use lazy_static::lazy_static;
#[cfg(target_os = "macos")]
use notify_rust::{Notification, Timeout}; use notify_rust::{Notification, Timeout};
use tracing::{debug, error}; use tracing::{debug, error};
#[cfg(windows)] #[cfg(windows)]
@@ -20,10 +17,7 @@ use which_crate::which;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::report::StepResult; use crate::report::StepResult;
#[cfg(target_os = "linux")]
use crate::terminal;
#[cfg(target_os = "linux")]
use crate::utils::which;
lazy_static! { lazy_static! {
static ref TERMINAL: Mutex<Terminal> = Mutex::new(Terminal::new()); static ref TERMINAL: Mutex<Terminal> = Mutex::new(Terminal::new());
} }
@@ -49,8 +43,6 @@ struct Terminal {
set_title: bool, set_title: bool,
display_time: bool, display_time: bool,
desktop_notification: bool, desktop_notification: bool,
#[cfg(target_os = "linux")]
notify_send: Option<PathBuf>,
} }
impl Terminal { impl Terminal {
@@ -65,8 +57,6 @@ impl Terminal {
set_title: true, set_title: true,
display_time: true, display_time: true,
desktop_notification: false, desktop_notification: false,
#[cfg(target_os = "linux")]
notify_send: which("notify-send"),
} }
} }
@@ -82,35 +72,18 @@ impl Terminal {
self.display_time = display_time self.display_time = display_time
} }
#[allow(unused_variables)]
fn notify_desktop<P: AsRef<str>>(&self, message: P, timeout: Option<Duration>) { fn notify_desktop<P: AsRef<str>>(&self, message: P, timeout: Option<Duration>) {
debug!("Desktop notification: {}", message.as_ref()); debug!("Desktop notification: {}", message.as_ref());
cfg_if::cfg_if! { let mut notification = Notification::new();
if #[cfg(target_os = "macos")] { notification
let mut notification = Notification::new(); .summary("Topgrade")
notification.summary("Topgrade") .body(message.as_ref())
.body(message.as_ref()) .appname("topgrade");
.appname("topgrade");
if let Some(timeout) = timeout { if let Some(timeout) = timeout {
notification.timeout(Timeout::Milliseconds(timeout.as_millis() as u32)); notification.timeout(Timeout::Milliseconds(timeout.as_millis() as u32));
}
notification.show().ok();
} else if #[cfg(target_os = "linux")] {
if let Some(ns) = self.notify_send.as_ref() {
let mut command = Command::new(ns);
if let Some(timeout) = timeout {
command.arg("-t");
command.arg(format!("{}", timeout.as_millis()));
}
command.args(["-a", "Topgrade", "Topgrade"]);
command.arg(message.as_ref());
if let Err(err) = command.output_checked() {
terminal::print_warning("Sending notification failed with {err:?}");
}
}
}
} }
notification.show().ok();
} }
fn print_separator<P: AsRef<str>>(&mut self, message: P) { fn print_separator<P: AsRef<str>>(&mut self, message: P) {
@@ -345,8 +318,3 @@ pub fn notify_desktop<P: AsRef<str>>(message: P, timeout: Option<Duration>) {
pub fn display_time(display_time: bool) { pub fn display_time(display_time: bool) {
TERMINAL.lock().unwrap().display_time(display_time); TERMINAL.lock().unwrap().display_time(display_time);
} }
#[cfg(target_os = "linux")]
pub fn supports_notify_send() -> bool {
TERMINAL.lock().unwrap().notify_send.is_some()
}

View File

@@ -1,12 +1,15 @@
use crate::error::SkipStep;
use color_eyre::eyre::Result;
use std::env; use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Debug; use std::fmt::Debug;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command;
use color_eyre::eyre::Result;
use tracing::{debug, error}; use tracing::{debug, error};
use crate::command::CommandExt;
use crate::error::SkipStep;
pub trait PathExt pub trait PathExt
where where
Self: Sized, Self: Sized,
@@ -101,6 +104,13 @@ pub fn require_option<T>(option: Option<T>, cause: String) -> Result<T> {
} }
} }
pub fn string_prepend_str(string: &mut String, s: &str) {
let mut new_string = String::with_capacity(string.len() + s.len());
new_string.push_str(s);
new_string.push_str(string);
*string = new_string;
}
/* sys-info-rs /* sys-info-rs
* *
* Copyright (c) 2015 Siyu Wang * Copyright (c) 2015 Siyu Wang
@@ -144,11 +154,100 @@ pub fn hostname() -> Result<String> {
#[cfg(target_family = "windows")] #[cfg(target_family = "windows")]
pub fn hostname() -> Result<String> { pub fn hostname() -> Result<String> {
use crate::command::CommandExt;
use std::process::Command;
Command::new("hostname") Command::new("hostname")
.output_checked_utf8() .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()) .map(|output| output.stdout.trim().to_owned())
} }
pub mod merge_strategies {
use merge::Merge;
use crate::config::Commands;
/// Prepends right to left (both Option<Vec<T>>)
pub fn vec_prepend_opt<T>(left: &mut Option<Vec<T>>, right: Option<Vec<T>>) {
if let Some(left_vec) = left {
if let Some(mut right_vec) = right {
right_vec.append(left_vec);
let _ = std::mem::replace(left, Some(right_vec));
}
} else {
*left = right;
}
}
/// Appends an Option<String> to another Option<String>
pub fn string_append_opt(left: &mut Option<String>, right: Option<String>) {
if let Some(left_str) = left {
if let Some(right_str) = right {
left_str.push(' ');
left_str.push_str(&right_str);
}
} else {
*left = right;
}
}
pub fn inner_merge_opt<T>(left: &mut Option<T>, right: Option<T>)
where
T: Merge,
{
if let Some(ref mut left_inner) = left {
if let Some(right_inner) = right {
left_inner.merge(right_inner);
}
} else {
*left = right;
}
}
pub fn commands_merge_opt(left: &mut Option<Commands>, right: Option<Commands>) {
if let Some(ref mut left_inner) = left {
if let Some(right_inner) = right {
left_inner.extend(right_inner);
}
} else {
*left = right;
}
}
}
// Skip causes
// TODO: Put them in a better place when we have more of them
pub const REQUIRE_SUDO: &str = "Require sudo or counterpart but not found, skip";
/// Return `Err(SkipStep)` if `python` is a Python 2 or shim.
///
/// # Shim
/// On Windows, if you install `python` through `winget`, an actual `python`
/// is installed as well as a `python3` shim. Shim is invokable, but when you
/// execute it, the Microsoft App Store will be launched instead of a Python
/// shell.
///
/// We do this check through `python -V`, a shim will just give `Python` with
/// no version number.
pub fn check_is_python_2_or_shim(python: PathBuf) -> Result<PathBuf> {
let output = Command::new(&python).arg("-V").output_checked_utf8()?;
// "Python x.x.x\n"
let stdout = output.stdout;
// ["Python"] or ["Python", "x.x.x"], the newline char is trimmed.
let mut split = stdout.split_whitespace();
if let Some(version) = split.nth(1) {
let major_version = version
.split('.')
.next()
.expect("Should have a major version number")
.parse::<u32>()
.expect("Major version should be a valid number");
if major_version == 2 {
return Err(SkipStep(format!("{} is a Python 2, skip.", python.display())).into());
}
} else {
// No version number, is a shim
return Err(SkipStep(format!("{} is a Python shim, skip.", python.display())).into());
}
Ok(python)
}