feat(sudo): print warning if steps were skipped due to missing sudo

This commit is contained in:
Andre Toerien
2025-09-26 15:41:38 +02:00
committed by Gideon
parent a886d20a7b
commit ad9f2c2ccb
6 changed files with 91 additions and 17 deletions

View File

@@ -90,6 +90,15 @@ impl Display for UnsupportedSudo<'_> {
}
}
#[derive(Error, Debug)]
pub struct MissingSudo();
impl Display for MissingSudo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", t!("Could not find sudo"))
}
}
#[derive(Error, Debug)]
pub struct DryRun();

View File

@@ -6,13 +6,14 @@ use std::ffi::OsStr;
use std::process::Command;
use std::sync::{LazyLock, Mutex};
use crate::executor::DryCommand;
use crate::config::Config;
use crate::error::MissingSudo;
use crate::executor::{DryCommand, Executor};
use crate::powershell::Powershell;
#[cfg(target_os = "linux")]
use crate::steps::linux::Distribution;
use crate::sudo::Sudo;
use crate::utils::require_option;
use crate::{config::Config, executor::Executor};
/// An enum telling whether Topgrade should perform dry runs or actually perform the steps.
#[derive(Clone, Copy, Debug)]
@@ -95,10 +96,11 @@ impl<'a> ExecutionContext<'a> {
}
pub fn require_sudo(&self) -> Result<&Sudo> {
require_option(
self.sudo.as_ref(),
t!("Require sudo or counterpart but not found, skip").to_string(),
)
if let Some(value) = self.sudo() {
Ok(value)
} else {
Err(MissingSudo().into())
}
}
pub fn config(&self) -> &Config {

View File

@@ -25,6 +25,7 @@ use self::config::{CommandLineArgs, Config};
use self::error::StepFailed;
#[cfg(all(windows, feature = "self-update"))]
use self::error::Upgraded;
use self::runner::StepResult;
#[allow(clippy::wildcard_imports)]
use self::steps::{remote::*, *};
use self::sudo::{Sudo, SudoKind};
@@ -224,12 +225,30 @@ fn run() -> Result<()> {
if !report.is_empty() {
print_separator(t!("Summary"));
let mut skipped_missing_sudo = false;
for (key, result) in report {
if !failed && result.failed() {
failed = true;
}
if let StepResult::SkippedMissingSudo = result {
skipped_missing_sudo = true;
}
print_result(key, result);
}
if skipped_missing_sudo {
print_warning(t!(
"\nSome steps were skipped as sudo or equivalent could not be found."
));
#[cfg(unix)]
print_warning(t!(
"Install one of `sudo`, `doas`, `pkexec`, `run0` or `please` to run these steps."
));
#[cfg(windows)]
print_warning(t!("Install gsudo or enable Windows Sudo to run these steps."));
}
}
#[cfg(target_os = "linux")]

View File

@@ -1,18 +1,20 @@
use color_eyre::eyre::Result;
use rust_i18n::t;
use std::borrow::Cow;
use std::fmt::Debug;
use tracing::debug;
use crate::ctrlc;
use crate::error::{DryRun, SkipStep};
use crate::error::{DryRun, MissingSudo, SkipStep};
use crate::execution_context::ExecutionContext;
use crate::step::Step;
use crate::terminal::{print_error, should_retry};
use crate::terminal::{print_error, print_warning, should_retry};
pub enum StepResult {
Success,
Failure,
Ignored,
SkippedMissingSudo,
Skipped(String),
}
@@ -21,7 +23,7 @@ impl StepResult {
use StepResult::*;
match self {
Success | Ignored | Skipped(_) => false,
Success | Ignored | Skipped(_) | SkippedMissingSudo => false,
Failure => true,
}
}
@@ -74,6 +76,11 @@ impl<'a> Runner<'a> {
break;
}
Err(e) if e.downcast_ref::<DryRun>().is_some() => break,
Err(e) if e.downcast_ref::<MissingSudo>().is_some() => {
print_warning(t!("Skipping step, sudo is required"));
self.push_result(key, StepResult::SkippedMissingSudo);
break;
}
Err(e) if e.downcast_ref::<SkipStep>().is_some() => {
if self.ctx.config().verbose() || self.ctx.config().show_skipped() {
self.push_result(key, StepResult::Skipped(e.to_string()));

View File

@@ -173,6 +173,11 @@ impl Terminal {
StepResult::Success => format!("{}", style(t!("OK")).bold().green()),
StepResult::Failure => format!("{}", style(t!("FAILED")).bold().red()),
StepResult::Ignored => format!("{}", style(t!("IGNORED")).bold().yellow()),
StepResult::SkippedMissingSudo => format!(
"{}: {}",
style(t!("SKIPPED")).bold().yellow(),
t!("Could not find sudo")
),
StepResult::Skipped(reason) => format!("{}: {}", style(t!("SKIPPED")).bold().blue(), reason),
}
))