Skip to content

Commit

Permalink
feat(oma-refresh)!: read apt.conf.d to filter InRelease download li…
Browse files Browse the repository at this point in the history
…st (#69)

* feat(oma-refresh)!: read `apt.conf.d` to filter InRelease download list

* feat(oma-refresh)!: allow user download all file from `apt.conf.d` config

* Fix download all components

* fix(oma-refresh): fix download contents file name on acquire-by-hash mirror

* Do not download exist files

* Fix download total size

* Fix download compress contents order

* Support `$(LANGUAGE)`

* Improve filter logic

* refactor(oma-refresh): refector split file name and ext logic

* Decompress apt source Translation file to improve `oma search` performance

* Fix language match

* feat(oma-refresh)!: add `KeepCompressed` field read feature

* feat(oma-refresh): drop download compress only feature

* clippy and fmt

* Fix foreach config tree on Debian 12
  • Loading branch information
eatradish authored Sep 1, 2024
1 parent 08bfb1b commit a4deb37
Show file tree
Hide file tree
Showing 34 changed files with 535 additions and 349 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions data/config/oma.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
[general]
# Set to false to allow removal of "Essential" packages.
protect_essentials = true
# Set to true to force refresh uncompress package database file.
refresh_pure_database = false
# Set to true to no check dbus with install pacakge
no_check_dbus = false
# set to true to do not refresh topic manifest data
Expand Down
4 changes: 2 additions & 2 deletions oma-pm/examples/download_pkgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use oma_console::{
writer::Writer,
};
use oma_fetch::{reqwest::ClientBuilder, DownloadEvent};
use oma_pm::apt::{OmaApt, OmaAptArgsBuilder, OmaAptError};
use oma_pm::apt::{AptConfig, OmaApt, OmaAptArgsBuilder, OmaAptError};

fn main() -> Result<(), OmaAptError> {
let oma_apt_args = OmaAptArgsBuilder::default().build().unwrap();
let mut apt = OmaApt::new(vec![], oma_apt_args, false)?;
let mut apt = OmaApt::new(vec![], oma_apt_args, false, AptConfig::new())?;

let pkgs = apt.select_pkg(&vec!["vscodium", "go"], false, true, true)?;
std::fs::create_dir_all("./test").unwrap();
Expand Down
4 changes: 2 additions & 2 deletions oma-pm/examples/install_fish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use oma_console::{
writer::Writer,
};
use oma_fetch::{reqwest::ClientBuilder, DownloadEvent};
use oma_pm::apt::{AptArgs, OmaApt, OmaAptArgsBuilder, OmaAptError};
use oma_pm::apt::{AptArgs, AptConfig, OmaApt, OmaAptArgsBuilder, OmaAptError};

fn main() -> Result<(), OmaAptError> {
let oma_apt_args = OmaAptArgsBuilder::default().build().unwrap();
let mut apt = OmaApt::new(vec![], oma_apt_args, false)?;
let mut apt = OmaApt::new(vec![], oma_apt_args, false, AptConfig::new())?;
let pkgs = apt.select_pkg(&vec!["fish"], false, true, true)?;

let mb = Arc::new(MultiProgress::new());
Expand Down
13 changes: 8 additions & 5 deletions oma-pm/src/apt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,13 @@ pub enum FilterMode {

impl OmaApt {
/// Create a new apt manager
pub fn new(local_debs: Vec<String>, args: OmaAptArgs, dry_run: bool) -> OmaAptResult<Self> {
let config = Self::init_config(args)?;
pub fn new(
local_debs: Vec<String>,
args: OmaAptArgs,
dry_run: bool,
config: AptConfig,
) -> OmaAptResult<Self> {
let config = Self::init_config(config, args)?;

let bus = OmaBus {
status: Status::Configing,
Expand Down Expand Up @@ -214,9 +219,7 @@ impl OmaApt {
}

/// Init apt config (before create new apt manager)
fn init_config(args: OmaAptArgs) -> OmaAptResult<AptConfig> {
let config = AptConfig::new();

fn init_config(config: AptConfig, args: OmaAptArgs) -> OmaAptResult<AptConfig> {
let sysroot = Path::new(&args.sysroot);
let sysroot = sysroot
.canonicalize()
Expand Down
3 changes: 2 additions & 1 deletion oma-refresh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "oma-refresh"
version = "0.25.0"
edition = "2021"
description = "APT repository refresh handler library"
license = "MIT"
license = "GPL-3.0-or-later"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -25,6 +25,7 @@ smallvec = "1.1"
small-map = "0.1"
oma-repo-verify = { version = "^0.1.0", path = "../oma-repo-verify", default-features = false }
ahash = "0.8.11"
oma-apt = "0.7.0"
aho-corasick = "1.1.3"

[features]
Expand Down
7 changes: 5 additions & 2 deletions oma-refresh/examples/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{

use dashmap::DashMap;
use indicatif::{MultiProgress, ProgressBar};
use oma_apt::config::Config;
use oma_console::{
pb::{global_progress_bar_style, progress_bar_style, spinner_style},
writer::Writer,
Expand All @@ -23,14 +24,16 @@ async fn main() -> Result<(), RefreshError> {
tokio::fs::create_dir_all(p).await.unwrap();
let client = ClientBuilder::new().user_agent("oma").build().unwrap();

let apt_config = Config::new();

let refresher: OmaRefresh = OmaRefreshBuilder {
client: &client,
source: PathBuf::from("/"),
limit: Some(4),
arch: dpkg_arch("/").unwrap(),
download_dir: p.to_path_buf(),
download_compress: true,
refresh_topics: true,
refresh_topics: false,
apt_config: &apt_config,
}
.into();

Expand Down
250 changes: 250 additions & 0 deletions oma-refresh/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use std::{cmp::Ordering, collections::VecDeque, env, path::Path};

use ahash::AHashMap;
use oma_apt::config::Config;
use oma_fetch::CompressFile;
use smallvec::{smallvec, SmallVec};
use tracing::debug;

use crate::inrelease::ChecksumItem;

fn get_config(config: &Config) -> Vec<(String, String)> {
let Some(tree) = config.root_tree() else {
return vec![];
};

let mut list = vec![];

let mut stack = VecDeque::new();
stack.push_back((tree, 0));

let mut depth = 0;
let mut name = "".to_string();

while let Some((node, indent)) = stack.pop_back() {
let mut k = None;
let mut v = None;

if let Some(item) = node.sibling() {
stack.push_back((item, indent));
}

if let Some(item) = node.child() {
stack.push_back((item, indent + 2));
}

if let Some(tag) = node.tag() {
match indent.cmp(&depth) {
Ordering::Less => {
let mut tmp = name.split("::").collect::<Vec<_>>();
for _ in 0..=1 {
tmp.pop();
}
name = tmp.join("::");
name.push_str("::");
name.push_str(&tag);
}
Ordering::Equal => {
let mut tmp = name.split("::").collect::<Vec<_>>();
tmp.pop();
name = tmp.join("::");
name.push_str("::");
name.push_str(&tag);
}
Ordering::Greater => {
name.push_str("::");
name.push_str(&tag);
}
}

depth = indent;
k = Some(name.strip_prefix("::").unwrap().to_string());
}

if let Some(value) = node.value() {
v = Some(value);
}

if let Some(v) = k.zip(v) {
list.push((v.0, v.1));
}
}

list
}

#[derive(Debug)]
pub struct ChecksumDownloadEntry {
pub item: ChecksumItem,
pub keep_compress: bool,
pub msg: String,
}

pub fn fiilter_download_list(
checksums: &SmallVec<[ChecksumItem; 32]>,
config: &Config,
archs: &[String],
components: &[String],
native_arch: &str,
) -> SmallVec<[ChecksumDownloadEntry; 32]> {
let mut v = smallvec![];
let config_tree = get_config(config);

let mut filter_entry = vec![];

let mut archs_contains_all = vec![];
archs_contains_all.extend_from_slice(archs);
archs_contains_all.push("all".to_string());

for (k, v) in config_tree {
if (k.starts_with("APT::Acquire::IndexTargets::deb::"))
|| (k.starts_with("Acquire::IndexTargets::deb::")) && k.ends_with("::MetaKey")
{
for a in &archs_contains_all {
for c in components {
let s = replace_arch_and_component(&v, c, a, native_arch);
let e = k
.strip_prefix("APT::")
.unwrap_or(&k)
.strip_suffix("::MetaKey")
.unwrap();

let keep_compress = config.bool(&format!("{e}::KeepCompressed"), false);

debug!("{e} keep compress: {}", keep_compress);

let msg = if let Some(match_msg) = config.get(&format!("{e}::ShortDescription"))
{
let mut s = replace_arch_and_component(&match_msg, c, a, native_arch);

if let Ok(env_lang) = env::var("LANG") {
let langs = get_matches_language(&env_lang);

if !langs.is_empty() {
s = s.replace("$(LANGUAGE)", langs[0]);
}
}

s
} else {
"Other".to_string()
};

let mut list = vec![];

if v.contains("$(LANGUAGE)") {
if let Ok(env_lang) = env::var("LANG") {
let langs = get_matches_language(&env_lang);

for i in langs {
list.push((
s.replace("$(LANGUAGE)", i),
keep_compress,
msg.clone(),
));
}
}
}

if list.is_empty() {
filter_entry.push((s, keep_compress, msg.clone()));
} else {
filter_entry.extend(list);
}
}
}
}
}

debug!("{:?}", filter_entry);

let mut map: AHashMap<&str, ChecksumDownloadEntry> = AHashMap::new();

for i in checksums {
if let Some(x) = filter_entry.iter().find(|x| {
let path = Path::new(&i.name);
let path = path.with_extension("");
let path = path.to_string_lossy();
path == x.0
}) {
if let Some(y) = map.get_mut(x.0.as_str()) {
if compress_file(&y.item.name) > compress_file(&i.name) {
continue;
} else {
*y = ChecksumDownloadEntry {
item: i.clone(),
keep_compress: x.1,
msg: x.2.clone(),
}
}
} else {
map.insert(
&x.0,
ChecksumDownloadEntry {
item: i.clone(),
keep_compress: x.1,
msg: x.2.clone(),
},
);
}
}
}

for (_, i) in map {
v.push(i);
}

debug!("{:?}", v);

v
}

fn get_matches_language(env_lang: &str) -> Vec<&str> {
let mut langs = vec![];
let env_lang = env_lang.split_once('.').map(|x| x.0).unwrap_or(env_lang);

let lang = if env_lang == "C" { "en" } else { env_lang };

langs.push(lang);

// en_US.UTF-8 => en
if let Some((a, _)) = lang.split_once('_') {
langs.push(a);
}

langs
}

fn replace_arch_and_component(
input: &str,
component: &str,
arch: &str,
native_arch: &str,
) -> String {
let mut output = input
.replace("$(COMPONENT)", component)
.replace("$(ARCHITECTURE)", arch);

if arch == native_arch {
output = output.replace("$(NATIVE_ARCHITECTURE)", arch);
}

output
}

fn compress_file(name: &str) -> CompressFile {
CompressFile::from(
Path::new(name)
.extension()
.map(|x| x.to_string_lossy())
.unwrap_or_default()
.to_string()
.as_str(),
)
}

#[test]
fn test() {
let map = get_config(&Config::new());
dbg!(map);
}
Loading

0 comments on commit a4deb37

Please sign in to comment.