From 13a97ff1d8f6b4abed95f21c138783a9426c4f6e Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sun, 16 Apr 2023 19:15:12 -0400 Subject: [PATCH] Adjust package installer to take &mut hooks as a parameter, which will be passed to each package is it is installed with all hooks being run after all packages have finished installation. Also fix some ordering issues with package installation so that post install scripts and appstream data are not included in the number of archive members for the sake of progress bars, etc. --- src/hpk.rs.bak | 231 +++++++++++++++++++++++++++++++++++++++++++ src/installer/mod.rs | 40 ++++---- 2 files changed, 250 insertions(+), 21 deletions(-) create mode 100644 src/hpk.rs.bak diff --git a/src/hpk.rs.bak b/src/hpk.rs.bak new file mode 100644 index 0000000..17dc354 --- /dev/null +++ b/src/hpk.rs.bak @@ -0,0 +1,231 @@ +#![warn(clippy::all, clippy::pedantic)] + +use hpk::{tar::Archive, Database}; +use zstd::Decoder; + +mod cli; +use { + clap::ArgMatches, + cli::cli, + hpk::{CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs, Version}, + indicatif::{ProgressBar, ProgressStyle}, + ron::ser::{to_writer_pretty, PrettyConfig}, + std::{ + env, + error::Error, + ffi::OsStr, + fmt, + fs::File, + io::{self, BufWriter, ErrorKind}, + path::{Path, PathBuf}, + sync::mpsc, + thread, + }, +}; + +static TEMPLATE: &str = "[ {prefix} ] {wide_bar}{pos:>5.cyan}/{len:5.green}{msg:>30}"; + +fn main() -> Result<(), Box> { + let matches = cli().get_matches(); + match matches.subcommand() { + Some(("create", matches)) => create(matches)?, + Some(("init", matches)) => { + let specsfile = init(matches)?; + println!("Created specsfile {}", specsfile.display()); + if matches.get_flag("edit") { + cli::edit(specsfile.to_str().unwrap())?; + } + } + Some(("search", matches)) => search(matches)?, + Some(("install", matches)) => install(matches)?, + Some(("remove", matches)) => remove(matches)?, + Some(("upgrade", _)) => upgrade()?, + _ => {} + } + Ok(()) +} + +fn create(matches: &ArgMatches) -> Result<(), Box> { + let dir = PathBuf::from(matches.get_one::("directory").unwrap().to_string()); + let outdir = if let Some(d) = matches.get_one::("output") { + PathBuf::from(&d.to_string()) + } else { + env::current_dir()? + }; + let mut specs = if let Some(s) = matches.get_one::("specs") { + let path = PathBuf::from(s.to_string()); + let fd = File::open(path)?; + ron::de::from_reader(fd)? + } else { + Specs::default() + }; + if let Some(n) = matches.get_one::("name") { + specs.name = n.to_string(); + } + if let Some(v) = matches.get_one::("package-version") { + specs.version = v.to_string().parse::()?; + } + if let Some(r) = matches.get_one::("release") { + specs.release = *r; + } + if let Some(deps) = matches.get_many::("dependencies") { + for d in deps { + let d = Dependency { + name: d.to_string(), + version: (None, None), + }; + specs.dependencies.push(d); + } + } + let creator = Creator::new(&dir, specs.clone())?; + let (sender, receiver) = mpsc::channel(); + let len = creator.len(); + let pb = ProgressBar::new(len as u64); + pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap()); + pb.set_prefix("Adding files"); + pb.println(format!( + "Creating package {}-{}_{}.tar.zst", + &specs.name, &specs.version, &specs.release + )); + let handle = thread::spawn(move || { + for msg in receiver.iter() { + match msg { + Message::MemberAdded(s) => { + pb.set_message(s.split('/').last().unwrap().to_string()); + pb.inc(1); + } + Message::Success(_s) => { + pb.inc(1); + pb.finish_and_clear(); + } + } + } + Ok::<(), CreationError>(()) + }); + creator.create(&outdir, sender)?; + match handle.join() { + Ok(_) => { + println!("Package created successfully"); + Ok(()) + } + Err(e) => Err(io::Error::new(ErrorKind::Other, format!("{e:?}")).into()), + } +} + +fn install_local + fmt::Display>( + archive: P, + root: P, +) -> Result<(), Box> { + let (sender, receiver) = mpsc::channel::(); + let pb = ProgressBar::new(0); + pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap()); + pb.println(format!("Installing package {}", archive,)); + let fd = File::open(Path::new(&archive))?; + let decoder = Decoder::new(fd)?; + let ar = Archive::read(decoder)?; + let Some(node) = ar.get("package.ron") else { + return Err(io::Error::new(ErrorKind::Other, "not a valid package").into()); + }; + let mut package = vec![]; + node.write(&mut package)?; + let package = ron::de::from_bytes(&package)?; + let db = Database::from_file(Some(PathBuf::from(&root)))?; + let mut queue = vec![]; + package.dependencies.iter().for_each(|dep| { + if let Some(p) = db.packages.get(&dep.name) { + if !dep.satisfied(p) { + } + } + }); + let installer = Installer::new_for_file(root, archive)?; + let handle = thread::spawn(move || { + for msg in receiver.iter() { + match msg { + InstallMessage::ArchiveLen(len) => pb.set_length(len.try_into().unwrap()), + InstallMessage::LinkCreated(link) => { + pb.inc(1); + } + InstallMessage::DirectoryCreated(dir) => { + pb.inc(1); + } + InstallMessage::FileInstalled(file) => { + pb.inc(1); + } + _ => {} + } + } + }); + installer.install(sender)?; + match handle.join() { + Ok(hooks) => { + println!("hooks: {hooks:?}"); + Ok(()) + } + Err(_) => Err(io::Error::new(ErrorKind::Other, "Unknown thread error").into()), + } +} + +fn init(matches: &ArgMatches) -> Result> { + let specsfile = PathBuf::from("package.specs"); + let cfg = PrettyConfig::new().struct_names(true); + let buf = File::create(&specsfile)?; + let writer = BufWriter::new(buf); + let specs = create_specs(matches)?; + to_writer_pretty(writer, &specs, cfg)?; + Ok(specsfile) +} + +fn search(_matches: &ArgMatches) -> Result<(), Box> { + unimplemented!(); +} + +fn install(_matches: &ArgMatches) -> Result<(), Box> { + unimplemented!(); +} + +fn remove(_matches: &ArgMatches) -> Result<(), Box> { + unimplemented!(); +} + +fn upgrade() -> Result<(), Box> { + unimplemented!(); +} + +#[allow(clippy::cast_possible_truncation)] +pub fn create_specs(matches: &ArgMatches) -> Result> { + let mut specs = Specs::default(); + let mut name: Option = None; + let mut version: Option = None; + let cwd = env::current_dir()?; + if let Some(dir) = cwd.file_name().and_then(OsStr::to_str) { + if let Some((n, v)) = dir.split_once('-') { + name = Some(n.to_string()); + if let Ok(v) = v.parse() { + version = Some(v); + } + } + } + if let Some(name) = matches.get_one::("name") { + specs.name = name.to_string(); + } else if let Some(n) = name { + specs.name = n; + } + if let Some(version) = matches.get_one::("package-version") { + specs.version = version.parse()?; + } else if let Some(v) = version { + specs.version = v; + } + if let Some(release) = matches.get_one::("release") { + specs.release = *release as u8; + } + if let Some(deps) = matches.get_many::("dependencies") { + let deps = deps + .map(|d| Dependency { + name: d.to_string(), + version: (None, None), + }) + .collect::>(); + specs.dependencies = deps; + } + Ok(specs) +} diff --git a/src/installer/mod.rs b/src/installer/mod.rs index c6dfddd..eb73030 100644 --- a/src/installer/mod.rs +++ b/src/installer/mod.rs @@ -77,7 +77,11 @@ impl Installer { Self { root, reader } } - pub fn install(self, sender: Sender) -> Result, InstallError> { + pub fn install( + self, + hooks: &mut Vec, + sender: Sender, + ) -> Result { let reader = Decoder::new(self.reader)?; let mut archive = Archive::read(reader)?; sender.send(InstallMessage::ArchiveRead)?; @@ -88,16 +92,25 @@ impl Installer { let mut buf = vec![]; pr_node.write(&mut buf)?; let package: Package = ron::de::from_bytes(&buf)?; - let mut hooks = init_hooks(&package, &self.root); - let len = archive.nodes.len(); - sender.send(InstallMessage::ArchiveLen(len))?; + if let Some(ref users) = package.users { + users + .iter() + .for_each(|u| hooks.push((u.clone(), Some(self.root.to_path_buf())).into())); + } + if let Some(ref groups) = package.groups { + groups + .iter() + .for_each(|g| hooks.push((g.clone(), Some(self.root.to_path_buf())).into())); + } let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone())); db_pkgdir.push(&package.name); if !db_pkgdir.exists() { fs::create_dir_all(&db_pkgdir)?; } pop_appstream(&mut archive, &db_pkgdir)?; - pop_pinstall(&mut archive, &mut hooks, &package.name, &self.root)?; + pop_pinstall(&mut archive, hooks, &package.name, &self.root)?; + let len = archive.nodes.len(); + sender.send(InstallMessage::ArchiveLen(len))?; let mut db_file = db_pkgdir; db_file.push("package.ron"); if db_file.exists() { @@ -166,25 +179,10 @@ impl Installer { sender.send(msg)?; Ok::<(), InstallError>(()) })?; - Ok(hooks.into_inner()?) + Ok(package) } } -fn init_hooks(package: &Package, root: &Path) -> Vec { - let mut hooks = Vec::::new(); - if let Some(ref users) = package.users { - users - .iter() - .for_each(|u| hooks.push((u.clone(), Some(root.to_path_buf())).into())); - } - if let Some(ref groups) = package.groups { - groups - .iter() - .for_each(|g| hooks.push((g.clone(), Some(root.to_path_buf())).into())); - } - hooks -} - fn extract_entry( entry: &Entry, node: &Node,