Merge branch 'odin' of git.hitchhiker-linux.org:jeang3nie/hpk into odin
This commit is contained in:
commit
6037836f6b
@ -178,6 +178,7 @@ pub fn install() -> Command {
|
||||
.help("install packages into a different root")
|
||||
.short('r')
|
||||
.long("root")
|
||||
.default_value("/")
|
||||
.value_hint(ValueHint::DirPath)
|
||||
.num_args(1),
|
||||
])
|
||||
|
41
src/hpk.rs
41
src/hpk.rs
@ -1,16 +1,18 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
mod cli;
|
||||
use {
|
||||
clap::ArgMatches,
|
||||
cli::cli,
|
||||
hpk::{CreationError, Creator, Dependency, Message, Specs, Version},
|
||||
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::PathBuf,
|
||||
@ -108,6 +110,43 @@ fn create(matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn install_local<P: AsRef<OsStr> + fmt::Display>(
|
||||
archive: P,
|
||||
root: P,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
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 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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
let mut hooks = vec![];
|
||||
installer.install(&mut hooks, sender)?;
|
||||
match handle.join() {
|
||||
Ok(package) => {
|
||||
println!("hooks: {package:?}");
|
||||
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);
|
||||
|
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)
|
||||
}
|
@ -12,7 +12,7 @@ use {
|
||||
ffi::OsStr,
|
||||
fmt::Write as _,
|
||||
fs::{self, DirBuilder, File},
|
||||
io::{self, BufWriter, Write},
|
||||
io::{self, BufReader, BufWriter, Write},
|
||||
os::{
|
||||
self,
|
||||
unix::{fs::DirBuilderExt, prelude::OpenOptionsExt},
|
||||
@ -55,24 +55,32 @@ pub enum InstallMessage {
|
||||
pub struct Installer<T: io::Read> {
|
||||
/// The filesystem root under which to install this package.
|
||||
/// Normally this is "/".
|
||||
path: PathBuf,
|
||||
package: Package,
|
||||
root: PathBuf,
|
||||
reader: T,
|
||||
}
|
||||
|
||||
impl Installer<Decoder<'_, BufReader<File>>> {
|
||||
/// creates a new installer from an archive path
|
||||
pub fn new_for_file<P: AsRef<OsStr>>(root: P, archive: P) -> Result<Self, io::Error> {
|
||||
let root = Path::new(&root).to_path_buf();
|
||||
let fd = File::open(Path::new(&archive))?;
|
||||
let decoder = Decoder::new(fd)?;
|
||||
Ok(Self::new(root, decoder))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Read> Installer<T> {
|
||||
/// Creates a new installer from an open archive.
|
||||
pub fn new<P: AsRef<OsStr>>(path: P, package: Package, reader: T) -> Self {
|
||||
let path = Path::new(&path).to_path_buf();
|
||||
Self {
|
||||
path,
|
||||
package,
|
||||
reader,
|
||||
}
|
||||
/// Creates a new installer from an open archive stream
|
||||
pub fn new<P: AsRef<OsStr>>(root: P, reader: T) -> Self {
|
||||
let root = Path::new(&root).to_path_buf();
|
||||
Self { root, reader }
|
||||
}
|
||||
|
||||
pub fn install(mut self, sender: Sender<InstallMessage>) -> Result<Vec<Hooks>, InstallError> {
|
||||
let mut hooks = self.init_hooks();
|
||||
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)?;
|
||||
@ -80,15 +88,28 @@ impl<T: io::Read> Installer<T> {
|
||||
Some(node) => node,
|
||||
None => return Err(InstallError::MissingManifest),
|
||||
};
|
||||
let len = archive.nodes.len();
|
||||
sender.send(InstallMessage::ArchiveLen(len))?;
|
||||
let mut db_pkgdir = crate::get_dbdir(Some(self.path.clone()));
|
||||
db_pkgdir.push(&self.package.name);
|
||||
let mut buf = vec![];
|
||||
pr_node.write(&mut buf)?;
|
||||
let package: Package = ron::de::from_bytes(&buf)?;
|
||||
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, &self.package.name, &self.path)?;
|
||||
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() {
|
||||
@ -97,15 +118,14 @@ impl<T: io::Read> Installer<T> {
|
||||
let fd = File::create(&db_file)?;
|
||||
let writer = BufWriter::new(fd);
|
||||
pr_node.write(writer)?;
|
||||
let path = &self.path;
|
||||
let package = &self.package;
|
||||
let root = &self.root;
|
||||
let hooks = Mutex::new(hooks);
|
||||
let s = sender.clone();
|
||||
archive
|
||||
.nodes
|
||||
.par_iter()
|
||||
.try_for_each_with(s, |sender, node| {
|
||||
let mut path = path.clone();
|
||||
let mut path = root.clone();
|
||||
let fpath = node.header.file_path()?;
|
||||
if let Some(s) = node.header.prefix() {
|
||||
if s.contains("/share/man/") {
|
||||
@ -158,22 +178,7 @@ impl<T: io::Read> Installer<T> {
|
||||
sender.send(msg)?;
|
||||
Ok::<(), InstallError>(())
|
||||
})?;
|
||||
Ok(hooks.into_inner()?)
|
||||
}
|
||||
|
||||
fn init_hooks(&mut self) -> Vec<Hooks> {
|
||||
let mut hooks = Vec::<Hooks>::new();
|
||||
if let Some(ref users) = self.package.users {
|
||||
users
|
||||
.iter()
|
||||
.for_each(|u| hooks.push((u.clone(), Some(self.path.clone())).into()));
|
||||
}
|
||||
if let Some(ref groups) = self.package.groups {
|
||||
groups
|
||||
.iter()
|
||||
.for_each(|g| hooks.push((g.clone(), Some(self.path.clone())).into()));
|
||||
}
|
||||
hooks
|
||||
Ok(package)
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,6 +261,7 @@ fn pop_pinstall(
|
||||
mod error {
|
||||
use super::InstallMessage;
|
||||
use crate::{tar, Hooks};
|
||||
use ron::error::SpannedError;
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt, io,
|
||||
@ -270,6 +276,7 @@ mod error {
|
||||
pub enum InstallError {
|
||||
Fmt(fmt::Error),
|
||||
IO(io::Error),
|
||||
RonError(SpannedError),
|
||||
SendError(SendError<InstallMessage>),
|
||||
Tar(tar::Error),
|
||||
ChecksumMismatch,
|
||||
@ -291,6 +298,7 @@ mod error {
|
||||
Self::IO(e) => Some(e),
|
||||
Self::Tar(e) => Some(e),
|
||||
Self::SendError(e) => Some(e),
|
||||
Self::RonError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -326,6 +334,12 @@ mod error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpannedError> for InstallError {
|
||||
fn from(value: SpannedError) -> Self {
|
||||
Self::RonError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for InstallError {
|
||||
fn from(_value: FromUtf8Error) -> Self {
|
||||
Self::Utf8
|
||||
|
Loading…
Reference in New Issue
Block a user