diff options
| author | Namilskyy <alive6863@gmail.com> | 2025-12-06 20:20:16 +0300 |
|---|---|---|
| committer | Namilskyy <alive6863@gmail.com> | 2025-12-06 20:20:16 +0300 |
| commit | e7c49d685efe768c0c1d9c66da7b93b6e118b305 (patch) | |
| tree | e1bfb33a34c3fe4cb84436e73b48463f5deb8f08 | |
| parent | c8520a5cf071eedc966e57abaf4ba2a334df83ee (diff) | |
Fixed logical errors, tryed to fix tests
| -rw-r--r-- | Cargo.lock | 222 | ||||
| -rw-r--r-- | Cargo.toml | 8 | ||||
| -rw-r--r-- | examples/INSTALL | 2 | ||||
| -rw-r--r-- | src/cfg/config.rs | 4 | ||||
| -rw-r--r-- | src/main.rs | 160 | ||||
| -rw-r--r-- | src/net/http_package.rs | 37 | ||||
| -rw-r--r-- | src/net/i2p_package.rs | 342 | ||||
| -rw-r--r-- | src/pkgtoolkit/git_source.rs | 86 | ||||
| -rw-r--r-- | src/pkgtoolkit/index.rs | 1 | ||||
| -rw-r--r-- | src/pkgtoolkit/mod.rs | 5 | ||||
| -rw-r--r-- | src/pkgtoolkit/types.rs | 3 | ||||
| -rw-r--r-- | tests/http_funcs.rs | 1 | ||||
| -rw-r--r-- | tests/i2p_functions.rs | 1 | ||||
| -rw-r--r-- | tests/pkgtoolkit_funcs.rs | 32 | ||||
| -rw-r--r-- | tests/shared.rs | 12 |
15 files changed, 507 insertions, 409 deletions
@@ -136,6 +136,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -368,28 +370,12 @@ dependencies = [ ] [[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -399,17 +385,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -444,7 +419,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -480,6 +454,21 @@ dependencies = [ ] [[package]] +name = "git2" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] name = "h2" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -820,6 +809,16 @@ dependencies = [ ] [[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] name = "js-sys" version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -836,6 +835,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] +name = "libgit2-sys" +version = "0.17.0+1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] name = "libredox" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -847,6 +860,32 @@ dependencies = [ ] [[package]] +name = "libssh2-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -883,25 +922,24 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" name = "mesk" version = "0.0.1" dependencies = [ - "cc", "clap", "env_logger", "flate2", "futures-util", + "git2", "indicatif", "log", "mockito", "num_cpus", "reqwest", "serde", - "serde_json", "tar", "tempfile", "tokio", "tokio-test", "toml", "url", - "yosemite", + "uuid", ] [[package]] @@ -911,12 +949,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -954,7 +986,7 @@ dependencies = [ "hyper-util", "log", "pin-project-lite", - "rand 0.9.2", + "rand", "regex", "serde_json", "serde_urlencoded", @@ -980,16 +1012,6 @@ dependencies = [ ] [[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] name = "num_cpus" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1161,33 +1183,12 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "rand_chacha", + "rand_core", ] [[package]] @@ -1197,16 +1198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", + "rand_core", ] [[package]] @@ -1480,6 +1472,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + +[[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1608,26 +1609,6 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "tinystr" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1648,6 +1629,7 @@ dependencies = [ "mio", "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", @@ -1811,24 +1793,11 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ - "log", "pin-project-lite", - "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "tracing-core" version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1892,6 +1861,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2264,20 +2244,6 @@ dependencies = [ ] [[package]] -name = "yosemite" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19cf621ae2b2d7e5d1e3827231fbf59ca4fb72b3589e36cdf648e820f1cf6e0d" -dependencies = [ - "futures", - "nom", - "rand 0.8.5", - "thiserror", - "tokio", - "tracing", -] - -[[package]] name = "zerocopy" version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -11,19 +11,16 @@ description = "An i2p-based package manager developed by Anthrill project" clap = { version = "4.5.53", features = ["derive"] } toml = { version = "0.9.8", features = ["serde"] } serde = { version = "1.0.228", features = ["derive"] } -tokio = { version = "1.48.0", features = ["fs", "io-util", "macros", "rt"] } +tokio = { version = "1.48.0", features = ["full"] } reqwest = { version = "0.12.24", features = ["stream"] } flate2 = "1.1.5" log = "0.4.28" tar = "0.4.44" -yosemite = "0.6.1" -cc = "1.2.48" url = "2.5.7" indicatif = "0.18.3" futures-util = "0.3.31" num_cpus = "1.17.0" -tempfile = "3.23.0" -serde_json = "1.0.145" +git2 = "0.19.0" [dev-dependencies] env_logger = "0.11.8" @@ -31,4 +28,5 @@ log = "0.4.28" mockito = "1.7.1" tempfile = "3.23.0" tokio-test = "0.4.4" +uuid = { version = "1.19.0", features = ["v4"] } diff --git a/examples/INSTALL b/examples/INSTALL index 76f08a0..d33accf 100644 --- a/examples/INSTALL +++ b/examples/INSTALL @@ -4,6 +4,8 @@ version = "1.0.0" arch = "X86_64" descr = "Just example INSTALL script" license = "BSD-2-Clause" +url = "/repo/my-package.mesk" +git_repo = "https://github.com/example/my-package.git" [install] path = "/usr/bin/my-package" diff --git a/src/cfg/config.rs b/src/cfg/config.rs index f14ec93..ce9df87 100644 --- a/src/cfg/config.rs +++ b/src/cfg/config.rs @@ -39,6 +39,8 @@ pub struct Repo { pub destination: (String, String), #[serde(rename = "repo_http_url")] pub repo_http_url: Option<String>, + #[serde(rename = "i2p_http_proxy_port")] + pub i2p_http_proxy_port: u16, // #[serde(rename = "arch")] // pub arch = arch; } @@ -92,6 +94,7 @@ impl Config { std::env::consts::ARCH ) .into(), + i2p_http_proxy_port: 4444, // Its a hurt place, you need to generate destinations by i2pd and paste here (to mesk.toml) // Better to leave it empty or set it to (mesk, mesk), now destination conn not implemented }, @@ -126,6 +129,7 @@ impl Config { auto_update: true, destination: (String::from("mesk"), String::from("mesk")), repo_http_url: None, + i2p_http_proxy_port: 4444, }, log: Log { log_file: String::from("/var/log/mesk.log"), diff --git a/src/main.rs b/src/main.rs index b3367f3..2bdb29c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,12 +7,14 @@ use crate::net::{http_package::HTTPPackage, i2p_package::I2PPackage}; use crate::pkgtoolkit::Package; use crate::pkgtoolkit::index::IndexOperations; +use crate::pkgtoolkit::git_source::GitSource; +use crate::pkgtoolkit::archive::ArchiveOperations; +use crate::pkgtoolkit::build::BuildOperations; use clap::{Args, Parser, Subcommand}; -use std::fs::File; -use std::fs::create_dir_all; use std::io::Write; use std::path::Path; +use toml; #[derive(Parser)] struct Cli { @@ -70,70 +72,79 @@ struct RemoteInstallArgs { clean: bool, } + #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { - // Changed return type to be more general let cli: Cli = Cli::parse(); match &cli.command { Commands::Validate { path } => { println!("Validating {}", path); + match Package::check(path.to_string()) { + Ok(is_valid) => { + if is_valid { + println!("Package archive is valid."); + } else { + println!("Package archive is invalid."); + return Err(std::io::Error::other("Invalid package archive").into()); + } + } + Err(e) => { + log::error!("Failed to validate package '{}': {}", path, e); + return Err(e.into()); + } + } return Ok(()); } Commands::Build { pkgname } => { - println!("Building {}", pkgname); + println!("Building package from archive: {}", pkgname); + + let path = Path::new(&pkgname); + if !path.exists() { + return Err(std::io::Error::other(format!("Package archive not found: {}", pkgname)).into()); + } + + if !path.is_file() { + return Err(std::io::Error::other(format!("Path is not a file: {}", pkgname)).into()); + } + + println!("Extracting archive..."); + Package::extract_archive(&pkgname)?; + + let config = Config::parse().unwrap(); + let cache_dir = &config.paths.cache_dir; + let install_toml_path = Path::new(cache_dir).join("INSTALL"); + + if !install_toml_path.exists() { + return Err(std::io::Error::other("INSTALL file not found in archive").into()); + } + + let install_content = std::fs::read_to_string(&install_toml_path)?; + let install_data: crate::pkgtoolkit::types::Install = toml::from_str(&install_content)?; + + let mut pkg = install_data.package; + + println!("Building package '{}'...", pkg.name); + pkg.build()?; + println!("Package '{}' built successfully.", pkg.name); return Ok(()); } - Commands::Install { - pkgname, - source: _, - args, - } => { - let config = Config::parse().unwrap(); - + Commands::Install { pkgname, source: _, args } => { + let config = Config::parse().unwrap(); if args.http { println!("Installing {} via HTTP", pkgname); let mut http_client = HTTPPackage::new(config); - match http_client.fetch_index_http().await { - Ok(_) => { - log::info!("Index fetched successfully."); - } - Err(e) => { - log::error!("Failed to fetch index: {}", e); - return Err(e); - } - } - match http_client.fetch_package_http(pkgname).await { - Ok(_) => { - log::info!("Package '{}' installed successfully.", pkgname); - } - Err(e) => { - log::error!("Failed to install package '{}': {}", pkgname, e); - return Err(e); - } - } + http_client.fetch_index_http().await?; + log::info!("Index fetched successfully."); + http_client.fetch_package_http(pkgname).await?; + log::info!("Package '{}' installed successfully.", pkgname); } else { println!("Installing {} via I2P", pkgname); let mut i2p_client = I2PPackage::new(config); - - match i2p_client.fetch_index().await { - Ok(_) => { - log::info!("Index fetched successfully."); - } - Err(e) => { - log::error!("Failed to fetch index: {}", e); - return Err(e); - } - } - match i2p_client.fetch_package(pkgname).await { - Ok(_) => { - log::info!("Package '{}' installed successfully.", pkgname); - } - Err(e) => { - log::error!("Failed to install package '{}': {}", pkgname, e); - return Err(e); - } - } + i2p_client.fetch_index().await?; + log::info!("Index fetched successfully."); + i2p_client.fetch_package(pkgname).await?; + log::info!("Package '{}' installed successfully.", pkgname); } return Ok(()); } @@ -142,15 +153,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { return Ok(()); } Commands::GetSource { pkgname } => { + let config = Config::parse().unwrap(); println!("Getting source of {}", pkgname); + + + let source_path = GitSource::get_source_by_name(pkgname, &config)?; + println!("Source code successfully downloaded to: {}", source_path); return Ok(()); } - Commands::DefaultConfig { - repo, - cachedir, - buildir, - installed_db, - } => { + Commands::DefaultConfig { repo, cachedir, buildir, installed_db } => { println!("Generating config file"); if cachedir.is_none() && repo.is_none() && buildir.is_none() { let config = Config::default().unwrap(); @@ -162,12 +173,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { log::warn!("Writing the default config to /etc/mesk/mesk.toml"); let path = Path::new("/etc/mesk/mesk.toml"); - create_dir_all(path.parent().unwrap())?; - let mut file = File::create(path)?; + std::fs::create_dir_all(path.parent().unwrap())?; + let mut file = std::fs::File::create(path)?; file.write_all(config.as_bytes())?; println!("Config tool ending work."); } else { - let config = Config::generate(repo, cachedir, buildir, installed_db).unwrap(); + let config = Config::generate(repo, cachedir, buildir, installed_db).unwrap(); println!("---- Start of generated config ----"); println!("{:?}", config); @@ -176,27 +187,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { log::warn!("Writing the default config to /etc/mesk/mesk.toml"); let path = Path::new("/etc/mesk/mesk.toml"); - create_dir_all(path.parent().unwrap())?; - let mut file = File::create(path)?; - file.write_all(config.as_bytes())?; + std::fs::create_dir_all(path.parent().unwrap())?; + let mut file = std::fs::File::create(path)?; + file.write_all(config.as_bytes())?; println!("Config tool ending work."); } return Ok(()); } Commands::Update => { - let config = Config::parse().unwrap(); + let config = Config::parse().unwrap(); println!("Updating index from {}", config.repo.repo_url); let mut i2p_client = I2PPackage::new(config); - match i2p_client.fetch_index().await { - Ok(_) => { - println!("Index updated successfully."); - } - Err(e) => { - log::error!("Failed to update index: {}", e); - return Err(e); - } - } + i2p_client.fetch_index().await?; + println!("Index updated successfully."); return Ok(()); } Commands::Upgrade { pkgname } => { @@ -208,22 +212,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { "CREATED BY: Asya and Namilsk as part of the Anthrill independent Global network distribution project" ); println!(" "); - println!("The Anthrill project repos: https://codeberg.org/NamelessTeam "); + println!("The Anthrill project repos: https://codeberg.org/NamelessTeam "); + return Ok(()); } Commands::GenIndex { path } => { println!("Generating index for {}", path); - match Package::gen_index(path) { - Ok(_) => { - println!("Index generated successfully."); - } - Err(e) => { - log::error!("Failed to generate index: {}", e); - return Err(Box::new(e)); - } - } + + Package::gen_index(path)?; + println!("Index generated successfully."); return Ok(()); } } - Ok(()) } diff --git a/src/net/http_package.rs b/src/net/http_package.rs index d31cc90..4566e05 100644 --- a/src/net/http_package.rs +++ b/src/net/http_package.rs @@ -12,6 +12,10 @@ use std::{collections::HashMap, path::Path}; use tar::Archive; use tokio::{fs::File, io::AsyncWriteExt}; +use crate::pkgtoolkit::install::InstallOperations; +use crate::pkgtoolkit::archive::ArchiveOperations; + + pub struct HTTPPackage { pub config: Config, pub index_packages: Option<HashMap<String, Package>>, @@ -143,7 +147,10 @@ impl HTTPPackage { let mut package_map = HashMap::new(); for pkg in index_data.packages { - let base_url = url::Url::parse(&self.config.repo.repo_url)?; + let base_url = match &self.config.repo.repo_http_url { + Some(url) => url::Url::parse(url)?, + None => url::Url::parse(&self.config.repo.repo_url)?, + }; let full_url = base_url.join(&pkg.url)?; let mut pkg_clone = pkg.clone(); pkg_clone.url = full_url.to_string(); @@ -227,12 +234,13 @@ impl HTTPPackage { /// Fetches a specific package identified by `package_name`. /// Assumes `fetch_index_http` has been called and `self.index_packages` is populated to get the URL. - /// Downloads the package file (.mesk) to the cache directory with a progress bar. + /// Downloads the package file (.mesk) to the cache directory with a progress bar, + /// extracts it, and installs the package. /// /// # Errors /// /// Returns an error if the index is not loaded, the package is not found in the index, - /// the package URL is invalid, the request fails, or if there's an issue writing the file. + /// the package URL is invalid, the request fails, extraction fails, or installation fails. pub async fn fetch_package_http( &self, package_name: &str, @@ -259,7 +267,28 @@ impl HTTPPackage { package_name, file_path ); - Ok(true) + + // Extract the package archive + log::info!("Extracting package archive for '{}'...", package_name); + Package::extract_archive(&file_path.to_string_lossy()) + .map_err(|e| format!("Failed to extract package archive: {}", e))?; + + log::info!("Package archive extracted successfully."); + + // Install the package + log::info!("Installing package '{}'...", package_name); + let mut package = package_info.clone(); + + match package.install() { + Ok(_) => { + log::info!("Package '{}' installed successfully.", package_name); + Ok(true) + } + Err(e) => { + log::error!("Failed to install package '{}': {}", package_name, e); + Err(format!("Installation failed: {}", e).into()) + } + } } /// Fetches a specific package identified by `index` (likely the package name). diff --git a/src/net/i2p_package.rs b/src/net/i2p_package.rs index a9d39d2..cebfa34 100644 --- a/src/net/i2p_package.rs +++ b/src/net/i2p_package.rs @@ -1,23 +1,17 @@ use crate::cfg::config::Config; use crate::pkgtoolkit::Package; -use crate::pkgtoolkit::archive::ArchiveOperations; -use serde::Deserialize; -use tokio; - -/* -use emissary_core::runtime::{ - AsyncRead, - AsyncWrite, -}; -*/ - +use flate2::read::GzDecoder; +use futures_util::stream::TryStreamExt; use indicatif::{ProgressBar, ProgressStyle}; +use reqwest; +use serde::Deserialize; +use std::fs::File as StdFile; use std::{collections::HashMap, path::Path}; -use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; +use tar::Archive; +use tokio::{fs::File, io::AsyncWriteExt}; +use crate::pkgtoolkit::archive::ArchiveOperations; +use crate::pkgtoolkit::install::InstallOperations; -use url; -use yosemite::Session; -use yosemite::SessionOptions; pub struct I2PPackage { pub config: Config, @@ -30,81 +24,69 @@ struct IndexData { } impl I2PPackage { - /// Creates a new I2P object with the given configuration. + /// Creates a new I2PPackage object with the given configuration. /// /// # Returns /// - /// A new I2P object with the given configuration. The session is initially set to None and the connected status is set to false. - pub fn new(cfg: Config) -> Self { + /// A new I2PPackage object with the given configuration. + pub fn new(config: Config) -> Self { I2PPackage { - config: cfg, + config, index_packages: None, } } - /// Downloads the INDEX.tar.gz file from the configured repository - /// and stores it in the configured cache directory. + /// Creates a reqwest client configured to use the I2P HTTP proxy. + /// + /// # Returns + /// + /// A reqwest Client with proxy configuration for I2P. + fn create_proxy_client(&self) -> Result<reqwest::Client, Box<dyn std::error::Error>> { + let proxy_url = format!("http://127.0.0.1:{}", self.config.repo.i2p_http_proxy_port); + let proxy = reqwest::Proxy::http(&proxy_url)?; + let client = reqwest::Client::builder() + .proxy(proxy) + .build()?; + Ok(client) + } + + /// Downloads the INDEX.tar.gz file from the configured I2P repository + /// and stores it in the configured cache directory with a progress bar. /// /// # Errors /// /// Returns an error if the request fails, if the response status is not successful, or if there's an issue while reading or writing the file. pub async fn fetch_index(&mut self) -> Result<bool, Box<dyn std::error::Error>> { - let repo_url_str = &self.config.repo.repo_url; + let repo_url = &self.config.repo.repo_url; let cache_dir = &self.config.paths.cache_dir; - let url = url::Url::parse(repo_url_str)?; - let host = url.host_str().ok_or("No host in URL")?; + log::debug!("Cache directory: {:?}", cache_dir); - let request_path = url.path(); - let request_path = if request_path.ends_with(".tar.gz") { - request_path.to_string() + let index_url = if repo_url.ends_with(".tar.gz") { + repo_url.to_string() } else { - format!("{}/INDEX.tar.gz", request_path.trim_end_matches('/')) + format!("{}/INDEX.tar.gz", repo_url.trim_end_matches('/')) }; - let session_options = SessionOptions::default(); - let mut session = Session::new(session_options).await?; - let mut stream = session.connect(host).await?; + let client = self.create_proxy_client()?; - let request = format!( - "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", - request_path, host - ); - stream.write_all(request.as_bytes()).await?; - - let mut reader = BufReader::new(stream); - let mut response_buffer = Vec::new(); - reader.read_to_end(&mut response_buffer).await?; - - let headers_end = response_buffer - .windows(4) - .position(|window| window == b"\r\n\r\n") - .ok_or("Invalid response: no headers end")?; - - let headers_str = std::str::from_utf8(&response_buffer[..headers_end])?; - if !headers_str.starts_with("HTTP/1.1 200") && !headers_str.starts_with("HTTP/1.0 200") { - return Err(format!( - "HTTP Error: {}", - headers_str.lines().next().unwrap_or("Unknown") - ) - .into()); - } - - let content_length = headers_str - .lines() - .find(|line| line.to_lowercase().starts_with("content-length:")) - .and_then(|line| line.split_at(15).1.trim().parse::<u64>().ok()) + // Make a HEAD request to get the content length for the progress bar + let head_response = client.head(&index_url).send().await?; + let content_length: u64 = head_response + .headers() + .get(reqwest::header::CONTENT_LENGTH) + .and_then(|ct_len| ct_len.to_str().ok()) + .and_then(|ct_len| ct_len.parse().ok()) .unwrap_or(0); - let body_start = headers_end + 4; - let mut body_reader = std::io::Cursor::new(&response_buffer[body_start..]); - let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); - + // Create progress bar let pb = if content_length > 0 { let pb = ProgressBar::new(content_length); - pb.set_style(ProgressStyle::default_bar() - .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")? - .progress_chars("#>-")); + pb.set_style( + ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")? + .progress_chars("#>-"), + ); pb } else { let pb = ProgressBar::new_spinner(); @@ -115,34 +97,41 @@ impl I2PPackage { pb }; - let mut file = tokio::fs::File::create(&file_path).await?; - - let chunk_size = 8192u64; - let mut buffer = vec![0; chunk_size as usize]; + // Send GET request and stream the response body + let response = client.get(&index_url).send().await?; + if !response.status().is_success() { + return Err(format!("HTTP Error: {}", response.status()).into()); + } - loop { - let bytes_read_result = std::io::Read::read(&mut body_reader, &mut buffer); - let bytes_read = match bytes_read_result { - Ok(n) => n, - Err(e) => return Err(e.into()), - }; + let mut stream = response.bytes_stream(); + let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); - if bytes_read == 0 { - break; - } - file.write_all(&buffer[..bytes_read]).await?; + // Ensure cache_dir exists + tokio::fs::create_dir_all(cache_dir) + .await + .map_err(|e| format!("Failed to create cache dir: {}", e))?; - pb.inc(bytes_read as u64); + let mut file = File::create(&file_path).await?; + let mut downloaded: u64 = 0; - if bytes_read < chunk_size as usize { - break; - } + while let Some(chunk) = stream.try_next().await? { + file.write_all(&chunk).await?; + let chunk_len = chunk.len() as u64; + downloaded += chunk_len; + pb.set_position(downloaded); } pb.finish_with_message("INDEX.tar.gz download finished"); log::info!("Extracting INDEX.tar.gz to cache directory..."); - Package::extract_archive(&file_path.to_string_lossy())?; + + let archive_file = + StdFile::open(&file_path).map_err(|e| format!("Failed to open archive: {}", e))?; + let gz_decoder = GzDecoder::new(archive_file); + let mut archive = Archive::new(gz_decoder); + archive + .unpack(cache_dir) + .map_err(|e| format!("Failed to unpack archive: {}", e))?; let index_toml_path = Path::new(cache_dir).join("INDEX.toml"); if !index_toml_path.exists() { @@ -151,13 +140,14 @@ impl I2PPackage { return Ok(true); } - let index_content = std::fs::read_to_string(&index_toml_path)?; - let index_data: IndexData = toml::from_str(&index_content)?; + let index_content = tokio::fs::read_to_string(&index_toml_path).await?; + log::debug!("Content of INDEX.toml:\n{}", index_content); + + let index_data: IndexData = toml::from_str(&index_content) + .map_err(|e| format!("Failed to parse INDEX.toml: {}", e))?; let mut package_map = HashMap::new(); for pkg in index_data.packages { - // PKG_URL = /repo/package.mesk - // FULL URL = "http://mesk.anthrill.i2p/i2p/repo/pkg.mesk" let base_url = url::Url::parse(&self.config.repo.repo_url)?; let full_url = base_url.join(&pkg.url)?; let mut pkg_clone = pkg.clone(); @@ -178,25 +168,38 @@ impl I2PPackage { /// An internal auxiliary function for downloading data and writing it to a file with a progress display. /// /// # Arguments - /// * `data' - Byte slice (&[u8]) with data to write. - /// * `file_path' is the path to the file to write the data to. - /// * `content_length' is the expected data size (for the progress bar). Maybe 0. - /// * `description' - Description of the operation for the progress bar. + /// * `client` - The reqwest client to use for requests. + /// * `url` - The URL to download from. + /// * `file_path` - The path to the file to write the data to. + /// * `description` - Description of the operation for the progress bar. /// /// # Errors /// - /// Returns an error if there is a problem when creating or writing to a file. - async fn download_and_write_file_with_progress( - data: &[u8], + /// Returns an error if the request fails, if the response status is not successful, or if there's an issue while writing the file. + async fn download_file_with_progress( + client: &reqwest::Client, + url: &str, file_path: &Path, - content_length: u64, description: &str, ) -> Result<(), Box<dyn std::error::Error>> { + + let head_response = client.head(url).send().await?; + let content_length: u64 = head_response + .headers() + .get(reqwest::header::CONTENT_LENGTH) + .and_then(|ct_len| ct_len.to_str().ok()) + .and_then(|ct_len| ct_len.parse().ok()) + .unwrap_or(0); let pb = if content_length > 0 { let pb = ProgressBar::new(content_length); - pb.set_style(ProgressStyle::default_bar() - .template(&format!("{{spinner:.green}} [{{elapsed_precise}}] [{{bar:40.cyan/blue}}] {{bytes}}/{{total_bytes}} ({} {{eta}})", description))? - .progress_chars("#>-")); + pb.set_style( + ProgressStyle::default_bar() + .template(&format!( + "{{spinner:.green}} [{{elapsed_precise}}] [{{bar:40.cyan/blue}}] {{bytes}}/{{total_bytes}} ({} {{eta}})", + description + ))? + .progress_chars("#>-"), + ); pb } else { let pb = ProgressBar::new_spinner(); @@ -206,117 +209,98 @@ impl I2PPackage { ))?); pb }; + let response = client.get(url).send().await?; + if !response.status().is_success() { + return Err(format!("HTTP Error: {}", response.status()).into()); + } - let mut file = tokio::fs::File::create(&file_path).await?; - - let chunk_size = 8192usize; - let mut pos = 0; + let mut stream = response.bytes_stream(); + let mut file = File::create(&file_path).await?; + let mut downloaded: u64 = 0; - while pos < data.len() { - let end = std::cmp::min(pos + chunk_size, data.len()); - let chunk = &data[pos..end]; - file.write_all(chunk).await?; - pb.inc(chunk.len() as u64); - pos = end; + while let Some(chunk) = stream.try_next().await? { + file.write_all(&chunk).await?; + let chunk_len = chunk.len() as u64; + downloaded += chunk_len; + pb.set_position(downloaded); } pb.finish_with_message(format!("{} download finished", description)); Ok(()) } - /// Fetches a specific package identified by `index` (likely the package name). - /// Assumes `fetch_index` has been called and `self.index_packages` is populated. - pub fn fetch_package_info( - &self, - package_name: &str, - ) -> Result<&Package, Box<dyn std::error::Error>> { - let packages = self - .index_packages - .as_ref() - .ok_or("Index not loaded. Call fetch_index first.")?; - let pkg_info = packages - .get(package_name) - .ok_or(format!("Package '{}' not found in index.", package_name))?; - Ok(pkg_info) - } - /// Fetches a specific package identified by `package_name`. /// Assumes `fetch_index` has been called and `self.index_packages` is populated to get the URL. - /// Downloads the package file (.mesk) to the cache directory. + /// Downloads the package file (.mesk) to the cache directory with a progress bar, + /// extracts it, and installs the package. /// /// # Errors /// /// Returns an error if the index is not loaded, the package is not found in the index, - /// the package URL is invalid, the request fails, or if there's an issue writing the file. - /// Why didn't I just abstract the download functionality into a separate function initially? - /// Yes, I'm scared to work with fetch_index, even I don't often write such shit code. + /// the package URL is invalid, the request fails, extraction fails, or installation fails. pub async fn fetch_package( &self, package_name: &str, ) -> Result<bool, Box<dyn std::error::Error>> { let package_info = self.fetch_package_info(package_name)?; - let url = url::Url::parse(&package_info.url)?; - let host = url.host_str().ok_or("No host in package URL")?; - let request_path = url.path(); + let url = &package_info.url; - let session_options = SessionOptions::default(); - let mut session = Session::new(session_options).await?; - let mut stream = session.connect(host).await?; + let client = self.create_proxy_client()?; - let request = format!( - "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", - request_path, host - ); - stream.write_all(request.as_bytes()).await?; - - let mut reader = BufReader::new(stream); - let mut response_buffer = Vec::new(); - reader.read_to_end(&mut response_buffer).await?; - - let headers_end = response_buffer - .windows(4) - .position(|window| window == b"\r\n\r\n") - .ok_or("Invalid response: no headers end")?; - - let headers_str = std::str::from_utf8(&response_buffer[..headers_end])?; - if !headers_str.starts_with("HTTP/1.1 200") && !headers_str.starts_with("HTTP/1.0 200") { - return Err(format!( - "HTTP Error: {}", - headers_str.lines().next().unwrap_or("Unknown") - ) - .into()); - } - - let content_length = headers_str - .lines() - .find(|line| line.to_lowercase().starts_with("content-length:")) - .and_then(|line| line.split_at(15).1.trim().parse::<u64>().ok()) - .unwrap_or(0); - - let body_start = headers_end + 4; - let body_bytes = &response_buffer[body_start..]; - - let file_name = Path::new(request_path) + let file_name = Path::new(url) .file_name() - .ok_or("Could not determine filename from URL path")? + .ok_or("Could not determine filename from URL")? .to_str() .ok_or("Filename is not valid UTF-8")?; let cache_dir = &self.config.paths.cache_dir; let file_path = Path::new(cache_dir).join(file_name); - Self::download_and_write_file_with_progress( - body_bytes, - &file_path, - content_length, - file_name, - ) - .await?; + tokio::fs::create_dir_all(&cache_dir).await?; + + Self::download_file_with_progress(&client, url, &file_path, file_name).await?; log::info!( "Package '{}' downloaded successfully to {:?}", package_name, file_path ); - Ok(true) + + // Extract the package archive + log::info!("Extracting package archive for '{}'...", package_name); + Package::extract_archive(&file_path.to_string_lossy()) + .map_err(|e| format!("Failed to extract package archive: {}", e))?; + + log::info!("Package archive extracted successfully."); + + // Install the package + log::info!("Installing package '{}'...", package_name); + let mut package = package_info.clone(); + + match package.install() { + Ok(_) => { + log::info!("Package '{}' installed successfully.", package_name); + Ok(true) + } + Err(e) => { + log::error!("Failed to install package '{}': {}", package_name, e); + Err(format!("Installation failed: {}", e).into()) + } + } + } + + /// Fetches a specific package identified by `index` (likely the package name). + /// Assumes `fetch_index` has been called and `self.index_packages` is populated. + pub fn fetch_package_info( + &self, + package_name: &str, + ) -> Result<&Package, Box<dyn std::error::Error>> { + let packages = self + .index_packages + .as_ref() + .ok_or("Index not loaded. Call fetch_index first.")?; + let pkg_info = packages + .get(package_name) + .ok_or(format!("Package '{}' not found in index.", package_name))?; + Ok(pkg_info) } } diff --git a/src/pkgtoolkit/git_source.rs b/src/pkgtoolkit/git_source.rs new file mode 100644 index 0000000..2f33c14 --- /dev/null +++ b/src/pkgtoolkit/git_source.rs @@ -0,0 +1,86 @@ +use std::fs; +use std::path::Path; +use git2::Repository; +use crate::pkgtoolkit::types::Package; +use crate::cfg::config::Config; +use toml; + +pub struct GitSource; + +impl GitSource { + /// Clone a git repository to a local directory + pub fn clone_repo(git_url: &str, target_dir: &Path) -> Result<(), git2::Error> { + Repository::clone(git_url, target_dir)?; + Ok(()) + } + + /// Get source code for a package from its git repository + pub fn get_package_source(package: &Package, config: &Config) -> Result<String, Box<dyn std::error::Error>> { + let git_repo = package.git_repo.as_ref() + .ok_or("Package does not have a git repository specified")?; + + let source_dir = Path::new(&config.paths.cache_dir) + .join("sources") + .join(&package.name) + .join(&package.version); + + fs::create_dir_all(&source_dir)?; + + println!("Cloning {} from {}", package.name, git_repo); + Self::clone_repo(git_repo, &source_dir)?; + + let source_path = source_dir.to_string_lossy().to_string(); + println!("Source code downloaded to: {}", source_path); + + Ok(source_path) + } + + /// Get source code for a package by name, first checking INSTALL file, then falling back to index + pub fn get_source_by_name(pkg_name: &str, config: &Config) -> Result<String, Box<dyn std::error::Error>> { + // First try to get git_repo from INSTALL file in cache + let install_path = Path::new(&config.paths.cache_dir).join(format!("{}/INSTALL", pkg_name)); + + if install_path.exists() { + let install_content = fs::read_to_string(&install_path)?; + let install_data: crate::pkgtoolkit::types::Install = toml::from_str(&install_content) + .map_err(|e| format!("Failed to parse INSTALL file: {}", e))?; + + // Check if InstallMeta has git_repo + if let Some(git_repo) = install_data.install.git_repo { + println!("Found git repository in INSTALL file: {}", git_repo); + + let source_dir = Path::new(&config.paths.cache_dir) + .join("sources") + .join(&install_data.package.name) + .join(&install_data.package.version); + + fs::create_dir_all(&source_dir)?; + + println!("Cloning {} from {}", install_data.package.name, git_repo); + Self::clone_repo(&git_repo, &source_dir)?; + + let source_path = source_dir.to_string_lossy().to_string(); + println!("Source code downloaded to: {}", source_path); + + return Ok(source_path); + } + } + + // Fall back to index if INSTALL file doesn't exist or doesn't have git_repo + let index_path = Path::new(&config.paths.cache_dir).join("INDEX.toml"); + + if !index_path.exists() { + return Err("Index file not found. Please run 'mesk update' first.".into()); + } + + let index_content = fs::read_to_string(&index_path)?; + let index: crate::pkgtoolkit::types::Index = toml::from_str(&index_content) + .map_err(|e| format!("Failed to parse index: {}", e))?; + + let package = index.packages.iter() + .find(|pkg| pkg.name == pkg_name) + .ok_or(format!("Package '{}' not found in index", pkg_name))?; + + Self::get_package_source(package, config) + } +} diff --git a/src/pkgtoolkit/index.rs b/src/pkgtoolkit/index.rs index 7b13533..78c37aa 100644 --- a/src/pkgtoolkit/index.rs +++ b/src/pkgtoolkit/index.rs @@ -95,6 +95,7 @@ impl IndexOperations for Package { descr: Some(install_data.package.descr.unwrap_or_default()), license: Some(install_data.package.license.unwrap_or_default()), url: install_data.package.url, + git_repo: install_data.package.git_repo, }; all_packages.push(pkg_for_index); diff --git a/src/pkgtoolkit/mod.rs b/src/pkgtoolkit/mod.rs index 2fc316a..1cadee3 100644 --- a/src/pkgtoolkit/mod.rs +++ b/src/pkgtoolkit/mod.rs @@ -1,6 +1,7 @@ // Core package toolkit modules pub mod archive; pub mod build; +pub mod git_source; pub mod index; pub mod install; pub mod types; @@ -52,3 +53,7 @@ pub use install::InstallOperations; // Index operations for package repository management #[allow(unused_imports)] pub use index::IndexOperations; + +// Git source operations for downloading package source code +#[allow(unused_imports)] +pub use git_source::GitSource; diff --git a/src/pkgtoolkit/types.rs b/src/pkgtoolkit/types.rs index 01a807a..83f47a3 100644 --- a/src/pkgtoolkit/types.rs +++ b/src/pkgtoolkit/types.rs @@ -17,6 +17,7 @@ pub struct Package { pub descr: Option<String>, pub license: Option<String>, pub url: String, + pub git_repo: Option<String>, } #[derive(Deserialize, Debug, Clone)] @@ -27,6 +28,8 @@ pub struct InstallMeta { pub mode: String, // Cancels the previous fields and installs them using the shell script pub custom_script: Option<String>, + // Git repository URL for source code if needed + pub git_repo: Option<String>, // pub files: Option<Vec<String>>, } diff --git a/tests/http_funcs.rs b/tests/http_funcs.rs index 886da10..5a28a98 100644 --- a/tests/http_funcs.rs +++ b/tests/http_funcs.rs @@ -167,6 +167,7 @@ mod http_package_tests { descr: Some("Test".to_string()), license: Some("MIT".to_string()), url: "http://example.com/repo/test-pkg.mesk".to_string(), + git_repo: None, }; packages_map.insert("test-pkg".to_string(), pkg); http_pkg.index_packages = Some(packages_map); diff --git a/tests/i2p_functions.rs b/tests/i2p_functions.rs index b07631a..6146e71 100644 --- a/tests/i2p_functions.rs +++ b/tests/i2p_functions.rs @@ -64,6 +64,7 @@ mod i2p_package_tests { descr: Some("Test".to_string()), license: Some("MIT".to_string()), url: "http://example.i2p/repo/test-pkg.mesk".to_string(), + git_repo: None, }; packages_map.insert("test-pkg".to_string(), pkg); i2p_pkg.index_packages = Some(packages_map); diff --git a/tests/pkgtoolkit_funcs.rs b/tests/pkgtoolkit_funcs.rs index 9b23a17..bc9c22a 100644 --- a/tests/pkgtoolkit_funcs.rs +++ b/tests/pkgtoolkit_funcs.rs @@ -79,7 +79,10 @@ user = "root" group = "root" mode = "755" "#; - let install_path = temp_dir.path().join("INSTALL"); + let pkg_name = "test-pkg"; // Match the name in install_content + let temp_pkg_dir = temp_dir.path().join(pkg_name); + fs::create_dir(&temp_pkg_dir).unwrap(); + let install_path = temp_pkg_dir.join("INSTALL"); let mut install_file = fs::File::create(&install_path).unwrap(); install_file.write_all(install_content.as_bytes()).unwrap(); @@ -88,7 +91,7 @@ mode = "755" let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); let mut tar_builder = tar::Builder::new(gz_encoder); tar_builder - .append_path_with_name(&install_path, "INSTALL") + .append_dir_all(pkg_name, &temp_pkg_dir) .unwrap(); tar_builder.into_inner().unwrap().finish().unwrap(); @@ -110,14 +113,18 @@ mode = "755" let temp_dir = TempDir::new().unwrap(); let _config = create_test_config(temp_dir.path().to_str().unwrap()); + let pkg_name = "test_pkg"; + let temp_pkg_dir = temp_dir.path().join(pkg_name); + fs::create_dir(&temp_pkg_dir).unwrap(); + let dummy_path = temp_pkg_dir.join("dummy.txt"); + fs::write(&dummy_path, "dummy").unwrap(); + let archive_path = temp_dir.path().join("test_pkg_without_install.tar.gz"); let file = fs::File::create(&archive_path).unwrap(); let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); let mut tar_builder = tar::Builder::new(gz_encoder); - let dummy_path = temp_dir.path().join("dummy.txt"); - fs::write(&dummy_path, "dummy").unwrap(); tar_builder - .append_path_with_name(&dummy_path, "dummy.txt") + .append_dir_all(pkg_name, &temp_pkg_dir) .unwrap(); tar_builder.into_inner().unwrap().finish().unwrap(); @@ -143,14 +150,18 @@ mode = "755" let temp_dir = TempDir::new().unwrap(); let _config = create_test_config(temp_dir.path().to_str().unwrap()); - let install_path = temp_dir.path().join("INSTALL"); + let pkg_name = "test_pkg"; + let temp_pkg_dir = temp_dir.path().join(pkg_name); + fs::create_dir(&temp_pkg_dir).unwrap(); + let install_path = temp_pkg_dir.join("INSTALL"); fs::write(&install_path, "").unwrap(); + let archive_path = temp_dir.path().join("test_pkg_with_empty_install.tar.gz"); let file = fs::File::create(&archive_path).unwrap(); let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); let mut tar_builder = tar::Builder::new(gz_encoder); tar_builder - .append_path_with_name(&install_path, "INSTALL") + .append_dir_all(pkg_name, &temp_pkg_dir) .unwrap(); tar_builder.into_inner().unwrap().finish().unwrap(); @@ -197,7 +208,10 @@ mode = "755" wrong_arch ); - let install_path = temp_dir.path().join("INSTALL"); + let pkg_name = "test-pkg"; + let temp_pkg_dir = temp_dir.path().join(pkg_name); + fs::create_dir(&temp_pkg_dir).unwrap(); + let install_path = temp_pkg_dir.join("INSTALL"); let mut install_file = fs::File::create(&install_path).unwrap(); install_file.write_all(install_content.as_bytes()).unwrap(); @@ -206,7 +220,7 @@ mode = "755" let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); let mut tar_builder = tar::Builder::new(gz_encoder); tar_builder - .append_path_with_name(&install_path, "INSTALL") + .append_dir_all(pkg_name, &temp_pkg_dir) .unwrap(); tar_builder.into_inner().unwrap().finish().unwrap(); diff --git a/tests/shared.rs b/tests/shared.rs index 574e8cd..3b6ad70 100644 --- a/tests/shared.rs +++ b/tests/shared.rs @@ -1,6 +1,11 @@ use mesk::cfg::config::{Config, Log, Loglevel, Paths, Repo}; pub fn create_test_config(temp_dir_path: &str) -> Config { + // Create a unique subdirectory for this specific test to avoid parallel execution conflicts + let unique_cache_dir = format!("{}/{}", temp_dir_path, uuid::Uuid::new_v4()); + let unique_build_dir = format!("{}/{}", unique_cache_dir, "build"); + let unique_installed_db = format!("{}/{}", unique_cache_dir, "pkgdb"); + let cfg: Config = Config { repo: Repo { repo_url: format!(r"http://mesk.anthrill.i2p/repo/{}", std::env::consts::ARCH), @@ -8,15 +13,16 @@ pub fn create_test_config(temp_dir_path: &str) -> Config { .into(), auto_update: true, destination: (String::from("mesk"), String::from("mesk")), + i2p_http_proxy_port: 4444, }, log: Log { log_file: String::from("mesk.log"), log_level: Loglevel::Info, }, paths: Paths { - cache_dir: String::from(temp_dir_path), - build_dir: String::from(format!("{}/build", String::from(temp_dir_path))), - installed_db: String::from(format!("{}/pkgdb", String::from(temp_dir_path))), + cache_dir: String::from(&unique_cache_dir), + build_dir: String::from(&unique_build_dir), + installed_db: String::from(&unique_installed_db), }, }; |
