2026-09-10 16:59:11 +03:00
|
|
|
use std::fs;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
use std::io::{self, Write};
|
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
|
|
|
|
|
use super::get_aeropkg_base;
|
|
|
|
|
use crate::utils::parser::{self, env::get_install_env, pkginfo, repoinfo};
|
|
|
|
|
use crate::commands::link::link;
|
2026-09-10 17:33:37 +03:00
|
|
|
use crate::commands::run::config::config;
|
2026-09-10 16:59:11 +03:00
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
struct DownloadSummary {
|
|
|
|
|
no_changes: Vec<String>,
|
|
|
|
|
updated: Vec<(String, u64)>,
|
|
|
|
|
errors: Vec<(String, String)>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_local! {
|
|
|
|
|
static SUMMARY: RefCell<DownloadSummary> = RefCell::new(DownloadSummary::default());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn print_download_summary() {
|
|
|
|
|
print!("\r\x1B[K");
|
|
|
|
|
io::stdout().flush().unwrap();
|
|
|
|
|
|
|
|
|
|
SUMMARY.with(|s| {
|
|
|
|
|
let summary = s.borrow();
|
|
|
|
|
|
|
|
|
|
if !summary.no_changes.is_empty() {
|
|
|
|
|
println!("No changes: {}", summary.no_changes.join(", "));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !summary.updated.is_empty() {
|
|
|
|
|
println!("Updated:");
|
|
|
|
|
for (pkg, count) in &summary.updated {
|
|
|
|
|
println!(" {}: {} files", pkg, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !summary.errors.is_empty() {
|
|
|
|
|
eprintln!("Errors:");
|
|
|
|
|
for (pkg, err) in &summary.errors {
|
|
|
|
|
eprintln!(" {}: {}", pkg, err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drop(summary);
|
|
|
|
|
s.replace(DownloadSummary::default());
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn download_packages(
|
|
|
|
|
repo: Option<&str>,
|
|
|
|
|
packages: &[String],
|
|
|
|
|
force: bool,
|
|
|
|
|
recursive: bool,
|
|
|
|
|
) {
|
|
|
|
|
for pkgname in packages {
|
|
|
|
|
let effective_repo = match repo {
|
|
|
|
|
Some(r) => r.to_string(),
|
|
|
|
|
None => pkginfo::get_priority_repo(pkgname),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
download(&effective_repo, pkgname, force, recursive);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn download(repo: &String, pkgname: &String, force: bool, recursive: bool) {
|
|
|
|
|
let pkg_md_path = &crate::commands::get_aeropkg_var().join(format!("{}/{}.md", repo, pkgname));
|
|
|
|
|
|
|
|
|
|
check_build_dependency(repo, pkg_md_path, force, recursive);
|
|
|
|
|
check_run_dependency(pkg_md_path, force, recursive);
|
|
|
|
|
|
|
|
|
|
let source = repoinfo::get_download_source(repo);
|
|
|
|
|
|
|
|
|
|
if source.is_empty() {
|
|
|
|
|
let msg = format!("Download source not found for repo '{}'", repo);
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().errors.push((pkgname.clone(), msg)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let remote_path = format!("{}/{}/", source.trim_end_matches('/'), pkgname);
|
|
|
|
|
let local_path = format!("/pkg/{}/{}", repo, pkgname);
|
|
|
|
|
|
|
|
|
|
if let Err(e) = std::fs::create_dir_all(&local_path) {
|
|
|
|
|
let msg = format!("Failed to create directory {}: {}", local_path, e);
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().errors.push((pkgname.clone(), msg)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let local_path_sync = format!("{}/", local_path);
|
|
|
|
|
|
|
|
|
|
print!("\r\x1B[KDownloading {}", pkgname);
|
|
|
|
|
io::stdout().flush().unwrap();
|
|
|
|
|
|
|
|
|
|
let mut cmd = Command::new("rsync");
|
2026-09-10 18:45:16 +03:00
|
|
|
cmd.arg("-azH").arg("--stats");
|
2026-09-10 16:59:11 +03:00
|
|
|
if force { cmd.arg("--delete"); }
|
|
|
|
|
cmd.arg(&remote_path);
|
|
|
|
|
cmd.arg(&local_path_sync);
|
|
|
|
|
|
|
|
|
|
let result = cmd.output();
|
|
|
|
|
|
|
|
|
|
match result {
|
|
|
|
|
Ok(output) => {
|
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
|
|
|
|
|
let transferred = stdout
|
|
|
|
|
.lines()
|
|
|
|
|
.find(|line| line.contains("Number of regular files transferred:"))
|
|
|
|
|
.and_then(|line| line.split(':').nth(1))
|
|
|
|
|
.map(|s| s.trim().parse::<u64>().unwrap_or(0))
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
if transferred == 0 && output.status.success() {
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().no_changes.push(pkgname.clone()));
|
|
|
|
|
} else if !output.status.success() {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
let err_msg = stderr.lines().rev().find(|l| !l.trim().is_empty()).unwrap_or("unknown error").to_string();
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().errors.push((pkgname.clone(), err_msg)));
|
|
|
|
|
} else {
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().updated.push((pkgname.clone(), transferred)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
SUMMARY.with(|s| s.borrow_mut().errors.push((pkgname.clone(), e.to_string())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-10 17:33:37 +03:00
|
|
|
config(repo, pkgname);
|
2026-09-10 16:59:11 +03:00
|
|
|
let full_env = get_install_env(repo, pkgname, pkg_md_path, "build");
|
|
|
|
|
if full_env.get("disable").is_some() {
|
|
|
|
|
let _ = fs::File::create(crate::commands::get_aeropkg_base().join(repo).join(pkgname).join("disabled"));
|
|
|
|
|
} else {
|
|
|
|
|
link(repo, pkgname)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn check_build_dependency(repo: &String, pkg_md_path: &Path, force: bool, recursive: bool) {
|
|
|
|
|
let deps = match parser::get_build_deps(pkg_md_path) {
|
|
|
|
|
Ok(deps) => deps,
|
|
|
|
|
Err(_) => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for dep in deps.split_whitespace() {
|
|
|
|
|
let dep = dep.trim();
|
|
|
|
|
if dep.is_empty() { continue }
|
|
|
|
|
|
|
|
|
|
if !get_aeropkg_base().join(repo).join(dep).exists() || recursive {
|
|
|
|
|
download(repo, &dep.to_string(), force, recursive);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn check_run_dependency(pkg_md_path: &Path, force: bool, recursive: bool) {
|
|
|
|
|
let deps = match parser::get_run_deps(pkg_md_path) {
|
|
|
|
|
Ok(deps) => deps,
|
|
|
|
|
Err(_) => return,
|
|
|
|
|
};
|
|
|
|
|
let repo_list = parser::repoinfo::get_repo_list();
|
|
|
|
|
|
|
|
|
|
for dep in deps.split_whitespace() {
|
|
|
|
|
let dep = dep.trim();
|
|
|
|
|
if dep.is_empty() { continue }
|
|
|
|
|
|
|
|
|
|
let found_repo = repo_list.iter().find(|repo_name| {
|
|
|
|
|
get_aeropkg_base().join(repo_name).join(dep).exists()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
match found_repo {
|
|
|
|
|
Some(found) if recursive => {
|
|
|
|
|
download(found, &dep.to_string(), force, recursive);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
download(&pkginfo::get_priority_repo(&dep.to_string()), &dep.to_string(), force, recursive);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|