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.
This commit is contained in:
parent
f479dd28ad
commit
13a97ff1d8
231
src/hpk.rs.bak
Normal file
231
src/hpk.rs.bak
Normal file
@ -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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
let dir = PathBuf::from(matches.get_one::<String>("directory").unwrap().to_string());
|
||||
let outdir = if let Some(d) = matches.get_one::<String>("output") {
|
||||
PathBuf::from(&d.to_string())
|
||||
} else {
|
||||
env::current_dir()?
|
||||
};
|
||||
let mut specs = if let Some(s) = matches.get_one::<String>("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::<String>("name") {
|
||||
specs.name = n.to_string();
|
||||
}
|
||||
if let Some(v) = matches.get_one::<String>("package-version") {
|
||||
specs.version = v.to_string().parse::<Version>()?;
|
||||
}
|
||||
if let Some(r) = matches.get_one::<u8>("release") {
|
||||
specs.release = *r;
|
||||
}
|
||||
if let Some(deps) = matches.get_many::<String>("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<P: AsRef<OsStr> + fmt::Display>(
|
||||
archive: P,
|
||||
root: P,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let (sender, receiver) = mpsc::channel::<InstallMessage>();
|
||||
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<PathBuf, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn install(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn remove(_matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn upgrade() -> Result<(), Box<dyn Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn create_specs(matches: &ArgMatches) -> Result<Specs, Box<dyn Error>> {
|
||||
let mut specs = Specs::default();
|
||||
let mut name: Option<String> = None;
|
||||
let mut version: Option<Version> = 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::<String>("name") {
|
||||
specs.name = name.to_string();
|
||||
} else if let Some(n) = name {
|
||||
specs.name = n;
|
||||
}
|
||||
if let Some(version) = matches.get_one::<String>("package-version") {
|
||||
specs.version = version.parse()?;
|
||||
} else if let Some(v) = version {
|
||||
specs.version = v;
|
||||
}
|
||||
if let Some(release) = matches.get_one::<usize>("release") {
|
||||
specs.release = *release as u8;
|
||||
}
|
||||
if let Some(deps) = matches.get_many::<String>("dependencies") {
|
||||
let deps = deps
|
||||
.map(|d| Dependency {
|
||||
name: d.to_string(),
|
||||
version: (None, None),
|
||||
})
|
||||
.collect::<Vec<Dependency>>();
|
||||
specs.dependencies = deps;
|
||||
}
|
||||
Ok(specs)
|
||||
}
|
@ -77,7 +77,11 @@ impl<T: io::Read> Installer<T> {
|
||||
Self { root, reader }
|
||||
}
|
||||
|
||||
pub fn install(self, sender: Sender<InstallMessage>) -> Result<Vec<Hooks>, InstallError> {
|
||||
pub fn install(
|
||||
self,
|
||||
hooks: &mut Vec<Hooks>,
|
||||
sender: Sender<InstallMessage>,
|
||||
) -> Result<Package, InstallError> {
|
||||
let reader = Decoder::new(self.reader)?;
|
||||
let mut archive = Archive::read(reader)?;
|
||||
sender.send(InstallMessage::ArchiveRead)?;
|
||||
@ -88,16 +92,25 @@ impl<T: io::Read> Installer<T> {
|
||||
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<T: io::Read> Installer<T> {
|
||||
sender.send(msg)?;
|
||||
Ok::<(), InstallError>(())
|
||||
})?;
|
||||
Ok(hooks.into_inner()?)
|
||||
Ok(package)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_hooks(package: &Package, root: &Path) -> Vec<Hooks> {
|
||||
let mut hooks = Vec::<Hooks>::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,
|
||||
|
Loading…
Reference in New Issue
Block a user