pkg download command and patches
This commit is contained in:
178
src/commands/download.rs
Normal file
178
src/commands/download.rs
Normal file
@ -0,0 +1,178 @@
|
||||
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;
|
||||
|
||||
#[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");
|
||||
cmd.arg("-azHX").arg("--stats");
|
||||
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())));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::utils::fs::hardcopy::hardcopy_handler;
|
||||
use crate::utils::shell::*;
|
||||
use crate::utils::parser::env::get_install_env;
|
||||
|
||||
pub fn link(repo: &String, pkgname: &String) {
|
||||
let source = crate::commands::get_aeropkg_base().join(repo).join(pkgname);
|
||||
@ -26,6 +27,12 @@ pub fn link(repo: &String, pkgname: &String) {
|
||||
}
|
||||
}
|
||||
|
||||
mount_overlay();
|
||||
let builded_pkg_md_path = &crate::commands::get_aeropkg_base().join(repo).join(pkgname).join("build-script.md");
|
||||
let full_env = get_install_env(repo, pkgname, builded_pkg_md_path, "link");
|
||||
|
||||
let mount_usr_dir_flag = full_env.get("mount_usr_dir").is_some();
|
||||
if mount_usr_dir_flag {
|
||||
mount_overlay();
|
||||
}
|
||||
shell_update()
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ pub mod disable;
|
||||
pub mod enable;
|
||||
pub mod trim;
|
||||
pub mod run;
|
||||
pub mod download;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@ -31,8 +31,8 @@ pub fn build(repo: &String, pkgname: &String) {
|
||||
if build_script == builded_script { return }
|
||||
}
|
||||
|
||||
if src.join("aeropkg.applied-build").exists() {
|
||||
let src_build = fs::read_to_string(src.join("aeropkg.applied-build")).unwrap_or("".to_string());
|
||||
if src.join(".aeropkg.applied-build").exists() {
|
||||
let src_build = fs::read_to_string(src.join(".aeropkg.applied-build")).unwrap_or("".to_string());
|
||||
if build_script == src_build {
|
||||
return
|
||||
} else {
|
||||
@ -51,21 +51,21 @@ pub fn build(repo: &String, pkgname: &String) {
|
||||
let dest_path = &dest_dir.join("build-script.md");
|
||||
fs::remove_file(dest_path).ok();
|
||||
if let Err(e) = fs::hard_link(pkg_md_path, dest_path) { panic!("Failed to copy build script to destination: {}", e) }
|
||||
fs::write(src.join("aeropkg.build-script"), &build_script).unwrap();
|
||||
fs::write(src.join(".aeropkg.applied-build"), &build_script).unwrap();
|
||||
|
||||
|
||||
let save_source_flag = full_env.get("save_source").map_or(false, |v| v == "true");
|
||||
let save_source_flag = full_env.get("save_source").is_some();
|
||||
if !save_source_flag {
|
||||
if let Err(e) = fs::remove_dir_all(&src) { panic!("Failed to remove source directory: {}", e) }
|
||||
}
|
||||
|
||||
hook(repo, pkgname);
|
||||
|
||||
let link_flag = full_env.get("disable").map_or(true, |v| v != "true");
|
||||
if !link_flag {
|
||||
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)
|
||||
}
|
||||
link(repo, pkgname)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ pub fn download(repo: &String, pkgname: &String) {
|
||||
|
||||
let src = pkgpath.join("src");
|
||||
if src.exists() {
|
||||
let src_url = fs::read_to_string(src.join("aeropkg.download-url")).unwrap_or("".to_string());
|
||||
let src_url = fs::read_to_string(src.join(".aeropkg.download-url")).unwrap_or("".to_string());
|
||||
if url == src_url { return }
|
||||
else {
|
||||
fs::remove_dir_all(&src).unwrap()
|
||||
@ -137,7 +137,10 @@ pub fn download(repo: &String, pkgname: &String) {
|
||||
.collect();
|
||||
|
||||
if dirs.len() == 1 {
|
||||
let single_dir = dirs[0].path();
|
||||
let old_path = dirs[0].path();
|
||||
let single_dir = old_path.parent().unwrap()
|
||||
.join(format!("{}.aeropkg.bkp", old_path.file_name().unwrap().to_string_lossy()));
|
||||
fs::rename(&old_path, &single_dir).unwrap();
|
||||
|
||||
for entry in fs::read_dir(&single_dir).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
@ -149,7 +152,7 @@ pub fn download(repo: &String, pkgname: &String) {
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(src.join("aeropkg.download-url"), &url).unwrap();
|
||||
fs::write(src.join(".aeropkg.download-url"), &url).unwrap();
|
||||
|
||||
run_install_script_hook(repo, "download", &full_env)
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ pub fn patch(repo: &String, pkgname: &String) {
|
||||
|
||||
let pkg_md_path = &crate::commands::get_aeropkg_var().join(format!("{}/{}.md", repo, pkgname));
|
||||
let patch_script = &parser::get_patch_script(pkg_md_path).unwrap_or("".to_string());
|
||||
if src.join("aeropkg.applied-patch").exists() {
|
||||
let src_patch = &fs::read_to_string(src.join("aeropkg.applied-patch")).unwrap_or("".to_string());
|
||||
if src.join(".aeropkg.applied-patch").exists() {
|
||||
let src_patch = &fs::read_to_string(src.join(".aeropkg.applied-patch")).unwrap_or("".to_string());
|
||||
if patch_script == src_patch { return }
|
||||
else if src_patch != "" {
|
||||
fs::remove_dir_all(src).unwrap();
|
||||
@ -23,5 +23,5 @@ pub fn patch(repo: &String, pkgname: &String) {
|
||||
let full_env = get_install_env(repo, pkgname, pkg_md_path, "patch");
|
||||
run_install_script(patch_script, src, &full_env);
|
||||
run_install_script_hook(repo, "patch", &full_env);
|
||||
fs::write(src.join("aeropkg.applied-patch"), &patch_script).unwrap()
|
||||
fs::write(src.join(".aeropkg.applied-patch"), &patch_script).unwrap()
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ fn main() {
|
||||
.subcommand(utils::command_handler::disable::command())
|
||||
.subcommand(utils::command_handler::enable::command())
|
||||
.subcommand(utils::command_handler::trim::command())
|
||||
.subcommand(utils::command_handler::download::command())
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
@ -36,6 +37,7 @@ fn main() {
|
||||
Some(("disable", sub_m)) => utils::command_handler::disable::disable(sub_m),
|
||||
Some(("enable", sub_m)) => utils::command_handler::enable::enable(sub_m),
|
||||
Some(("trim", sub_m)) => utils::command_handler::trim::trim(sub_m),
|
||||
Some(("download", sub_m)) => {utils::command_handler::download::download(sub_m); crate::commands::download::print_download_summary();},
|
||||
_ => { println!("No command provided. Use `pkg --help` for usage information.") }
|
||||
}
|
||||
}
|
||||
|
||||
50
src/utils/command_handler/download.rs
Normal file
50
src/utils/command_handler/download.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use crate::commands;
|
||||
|
||||
pub fn command() -> Command {
|
||||
Command::new("download")
|
||||
.about("Download packages from repositories")
|
||||
.arg(
|
||||
Arg::new("packages")
|
||||
.help("List of packages to download")
|
||||
.required(true)
|
||||
.num_args(1..)
|
||||
.index(1)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("repo")
|
||||
.short('r')
|
||||
.long("repo")
|
||||
.help("Repository name (e.g., gnu, musl). If omitted, determined per-package.")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("force")
|
||||
.short('d')
|
||||
.long("delete")
|
||||
.help("Pass --delete to rsync and force re-download")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("recursive")
|
||||
.short('R')
|
||||
.long("recursive")
|
||||
.help("Recursively download dependencies")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn download(matches: &ArgMatches) {
|
||||
let packages: Vec<String> = matches
|
||||
.get_many::<String>("packages")
|
||||
.unwrap_or_default()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
let force = matches.get_flag("force");
|
||||
let recursive = matches.get_flag("recursive");
|
||||
|
||||
let repo = matches.get_one::<String>("repo").map(|s| s.as_str());
|
||||
|
||||
commands::download::download_packages(repo, &packages, force, recursive);
|
||||
}
|
||||
@ -5,3 +5,4 @@ pub mod enable;
|
||||
pub mod link;
|
||||
pub mod run;
|
||||
pub mod trim;
|
||||
pub mod download;
|
||||
|
||||
@ -85,6 +85,7 @@ fn hardcopy(
|
||||
}
|
||||
fs::remove_file(destination).ok();
|
||||
}
|
||||
fs::remove_file(destination).ok();
|
||||
unix::fs::symlink(symlink_value, destination)?
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ fn extract_block(file_path: &Path, start_marker: &str, end_marker: &str) -> io::
|
||||
if line.trim() == start_marker { block_started = true; continue }
|
||||
if block_started {
|
||||
if line.trim() == end_marker { break }
|
||||
result.push(line.trim().to_string())
|
||||
result.push(line.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,25 @@ use super::*;
|
||||
pub fn get_repo_addr(repo: &str) -> String {
|
||||
let file_path = &crate::commands::get_aeropkg_etc().join("aeropkg.md");
|
||||
|
||||
let block = extract_block(file_path, "``` cfg *** Repository list and priority ***", "```").expect("Can't parse repo list block");
|
||||
let block = extract_block(file_path, "``` cfg *** .pkg files sources ***", "```").expect("Can't parse .pkg source block");
|
||||
|
||||
for line in block.lines() {
|
||||
let trimmed_line = line.trim();
|
||||
if !trimmed_line.is_empty() {
|
||||
let parts: Vec<&str> = trimmed_line.split_whitespace().collect();
|
||||
if parts.len() >= 2 && parts[0] == repo {
|
||||
return parts[1].to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Repository '{}' not found in the repository list", repo)
|
||||
}
|
||||
|
||||
pub fn get_download_source(repo: &str) -> String {
|
||||
let file_path = &crate::commands::get_aeropkg_etc().join("aeropkg.md");
|
||||
|
||||
let block = extract_block(file_path, "``` cfg *** download sources ***", "```").expect("Can't parse download sources block");
|
||||
|
||||
for line in block.lines() {
|
||||
let trimmed_line = line.trim();
|
||||
@ -21,7 +39,7 @@ pub fn get_repo_addr(repo: &str) -> String {
|
||||
pub fn get_repo_list() -> Vec<String> {
|
||||
let file_path = &crate::commands::get_aeropkg_etc().join("aeropkg.md");
|
||||
|
||||
let block = extract_block(file_path, "``` cfg *** Repository list and priority ***", "```").expect("Can't parse repo list block");
|
||||
let block = extract_block(file_path, "``` cfg *** .pkg files sources ***", "```").expect("Can't parse repo list block");
|
||||
|
||||
let mut repo_list = Vec::new();
|
||||
for line in block.lines() {
|
||||
|
||||
Reference in New Issue
Block a user