2021-10-25 22:03:57 +03:00
|
|
|
use std::env::var_os;
|
|
|
|
|
use std::ffi::OsString;
|
|
|
|
|
use std::path::{Path, PathBuf};
|
2022-01-06 06:27:37 +02:00
|
|
|
|
2022-11-11 09:39:29 -05:00
|
|
|
use color_eyre::eyre;
|
2025-04-13 10:43:08 +02:00
|
|
|
use color_eyre::eyre::{Context, Result};
|
2024-10-03 12:47:35 +02:00
|
|
|
use rust_i18n::t;
|
2021-10-25 22:53:53 +03:00
|
|
|
use walkdir::WalkDir;
|
2021-10-25 22:03:57 +03:00
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
use crate::command::CommandExt;
|
2022-01-06 06:27:37 +02:00
|
|
|
use crate::error::TopgradeError;
|
|
|
|
|
use crate::execution_context::ExecutionContext;
|
2024-09-16 13:01:05 +08:00
|
|
|
use crate::utils::require_option;
|
2022-01-06 06:27:37 +02:00
|
|
|
use crate::utils::which;
|
2025-04-13 10:43:08 +02:00
|
|
|
use crate::{config, output_changed_message, Step};
|
2022-01-06 06:27:37 +02:00
|
|
|
|
2021-10-25 22:03:57 +03:00
|
|
|
fn get_execution_path() -> OsString {
|
|
|
|
|
let mut path = OsString::from("/usr/bin:");
|
|
|
|
|
path.push(var_os("PATH").unwrap());
|
|
|
|
|
path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait ArchPackageManager {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct YayParu {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
pacman: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for YayParu {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
2021-10-29 09:53:30 +03:00
|
|
|
if ctx.config().show_arch_news() {
|
2022-11-29 06:49:26 +00:00
|
|
|
ctx.run_type()
|
|
|
|
|
.execute(&self.executable)
|
|
|
|
|
.arg("-Pw")
|
|
|
|
|
.status_checked_with_codes(&[1, 0])?;
|
2021-10-29 09:53:30 +03:00
|
|
|
}
|
2021-10-25 22:03:57 +03:00
|
|
|
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.arg("--pacman")
|
|
|
|
|
.arg(&self.pacman)
|
|
|
|
|
.arg("-Syu")
|
|
|
|
|
.args(ctx.config().yay_arguments().split_whitespace())
|
|
|
|
|
.env("PATH", get_execution_path());
|
|
|
|
|
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
|
|
|
|
|
if ctx.config().cleanup() {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
command.arg("--pacman").arg(&self.pacman).arg("-Scc");
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl YayParu {
|
|
|
|
|
fn get(exec_name: &str, pacman: &Path) -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
executable: which(exec_name)?,
|
|
|
|
|
pacman: pacman.to_owned(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 14:38:18 -05:00
|
|
|
pub struct GarudaUpdate {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for GarudaUpdate {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
2023-01-29 19:19:27 +00:00
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.env("PATH", get_execution_path())
|
|
|
|
|
.env("UPDATE_AUR", "1")
|
|
|
|
|
.env("SKIP_MIRRORLIST", "1");
|
|
|
|
|
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
command.env("PACMAN_NOCONFIRM", "1");
|
|
|
|
|
}
|
|
|
|
|
command.args(ctx.config().garuda_update_arguments().split_whitespace());
|
2022-11-26 14:38:18 -05:00
|
|
|
command.status_checked()?;
|
2023-01-29 19:19:27 +00:00
|
|
|
|
2022-11-26 14:38:18 -05:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GarudaUpdate {
|
|
|
|
|
fn get() -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
executable: which("garuda-update")?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 22:03:57 +03:00
|
|
|
pub struct Trizen {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for Trizen {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.arg("-Syu")
|
|
|
|
|
.args(ctx.config().trizen_arguments().split_whitespace())
|
|
|
|
|
.env("PATH", get_execution_path());
|
|
|
|
|
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
|
|
|
|
|
if ctx.config().cleanup() {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
command.arg("-Sc");
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Trizen {
|
|
|
|
|
fn get() -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
executable: which("trizen")?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Pacman {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for Pacman {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
2024-09-16 13:01:05 +08:00
|
|
|
let sudo = require_option(ctx.sudo().as_ref(), "sudo is required to run pacman".into())?;
|
|
|
|
|
let mut command = ctx.run_type().execute(sudo);
|
2021-10-25 22:03:57 +03:00
|
|
|
command
|
|
|
|
|
.arg(&self.executable)
|
|
|
|
|
.arg("-Syu")
|
|
|
|
|
.env("PATH", get_execution_path());
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
|
|
|
|
|
if ctx.config().cleanup() {
|
2024-09-16 13:01:05 +08:00
|
|
|
let mut command = ctx.run_type().execute(sudo);
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg(&self.executable).arg("-Scc");
|
2021-12-06 14:44:20 +02:00
|
|
|
if ctx.config().yes(Step::System) {
|
2021-10-25 22:03:57 +03:00
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2021-10-25 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pacman {
|
2024-09-16 13:01:05 +08:00
|
|
|
pub fn get() -> Option<Self> {
|
2021-10-25 22:03:57 +03:00
|
|
|
Some(Self {
|
2021-12-09 15:16:42 +02:00
|
|
|
executable: which("powerpill").unwrap_or_else(|| PathBuf::from("pacman")),
|
2021-10-25 22:03:57 +03:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-22 23:28:01 +03:00
|
|
|
pub struct Pikaur {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pikaur {
|
|
|
|
|
fn get() -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
executable: which("pikaur")?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for Pikaur {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.arg("-Syu")
|
|
|
|
|
.args(ctx.config().pikaur_arguments().split_whitespace())
|
|
|
|
|
.env("PATH", get_execution_path());
|
|
|
|
|
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2022-04-22 23:28:01 +03:00
|
|
|
|
|
|
|
|
if ctx.config().cleanup() {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
command.arg("-Sc");
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
command.arg("--noconfirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2022-04-22 23:28:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-18 04:22:23 -07:00
|
|
|
pub struct Pamac {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pamac {
|
|
|
|
|
fn get() -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
executable: which("pamac")?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl ArchPackageManager for Pamac {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
|
|
|
|
|
command
|
|
|
|
|
.arg("upgrade")
|
|
|
|
|
.args(ctx.config().pamac_arguments().split_whitespace())
|
|
|
|
|
.env("PATH", get_execution_path());
|
|
|
|
|
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
command.arg("--no-confirm");
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2022-06-18 04:22:23 -07:00
|
|
|
|
|
|
|
|
if ctx.config().cleanup() {
|
|
|
|
|
let mut command = ctx.run_type().execute(&self.executable);
|
|
|
|
|
command.arg("clean");
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
command.arg("--no-confirm");
|
|
|
|
|
}
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2022-06-18 04:22:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-23 21:29:25 +00:00
|
|
|
pub struct Aura {
|
|
|
|
|
executable: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Aura {
|
2024-09-16 13:01:05 +08:00
|
|
|
fn get() -> Option<Self> {
|
2022-10-23 21:29:25 +00:00
|
|
|
Some(Self {
|
|
|
|
|
executable: which("aura")?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ArchPackageManager for Aura {
|
|
|
|
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
2024-09-16 13:01:05 +08:00
|
|
|
use semver::Version;
|
|
|
|
|
|
|
|
|
|
let version_cmd_output = ctx
|
|
|
|
|
.run_type()
|
|
|
|
|
.execute(&self.executable)
|
|
|
|
|
.arg("--version")
|
|
|
|
|
.output_checked_utf8()?;
|
|
|
|
|
// Output will be something like: "aura x.x.x\n"
|
|
|
|
|
let version_cmd_stdout = version_cmd_output.stdout;
|
|
|
|
|
let version_str = version_cmd_stdout.trim_start_matches("aura ").trim_end();
|
2025-04-13 10:43:08 +02:00
|
|
|
let version = Version::parse(version_str)
|
|
|
|
|
.wrap_err_with(|| output_changed_message!("aura --version", "invalid version"))?;
|
2024-09-16 13:01:05 +08:00
|
|
|
|
|
|
|
|
// Aura, since version 4.0.6, no longer needs sudo.
|
|
|
|
|
//
|
|
|
|
|
// https://github.com/fosskers/aura/releases/tag/v4.0.6
|
|
|
|
|
let version_no_sudo = Version::new(4, 0, 6);
|
|
|
|
|
|
|
|
|
|
if version >= version_no_sudo {
|
|
|
|
|
let mut cmd = ctx.run_type().execute(&self.executable);
|
|
|
|
|
cmd.arg("-Au")
|
2022-10-23 21:29:25 +00:00
|
|
|
.args(ctx.config().aura_aur_arguments().split_whitespace());
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
2024-09-16 13:01:05 +08:00
|
|
|
cmd.arg("--noconfirm");
|
2022-10-23 21:29:25 +00:00
|
|
|
}
|
2024-09-16 13:01:05 +08:00
|
|
|
cmd.status_checked()?;
|
2022-10-23 21:29:25 +00:00
|
|
|
|
2024-09-16 13:01:05 +08:00
|
|
|
let mut cmd = ctx.run_type().execute(&self.executable);
|
|
|
|
|
cmd.arg("-Syu")
|
|
|
|
|
.args(ctx.config().aura_pacman_arguments().split_whitespace());
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
cmd.arg("--noconfirm");
|
|
|
|
|
}
|
|
|
|
|
cmd.status_checked()?;
|
2022-10-23 21:29:25 +00:00
|
|
|
} else {
|
2024-09-16 13:01:05 +08:00
|
|
|
let sudo = crate::utils::require_option(
|
|
|
|
|
ctx.sudo().as_ref(),
|
2024-10-03 12:47:35 +02:00
|
|
|
t!("Aura(<0.4.6) requires sudo installed to work with AUR packages").to_string(),
|
2024-09-16 13:01:05 +08:00
|
|
|
)?;
|
2022-10-23 21:29:25 +00:00
|
|
|
|
2024-09-16 13:01:05 +08:00
|
|
|
let mut cmd = ctx.run_type().execute(sudo);
|
|
|
|
|
cmd.arg(&self.executable)
|
|
|
|
|
.arg("-Au")
|
|
|
|
|
.args(ctx.config().aura_aur_arguments().split_whitespace());
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
cmd.arg("--noconfirm");
|
|
|
|
|
}
|
|
|
|
|
cmd.status_checked()?;
|
|
|
|
|
|
|
|
|
|
let mut cmd = ctx.run_type().execute(sudo);
|
|
|
|
|
cmd.arg(&self.executable)
|
|
|
|
|
.arg("-Syu")
|
|
|
|
|
.args(ctx.config().aura_pacman_arguments().split_whitespace());
|
|
|
|
|
if ctx.config().yes(Step::System) {
|
|
|
|
|
cmd.arg("--noconfirm");
|
|
|
|
|
}
|
|
|
|
|
cmd.status_checked()?;
|
2022-10-23 21:29:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 03:21:35 +07:00
|
|
|
fn box_package_manager<P: 'static + ArchPackageManager>(package_manager: P) -> Box<dyn ArchPackageManager> {
|
2021-10-25 22:03:57 +03:00
|
|
|
Box::new(package_manager) as Box<dyn ArchPackageManager>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_arch_package_manager(ctx: &ExecutionContext) -> Option<Box<dyn ArchPackageManager>> {
|
2021-12-09 15:16:42 +02:00
|
|
|
let pacman = which("powerpill").unwrap_or_else(|| PathBuf::from("pacman"));
|
2021-10-25 22:03:57 +03:00
|
|
|
|
|
|
|
|
match ctx.config().arch_package_manager() {
|
2022-11-26 14:38:18 -05:00
|
|
|
config::ArchPackageManager::Autodetect => GarudaUpdate::get()
|
2022-01-27 03:21:35 +07:00
|
|
|
.map(box_package_manager)
|
2022-11-26 14:38:18 -05:00
|
|
|
.or_else(|| YayParu::get("paru", &pacman).map(box_package_manager))
|
2022-01-27 03:21:35 +07:00
|
|
|
.or_else(|| YayParu::get("yay", &pacman).map(box_package_manager))
|
2022-04-22 23:28:01 +03:00
|
|
|
.or_else(|| Trizen::get().map(box_package_manager))
|
|
|
|
|
.or_else(|| Pikaur::get().map(box_package_manager))
|
2022-06-18 04:22:23 -07:00
|
|
|
.or_else(|| Pamac::get().map(box_package_manager))
|
2024-09-16 13:01:05 +08:00
|
|
|
.or_else(|| Pacman::get().map(box_package_manager))
|
|
|
|
|
.or_else(|| Aura::get().map(box_package_manager)),
|
2022-11-26 14:38:18 -05:00
|
|
|
config::ArchPackageManager::GarudaUpdate => GarudaUpdate::get().map(box_package_manager),
|
2022-01-27 03:21:35 +07:00
|
|
|
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),
|
2024-09-16 13:01:05 +08:00
|
|
|
config::ArchPackageManager::Pacman => Pacman::get().map(box_package_manager),
|
2022-04-22 23:28:01 +03:00
|
|
|
config::ArchPackageManager::Pikaur => Pikaur::get().map(box_package_manager),
|
2022-06-18 04:22:23 -07:00
|
|
|
config::ArchPackageManager::Pamac => Pamac::get().map(box_package_manager),
|
2024-09-16 13:01:05 +08:00
|
|
|
config::ArchPackageManager::Aura => Aura::get().map(box_package_manager),
|
2021-10-25 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn upgrade_arch_linux(ctx: &ExecutionContext) -> Result<()> {
|
2022-01-06 06:27:37 +02:00
|
|
|
let package_manager =
|
2022-11-11 09:39:29 -05:00
|
|
|
get_arch_package_manager(ctx).ok_or_else(|| eyre::Report::from(TopgradeError::FailedGettingPackageManager))?;
|
2021-10-25 22:03:57 +03:00
|
|
|
package_manager.upgrade(ctx)
|
|
|
|
|
}
|
2021-10-25 22:53:53 +03:00
|
|
|
|
|
|
|
|
pub fn show_pacnew() {
|
|
|
|
|
let mut iter = WalkDir::new("/etc")
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
|
.filter(|f| {
|
|
|
|
|
f.path()
|
|
|
|
|
.extension()
|
|
|
|
|
.filter(|ext| ext == &"pacnew" || ext == &"pacsave")
|
|
|
|
|
.is_some()
|
|
|
|
|
})
|
|
|
|
|
.peekable();
|
|
|
|
|
|
|
|
|
|
if iter.peek().is_some() {
|
2024-10-03 12:47:35 +02:00
|
|
|
println!("\n{}", t!("Pacman backup configuration files found:"));
|
2021-10-25 22:53:53 +03:00
|
|
|
|
|
|
|
|
for entry in iter {
|
|
|
|
|
println!("{}", entry.path().display());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|