From 1e35a909f41389efba64d4ae6f5ef09f113555d5 Mon Sep 17 00:00:00 2001 From: eatradish Date: Thu, 17 Oct 2024 13:30:08 +0800 Subject: [PATCH] feat(oma-pm): add read `X-AOSC-Features` field with `aosc` feature --- Cargo.lock | 75 +++++++++++++-------------- i18n/en-US/oma.ftl | 6 +++ i18n/zh-CN/oma.ftl | 4 ++ oma-pm/examples/install_fish.rs | 2 +- oma-pm/src/apt.rs | 21 ++++++++ src/error.rs | 8 ++- src/subcommand/remove.rs | 14 +++-- src/subcommand/upgrade.rs | 18 ++++--- src/subcommand/utils.rs | 90 ++++++++++++++++++++++++++++++--- 9 files changed, 178 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f355372f..dffbf635 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -229,9 +229,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998282f8f49ccd6116b0ed8a4de0fbd3151697920e7c7533416d6e25e76434a7" +checksum = "103db485efc3e41214fe4fda9f3dbeae2eb9082f48fd236e6095627a9422066e" dependencies = [ "bzip2", "flate2", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" dependencies = [ "arrayvec 0.7.6", ] @@ -1060,9 +1060,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" +checksum = "cbdc8cca144dce1c4981b5c9ab748761619979e515c3d53b5df385c677d1d007" dependencies = [ "cc", "cxxbridge-flags", @@ -1072,9 +1072,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" +checksum = "c5764c3142ab44fcf857101d12c0ddf09c34499900557c764f5ad0597159d1fc" dependencies = [ "cc", "codespan-reporting", @@ -1087,15 +1087,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" +checksum = "d422aff542b4fa28c2ce8e5cc202d42dbf24702345c1fba3087b2d3f8a1b90ff" [[package]] name = "cxxbridge-macro" -version = "1.0.128" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" +checksum = "a1719100f31492cd6adeeab9a0f46cdbc846e615fdb66d7b398aa46ec7fdd06f" dependencies = [ "proc-macro2", "quote", @@ -1839,9 +1839,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -2165,9 +2165,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +checksum = "d97eb9a8e0cd5b76afea91d7eecd5cf8338cd44ced04256cf1f800474b227c52" dependencies = [ "bytemuck", "byteorder-lite", @@ -2188,9 +2188,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" dependencies = [ "byteorder-lite", "quick-error", @@ -2198,9 +2198,9 @@ dependencies = [ [[package]] name = "imgref" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" @@ -3183,9 +3183,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -3215,9 +3215,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -3462,27 +3462,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", "syn 2.0.79", @@ -3834,9 +3834,6 @@ name = "rgb" version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" -dependencies = [ - "bytemuck", -] [[package]] name = "ring" @@ -3940,9 +3937,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ "once_cell", "ring", @@ -3963,9 +3960,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -3980,9 +3977,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" diff --git a/i18n/en-US/oma.ftl b/i18n/en-US/oma.ftl index c27404c5..9264b1f8 100644 --- a/i18n/en-US/oma.ftl +++ b/i18n/en-US/oma.ftl @@ -191,3 +191,9 @@ oma-refresh-lock-dueto = { $exec } ({ $pid }) has acquired the lock to the datab oma-refresh-success-invoke = Executing Post-refresh configuration script (Post-Invoke-Success) ... autoremove-tips-1 = { $count } unneeded packages on your system may be removed, which will free up { $size } in storage space; Please use { $cmd } to view the list of packages that can be removed. autoremove-tips-2 = If you would like to keep a particular package, use { $cmd1 } to mark the package as manually installed; Otherwise, you may use { $cmd2 } to clean up the packages that are no longer needed. +essential-tips = The package { $pkg } is a key component of this system, and the system will not function properly after removing it. +yes-do-as-i-say = If you are absolutely sure, please type the following: { $input } +features-without-value = The current operation would remove key AOSC OS components. If we proceed, Will cause some system features to be unavailable. +features-tips-1 = The current operation would remove key AOSC OS components. If we proceed, the system features below will no longer be available: +features-abort = To avoid system failure, oma has aborted the operation. +features-continue-prompt = Would you like to proceed with the operation? diff --git a/i18n/zh-CN/oma.ftl b/i18n/zh-CN/oma.ftl index e2ee04cb..8bca5f39 100644 --- a/i18n/zh-CN/oma.ftl +++ b/i18n/zh-CN/oma.ftl @@ -186,3 +186,7 @@ oma-refresh-lock-dueto = { $exec } ({ $pid }) 正占用数据库锁。 oma-refresh-success-invoke = 正在执行刷新后配置脚本 (Post-Invoke-Success) ... autoremove-tips-1 = 您的系统中有 { $count } 个可清理的软件包,清理后可释放 { $size } 存储空间;请使用 { $cmd } 查阅可清理的软件包。 autoremove-tips-2 = 如需保留某个软件包,请使用 { $cmd1 } 标记保留;否则,您可以使用 { $cmd2 } 清理不再需要的软件包。 +features-without-value = 当前操作可能导致部分关键 AOSC OS 组件被移除,如继续操作,将导致某些系统特性不可用。 +features-tips-1 = 当前操作可能导致部分关键 AOSC OS 组件被移除,如继续操作,将导致如下系统特性不可用: +features-abort = 为避免系统故障,oma 已中止该操作。 +features-continue-prompt = 您确定要继续该操作吗? diff --git a/oma-pm/examples/install_fish.rs b/oma-pm/examples/install_fish.rs index 02454e9c..4f274bbf 100644 --- a/oma-pm/examples/install_fish.rs +++ b/oma-pm/examples/install_fish.rs @@ -20,7 +20,7 @@ fn main() -> Result<(), OmaAptError> { apt.resolve(false, true)?; - let op = apt.summary(|_| false)?; + let op = apt.summary(|_| false, |_| false)?; let pm = MyProgressManager::default(); diff --git a/oma-pm/src/apt.rs b/oma-pm/src/apt.rs index 8b221148..65f3a00f 100644 --- a/oma-pm/src/apt.rs +++ b/oma-pm/src/apt.rs @@ -5,6 +5,7 @@ use std::{ process::Command, }; +use ahash::HashSet; use bon::{builder, Builder}; use chrono::Local; @@ -135,6 +136,8 @@ pub enum OmaAptError { PtrIsNone(#[from] PtrIsNone), #[error(transparent)] ChecksumError(#[from] ChecksumError), + #[error("Blocking install due to features")] + Features, } pub type OmaAptResult = Result; @@ -943,7 +946,9 @@ impl OmaApt { pub fn summary( &self, how_handle_essential: impl Fn(&str) -> bool, + how_handle_features: impl Fn(&HashSet>) -> bool, ) -> OmaAptResult { + let mut features = HashSet::with_hasher(ahash::RandomState::new()); let mut install = vec![]; let mut remove = vec![]; let changes = self.cache.get_changes(true); @@ -1014,6 +1019,18 @@ impl OmaApt { return Err(OmaAptError::PkgIsEssential(name)); } + #[cfg(feature = "aosc")] + if let Some(feat) = pkg + .installed() + .and_then(|x| x.get_record("X-AOSC-Features")) + { + for i in feat.split_ascii_whitespace() { + if !features.contains(i) { + features.insert(Box::from(i)); + } + } + } + let is_purge = pkg.marked_purge(); let mut tags = vec![]; @@ -1112,6 +1129,10 @@ impl OmaApt { .map(|x| x.download_size()) .sum(); + if !features.is_empty() && !how_handle_features(&features) { + return Err(OmaAptError::Features); + } + Ok(OmaOperation { install, remove, diff --git a/src/error.rs b/src/error.rs index f2bc7aa0..255e0303 100644 --- a/src/error.rs +++ b/src/error.rs @@ -647,8 +647,8 @@ pub fn oma_apt_error_to_output(err: OmaAptError) -> OutputError { source: None, } } - OmaAptError::PkgIsEssential(s) => OutputError { - description: fl!("pkg-is-essential", name = s), + OmaAptError::PkgIsEssential(pkg) => OutputError { + description: fl!("pkg-is-essential", name = pkg), source: None, }, OmaAptError::PkgNoCandidate(s) => OutputError { @@ -735,6 +735,10 @@ pub fn oma_apt_error_to_output(err: OmaAptError) -> OutputError { source: None, }, OmaAptError::ChecksumError(e) => oma_checksum_error(e), + OmaAptError::Features => OutputError { + description: fl!("features-abort"), + source: None, + }, } } diff --git a/src/subcommand/remove.rs b/src/subcommand/remove.rs index bc931794..20d1f748 100644 --- a/src/subcommand/remove.rs +++ b/src/subcommand/remove.rs @@ -95,18 +95,24 @@ pub fn execute( /// "Yes Do as I say" steps pub fn ask_user_do_as_i_say(pkg: &str) -> anyhow::Result { let theme = ColorfulTheme::default(); + let delete = Confirm::with_theme(&theme) - .with_prompt(format!("DELETE THIS PACKAGE? PACKAGE {pkg} IS ESSENTIAL!",)) + .with_prompt(fl!("essential-tips", pkg = pkg)) .default(false) .interact() .map_err(|_| anyhow!(""))?; + if !delete { - info!("Not confirmed."); + info!("Not confirm"); return Ok(false); } + info!( - "If you are absolutely sure, please type the following: {}", - style("Do as I say!").bold() + "{}", + fl!( + "yes-do-as-i-say", + input = style("Do as I say!").bold().to_string() + ), ); if Input::::with_theme(&theme) diff --git a/src/subcommand/upgrade.rs b/src/subcommand/upgrade.rs index ee482e92..0bc24606 100644 --- a/src/subcommand/upgrade.rs +++ b/src/subcommand/upgrade.rs @@ -28,6 +28,7 @@ use crate::OmaArgs; use crate::UpgradeArgs; use super::remove::ask_user_do_as_i_say; +use super::utils::handle_features; use super::utils::handle_no_result; use super::utils::is_nothing_to_do; use super::utils::lock_oma; @@ -126,13 +127,16 @@ pub fn execute( apt.resolve(false, true)?; } - let op = apt.summary(|pkg| { - if protect_essentials { - false - } else { - ask_user_do_as_i_say(pkg).unwrap_or(false) - } - })?; + let op = apt.summary( + |pkg| { + if protect_essentials { + false + } else { + ask_user_do_as_i_say(pkg).unwrap_or(false) + } + }, + |features| handle_features(features, protect_essentials).unwrap_or(false), + )?; apt.check_disk_size(&op)?; diff --git a/src/subcommand/utils.rs b/src/subcommand/utils.rs index 3b66cae7..b82b1d35 100644 --- a/src/subcommand/utils.rs +++ b/src/subcommand/utils.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::error::Error; use std::fmt::Debug; use std::io; @@ -15,8 +16,11 @@ use crate::pb::OmaProgressBar; use crate::table::table_for_install_pending; use crate::LOCKED; use crate::RT; +use ahash::HashSet; use chrono::Local; use dialoguer::console::style; +use dialoguer::theme::ColorfulTheme; +use dialoguer::Confirm; use oma_console::indicatif::HumanBytes; use oma_console::msg; use oma_console::pager::PagerExit; @@ -43,6 +47,8 @@ use oma_utils::oma::lock_oma_inner; use oma_utils::oma::unlock_oma; use reqwest::Client; use std::fmt::Display; +use tracing::debug; +use tracing::error; use tracing::info; use tracing::warn; @@ -249,13 +255,16 @@ impl<'a> CommitRequest<'a> { apt.resolve(no_fixbroken, fix_dpkg_status)?; - let op = apt.summary(|pkg| { - if protect_essential { - false - } else { - ask_user_do_as_i_say(pkg).unwrap_or(false) - } - })?; + let op = apt.summary( + |pkg| { + if protect_essential { + false + } else { + ask_user_do_as_i_say(pkg).unwrap_or(false) + } + }, + |features| handle_features(features, protect_essential).unwrap_or(false), + )?; apt.check_disk_size(&op)?; @@ -402,3 +411,70 @@ pub(crate) fn check_unsupported_stmt(s: &str) { pub(crate) fn no_check_dbus_warn() { warn!("{}", fl!("no-check-dbus-tips")); } + +pub fn handle_features(features: &HashSet>, protect: bool) -> Result { + debug!("{:?}", features); + + let theme = ColorfulTheme::default(); + + let features = match format_features(features) { + Ok(v) => v, + Err(e) => { + warn!("{e}"); + + if protect { + error!("{}", fl!("features-without-value")); + return Ok(false); + } + + warn!("{}", fl!("features-without-value")); + + let delete = Confirm::with_theme(&theme) + .with_prompt(fl!("features-continue-prompt")) + .default(false) + .interact() + .map_err(|_| anyhow::anyhow!(""))?; + + return Ok(delete); + } + }; + + if protect { + error!("{}", fl!("features-tips-1")); + msg!("\n{}\n", features); + return Ok(false); + } + + warn!("{}", fl!("features-tips-1")); + msg!("\n{}\n", features); + + let delete = Confirm::with_theme(&theme) + .with_prompt(fl!("features-continue-prompt")) + .default(false) + .interact() + .map_err(|_| anyhow::anyhow!(""))?; + + Ok(delete) +} + +pub fn format_features(features: &HashSet>) -> anyhow::Result { + let mut res = String::new(); + let features_data = std::fs::read_to_string("/usr/share/aosc-os/features.toml")?; + let features_data: HashMap, HashMap, Box>> = + toml::from_str(&features_data)?; + + let lang = std::env::var("LANG")?; + let lang = lang.split_once('.').unwrap_or(("en_US", "")).0; + + for (index, f) in features.iter().enumerate() { + if let Some(v) = features_data.get(f) { + let text = v.get(lang).unwrap_or_else(|| v.get("en_US").unwrap()); + res.push_str(&format!(" * {}", text)); + if index != features.len() - 1 { + res.push('\n'); + } + } + } + + Ok(res) +}