summaryrefslogtreecommitdiff
path: root/src/pkgtoolkit/pkgtools.rs
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-12-06 16:36:44 +0300
committerNamilskyy <alive6863@gmail.com>2025-12-06 16:36:44 +0300
commitdef46ae74c3f5974ed448e9877b0e0067a8e67d2 (patch)
tree4eda9fe8b6da96ea8f1824a14235286fe6e49c72 /src/pkgtoolkit/pkgtools.rs
parent79c8ecb6bf4d2fc2df5c90007e7c26b456ddc33f (diff)
Big code-cleaning in pkgtoolkit, implemented much functions and fixed logical mistakes
Diffstat (limited to 'src/pkgtoolkit/pkgtools.rs')
-rw-r--r--src/pkgtoolkit/pkgtools.rs615
1 files changed, 0 insertions, 615 deletions
diff --git a/src/pkgtoolkit/pkgtools.rs b/src/pkgtoolkit/pkgtools.rs
deleted file mode 100644
index 2a8a18e..0000000
--- a/src/pkgtoolkit/pkgtools.rs
+++ /dev/null
@@ -1,615 +0,0 @@
-use crate::cfg::config::Config;
-use std::{
- fs::{self, File, Permissions, create_dir_all, set_permissions},
- io,
- os::unix::fs::PermissionsExt,
- path::{Path, StripPrefixError},
- process::Command,
- str,
-};
-// use emissary_core::i2np::tunnel::build;
-use flate2::read::GzDecoder;
-use serde::{Deserialize, Serialize};
-use tar::Archive;
-use toml;
-
-#[derive(Serialize, Debug, Deserialize, Clone)]
-pub enum Archs {
- X86_64,
- Aarch64,
- X86,
- ArmV7,
- ArmV8,
-}
-
-#[derive(Serialize, Debug, Deserialize, Clone)]
-pub struct Package {
- pub name: String,
- pub version: String,
- pub arch: Archs,
- pub descr: Option<String>,
- pub license: Option<String>,
- pub url: String,
-}
-
-#[derive(Deserialize, Debug, Clone)]
-pub struct InstallMeta {
- pub path: String,
- pub user: String,
- pub group: String,
- pub mode: String,
- // Cancels the previous fields and installs them using the shell script
- pub custom_script: Option<String>,
- // pub files: Option<Vec<String>>,
-}
-
-#[allow(dead_code)]
-#[derive(Deserialize, Debug, Clone)]
-struct Install {
- package: Package,
- install: InstallMeta,
- #[serde(default)]
- files: Vec<String>,
-}
-
-#[allow(dead_code)]
-#[derive(Deserialize, Debug)]
-struct Setts {
- // Export environment variables if this needed
- env: Option<String>,
- // Test the package after installation
- test: Option<String>,
-}
-
-#[derive(Deserialize, Serialize, Debug)]
-pub enum BuildSystems {
- Make,
- CMake,
- Meson,
- Cargo,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct PackageManifest {
- pub name: String,
- pub version: String,
- pub all_files: Vec<String>,
-}
-
-#[allow(dead_code)]
-#[derive(Deserialize)]
-struct Build {
- build_system: BuildSystems,
- env: Option<String>,
- script: Option<String>,
-}
-
-impl Archs {
- pub fn as_str(&self) -> &'static str {
- match self {
- Archs::X86_64 => "x86_64",
- Archs::Aarch64 => "aarch64",
- Archs::X86 => "x86",
- Archs::ArmV7 => "armv7",
- Archs::ArmV8 => "armv8",
- }
- }
-}
-
-#[allow(dead_code)]
-impl Package {
- /// Execute the build script for the package.
- ///
- /// This function takes the `Build` meta information as an argument and
- /// executes the build script accordingly. It also handles the different
- /// build systems supported (Make, CMake, Meson, Cargo).
- ///
- /// # Errors
- ///
- /// Returns an error if the build command fails.
- fn execute_build(&self, build_meta: &Build) -> Result<(), std::io::Error> {
- let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
- let build_dir =
- Path::new(&config.paths.cache_dir).join(format!("{}-{}", self.name, self.version));
-
- let mut cmd = match build_meta.build_system {
- BuildSystems::Make => {
- let mut c = Command::new("make");
- c.current_dir(&build_dir);
- if build_meta.script.is_some() {
- c.arg("-f").arg(build_meta.script.as_ref().unwrap());
- }
- c.arg("all");
- c
- }
- BuildSystems::CMake => {
- let build_dir_build = build_dir.join("build");
- create_dir_all(&build_dir_build)?;
- let mut c = Command::new("cmake");
- c.arg("-S")
- .arg(&build_dir)
- .arg("-B")
- .arg(&build_dir_build)
- .current_dir(&build_dir);
- c
- }
- BuildSystems::Meson => {
- let build_dir_build = build_dir.join("build");
- create_dir_all(&build_dir_build)?;
- let mut c = Command::new("meson");
- c.arg("setup").arg(&build_dir_build).current_dir(&build_dir);
- c
- }
- BuildSystems::Cargo => {
- let mut c = Command::new("cargo");
- c.arg("build").arg("--release").current_dir(&build_dir);
- c
- }
- };
-
- let output = cmd
- .output()
- .map_err(|e| std::io::Error::other(format!("Build command failed: {}", e)))?;
-
- if !output.status.success() {
- let stderr = String::from_utf8_lossy(&output.stderr);
- return Err(std::io::Error::other(format!("Build failed:\n{}", stderr)));
- }
-
- Ok(())
- }
-
- /// Recursively collects all files from a directory and its subdirectories
- /// and returns them as a vector of strings.
- ///
- /// # Arguments
- ///
- /// * `root`: The root directory from which to collect files.
- /// * `base`: The base directory from which to strip the prefix from the file paths.
- ///
- /// # Returns
- ///
- /// A vector of strings containing the file paths relative to the `base` directory.
- fn collect_files_from_dir(root: &Path, base: &Path) -> Result<Vec<String>, std::io::Error> {
- let mut files = Vec::new();
- for entry in fs::read_dir(root)? {
- let entry = entry?;
- let path = entry.path();
- if path.is_dir() {
- files.extend(Self::collect_files_from_dir(&path, base)?);
- } else {
- let rel_path = path
- .strip_prefix(base)
- .map_err(|e: StripPrefixError| io::Error::new(io::ErrorKind::InvalidData, e))?
- .to_string_lossy()
- .to_string();
- files.push(rel_path);
- }
- }
- Ok(files)
- }
-
- /// Extracts a .tar.gz archive to the cache directory specified in Config.
- ///
- /// This function handles opening the archive file, decompressing it with GzDecoder,
- /// and unpacking the contents into the configured cache directory.
- ///
- /// # Arguments
- /// * `path_to_archive` - A string representing the path to the .tar.gz file.
- ///
- /// # Returns
- /// * `Ok(())` if the archive is successfully unpacked.
- /// * `Err(std::io::Error)` if there's an issue opening, reading, or unpacking the archive.
- pub fn extract_archive(path_to_archive: &str) -> Result<(), std::io::Error> {
- let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
- let cache_dir = &config.paths.cache_dir;
- create_dir_all(cache_dir)?;
- // Очистим возможные мета-файлы предыдущих распаковок, чтобы не было утечек состояния
- for meta_name in ["INSTALL", "SETTS", "BUILD"] {
- let meta_path = Path::new(cache_dir).join(meta_name);
- if meta_path.exists() {
- fs::remove_file(&meta_path)?;
- }
- }
- let file = File::open(path_to_archive)?;
- let gz = GzDecoder::new(file);
- let mut archive = Archive::new(gz);
- // Unpack directly into the cache directory. Игнорируем AlreadyExists, чтобы не мешать валидации.
- match archive.unpack(cache_dir) {
- Ok(()) => Ok(()),
- Err(e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
- Err(e) => Err(e),
- }
- }
-
- /// Load meta information from the .mesk archive.
- ///
- /// This function parses the meta information from the .mesk archive,
- /// which includes the package name, version, architecture, description,
- /// installation path, user, group, mode, and custom installation script
- /// and deserializing this information. Returns (Install, Option<Setts>, Option<Build>)
- ///
- /// The function expects the 'INSTALL', 'SETTS', and 'BUILD' files to be present
- /// in the `config.paths.cache_dir`. It specifically requires the 'INSTALL' file.
- ///
- /// # Errors
- ///
- /// Returns an error if the `cache_dir` cannot be created, if the required 'INSTALL' file
- /// is not found, or if the 'INSTALL' file is empty.
- #[allow(clippy::type_complexity)]
- fn loadmeta(
- minimal_package_meta: &mut Self,
- ) -> Result<(Install, Option<Setts>, Option<Build>), std::io::Error> {
- // Changed return type for more flexibility
- /*
- Example INSTALL format:
- [package]
- name = "my-package"
- version = "1.0.0"
- arch = "X86_64"
- descr = "Just example INSTALL script"
-
- [install]
- path = "/usr/bin/my-package"
- user = "root"
- group = "root"
- mode = "755"
-
- # Also [install] can be
- # path = "/usr/bin/my-package"
- # user = "root"
- # group = "root"
- # mode = "755"
- # custom_script = "./install.sh" OR
- # custom_script = """
- # echo "Installing my-package"
- # sudo apt-get install my-package
- # """
- */
-
- let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?; // Propagate error if parsing fails
-
- // Ensure the cache directory exists
- fs::create_dir_all(&config.paths.cache_dir)?;
-
- let cache_dir = &config.paths.cache_dir;
- let install_path =
- Path::new(cache_dir).join(format!("{}/INSTALL", minimal_package_meta.name));
- let setts_path = Path::new(cache_dir).join(format!("{}/SETTS", minimal_package_meta.name));
- let build_path = Path::new(cache_dir).join(format!("{}/BUILD", minimal_package_meta.name));
-
- if !install_path.exists() {
- return Err(io::Error::new(
- io::ErrorKind::NotFound,
- "File INSTALL not found in cache directory",
- ));
- }
-
- let install_content = fs::read_to_string(&install_path)?;
- let install_meta: Install = toml::from_str(&install_content)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
-
- let mut setts_meta: Option<Setts> = None;
- let mut build_meta: Option<Build> = None;
-
- if setts_path.exists() {
- let setts_content = fs::read_to_string(&setts_path)?;
- setts_meta = Some(
- toml::from_str(&setts_content)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
- );
- }
-
- if build_path.exists() {
- let build_content = fs::read_to_string(&build_path)?;
- build_meta = Some(
- toml::from_str(&build_content)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
- );
- }
-
- if let Some(ref _script) = install_meta.install.custom_script {
- println!(
- "Custom script found for package: {}",
- install_meta.package.name
- );
- } else {
- println!(
- "No custom script for package: {}",
- install_meta.package.name
- );
- }
-
- Ok((install_meta, setts_meta, build_meta))
- }
-
- /// Checks if the archive contains INSTALL, SETTS and BUILD files.
- ///
- /// Checks if INSTALL file exists and is not empty. If it does not exist or is empty, returns an error.
- ///
- /// Checks if SETTS and BUILD files exist and are not empty. If they do not exist or are empty, logs a warning.
- /// # Errors
- /// * Returns an error if INSTALL file does not exist or is empty.
- /// * Returns an error if INSTALL file is empty.
- ///
- // TODO: Add meta-files validation here.
- pub fn check(path_to_archive: String) -> Result<bool, std::io::Error> {
- Self::extract_archive(&path_to_archive)?;
-
- let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
-
- let install_path = Path::new(&config.paths.cache_dir).join("INSTALL");
- let setts_path = Path::new(&config.paths.cache_dir).join("SETTS");
- let build_path = Path::new(&config.paths.cache_dir).join("BUILD");
-
- if !install_path.exists() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::NotFound,
- "INSTALL file not found in archive",
- ));
- }
-
- let install_content = std::fs::read_to_string(&install_path)?;
- if install_content.trim().is_empty() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::InvalidData,
- "INSTALL file is empty",
- ));
- }
-
- if !setts_path.exists() {
- log::warn!("SETTS file not found in archive. Make sure you dont need this.");
- } else {
- let setts_content = std::fs::read_to_string(&setts_path)?;
- if setts_content.trim().is_empty() {
- log::warn!("SETTS file is empty. Make sure you dont need this.");
- }
- }
-
- if !build_path.exists() {
- log::warn!("BUILD file not found in archive. Make sure you dont need this.");
- } else {
- let build_content = std::fs::read_to_string(&build_path)?;
- if build_content.trim().is_empty() {
- log::warn!("BUILD file is empty. Make sure you dont need this.");
- }
- }
-
- let content = std::fs::read_to_string(&install_path).map_err(|e| {
- log::warn!("Failed to read file: {}", e);
- e
- })?;
- let install_content: Result<Install, toml::de::Error> = toml::from_str(&content);
-
- log::info!("Validating arch...");
- let install_parsed = match install_content {
- Ok(v) => v,
- Err(e) => {
- log::error!("Arch mismatch while parsing INSTALL: {}", e);
- return Err(std::io::Error::new(
- std::io::ErrorKind::InvalidData,
- format!("Arch mismatch: {}", e),
- ));
- }
- };
- if std::env::consts::ARCH != install_parsed.package.arch.as_str() {
- let pkg_arch = &install_parsed.package.arch;
- log::error!(
- "Arch mismatch. Package arch: {:?}, Host arch: {}",
- pkg_arch,
- std::env::consts::ARCH
- );
- return Err(std::io::Error::new(
- std::io::ErrorKind::InvalidData,
- "Arch mismatch",
- ));
- }
-
- Ok(true)
- }
-
- /// Builds the package according to the BUILD file in the archive.
- ///
- /// There are two strategies for building the package. If the BUILD file is empty, the package is assumed to be a binary package and the default install hook is skipped. If the BUILD file is not empty, the package is assumed to be a source package and the default build hook is skipped.
- ///
- /// If the BUILD file is empty and the INSTALL file contains a custom script, the custom script is run instead of the default install hook.
- /// If the BUILD file is not empty and the INSTALL file contains a custom script, the custom script is ignored.
- ///
- ///
- /// # Errors
- ///
- /// Returns an error if the BUILD file is invalid or if the build or install hook fails.
- pub fn build(&mut self) -> Result<bool, std::io::Error> {
- let meta = Self::loadmeta(self)?;
- let install_meta = meta.0;
- // let setts_meta = meta.1;
- let build_meta = meta.2;
-
- // BUILD NOT EMPTY. SOURCE: -> BUILD -> INSTALL -> SETTS
- // BUILD EMPTY. BIN: -> INSTALL -> SETTS
- enum Strategies {
- Bin,
- Source,
- }
-
- let strategy; //default
-
- if build_meta.is_none() {
- log::info!("BUILD file is empty. Skipping build, preparing to install");
- strategy = Strategies::Bin;
- } else {
- strategy = Strategies::Source;
- log::info!("BUILD file is not empty. Skipping install, preparing to build");
- }
-
- match strategy {
- Strategies::Bin => {
- if install_meta.install.custom_script.is_none() {
- log::info!("Strategy: BIN; No custom script. Running default install hook.");
- } else {
- log::info!("Strategy: BIN; Running custom script.");
- let script = install_meta.install.custom_script.as_ref().unwrap();
- if !script.starts_with("./") {
- let _output = std::process::Command::new(script);
- } else {
- let _output = std::process::Command::new(format!("/bin/sh '{}'", script));
- }
- }
- }
- Strategies::Source => {
- log::info!("Strategy: SOURCE; Running default build hook.");
- let _ = self.execute_build(&build_meta.unwrap());
- }
- }
-
- Ok(true)
- }
-
- /// Installs the package according to the INSTALL file in the archive.
- ///
- /// There are two strategies for installing the package. If the BUILD file is empty, the package is assumed to be a binary package and the default install hook is skipped. If the BUILD file is not empty, the package is assumed to be a source package and the default build hook is skipped.
- ///
- /// If the BUILD file is empty and the INSTALL file contains a custom script, the custom script is run instead of the default install hook.
- /// If the BUILD file is not empty and the INSTALL file contains a custom script, the custom script is ignored.
- ///
- /// # Errors
- ///
- /// Returns an error if the BUILD file is invalid or if the build or install hook fails.
- pub fn install(&mut self) -> Result<bool, std::io::Error> {
- let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
- let (install_meta, _setts_meta, build_meta) = Self::loadmeta(self)?;
-
- let installed_db = Path::new(&config.paths.installed_db);
- create_dir_all(installed_db)?;
-
- let mut all_files: Vec<String> = Vec::new();
-
- let is_build_present_and_not_empty = build_meta.is_some();
-
- if is_build_present_and_not_empty {
- log::info!(
- "Found BUILD file, preparing to build and install package: {}",
- self.name
- );
- let build_meta_ref = build_meta.as_ref().unwrap();
- let _ = self.execute_build(build_meta_ref);
-
- let staging_dir = Path::new(&config.paths.cache_dir).join("staging");
- create_dir_all(&staging_dir)?;
- all_files = Self::collect_files_from_dir(&staging_dir, &staging_dir)?
- .iter()
- .map(|rel| format!("/{}", rel)) // Преобразуйте в абсолютные пути (предполагаем root=/)
- .collect();
-
- for file in &all_files {
- let src = staging_dir.join(file.trim_start_matches('/'));
- let dest = Path::new(file);
- if let Some(parent) = dest.parent() {
- create_dir_all(parent)?;
- }
- fs::copy(&src, dest)?;
- // TODO: Permission
- }
-
- // Cleanup staging
- fs::remove_dir_all(&staging_dir)?;
- } else {
- log::info!(
- "No BUILD file or it's empty. Treating as binary package. Installing via INSTALL config or custom script."
- );
- if let Some(ref script) = install_meta.install.custom_script {
- log::info!(
- "Executing custom install script for {}",
- install_meta.package.name
- );
- let status = if script.starts_with("./") || script.contains('/') {
- Command::new("/bin/sh").arg("-c").arg(script).status()
- } else {
- Command::new(script).status()
- }
- .map_err(|e| {
- std::io::Error::other(format!("Failed to run custom script: {}", e))
- })?;
-
- if !status.success() {
- return Err(std::io::Error::other("Custom install script failed"));
- }
-
- log::warn!(
- "Custom script used; file list may be incomplete. Add manual tracking if needed."
- );
- // Опционально: all_files = vec![]; или сканируйте систему (не рекомендуется)
- } else {
- log::info!(
- "No custom script. Running default install hook for {}",
- install_meta.package.name
- );
- let source_file_name = &self.name;
- let build_dir = Path::new(&config.paths.cache_dir)
- .join(format!("{}-{}", self.name, self.version));
- let src_path = build_dir.join(source_file_name);
- let dest_path = Path::new(&install_meta.install.path);
-
- if let Some(parent) = dest_path.parent() {
- create_dir_all(parent).map_err(|e| {
- std::io::Error::other(format!("Failed to create parent dir: {}", e))
- })?;
- }
-
- fs::copy(&src_path, dest_path)
- .map_err(|e| std::io::Error::other(format!("Failed to copy file: {}", e)))?;
-
- let mode = u32::from_str_radix(&install_meta.install.mode, 8).map_err(|_| {
- std::io::Error::new(
- std::io::ErrorKind::InvalidData,
- "Invalid mode string in INSTALL",
- )
- })?;
- let perms = Permissions::from_mode(mode);
- set_permissions(dest_path, perms).map_err(|e| {
- std::io::Error::other(format!("Failed to set permissions: {}", e))
- })?;
-
- let output = Command::new("chown")
- .arg(format!(
- "{}:{}",
- install_meta.install.user, install_meta.install.group
- ))
- .arg(dest_path)
- .output()
- .map_err(|e| std::io::Error::other(format!("'chown' command failed: {}", e)))?;
-
- if !output.status.success() {
- let stderr = String::from_utf8_lossy(&output.stderr);
- log::warn!(
- "Warning: 'chown' command failed (requires root?):\n{}",
- stderr
- );
- }
-
- all_files = install_meta.files;
- }
- }
-
- let manifest = PackageManifest {
- name: self.name.clone(),
- version: self.version.clone(),
- all_files,
- };
- let manifest_path = installed_db.join(format!("{}-{}.toml", self.name, self.version));
- let manifest_toml =
- toml::to_string(&manifest).map_err(|e| std::io::Error::other(e.to_string()))?;
- fs::write(&manifest_path, manifest_toml)?;
-
- log::info!(
- "Package {} installed successfully. Manifest generated at {:?}",
- self.name,
- manifest_path
- );
- Ok(true)
- }
-
- pub fn gen_index() -> Result<bool, std::io::Error> {
- todo!();
- }
-}