10.2.1 release (#244)

This commit is contained in:
Thomas Schönauer
2022-11-30 21:08:15 +00:00
committed by GitHub
18 changed files with 347 additions and 209 deletions

View File

@@ -23,10 +23,11 @@ jobs:
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.RUST_VER }}'
targets: ${{ matrix.target }}
components: clippy, rustfmt
components: rustfmt
- name: Run cargo fmt
env:
TERM: xterm-256color
run: |
cargo fmt --all -- --check
@@ -72,8 +73,7 @@ jobs:
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.RUST_VER }}'
targets: ${{ matrix.target }}
components: clippy, rustfmt
components: clippy
- name: Setup Rust Cache
uses: Swatinem/rust-cache@v2

109
Cargo.lock generated
View File

@@ -59,11 +59,11 @@ dependencies = [
[[package]]
name = "async-channel"
version = "1.7.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
dependencies = [
"concurrent-queue 1.2.4",
"concurrent-queue",
"event-listener",
"futures-core",
]
@@ -76,7 +76,7 @@ checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
dependencies = [
"async-lock",
"async-task",
"concurrent-queue 2.0.0",
"concurrent-queue",
"fastrand",
"futures-lite",
"slab",
@@ -84,13 +84,13 @@ dependencies = [
[[package]]
name = "async-io"
version = "1.10.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794"
dependencies = [
"async-lock",
"autocfg",
"concurrent-queue 1.2.4",
"concurrent-queue",
"futures-lite",
"libc",
"log",
@@ -99,7 +99,7 @@ dependencies = [
"slab",
"socket2",
"waker-fn",
"winapi",
"windows-sys",
]
[[package]]
@@ -131,9 +131,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]]
name = "async-trait"
version = "0.1.58"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
dependencies = [
"proc-macro2",
"quote",
@@ -167,7 +167,7 @@ dependencies = [
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"miniz_oxide 0.5.4",
"object",
"rustc-demangle",
]
@@ -208,12 +208,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]]
name = "cache-padded"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "cc"
version = "1.0.77"
@@ -236,7 +230,7 @@ dependencies = [
"js-sys",
"num-integer",
"num-traits",
"time 0.1.44",
"time 0.1.45",
"wasm-bindgen",
"winapi",
]
@@ -258,6 +252,15 @@ dependencies = [
"textwrap",
]
[[package]]
name = "clap_complete"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da92e6facd8d73c22745a5d3cbb59bdf8e46e3235c923e516527d8e81eec14a4"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "3.1.18"
@@ -280,6 +283,16 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "clap_mangen"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121b598d1b9d63d4ff3b7dae8defd88cc23e41850ee89fd2feec2e7d992e6ec8"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@@ -317,15 +330,6 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "concurrent-queue"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
dependencies = [
"cache-padded",
]
[[package]]
name = "concurrent-queue"
version = "2.0.0"
@@ -565,12 +569,12 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.24"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide",
"miniz_oxide 0.6.2",
]
[[package]]
@@ -1046,6 +1050,15 @@ dependencies = [
"adler",
]
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.7.14"
@@ -1261,9 +1274,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if",
"libc",
@@ -1305,16 +1318,16 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "polling"
version = "2.4.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2"
checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748"
dependencies = [
"autocfg",
"cfg-if",
"libc",
"log",
"wepoll-ffi",
"winapi",
"windows-sys",
]
[[package]]
@@ -1533,6 +1546,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "roff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "rust-ini"
version = "0.18.0"
@@ -1642,18 +1661,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]]
name = "serde"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
dependencies = [
"proc-macro2",
"quote",
@@ -1830,9 +1849,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.103"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
dependencies = [
"proc-macro2",
"quote",
@@ -1931,9 +1950,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.44"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
@@ -2036,11 +2055,13 @@ dependencies = [
[[package]]
name = "topgrade"
version = "10.2.0"
version = "10.2.1"
dependencies = [
"cfg-if",
"chrono",
"clap",
"clap_complete",
"clap_mangen",
"color-eyre",
"console",
"directories",

View File

@@ -6,7 +6,7 @@ keywords = ["upgrade", "update"]
license = "GPL-3.0"
# license-file = "LICENSE"
repository = "https://github.com/topgrade-rs/topgrade"
version = "10.2.0"
version = "10.2.1"
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
exclude = ["doc/screenshot.gif"]
edition = "2021"
@@ -28,6 +28,8 @@ toml = "0.5"
which_crate = { version = "~4.1", package = "which" }
shellexpand = "~2.1"
clap = { version = "~3.1", features = ["cargo", "derive"] }
clap_complete = "~3.1"
clap_mangen = "~0.1"
walkdir = "~2.3"
console = "~0.15"
lazy_static = "~1.4"

View File

@@ -13,6 +13,10 @@
# Do not ask to retry failed steps (default: false)
#no_retry = true
# Run `sudo -v` to cache credentials at the start of the run; this avoids a
# blocking password prompt in the middle of a possibly-unattended run.
#pre_sudo = false
# Run inside tmux
#run_in_tmux = true

View File

@@ -6,6 +6,7 @@ use std::process::Command;
use std::{env, fs};
use clap::{ArgEnum, Parser};
use clap_complete::Shell;
use color_eyre::eyre;
use color_eyre::eyre::Context;
use color_eyre::eyre::Result;
@@ -106,6 +107,7 @@ pub enum Step {
HomeManager,
Jetpack,
Julia,
Juliaup,
Kakoune,
Krew,
Macports,
@@ -129,6 +131,7 @@ pub enum Step {
Remotes,
Restarts,
Rtcl,
RubyGems,
Rustup,
Scoop,
Sdkman,
@@ -223,6 +226,7 @@ pub struct Brew {
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ArchPackageManager {
GarudaUpdate,
Autodetect,
Trizen,
Paru,
@@ -269,6 +273,7 @@ pub struct Vim {
#[serde(deny_unknown_fields)]
/// Configuration file
pub struct ConfigFile {
pre_sudo: Option<bool>,
pre_commands: Option<Commands>,
post_commands: Option<Commands>,
commands: Option<Commands>,
@@ -485,6 +490,14 @@ pub struct CommandLineArgs {
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
#[clap(long, default_value = "info")]
pub log_filter: String,
/// Print completion script for the given shell and exit
#[clap(long, arg_enum, hide = true)]
pub gen_completion: Option<Shell>,
/// Print roff manpage and exit
#[clap(long, hide = true)]
pub gen_manpage: bool,
}
impl CommandLineArgs {
@@ -947,6 +960,12 @@ impl Config {
.unwrap_or(false)
}
/// If `true`, `sudo` should be called after `pre_commands` in order to elevate at the
/// start of the session (and not in the middle).
pub fn pre_sudo(&self) -> bool {
self.config_file.pre_sudo.unwrap_or(false)
}
#[cfg(target_os = "linux")]
pub fn npm_use_sudo(&self) -> bool {
self.config_file

View File

@@ -1,16 +1,17 @@
#![allow(dead_code)]
use crate::executor::RunType;
use crate::git::Git;
use crate::sudo::Sudo;
use crate::utils::require_option;
use crate::{config::Config, executor::Executor};
use color_eyre::eyre::Result;
use directories::BaseDirs;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::sync::Mutex;
pub struct ExecutionContext<'a> {
run_type: RunType,
sudo: &'a Option<PathBuf>,
sudo: Option<Sudo>,
git: &'a Git,
config: &'a Config,
base_dirs: &'a BaseDirs,
@@ -23,7 +24,7 @@ pub struct ExecutionContext<'a> {
impl<'a> ExecutionContext<'a> {
pub fn new(
run_type: RunType,
sudo: &'a Option<PathBuf>,
sudo: Option<Sudo>,
git: &'a Git,
config: &'a Config,
base_dirs: &'a BaseDirs,
@@ -40,18 +41,7 @@ impl<'a> ExecutionContext<'a> {
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 mut cmd = self.run_type.execute(&sudo);
if sudo.ends_with("sudo") {
cmd.arg("--preserve-env=DIFFPROG");
}
if interactive {
cmd.arg("-i");
}
cmd.arg(command);
Ok(cmd)
Ok(sudo.execute_elevated(self, command, interactive))
}
pub fn run_type(&self) -> RunType {
@@ -62,8 +52,8 @@ impl<'a> ExecutionContext<'a> {
self.git
}
pub fn sudo(&self) -> &Option<PathBuf> {
self.sudo
pub fn sudo(&self) -> &Option<Sudo> {
&self.sudo
}
pub fn config(&self) -> &Config {

View File

@@ -176,7 +176,7 @@ impl Executor {
/// An extension of `status_checked` that allows you to set a sequence of codes
/// that can indicate success of a script
#[cfg_attr(windows, allow(dead_code))]
#[allow(dead_code)]
pub fn status_checked_with_codes(&mut self, codes: &[i32]) -> Result<()> {
match self {
Executor::Wet(c) => c.status_checked_with(|status| {

View File

@@ -4,6 +4,7 @@ use std::env;
use std::io;
use std::process::exit;
use clap::CommandFactory;
use clap::{crate_version, Parser};
use color_eyre::eyre::Context;
use color_eyre::eyre::{eyre, Result};
@@ -30,6 +31,7 @@ mod self_renamer;
#[cfg(feature = "self-update")]
mod self_update;
mod steps;
mod sudo;
mod terminal;
mod utils;
@@ -41,6 +43,18 @@ fn run() -> Result<()> {
let opt = CommandLineArgs::parse();
if let Some(shell) = opt.gen_completion {
let cmd = &mut CommandLineArgs::command();
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut std::io::stdout());
return Ok(());
}
if opt.gen_manpage {
let man = clap_mangen::Man::new(CommandLineArgs::command());
man.render(&mut std::io::stdout())?;
return Ok(());
}
install_tracing(&opt.tracing_filter_directives())?;
for env in opt.env_variables() {
@@ -82,10 +96,10 @@ fn run() -> Result<()> {
let git = git::Git::new();
let mut git_repos = git::Repositories::new(&git);
let sudo = utils::sudo();
let sudo = sudo::Sudo::detect();
let run_type = executor::RunType::new(config.dry_run());
let ctx = execution_context::ExecutionContext::new(run_type, &sudo, &git, &config, &base_dirs);
let ctx = execution_context::ExecutionContext::new(run_type, sudo, &git, &config, &base_dirs);
let mut runner = runner::Runner::new(&ctx);
@@ -119,6 +133,12 @@ fn run() -> Result<()> {
}
}
if config.pre_sudo() {
if let Some(sudo) = ctx.sudo() {
sudo.elevate(&ctx)?;
}
}
let powershell = powershell::Powershell::new();
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
@@ -197,17 +217,17 @@ fn run() -> Result<()> {
#[cfg(target_os = "dragonfly")]
runner.execute(Step::Pkg, "DragonFly BSD Packages", || {
dragonfly::upgrade_packages(sudo.as_ref(), run_type)
dragonfly::upgrade_packages(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "freebsd")]
runner.execute(Step::Pkg, "FreeBSD Packages", || {
freebsd::upgrade_packages(&ctx, sudo.as_ref(), run_type)
freebsd::upgrade_packages(&ctx, ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "openbsd")]
runner.execute(Step::Pkg, "OpenBSD Packages", || {
openbsd::upgrade_packages(sudo.as_ref(), run_type)
openbsd::upgrade_packages(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "android")]
@@ -319,6 +339,7 @@ fn run() -> Result<()> {
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(&base_dirs, run_type))?;
runner.execute(Step::Juliaup, "juliaup", || generic::run_juliaup(&base_dirs, 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))?;
@@ -354,6 +375,9 @@ fn run() -> Result<()> {
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
runner.execute(Step::Krew, "krew", || generic::run_krew_upgrade(run_type))?;
runner.execute(Step::Gem, "gem", || generic::run_gem(&base_dirs, run_type))?;
runner.execute(Step::RubyGems, "rubygems", || {
generic::run_rubygems(&base_dirs, run_type)
})?;
runner.execute(Step::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))?;
@@ -374,7 +398,7 @@ fn run() -> Result<()> {
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(sudo.as_ref(), run_type))?;
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))?;
@@ -394,11 +418,11 @@ fn run() -> Result<()> {
#[cfg(target_os = "linux")]
{
runner.execute(Step::System, "pihole", || {
linux::run_pihole_update(sudo.as_ref(), run_type)
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(sudo.as_ref(), run_type)
linux::run_needrestart(ctx.sudo().as_ref(), run_type)
})?;
}
@@ -411,12 +435,12 @@ fn run() -> Result<()> {
#[cfg(target_os = "freebsd")]
runner.execute(Step::System, "FreeBSD Upgrade", || {
freebsd::upgrade_freebsd(sudo.as_ref(), run_type)
freebsd::upgrade_freebsd(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(target_os = "openbsd")]
runner.execute(Step::System, "OpenBSD Upgrade", || {
openbsd::upgrade_openbsd(sudo.as_ref(), run_type)
openbsd::upgrade_openbsd(ctx.sudo().as_ref(), run_type)
})?;
#[cfg(windows)]
@@ -448,10 +472,10 @@ fn run() -> Result<()> {
}
#[cfg(target_os = "freebsd")]
freebsd::audit_packages(&sudo).ok();
freebsd::audit_packages(ctx.sudo().as_ref()).ok();
#[cfg(target_os = "dragonfly")]
dragonfly::audit_packages(&sudo).ok();
dragonfly::audit_packages(ctx.sudo().as_ref()).ok();
}
let mut post_command_failed = false;

View File

@@ -70,7 +70,7 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
let gem = utils::require("gem")?;
base_dirs.home_dir().join(".gem").require()?;
print_separator("RubyGems");
print_separator("Gems");
let mut command = run_type.execute(gem);
command.arg("update");
@@ -83,6 +83,23 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
command.status_checked()
}
pub fn run_rubygems(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
let gem = utils::require("gem")?;
base_dirs.home_dir().join(".gem").require()?;
print_separator("RubyGems");
let mut command = run_type.execute(gem);
command.arg("update --system");
if env::var_os("RBENV_SHELL").is_none() {
debug!("Detected rbenv. Avoiding --user-install");
command.arg("--user-install");
}
command.status_checked()
}
pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
let haxelib = utils::require("haxelib")?;
@@ -175,6 +192,18 @@ pub fn run_rustup(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
run_type.execute(&rustup).arg("update").status_checked()
}
pub fn run_juliaup(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
let juliaup = utils::require("juliaup")?;
print_separator("juliaup");
if juliaup.canonicalize()?.is_descendant_of(base_dirs.home_dir()) {
run_type.execute(&juliaup).args(["self", "update"]).status_checked()?;
}
run_type.execute(&juliaup).arg("update").status_checked()
}
pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> {
let choosenim = utils::require("choosenim")?;

View File

@@ -12,9 +12,7 @@ use semver::Version;
use tracing::debug;
use crate::command::CommandExt;
use crate::executor::RunType;
use crate::terminal::print_separator;
use crate::utils::sudo;
use crate::utils::{require, PathExt};
use crate::{error::SkipStep, execution_context::ExecutionContext};
@@ -90,14 +88,17 @@ impl NPM {
Version::parse(&version_str?).map_err(|err| err.into())
}
fn upgrade(&self, run_type: RunType, use_sudo: bool) -> Result<()> {
fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> {
let args = ["update", self.global_location_arg()];
if use_sudo {
let sudo_option = sudo();
let sudo = require_option(sudo_option, String::from("sudo is not installed"))?;
run_type.execute(sudo).arg(&self.command).args(args).status_checked()?;
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?;
ctx.run_type()
.execute(sudo)
.arg(&self.command)
.args(args)
.status_checked()?;
} else {
run_type.execute(&self.command).args(args).status_checked()?;
ctx.run_type().execute(&self.command).args(args).status_checked()?;
}
Ok(())
@@ -150,17 +151,18 @@ impl Yarn {
.map(|s| PathBuf::from(s.stdout.trim()))
}
fn upgrade(&self, run_type: RunType, use_sudo: bool) -> Result<()> {
fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> {
let args = ["global", "upgrade"];
if use_sudo {
run_type
.execute("sudo")
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?;
ctx.run_type()
.execute(sudo)
.arg(self.yarn.as_ref().unwrap_or(&self.command))
.args(args)
.status_checked()?;
} else {
run_type.execute(&self.command).args(args).status_checked()?;
ctx.run_type().execute(&self.command).args(args).status_checked()?;
}
Ok(())
@@ -215,12 +217,12 @@ pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
#[cfg(target_os = "linux")]
{
npm.upgrade(ctx.run_type(), should_use_sudo(&npm, ctx)?)
npm.upgrade(ctx, should_use_sudo(&npm, ctx)?)
}
#[cfg(not(target_os = "linux"))]
{
npm.upgrade(ctx.run_type(), false)
npm.upgrade(ctx, false)
}
}
@@ -231,12 +233,12 @@ pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> {
#[cfg(target_os = "linux")]
{
pnpm.upgrade(ctx.run_type(), should_use_sudo(&pnpm, ctx)?)
pnpm.upgrade(ctx, should_use_sudo(&pnpm, ctx)?)
}
#[cfg(not(target_os = "linux"))]
{
pnpm.upgrade(ctx.run_type(), false)
pnpm.upgrade(ctx, false)
}
}
@@ -252,12 +254,12 @@ pub fn run_yarn_upgrade(ctx: &ExecutionContext) -> Result<()> {
#[cfg(target_os = "linux")]
{
yarn.upgrade(ctx.run_type(), should_use_sudo_yarn(&yarn, ctx)?)
yarn.upgrade(ctx, should_use_sudo_yarn(&yarn, ctx)?)
}
#[cfg(not(target_os = "linux"))]
{
yarn.upgrade(ctx.run_type(), false)
yarn.upgrade(ctx, false)
}
}

View File

@@ -1,7 +1,6 @@
use std::env::var_os;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command;
use color_eyre::eyre;
use color_eyre::eyre::Result;
@@ -10,6 +9,7 @@ use walkdir::WalkDir;
use crate::command::CommandExt;
use crate::error::TopgradeError;
use crate::execution_context::ExecutionContext;
use crate::sudo::Sudo;
use crate::utils::which;
use crate::{config, Step};
@@ -31,7 +31,10 @@ pub struct YayParu {
impl ArchPackageManager for YayParu {
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
if ctx.config().show_arch_news() {
Command::new(&self.executable).arg("-Pw").status_checked()?;
ctx.run_type()
.execute(&self.executable)
.arg("-Pw")
.status_checked_with_codes(&[1, 0])?;
}
let mut command = ctx.run_type().execute(&self.executable);
@@ -70,6 +73,27 @@ impl YayParu {
}
}
pub struct GarudaUpdate {
executable: PathBuf,
}
impl ArchPackageManager for GarudaUpdate {
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
let mut command = ctx.run_type().execute(&self.executable);
command.env("PATH", get_execution_path());
command.status_checked()?;
Ok(())
}
}
impl GarudaUpdate {
fn get() -> Option<Self> {
Some(Self {
executable: which("garuda-update")?,
})
}
}
pub struct Trizen {
executable: PathBuf,
}
@@ -110,7 +134,7 @@ impl Trizen {
}
pub struct Pacman {
sudo: PathBuf,
sudo: Sudo,
executable: PathBuf,
}
@@ -229,7 +253,7 @@ impl ArchPackageManager for Pamac {
pub struct Aura {
executable: PathBuf,
sudo: PathBuf,
sudo: Sudo,
}
impl Aura {
@@ -282,14 +306,16 @@ pub fn get_arch_package_manager(ctx: &ExecutionContext) -> Option<Box<dyn ArchPa
let pacman = which("powerpill").unwrap_or_else(|| PathBuf::from("pacman"));
match ctx.config().arch_package_manager() {
config::ArchPackageManager::Autodetect => YayParu::get("paru", &pacman)
config::ArchPackageManager::Autodetect => GarudaUpdate::get()
.map(box_package_manager)
.or_else(|| YayParu::get("paru", &pacman).map(box_package_manager))
.or_else(|| YayParu::get("yay", &pacman).map(box_package_manager))
.or_else(|| Trizen::get().map(box_package_manager))
.or_else(|| Pikaur::get().map(box_package_manager))
.or_else(|| Pamac::get().map(box_package_manager))
.or_else(|| Pacman::get(ctx).map(box_package_manager))
.or_else(|| Aura::get(ctx).map(box_package_manager)),
config::ArchPackageManager::GarudaUpdate => GarudaUpdate::get().map(box_package_manager),
config::ArchPackageManager::Trizen => Trizen::get().map(box_package_manager),
config::ArchPackageManager::Paru => YayParu::get("paru", &pacman).map(box_package_manager),
config::ArchPackageManager::Yay => YayParu::get("yay", &pacman).map(box_package_manager),

View File

@@ -1,12 +1,12 @@
use crate::command::CommandExt;
use crate::executor::RunType;
use crate::sudo::Sudo;
use crate::terminal::print_separator;
use crate::utils::require_option;
use color_eyre::eyre::Result;
use std::path::PathBuf;
use std::process::Command;
pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn upgrade_packages(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?;
print_separator("DragonFly BSD Packages");
run_type
@@ -15,7 +15,7 @@ pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()>
.status_checked()
}
pub fn audit_packages(sudo: &Option<PathBuf>) -> Result<()> {
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> {
if let Some(sudo) = sudo {
println!();
Command::new(sudo)

View File

@@ -1,14 +1,14 @@
use crate::command::CommandExt;
use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::sudo::Sudo;
use crate::terminal::print_separator;
use crate::utils::require_option;
use crate::Step;
use color_eyre::eyre::Result;
use std::path::PathBuf;
use std::process::Command;
pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn upgrade_freebsd(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?;
print_separator("FreeBSD Update");
run_type
@@ -17,7 +17,7 @@ pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()>
.status_checked()
}
pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("No sudo detected"))?;
print_separator("FreeBSD Packages");
@@ -30,7 +30,7 @@ pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&PathBuf>, run_type
command.status_checked()
}
pub fn audit_packages(sudo: &Option<PathBuf>) -> Result<()> {
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> {
if let Some(sudo) = sudo {
println!();
Command::new(sudo)

View File

@@ -10,6 +10,7 @@ use crate::error::{SkipStep, TopgradeError};
use crate::execution_context::ExecutionContext;
use crate::executor::RunType;
use crate::steps::os::archlinux;
use crate::sudo::Sudo;
use crate::terminal::{print_separator, print_warning};
use crate::utils::{require, require_option, which, PathExt};
use crate::Step;
@@ -498,7 +499,7 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn run_needrestart(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let needrestart = require("needrestart")?;
let distribution = Distribution::detect()?;
@@ -603,7 +604,7 @@ pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> {
Ok(())
}
pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn run_snap(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let snap = require("snap")?;
@@ -615,7 +616,7 @@ pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
run_type.execute(sudo).arg(snap).arg("refresh").status_checked()
}
pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
pub fn run_pihole_update(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
let sudo = require_option(sudo, String::from("sudo is not installed"))?;
let pihole = require("pihole")?;
Path::new("/opt/pihole/update.sh").require()?;

View File

@@ -19,17 +19,16 @@ pub fn run_chocolatey(ctx: &ExecutionContext) -> Result<()> {
print_separator("Chocolatey");
let mut cmd = &choco;
let mut args = vec!["upgrade", "all"];
let mut command = match ctx.sudo() {
Some(sudo) => {
let mut command = ctx.run_type().execute(sudo);
command.arg(choco);
command
}
None => ctx.run_type().execute(choco),
};
if let Some(sudo) = ctx.sudo() {
cmd = sudo;
args.insert(0, "choco");
}
let mut command = ctx.run_type().execute(cmd);
command.args(&args);
command.args(["upgrade", "all"]);
if yes {
command.arg("--yes");

108
src/sudo.rs Normal file
View File

@@ -0,0 +1,108 @@
use std::ffi::OsStr;
use std::path::Path;
use std::path::PathBuf;
use color_eyre::eyre::Context;
use color_eyre::eyre::Result;
use crate::command::CommandExt;
use crate::execution_context::ExecutionContext;
use crate::executor::Executor;
use crate::terminal::print_separator;
use crate::utils::which;
#[derive(Clone, Debug)]
pub struct Sudo {
/// The path to the `sudo` binary.
path: PathBuf,
/// The type of program being used as `sudo`.
kind: SudoKind,
}
impl Sudo {
/// Get the `sudo` binary for this platform.
pub fn detect() -> Option<Self> {
which("doas")
.map(|p| (p, SudoKind::Doas))
.or_else(|| which("sudo").map(|p| (p, SudoKind::Sudo)))
.or_else(|| which("gsudo").map(|p| (p, SudoKind::Gsudo)))
.or_else(|| which("pkexec").map(|p| (p, SudoKind::Pkexec)))
.map(|(path, kind)| Self { path, kind })
}
/// Elevate permissions with `sudo`.
///
/// This helps prevent blocking `sudo` prompts from stopping the run in the middle of a
/// step.
///
/// See: https://github.com/topgrade-rs/topgrade/issues/205
pub fn elevate(&self, ctx: &ExecutionContext) -> Result<()> {
print_separator("Sudo");
let mut cmd = ctx.run_type().execute(self);
match self.kind {
SudoKind::Doas => {
// `doas` doesn't have anything like `sudo -v` to cache credentials,
// so we just execute a dummy `echo` command so we have something
// unobtrusive to run.
// See: https://man.openbsd.org/doas
cmd.arg("echo");
}
SudoKind::Sudo => {
// From `man sudo` on macOS:
// -v, --validate
// Update the user's cached credentials, authenticating the user
// if necessary. For the sudoers plugin, this extends the sudo
// timeout for another 5 minutes by default, but does not run a
// command. Not all security policies support cached credentials.
cmd.arg("-v");
}
SudoKind::Gsudo => {
// Shows current user, cache and console status.
// See: https://gerardog.github.io/gsudo/docs/usage
cmd.arg("status");
}
SudoKind::Pkexec => {
// I don't think this does anything; `pkexec` usually asks for
// authentication every time, although it can be configured
// differently.
//
// See the note for `doas` above.
//
// See: https://linux.die.net/man/1/pkexec
cmd.arg("echo");
}
}
cmd.status_checked().wrap_err("Failed to elevate permissions")
}
/// Execute a command with `sudo`.
pub fn execute_elevated(&self, ctx: &ExecutionContext, command: &Path, interactive: bool) -> Executor {
let mut cmd = ctx.run_type().execute(self);
if let SudoKind::Sudo = self.kind {
cmd.arg("--preserve-env=DIFFPROG");
}
if interactive {
cmd.arg("-i");
}
cmd.arg(command);
cmd
}
}
#[derive(Clone, Copy, Debug)]
enum SudoKind {
Doas,
Sudo,
Gsudo,
Pkexec,
}
impl AsRef<OsStr> for Sudo {
fn as_ref(&self) -> &OsStr {
self.path.as_ref()
}
}

View File

@@ -67,13 +67,6 @@ pub fn which<T: AsRef<OsStr> + Debug>(binary_name: T) -> Option<PathBuf> {
}
}
pub fn sudo() -> Option<PathBuf> {
which("doas")
.or_else(|| which("sudo"))
.or_else(|| which("gsudo"))
.or_else(|| which("pkexec"))
}
pub fn editor() -> Vec<String> {
env::var("EDITOR")
.unwrap_or_else(|_| String::from(if cfg!(windows) { "notepad" } else { "vi" }))

View File

@@ -1,80 +0,0 @@
.hy
.TH "topgrade" "8"
.SH NAME
.PP
Topgrade \- Upgrade everything
.SH SYNOPSIS
.PP
topgrade [\fIoptions\f[]]
.SH DESCRIPTION
.PP
Keeping your system up to date usually involves invoking multiple package managers.
This results in big, non-portable shell one-liners saved in your shell.
To remedy this, \fBTopgrade\fR detects which tools you use and runs the appropriate commands to update them.
.SH OPTIONS
.TP
.B \-\-only <only>
Run only specific steps
.RS
.RE
.TP
.B \-\-disable <disable>
Disable specific steps
.RS
.RE
.TP
.B \-c, \-\-cleanup
Cleanup temporary or old files
.RS
.RE
.TP
.B \-n, \-\-dry\-run
List the commands that would be run
.RS
.RE
.TP
.B \-\-edit\-config
Edit the configuration file
.RS
.RE
.TP
.B \-h, \-\-help
Print help information
.RS
.RE
.TP
.B \-k, \-\-keep
Prompt for a key before exiting
.RS
.RE
.TP
.B \-\-no\-retry
Do not ask to retry failed steps
.RS
.RE
.TP
.B \-t, \-\-tmux
Run inside tmux
.RS
.RE
.TP
.B \-V, \-\-version
Print version information
.RS
.RE
.TP
.B \-v, \-\-verbose
Output logs
.RS
.RE
.B \-y, \-\-yes
Skip package manager's prompts (experimental)
.SH ARGUMENT FORMAT
Options can be given in any order.
A list of steps must be provided as a list of separate arguments, i.e. 'topgrade --only system shell'.
.SH BUGS
For a list of bugs see <\fIhttps://github.com/r-darwish/topgrade/issues\fR>.
.SH AUTHOR
\fBTopgrade\fR is maintained by Roey Dror (\[aq]r\-darwish\[aq]) and many other contributors.
You can view the full list at
<\fIhttps://github.com/r-darwish/topgrade/graphs/contributors\fR>