feat: added command functionality to install
This commit is contained in:
parent
3a21d12c07
commit
092c616ca4
20 changed files with 464 additions and 76 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 didier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -5,12 +5,12 @@ use toml;
|
||||||
|
|
||||||
use manifest::{self, Manifest};
|
use manifest::{self, Manifest};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct PKGR {
|
pub struct PKGR {
|
||||||
pub bootstrap: Option<Bootstrap>,
|
pub bootstrap: Option<Bootstrap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Bootstrap {
|
pub struct Bootstrap {
|
||||||
pub check_installed_commands: Vec<String>,
|
pub check_installed_commands: Vec<String>,
|
||||||
pub commands: Vec<String>,
|
pub commands: Vec<String>,
|
||||||
|
|
|
@ -11,6 +11,9 @@ To find the latest we'll use the `latest` symlink:
|
||||||
install_start = "2021-01-01T00:00:00Z" # install_start is the time the pkgr started installing the package
|
install_start = "2021-01-01T00:00:00Z" # install_start is the time the pkgr started installing the package
|
||||||
install_end = "2021-01-01T00:00:00Z" # install_end is the time the pkgr finished installing the package
|
install_end = "2021-01-01T00:00:00Z" # install_end is the time the pkgr finished installing the package
|
||||||
install_by = "pkgr" # install_by is the name of the package manager that installed the package
|
install_by = "pkgr" # install_by is the name of the package manager that installed the package
|
||||||
|
package_identifier = "uri"
|
||||||
|
package_id = "https://..."
|
||||||
|
|
||||||
|
|
||||||
[old_versions] # This logs old versions of the package (that were installed before)
|
[old_versions] # This logs old versions of the package (that were installed before)
|
||||||
9 = "../9/pkgr.pkg"
|
9 = "../9/pkgr.pkg"
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Bin {
|
pub struct Bin {
|
||||||
pub root: String,
|
pub root: String,
|
||||||
pub checksums: HashMap<String, String>,
|
pub checksums: HashMap<String, String>,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Build {
|
pub struct Build {
|
||||||
build_script: String,
|
build_script: String,
|
||||||
install_script: String,
|
install_script: String,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FS {
|
pub struct FS {
|
||||||
pub config: Option<String>,
|
pub config: Option<String>,
|
||||||
pub data: Option<String>,
|
pub data: Option<String>,
|
||||||
|
|
|
@ -8,9 +8,9 @@ pub mod fs;
|
||||||
pub mod package;
|
pub mod package;
|
||||||
pub mod pkgr;
|
pub mod pkgr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Manifest<P = Option<pkgr::PKGR>> {
|
pub struct Manifest<P: Clone = Option<pkgr::PKGR>> {
|
||||||
pub package: package::Package,
|
pub package: package::Package,
|
||||||
pub dependencies: HashMap<String, String>,
|
pub dependencies: HashMap<String, String>,
|
||||||
pub fs: fs::FS,
|
pub fs: fs::FS,
|
||||||
|
@ -19,7 +19,7 @@ pub struct Manifest<P = Option<pkgr::PKGR>> {
|
||||||
pub pkgr: P,
|
pub pkgr: P,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Manifest<T> {
|
impl<P: Clone> Manifest<P> {
|
||||||
pub fn valid(&self) -> bool {
|
pub fn valid(&self) -> bool {
|
||||||
if self.bin.is_none() && self.build.is_none() {
|
if self.bin.is_none() && self.build.is_none() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -28,6 +28,13 @@ impl<T> Manifest<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Manifest {
|
||||||
|
type Error = toml::de::Error;
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
toml::from_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for Manifest {
|
impl FromStr for Manifest {
|
||||||
type Err = toml::de::Error;
|
type Err = toml::de::Error;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
@ -36,7 +43,7 @@ impl FromStr for Manifest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Manifest {
|
impl Default for Manifest {
|
||||||
fn default() -> Self {
|
fn default() -> Manifest {
|
||||||
Manifest {
|
Manifest {
|
||||||
package: package::Package::default(),
|
package: package::Package::default(),
|
||||||
dependencies: HashMap::default(),
|
dependencies: HashMap::default(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum PackageType {
|
pub enum PackageType {
|
||||||
#[serde(rename = "application")]
|
#[serde(rename = "application")]
|
||||||
Application,
|
Application,
|
||||||
|
@ -12,7 +12,7 @@ pub enum PackageType {
|
||||||
Meta,
|
Meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Package {
|
pub struct Package {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct PKGR {}
|
pub struct PKGR {}
|
||||||
|
|
||||||
|
impl Display for PKGR {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "")
|
||||||
|
}
|
||||||
|
}
|
107
package.example.toml
Normal file
107
package.example.toml
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#* = required.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "packager" #*
|
||||||
|
description = "A package installation tool" #*
|
||||||
|
|
||||||
|
## package.version
|
||||||
|
# needs to be an int and be incremented every time
|
||||||
|
# if you use a custom version system and want to use that, you can use the tag system!
|
||||||
|
# the highest package.version on the server will be given the tag "latest"
|
||||||
|
version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
|
||||||
|
|
||||||
|
## package.tags
|
||||||
|
# you can add tags to a package to specify kinds
|
||||||
|
# there are some special tags:
|
||||||
|
# - latest
|
||||||
|
# automatically set on the last published version
|
||||||
|
# previous version will lose this tag
|
||||||
|
# - oldest
|
||||||
|
# assigned to the first version.
|
||||||
|
tags = [ "prod", "pkgr-spec-1" ]
|
||||||
|
|
||||||
|
## package.type
|
||||||
|
# Supported types:
|
||||||
|
# - application
|
||||||
|
# this type specifies that this package is an application.
|
||||||
|
# - library
|
||||||
|
# this type specifies that this package is a library.
|
||||||
|
# - meta
|
||||||
|
# this type specifies that this package does not install anything upon the system except for dependencies.
|
||||||
|
type = "application"
|
||||||
|
|
||||||
|
arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
||||||
|
|
||||||
|
## dependencies
|
||||||
|
# you may use the following syntax
|
||||||
|
# "<version/tag>" or "<version/tag>,<version/tag>,..."
|
||||||
|
[dependencies]
|
||||||
|
#example = { arch = "x86_64", tags = [ "stable" ], version = "v1" }
|
||||||
|
exnmbr2 = "v1,stable"
|
||||||
|
|
||||||
|
## fs
|
||||||
|
# specify some directories for ease of use
|
||||||
|
[fs]
|
||||||
|
## fs.config
|
||||||
|
# specifiy the config path
|
||||||
|
config = "/etc/packager"
|
||||||
|
## fs.data
|
||||||
|
# specify the data path
|
||||||
|
data = "/var/lib/packager"
|
||||||
|
## replace "pacakger" with your package name to find the default value.
|
||||||
|
|
||||||
|
## bin
|
||||||
|
# Used for systems that don't want to build pkgs.
|
||||||
|
[bin] # binary files root
|
||||||
|
|
||||||
|
## bin.root
|
||||||
|
# ** RELATIVE TO PACKAGE ROOT **
|
||||||
|
# bin.root specifies the root of the installed tree.
|
||||||
|
# anything in here will be overlayed on top of the system.
|
||||||
|
root = "/root" #*
|
||||||
|
|
||||||
|
## bin.checksums
|
||||||
|
# ** KEYS are relative to BIN.ROOT **
|
||||||
|
# ** VALUES are relative to PKG ROOT **
|
||||||
|
# checksums is used to perform checksums before install
|
||||||
|
# values may be paths or a uri. You may include variables.
|
||||||
|
# supported variables:
|
||||||
|
# - @name
|
||||||
|
# - @version
|
||||||
|
# - @display_version
|
||||||
|
[bin.checksums]
|
||||||
|
"/usr/bin/pkgr" = "https://ixvd.net/checksums/packager/@version"
|
||||||
|
|
||||||
|
## build
|
||||||
|
# Scripts will be copied to a custom root.
|
||||||
|
# After the pacakge is built, the install script is ran.
|
||||||
|
# After the install script the build directory will be deleted and an CTREE (changed tree) will be generated.
|
||||||
|
# Then the CTREE will be used to copy the files over.
|
||||||
|
[build]
|
||||||
|
build_script = "scripts/build" # relative to pkg
|
||||||
|
install_script = "scripts/install" # relative to pkg
|
||||||
|
|
||||||
|
[build.dependencies]
|
||||||
|
base = "latest,stable" # selected by default
|
||||||
|
|
||||||
|
## pkgr.*
|
||||||
|
# packager is the official client but you may use other clients supporting the "pkgr v1 spec".
|
||||||
|
# other clients may offer extra functionality this must be put under "pkgr.*"
|
||||||
|
[pkgr]
|
||||||
|
|
||||||
|
## pkgr.bootstrap
|
||||||
|
# This section is used for bootpkg. An edition of packager that bootstraps the full version.
|
||||||
|
# This exists so that packager is easy to install on anything!
|
||||||
|
# and only 1 release channel for pkgr
|
||||||
|
[pkgr.bootstrap]
|
||||||
|
## any non-zero = installed
|
||||||
|
check_installed_commands = [
|
||||||
|
"sh scripts/check_installed"
|
||||||
|
]
|
||||||
|
|
||||||
|
# any non-zero = fail
|
||||||
|
commands = [
|
||||||
|
"sh scripts/bootstrap/download_latest @version /tmp/pkgr.pkg",
|
||||||
|
"sh scripts/bootstrap/dirty_install /tmp/pkgr.pkg",
|
||||||
|
"sh scripts/check_installed"
|
||||||
|
]
|
38
package.toml
38
package.toml
|
@ -1,33 +1,8 @@
|
||||||
#* = required.
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "packager" #*
|
name = "packager" #*
|
||||||
description = "A package installation tool" #*
|
description = "A package installation tool" #*
|
||||||
|
|
||||||
## package.version
|
|
||||||
# needs to be an int and be incremented every time
|
|
||||||
# if you use a custom version system and want to use that, you can use the tag system!
|
|
||||||
# the highest package.version on the server will be given the tag "latest"
|
|
||||||
version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
|
version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
|
||||||
|
|
||||||
## package.tags
|
|
||||||
# you can add tags to a package to specify kinds
|
|
||||||
# there are some special tags:
|
|
||||||
# - latest
|
|
||||||
# automatically set on the last published version
|
|
||||||
# previous version will lose this tag
|
|
||||||
# - oldest
|
|
||||||
# assigned to the first version.
|
|
||||||
tags = [ "prod", "pkgr-spec-1" ]
|
tags = [ "prod", "pkgr-spec-1" ]
|
||||||
|
|
||||||
## package.type
|
|
||||||
# Supported types:
|
|
||||||
# - application
|
|
||||||
# this type specifies that this package is an application.
|
|
||||||
# - library
|
|
||||||
# this type specifies that this package is a library.
|
|
||||||
# - meta
|
|
||||||
# this type specifies that this package does not install anything upon the system except for dependencies.
|
|
||||||
type = "application"
|
type = "application"
|
||||||
|
|
||||||
arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
||||||
|
@ -36,19 +11,6 @@ arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
||||||
# you may use the following syntax
|
# you may use the following syntax
|
||||||
# "<version/tag>" or "<version/tag>,<version/tag>,..."
|
# "<version/tag>" or "<version/tag>,<version/tag>,..."
|
||||||
[dependencies]
|
[dependencies]
|
||||||
#example = { arch = "x86_64", tags = [ "stable" ], version = "v1" }
|
|
||||||
exnmbr2 = "v1,stable"
|
|
||||||
|
|
||||||
## fs
|
|
||||||
# specify some directories for ease of use
|
|
||||||
[fs]
|
|
||||||
## fs.config
|
|
||||||
# specifiy the config path
|
|
||||||
config = "/etc/packager"
|
|
||||||
## fs.data
|
|
||||||
# specify the data path
|
|
||||||
data = "/var/lib/packager"
|
|
||||||
## replace "pacakger" with your package name to find the default value.
|
|
||||||
|
|
||||||
## bin
|
## bin
|
||||||
# Used for systems that don't want to build pkgs.
|
# Used for systems that don't want to build pkgs.
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PKGFile {
|
pub struct PKGFile {
|
||||||
pub manifest: String,
|
pub manifest: String,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PKGFile {
|
||||||
|
pub fn new(manifest: String, data: Vec<u8>) -> PKGFile {
|
||||||
|
PKGFile { manifest, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PKGFile {
|
||||||
|
fn default() -> PKGFile {
|
||||||
|
PKGFile {
|
||||||
|
manifest: String::new(),
|
||||||
|
data: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for PKGFile {
|
impl TryFrom<Vec<u8>> for PKGFile {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pkgr"
|
name = "pkgr"
|
||||||
|
description = "A package manager and build tool."
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = [
|
||||||
|
"Didier <dev@faulty.nl>"
|
||||||
|
]
|
||||||
|
license-file = "../LICENSE"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
manifest = { path = "../manifest" }
|
||||||
|
pkgfile = { path = "../pkgfile" }
|
||||||
fern = "0.6.2"
|
fern = "0.6.2"
|
||||||
log = "0.4.19"
|
log = "0.4.19"
|
||||||
regex = "1.9.1"
|
regex = "1.9.1"
|
||||||
manifest = { path = "../manifest" }
|
|
||||||
clap = { version = "4.3.12", features = ["derive"] }
|
clap = { version = "4.3.12", features = ["derive"] }
|
||||||
colored = "2.0.4"
|
colored = "2.0.4"
|
||||||
|
toml = "0.7.6"
|
||||||
|
serde = { version = "1.0.171", features = ["derive"] }
|
||||||
|
libc = "0.2.80"
|
||||||
|
reqwest = { version = "0.11.18", features = ["blocking"] }
|
||||||
|
tar = "0.4.39"
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
use std::process::exit;
|
||||||
use crate::package::identifier::PackageIdentifier;
|
use crate::package::identifier::PackageIdentifier;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use log::error;
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use manifest::Manifest;
|
||||||
|
use crate::package::builder::{InstallType, PackageInstaller};
|
||||||
|
use crate::package::fetch::fetch_package;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(name = "pkgr", version)]
|
#[clap(name = "pkgr", version)]
|
||||||
|
@ -33,6 +37,8 @@ pub enum Command {
|
||||||
/// Update packages on the system
|
/// Update packages on the system
|
||||||
Update,
|
Update,
|
||||||
#[command(hide = true)]
|
#[command(hide = true)]
|
||||||
|
Debug,
|
||||||
|
#[command(hide = true)]
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,20 +47,47 @@ impl Command {
|
||||||
match self {
|
match self {
|
||||||
Command::Install {
|
Command::Install {
|
||||||
build,
|
build,
|
||||||
package_identifier: _,
|
package_identifier,
|
||||||
} => {
|
} => {
|
||||||
if *build {
|
warn!("Installer does not run in isolation.");
|
||||||
error!("Build is not yet implemented.");
|
|
||||||
} else {
|
let start = std::time::Instant::now();
|
||||||
error!("Install is not yet implemented.");
|
let unix_start = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
trace!("Fetching package: {}", package_identifier);
|
||||||
|
let pkgfile = fetch_package(package_identifier.clone()).unwrap();
|
||||||
|
debug!("size: manifest({}kb) data({}kb)", pkgfile.manifest.len() / 1024, pkgfile.data.len() / 1024);
|
||||||
|
trace!("parsing manifest");
|
||||||
|
let manifest = Manifest::try_from(pkgfile.manifest.clone()).unwrap();
|
||||||
|
debug!("manifest pkg name: {}", manifest.package.name);
|
||||||
|
trace!("creating installer");
|
||||||
|
let installer = PackageInstaller::new(manifest.clone(), pkgfile, if *build { InstallType::Build } else { InstallType::Bin });
|
||||||
|
|
||||||
|
trace!("starting install");
|
||||||
|
match installer.install() {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Sucessfully installed: {}", &manifest.package.name);
|
||||||
|
()
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Remove { package_identifier: _, index } => {
|
let unix_end = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
|
||||||
if *index {
|
let end = std::time::Instant::now();
|
||||||
error!("Index removal is not yet implemented.");
|
// float ms
|
||||||
} else {
|
let duration = (end - start).as_nanos() as f64;
|
||||||
error!("Remove is not yet implemented.");
|
info!("Install took: {}ms", (duration / 1_000_000.0));
|
||||||
}
|
}
|
||||||
|
Command::Remove { package_identifier, index } => {
|
||||||
|
if let PackageIdentifier::URI(_) = package_identifier {
|
||||||
|
error!("URI is unsupported when removing applications.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
info!("Index: {}", index);
|
||||||
|
info!("Package identifier: {}", package_identifier);
|
||||||
}
|
}
|
||||||
Command::List { installed: _ } => {
|
Command::List { installed: _ } => {
|
||||||
error!("List is not yet implemented.");
|
error!("List is not yet implemented.");
|
||||||
|
@ -62,6 +95,19 @@ impl Command {
|
||||||
Command::Update => {
|
Command::Update => {
|
||||||
error!("Update is not yet implemented.");
|
error!("Update is not yet implemented.");
|
||||||
}
|
}
|
||||||
|
Command::Debug => {
|
||||||
|
trace!("Trace message.");
|
||||||
|
debug!("Debug message.");
|
||||||
|
info!("Info message.");
|
||||||
|
warn!("Warning message.");
|
||||||
|
error!("Error message.");
|
||||||
|
info!("");
|
||||||
|
info!("PKGR VERSION: {}", env!("CARGO_PKG_VERSION"));
|
||||||
|
info!("PKGR AUTHORS: {}", env!("CARGO_PKG_AUTHORS"));
|
||||||
|
info!("PKGR DESCRIPTION: {}", env!("CARGO_PKG_DESCRIPTION"));
|
||||||
|
info!("");
|
||||||
|
info!("PKGR_LOG_LEVEL: {}", std::env::var("PKGR_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()));
|
||||||
|
}
|
||||||
Command::None => {
|
Command::None => {
|
||||||
error!("No command was specified.");
|
error!("No command was specified.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,35 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use crate::commands::Cli;
|
use crate::commands::Cli;
|
||||||
use log::{debug, info, SetLoggerError, trace};
|
use log::{SetLoggerError, trace};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod package;
|
mod package;
|
||||||
|
mod tmp;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
setup_logger().expect("Unable to setup logger.");
|
setup_logger().expect("Unable to setup logger.");
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
if unsafe { libc::getuid() } != 0 {
|
||||||
|
use log::error;
|
||||||
|
error!("pkgr must be run as root.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trace!("Parsing command line arguments...");
|
trace!("Parsing command line arguments...");
|
||||||
let c = Cli::parse();
|
let c = Cli::parse();
|
||||||
debug!("Command line arguments parsed.");
|
trace!("Command line arguments: {:?}", c);
|
||||||
|
|
||||||
trace!("Executing command...");
|
trace!("Executing command...");
|
||||||
c.command.execute();
|
c.command.execute();
|
||||||
debug!("Command executed.");
|
trace!("Command executed.");
|
||||||
|
|
||||||
trace!("Exiting...");
|
trace!("Exiting...");
|
||||||
info!("Done.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_logger() -> Result<(), SetLoggerError> {
|
fn setup_logger() -> Result<(), SetLoggerError> {
|
||||||
|
@ -27,16 +37,30 @@ fn setup_logger() -> Result<(), SetLoggerError> {
|
||||||
.format(|out, message, record| {
|
.format(|out, message, record| {
|
||||||
out.finish(format_args!(
|
out.finish(format_args!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
match record.level().to_string().chars().nth(0).unwrap_or('I') {
|
// Some logic so messages look nice
|
||||||
'T' => "##".cyan(),
|
if message.to_string().len() > 0 {
|
||||||
'D' => "::".yellow(),
|
match record
|
||||||
'E' | 'W' => "!!".red(),
|
.level()
|
||||||
|
.to_string()
|
||||||
|
.chars()
|
||||||
|
.nth(0)
|
||||||
|
.unwrap_or('T')
|
||||||
|
{
|
||||||
|
'T' => "[TRACE]".cyan(),
|
||||||
|
'D' => "??".green(),
|
||||||
|
'I' => "=>".blue(),
|
||||||
|
'W' => "##".yellow(),
|
||||||
|
'E' => "!!".red(),
|
||||||
_ => "**".blue(),
|
_ => "**".blue(),
|
||||||
},
|
}.to_string()
|
||||||
|
} else { "".to_string() },
|
||||||
message.to_string().bright_white()
|
message.to_string().bright_white()
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.level(env::var("PKGR_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()).parse().unwrap_or(log::LevelFilter::Info))
|
.level(env::var("PKGR_LOG_LEVEL")
|
||||||
|
.unwrap_or_else(|_| "info".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(log::LevelFilter::Info))
|
||||||
.chain(std::io::stdout())
|
.chain(std::io::stdout())
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
71
pkgr/src/package/builder/mod.rs
Normal file
71
pkgr/src/package/builder/mod.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use libc::fork;
|
||||||
|
use log::{debug, trace};
|
||||||
|
use manifest::Manifest;
|
||||||
|
use pkgfile::PKGFile;
|
||||||
|
use crate::tmp::TempDir;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InstallType {
|
||||||
|
Build,
|
||||||
|
Bin
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InstallError {
|
||||||
|
BuildError(String),
|
||||||
|
BinError(String),
|
||||||
|
InstallError
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for InstallError {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
InstallError::BuildError(e) => format!("Build error: {}", e),
|
||||||
|
InstallError::BinError(e) => format!("Bin error: {}", e),
|
||||||
|
InstallError::InstallError => "Install error".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PackageInstaller {
|
||||||
|
manifest: Manifest,
|
||||||
|
pkgfile: PKGFile,
|
||||||
|
install_type: InstallType
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageInstaller {
|
||||||
|
pub fn new(m: Manifest, p: PKGFile, i: InstallType) -> PackageInstaller {
|
||||||
|
PackageInstaller {
|
||||||
|
manifest: m,
|
||||||
|
pkgfile: p,
|
||||||
|
install_type: i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_to<S: Into<String>>(&self, path: S) -> Result<(), String> {
|
||||||
|
tar::Archive::new(self.pkgfile.data.as_slice())
|
||||||
|
.unpack(path.into())
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bin(&self) -> Result<(), String> {
|
||||||
|
let mut tmpdir = TempDir::default();
|
||||||
|
tmpdir.push(&self.manifest.package.name);
|
||||||
|
trace!("extracting package into: {}", tmpdir.to_string());
|
||||||
|
self.extract_to(tmpdir.to_string())?;
|
||||||
|
debug!("extracted package in: {}", tmpdir.to_string());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(&self) -> Result<(), String> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(&self) -> Result<(), InstallError> {
|
||||||
|
match self.install_type {
|
||||||
|
InstallType::Bin => self.bin().map_err(|e| InstallError::BinError(e)),
|
||||||
|
InstallType::Build => self.build().map_err(|e| InstallError::BuildError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
pkgr/src/package/fetch.rs
Normal file
59
pkgr/src/package/fetch.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use log::warn;
|
||||||
|
use pkgfile::PKGFile;
|
||||||
|
use crate::package::identifier::PackageIdentifier;
|
||||||
|
use reqwest::blocking::get;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FetchError {
|
||||||
|
HTTPError(reqwest::Error),
|
||||||
|
IOError(std::io::Error),
|
||||||
|
ParseError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FetchError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FetchError::HTTPError(e) => write!(f, "HTTP Error: {}", e),
|
||||||
|
FetchError::IOError(e) => write!(f, "IO Error: {}", e),
|
||||||
|
FetchError::ParseError => write!(f, "Parse Error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for FetchError {}
|
||||||
|
|
||||||
|
pub fn fetch_package(package_identifier: PackageIdentifier) -> Result<PKGFile, FetchError> {
|
||||||
|
match package_identifier {
|
||||||
|
PackageIdentifier::Path(path) => {
|
||||||
|
std::fs::read(path)
|
||||||
|
.map_err(|e| FetchError::IOError(e)).and_then(|bytes| {
|
||||||
|
PKGFile::try_from(bytes).map_err(|_| FetchError::ParseError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PackageIdentifier::URI(uri) => {
|
||||||
|
// get file contents as bytes
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
match get(&uri) {
|
||||||
|
Ok(mut response) => {
|
||||||
|
match response.take(1024 * 1024).read_to_end(&mut bytes) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => return Err(FetchError::IOError(e)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => return Err(FetchError::HTTPError(e)),
|
||||||
|
};
|
||||||
|
// parse bytes as PKGFile
|
||||||
|
match PKGFile::try_from(bytes) {
|
||||||
|
Ok(pkgfile) => Ok(pkgfile),
|
||||||
|
Err(e) => Err(FetchError::ParseError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PackageIdentifier::PackageLocator(package_locator) => {
|
||||||
|
Ok(PKGFile::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ impl Error for PackageIdentifierError {}
|
||||||
pub enum PackageIdentifier {
|
pub enum PackageIdentifier {
|
||||||
PackageLocator(PackageLocator),
|
PackageLocator(PackageLocator),
|
||||||
URI(String),
|
URI(String),
|
||||||
|
Path(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for PackageIdentifier {
|
impl std::fmt::Display for PackageIdentifier {
|
||||||
|
@ -33,6 +34,7 @@ impl std::fmt::Display for PackageIdentifier {
|
||||||
match self {
|
match self {
|
||||||
PackageIdentifier::PackageLocator(pl) => write!(f, "{}", pl),
|
PackageIdentifier::PackageLocator(pl) => write!(f, "{}", pl),
|
||||||
PackageIdentifier::URI(uri) => write!(f, "{}", uri),
|
PackageIdentifier::URI(uri) => write!(f, "{}", uri),
|
||||||
|
PackageIdentifier::Path(path) => write!(f, "{}", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,7 @@ impl FromStr for PackageIdentifier {
|
||||||
type Err = PackageIdentifierError;
|
type Err = PackageIdentifierError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let uri_re = regex::Regex::new(r"^[a-zA-Z0-9]+://").unwrap();
|
let uri_re = Regex::new(r"^[a-zA-Z0-9]+://").unwrap();
|
||||||
if uri_re.is_match(s) {
|
if uri_re.is_match(s) {
|
||||||
// there needs to be stuff after the protocol
|
// there needs to be stuff after the protocol
|
||||||
let split = s.split("://").collect::<Vec<&str>>();
|
let split = s.split("://").collect::<Vec<&str>>();
|
||||||
|
@ -69,6 +71,8 @@ impl FromStr for PackageIdentifier {
|
||||||
return Err(PackageIdentifierError::InvalidURI(s.to_string()));
|
return Err(PackageIdentifierError::InvalidURI(s.to_string()));
|
||||||
}
|
}
|
||||||
Ok(PackageIdentifier::URI(s.to_string()))
|
Ok(PackageIdentifier::URI(s.to_string()))
|
||||||
|
} else if std::path::Path::new(s).exists() {
|
||||||
|
return Ok(PackageIdentifier::Path(s.to_string()));
|
||||||
} else {
|
} else {
|
||||||
let pl = match PackageLocator::from_str(s) {
|
let pl = match PackageLocator::from_str(s) {
|
||||||
Ok(pl) => pl,
|
Ok(pl) => pl,
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
|
pub mod builder;
|
||||||
|
pub mod fetch;
|
48
pkgr/src/tmp.rs
Normal file
48
pkgr/src/tmp.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub struct TempDir {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempDir {
|
||||||
|
pub fn new(path: PathBuf) -> TempDir {
|
||||||
|
if !path.exists() {
|
||||||
|
std::fs::create_dir_all(&path).unwrap();
|
||||||
|
}
|
||||||
|
TempDir {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<S: Into<String>>(&mut self, path: S) {
|
||||||
|
self.path.push(path.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TempDir {
|
||||||
|
fn default() -> TempDir {
|
||||||
|
TempDir::new({
|
||||||
|
let mut t = std::env::temp_dir();
|
||||||
|
t.push("pkgr");
|
||||||
|
t
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<PathBuf> for TempDir {
|
||||||
|
fn into(self) -> PathBuf {
|
||||||
|
self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for TempDir {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.path.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for TempDir {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.path.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue