mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Refactor codebase to improve formatting, logging clarity, and error handling
- Standardize formatting by aligning and cleaning up indentation, spacing, and line breaks across multiple modules. - Enhance logging with improved message formatting for better context and readability. - Simplify error handling in `pkg6dev`, `pkg6repo`, and core libraries by consolidating redundant patterns and improving clarity. - Update test cases to reflect formatting and logging changes while extending coverage of edge cases. - Perform general cleanup, including removing unused imports, refining comments, and ensuring consistent style.
This commit is contained in:
parent
abb997df43
commit
21de26ae82
20 changed files with 782 additions and 524 deletions
|
|
@ -511,8 +511,10 @@ impl From<Action> for User {
|
||||||
// Parse ftpuser property into services
|
// Parse ftpuser property into services
|
||||||
match string_to_bool(&prop.value) {
|
match string_to_bool(&prop.value) {
|
||||||
// If it's a boolean value (backward compatibility)
|
// If it's a boolean value (backward compatibility)
|
||||||
Ok(true) => { user.services.insert("ftp".to_string()); },
|
Ok(true) => {
|
||||||
Ok(false) => {}, // No services if false
|
user.services.insert("ftp".to_string());
|
||||||
|
}
|
||||||
|
Ok(false) => {} // No services if false
|
||||||
// If the value not a boolean, treat as a comma-separated list of services
|
// If the value not a boolean, treat as a comma-separated list of services
|
||||||
_ => {
|
_ => {
|
||||||
for service in prop.value.split(',') {
|
for service in prop.value.split(',') {
|
||||||
|
|
@ -858,7 +860,7 @@ impl Manifest {
|
||||||
|
|
||||||
pub fn parse_file<P: AsRef<Path>>(f: P) -> Result<Manifest> {
|
pub fn parse_file<P: AsRef<Path>>(f: P) -> Result<Manifest> {
|
||||||
let content = read_to_string(f)?;
|
let content = read_to_string(f)?;
|
||||||
|
|
||||||
// Try to parse as JSON first
|
// Try to parse as JSON first
|
||||||
match serde_json::from_str::<Manifest>(&content) {
|
match serde_json::from_str::<Manifest>(&content) {
|
||||||
Ok(manifest) => Ok(manifest),
|
Ok(manifest) => Ok(manifest),
|
||||||
|
|
@ -966,7 +968,7 @@ pub enum ManifestError {
|
||||||
help("Check the action name and make sure it's one of the supported action types.")
|
help("Check the action name and make sure it's one of the supported action types.")
|
||||||
)]
|
)]
|
||||||
UnknownAction { line: usize, action: String },
|
UnknownAction { line: usize, action: String },
|
||||||
|
|
||||||
#[error("action string \"{action:?}\" at line {line:?} is invalid: {message:?}")]
|
#[error("action string \"{action:?}\" at line {line:?} is invalid: {message:?}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::action_error::manifest::invalid_action),
|
code(ips::action_error::manifest::invalid_action),
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ pub enum DigestError {
|
||||||
help("Use one of the supported algorithms: sha1, sha256t, sha512t, sha512t_256, sha3256t, sha3512t_256, sha3512t")
|
help("Use one of the supported algorithms: sha1, sha256t, sha512t, sha512t_256, sha3256t, sha3512t_256, sha3512t")
|
||||||
)]
|
)]
|
||||||
UnknownAlgorithm { algorithm: String },
|
UnknownAlgorithm { algorithm: String },
|
||||||
|
|
||||||
#[error("digest {digest:?} is not formatted properly: {details:?}")]
|
#[error("digest {digest:?} is not formatted properly: {details:?}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::digest_error::invalid_format),
|
code(ips::digest_error::invalid_format),
|
||||||
|
|
|
||||||
|
|
@ -73,35 +73,35 @@ pub enum FmriError {
|
||||||
help("FMRI should be in the format: [scheme://][publisher/]name[@version]")
|
help("FMRI should be in the format: [scheme://][publisher/]name[@version]")
|
||||||
)]
|
)]
|
||||||
InvalidFormat,
|
InvalidFormat,
|
||||||
|
|
||||||
#[error("invalid version format")]
|
#[error("invalid version format")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::fmri_error::invalid_version_format),
|
code(ips::fmri_error::invalid_version_format),
|
||||||
help("Version should be in the format: release[,branch][-build][:timestamp]")
|
help("Version should be in the format: release[,branch][-build][:timestamp]")
|
||||||
)]
|
)]
|
||||||
InvalidVersionFormat,
|
InvalidVersionFormat,
|
||||||
|
|
||||||
#[error("invalid release format")]
|
#[error("invalid release format")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::fmri_error::invalid_release_format),
|
code(ips::fmri_error::invalid_release_format),
|
||||||
help("Release should be a dot-separated vector of digits (e.g., 5.11)")
|
help("Release should be a dot-separated vector of digits (e.g., 5.11)")
|
||||||
)]
|
)]
|
||||||
InvalidReleaseFormat,
|
InvalidReleaseFormat,
|
||||||
|
|
||||||
#[error("invalid branch format")]
|
#[error("invalid branch format")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::fmri_error::invalid_branch_format),
|
code(ips::fmri_error::invalid_branch_format),
|
||||||
help("Branch should be a dot-separated vector of digits (e.g., 1)")
|
help("Branch should be a dot-separated vector of digits (e.g., 1)")
|
||||||
)]
|
)]
|
||||||
InvalidBranchFormat,
|
InvalidBranchFormat,
|
||||||
|
|
||||||
#[error("invalid build format")]
|
#[error("invalid build format")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::fmri_error::invalid_build_format),
|
code(ips::fmri_error::invalid_build_format),
|
||||||
help("Build should be a dot-separated vector of digits (e.g., 2020.0.1.0)")
|
help("Build should be a dot-separated vector of digits (e.g., 2020.0.1.0)")
|
||||||
)]
|
)]
|
||||||
InvalidBuildFormat,
|
InvalidBuildFormat,
|
||||||
|
|
||||||
#[error("invalid timestamp format")]
|
#[error("invalid timestamp format")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::fmri_error::invalid_timestamp_format),
|
code(ips::fmri_error::invalid_timestamp_format),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
mod properties;
|
mod properties;
|
||||||
|
|
||||||
use properties::*;
|
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
|
use properties::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
@ -16,7 +16,7 @@ pub enum ImageError {
|
||||||
help("Check system resources and permissions")
|
help("Check system resources and permissions")
|
||||||
)]
|
)]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("JSON error: {0}")]
|
#[error("JSON error: {0}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::image_error::json),
|
code(ips::image_error::json),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ pub enum PayloadError {
|
||||||
help("Check system resources and permissions")
|
help("Check system resources and permissions")
|
||||||
)]
|
)]
|
||||||
IOError(#[from] IOError),
|
IOError(#[from] IOError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
#[diagnostic(transparent)]
|
#[diagnostic(transparent)]
|
||||||
DigestError(#[from] DigestError),
|
DigestError(#[from] DigestError),
|
||||||
|
|
|
||||||
|
|
@ -22,36 +22,28 @@ pub enum CatalogError {
|
||||||
code(ips::repository_error::catalog::part_not_found),
|
code(ips::repository_error::catalog::part_not_found),
|
||||||
help("Check that the catalog part exists and is accessible")
|
help("Check that the catalog part exists and is accessible")
|
||||||
)]
|
)]
|
||||||
CatalogPartNotFound {
|
CatalogPartNotFound { name: String },
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("catalog part not loaded: {name}")]
|
#[error("catalog part not loaded: {name}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::repository_error::catalog::part_not_loaded),
|
code(ips::repository_error::catalog::part_not_loaded),
|
||||||
help("Load the catalog part before attempting to save it")
|
help("Load the catalog part before attempting to save it")
|
||||||
)]
|
)]
|
||||||
CatalogPartNotLoaded {
|
CatalogPartNotLoaded { name: String },
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("update log not loaded: {name}")]
|
#[error("update log not loaded: {name}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::repository_error::catalog::update_log_not_loaded),
|
code(ips::repository_error::catalog::update_log_not_loaded),
|
||||||
help("Load the update log before attempting to save it")
|
help("Load the update log before attempting to save it")
|
||||||
)]
|
)]
|
||||||
UpdateLogNotLoaded {
|
UpdateLogNotLoaded { name: String },
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("update log does not exist: {name}")]
|
#[error("update log does not exist: {name}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::repository_error::catalog::update_log_not_found),
|
code(ips::repository_error::catalog::update_log_not_found),
|
||||||
help("Check that the update log exists and is accessible")
|
help("Check that the update log exists and is accessible")
|
||||||
)]
|
)]
|
||||||
UpdateLogNotFound {
|
UpdateLogNotFound { name: String },
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("failed to serialize JSON: {0}")]
|
#[error("failed to serialize JSON: {0}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
// MPL was not distributed with this file, You can
|
// MPL was not distributed with this file, You can
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use super::{Result, RepositoryError};
|
use super::{RepositoryError, Result};
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression as GzipCompression;
|
use flate2::Compression as GzipCompression;
|
||||||
use lz4::EncoderBuilder;
|
use lz4::EncoderBuilder;
|
||||||
|
|
@ -316,13 +316,22 @@ impl Transaction {
|
||||||
// Check if the temp file already exists
|
// Check if the temp file already exists
|
||||||
if temp_file_path.exists() {
|
if temp_file_path.exists() {
|
||||||
// If it exists, remove it to avoid any issues with existing content
|
// If it exists, remove it to avoid any issues with existing content
|
||||||
fs::remove_file(&temp_file_path)
|
fs::remove_file(&temp_file_path).map_err(|e| {
|
||||||
.map_err(|e| RepositoryError::FileWriteError(format!("Failed to remove existing temp file: {}", e)))?;
|
RepositoryError::FileWriteError(format!(
|
||||||
|
"Failed to remove existing temp file: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the file content
|
// Read the file content
|
||||||
let file_content = fs::read(file_path)
|
let file_content = fs::read(file_path).map_err(|e| {
|
||||||
.map_err(|e| RepositoryError::FileReadError(format!("Failed to read file {}: {}", file_path.display(), e)))?;
|
RepositoryError::FileReadError(format!(
|
||||||
|
"Failed to read file {}: {}",
|
||||||
|
file_path.display(),
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Create a payload with the hash information if it doesn't exist
|
// Create a payload with the hash information if it doesn't exist
|
||||||
let mut updated_file_action = file_action;
|
let mut updated_file_action = file_action;
|
||||||
|
|
@ -338,18 +347,22 @@ impl Transaction {
|
||||||
let mut encoder = GzEncoder::new(Vec::new(), GzipCompression::default());
|
let mut encoder = GzEncoder::new(Vec::new(), GzipCompression::default());
|
||||||
|
|
||||||
// Write the file content to the encoder
|
// Write the file content to the encoder
|
||||||
encoder
|
encoder.write_all(&file_content).map_err(|e| {
|
||||||
.write_all(&file_content)
|
RepositoryError::Other(format!("Failed to write data to Gzip encoder: {}", e))
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to write data to Gzip encoder: {}", e)))?;
|
})?;
|
||||||
|
|
||||||
// Finish the compression and get the compressed data
|
// Finish the compression and get the compressed data
|
||||||
let compressed_data = encoder
|
let compressed_data = encoder.finish().map_err(|e| {
|
||||||
.finish()
|
RepositoryError::Other(format!("Failed to finish Gzip compression: {}", e))
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to finish Gzip compression: {}", e)))?;
|
})?;
|
||||||
|
|
||||||
// Write the compressed data to the temp file
|
// Write the compressed data to the temp file
|
||||||
fs::write(&temp_file_path, &compressed_data)
|
fs::write(&temp_file_path, &compressed_data).map_err(|e| {
|
||||||
.map_err(|e| RepositoryError::FileWriteError(format!("Failed to write compressed data to temp file: {}", e)))?;
|
RepositoryError::FileWriteError(format!(
|
||||||
|
"Failed to write compressed data to temp file: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Calculate hash of the compressed data
|
// Calculate hash of the compressed data
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
|
|
@ -358,21 +371,24 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
PayloadCompressionAlgorithm::LZ4 => {
|
PayloadCompressionAlgorithm::LZ4 => {
|
||||||
// Create an LZ4 encoder with the default compression level
|
// Create an LZ4 encoder with the default compression level
|
||||||
let mut encoder = EncoderBuilder::new()
|
let mut encoder = EncoderBuilder::new().build(Vec::new()).map_err(|e| {
|
||||||
.build(Vec::new())
|
RepositoryError::Other(format!("Failed to create LZ4 encoder: {}", e))
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to create LZ4 encoder: {}", e)))?;
|
})?;
|
||||||
|
|
||||||
// Write the file content to the encoder
|
// Write the file content to the encoder
|
||||||
encoder
|
encoder.write_all(&file_content).map_err(|e| {
|
||||||
.write_all(&file_content)
|
RepositoryError::Other(format!("Failed to write data to LZ4 encoder: {}", e))
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to write data to LZ4 encoder: {}", e)))?;
|
})?;
|
||||||
|
|
||||||
// Finish the compression and get the compressed data
|
// Finish the compression and get the compressed data
|
||||||
let (compressed_data, _) = encoder.finish();
|
let (compressed_data, _) = encoder.finish();
|
||||||
|
|
||||||
// Write the compressed data to the temp file
|
// Write the compressed data to the temp file
|
||||||
fs::write(&temp_file_path, &compressed_data).map_err(|e| {
|
fs::write(&temp_file_path, &compressed_data).map_err(|e| {
|
||||||
RepositoryError::FileWriteError(format!("Failed to write LZ4 compressed data to temp file: {}", e))
|
RepositoryError::FileWriteError(format!(
|
||||||
|
"Failed to write LZ4 compressed data to temp file: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Calculate hash of the compressed data
|
// Calculate hash of the compressed data
|
||||||
|
|
@ -424,9 +440,13 @@ impl Transaction {
|
||||||
// Extract the first two and next two characters from the hash
|
// Extract the first two and next two characters from the hash
|
||||||
let first_two = &hash[0..2];
|
let first_two = &hash[0..2];
|
||||||
let next_two = &hash[2..4];
|
let next_two = &hash[2..4];
|
||||||
|
|
||||||
// Create the path: $REPO/file/XX/YY/XXYY...
|
// Create the path: $REPO/file/XX/YY/XXYY...
|
||||||
self.repo.join("file").join(first_two).join(next_two).join(&hash)
|
self.repo
|
||||||
|
.join("file")
|
||||||
|
.join(first_two)
|
||||||
|
.join(next_two)
|
||||||
|
.join(&hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create parent directories if they don't exist
|
// Create parent directories if they don't exist
|
||||||
|
|
@ -460,7 +480,7 @@ impl Transaction {
|
||||||
Some(pub_name) => {
|
Some(pub_name) => {
|
||||||
debug!("Using specified publisher: {}", pub_name);
|
debug!("Using specified publisher: {}", pub_name);
|
||||||
pub_name.clone()
|
pub_name.clone()
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
debug!("No publisher specified, trying to use default publisher");
|
debug!("No publisher specified, trying to use default publisher");
|
||||||
// If no publisher is specified, use the default publisher from the repository config
|
// If no publisher is specified, use the default publisher from the repository config
|
||||||
|
|
@ -472,18 +492,19 @@ impl Transaction {
|
||||||
Some(default_pub) => {
|
Some(default_pub) => {
|
||||||
debug!("Using default publisher: {}", default_pub);
|
debug!("Using default publisher: {}", default_pub);
|
||||||
default_pub
|
default_pub
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
debug!("No default publisher set in repository");
|
debug!("No default publisher set in repository");
|
||||||
return Err(RepositoryError::Other(
|
return Err(RepositoryError::Other(
|
||||||
"No publisher specified and no default publisher set in repository".to_string()
|
"No publisher specified and no default publisher set in repository"
|
||||||
))
|
.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Repository configuration not found");
|
debug!("Repository configuration not found");
|
||||||
return Err(RepositoryError::Other(
|
return Err(RepositoryError::Other(
|
||||||
"No publisher specified and repository configuration not found".to_string()
|
"No publisher specified and repository configuration not found".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -498,7 +519,12 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the manifest path using the helper method
|
// Construct the manifest path using the helper method
|
||||||
let pkg_manifest_path = FileBackend::construct_manifest_path(&self.repo, &publisher, &package_stem, &package_version);
|
let pkg_manifest_path = FileBackend::construct_manifest_path(
|
||||||
|
&self.repo,
|
||||||
|
&publisher,
|
||||||
|
&package_stem,
|
||||||
|
&package_version,
|
||||||
|
);
|
||||||
debug!("Manifest path: {}", pkg_manifest_path.display());
|
debug!("Manifest path: {}", pkg_manifest_path.display());
|
||||||
|
|
||||||
// Create parent directories if they don't exist
|
// Create parent directories if they don't exist
|
||||||
|
|
@ -508,7 +534,11 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to pkg directory
|
// Copy to pkg directory
|
||||||
debug!("Copying manifest from {} to {}", manifest_path.display(), pkg_manifest_path.display());
|
debug!(
|
||||||
|
"Copying manifest from {} to {}",
|
||||||
|
manifest_path.display(),
|
||||||
|
pkg_manifest_path.display()
|
||||||
|
);
|
||||||
fs::copy(&manifest_path, &pkg_manifest_path)?;
|
fs::copy(&manifest_path, &pkg_manifest_path)?;
|
||||||
|
|
||||||
// Clean up the transaction directory
|
// Clean up the transaction directory
|
||||||
|
|
@ -653,13 +683,19 @@ impl ReadableRepository for FileBackend {
|
||||||
if publisher_pkg_dir.exists() {
|
if publisher_pkg_dir.exists() {
|
||||||
// Verify that the publisher is in the config
|
// Verify that the publisher is in the config
|
||||||
if !self.config.publishers.contains(&pub_name) {
|
if !self.config.publishers.contains(&pub_name) {
|
||||||
return Err(RepositoryError::Other(
|
return Err(RepositoryError::Other(format!(
|
||||||
format!("Publisher directory exists but is not in the repository configuration: {}", pub_name)
|
"Publisher directory exists but is not in the repository configuration: {}",
|
||||||
));
|
pub_name
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively walk through the directory and collect package manifests
|
// Recursively walk through the directory and collect package manifests
|
||||||
self.find_manifests_recursive(&publisher_pkg_dir, &pub_name, pattern, &mut packages)?;
|
self.find_manifests_recursive(
|
||||||
|
&publisher_pkg_dir,
|
||||||
|
&pub_name,
|
||||||
|
pattern,
|
||||||
|
&mut packages,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -964,16 +1000,16 @@ impl ReadableRepository for FileBackend {
|
||||||
// For each publisher, search the index
|
// For each publisher, search the index
|
||||||
for pub_name in publishers {
|
for pub_name in publishers {
|
||||||
debug!("Searching publisher: {}", pub_name);
|
debug!("Searching publisher: {}", pub_name);
|
||||||
|
|
||||||
// Check if the index exists
|
// Check if the index exists
|
||||||
let index_path = self.path.join("index").join(&pub_name).join("search.json");
|
let index_path = self.path.join("index").join(&pub_name).join("search.json");
|
||||||
debug!("Index path: {}", index_path.display());
|
debug!("Index path: {}", index_path.display());
|
||||||
debug!("Index exists: {}", index_path.exists());
|
debug!("Index exists: {}", index_path.exists());
|
||||||
|
|
||||||
if let Ok(Some(index)) = self.get_search_index(&pub_name) {
|
if let Ok(Some(index)) = self.get_search_index(&pub_name) {
|
||||||
debug!("Got search index for publisher: {}", pub_name);
|
debug!("Got search index for publisher: {}", pub_name);
|
||||||
debug!("Index terms: {:?}", index.terms.keys().collect::<Vec<_>>());
|
debug!("Index terms: {:?}", index.terms.keys().collect::<Vec<_>>());
|
||||||
|
|
||||||
// Search the index
|
// Search the index
|
||||||
let fmris = index.search(query, limit);
|
let fmris = index.search(query, limit);
|
||||||
debug!("Search results (FMRIs): {:?}", fmris);
|
debug!("Search results (FMRIs): {:?}", fmris);
|
||||||
|
|
@ -990,7 +1026,7 @@ impl ReadableRepository for FileBackend {
|
||||||
} else {
|
} else {
|
||||||
debug!("No search index found for publisher: {}", pub_name);
|
debug!("No search index found for publisher: {}", pub_name);
|
||||||
debug!("Falling back to simple search");
|
debug!("Falling back to simple search");
|
||||||
|
|
||||||
// If the index doesn't exist, fall back to the simple search
|
// If the index doesn't exist, fall back to the simple search
|
||||||
let all_packages = self.list_packages(Some(&pub_name), None)?;
|
let all_packages = self.list_packages(Some(&pub_name), None)?;
|
||||||
debug!("All packages: {:?}", all_packages);
|
debug!("All packages: {:?}", all_packages);
|
||||||
|
|
@ -1093,14 +1129,16 @@ impl WritableRepository for FileBackend {
|
||||||
|
|
||||||
// Remove the catalog directory if it exists
|
// Remove the catalog directory if it exists
|
||||||
if catalog_dir.exists() {
|
if catalog_dir.exists() {
|
||||||
fs::remove_dir_all(&catalog_dir)
|
fs::remove_dir_all(&catalog_dir).map_err(|e| {
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to remove catalog directory: {}", e)))?;
|
RepositoryError::Other(format!("Failed to remove catalog directory: {}", e))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the package directory if it exists
|
// Remove the package directory if it exists
|
||||||
if pkg_dir.exists() {
|
if pkg_dir.exists() {
|
||||||
fs::remove_dir_all(&pkg_dir)
|
fs::remove_dir_all(&pkg_dir).map_err(|e| {
|
||||||
.map_err(|e| RepositoryError::Other(format!("Failed to remove package directory: {}", e)))?;
|
RepositoryError::Other(format!("Failed to remove package directory: {}", e))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the updated configuration
|
// Save the updated configuration
|
||||||
|
|
@ -1146,8 +1184,11 @@ impl WritableRepository for FileBackend {
|
||||||
|
|
||||||
/// Rebuild repository metadata
|
/// Rebuild repository metadata
|
||||||
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
||||||
debug!("rebuild called with publisher: {:?}, no_catalog: {}, no_index: {}", publisher, no_catalog, no_index);
|
debug!(
|
||||||
|
"rebuild called with publisher: {:?}, no_catalog: {}, no_index: {}",
|
||||||
|
publisher, no_catalog, no_index
|
||||||
|
);
|
||||||
|
|
||||||
// Filter publishers if specified
|
// Filter publishers if specified
|
||||||
let publishers = if let Some(pub_name) = publisher {
|
let publishers = if let Some(pub_name) = publisher {
|
||||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||||
|
|
@ -1156,7 +1197,10 @@ impl WritableRepository for FileBackend {
|
||||||
debug!("rebuild: using specified publisher: {}", pub_name);
|
debug!("rebuild: using specified publisher: {}", pub_name);
|
||||||
vec![pub_name.to_string()]
|
vec![pub_name.to_string()]
|
||||||
} else {
|
} else {
|
||||||
debug!("rebuild: using all publishers: {:?}", self.config.publishers);
|
debug!(
|
||||||
|
"rebuild: using all publishers: {:?}",
|
||||||
|
self.config.publishers
|
||||||
|
);
|
||||||
self.config.publishers.clone()
|
self.config.publishers.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1227,7 +1271,12 @@ impl WritableRepository for FileBackend {
|
||||||
|
|
||||||
impl FileBackend {
|
impl FileBackend {
|
||||||
/// Helper method to construct a manifest path consistently
|
/// Helper method to construct a manifest path consistently
|
||||||
fn construct_manifest_path(base_path: &Path, publisher: &str, stem: &str, version: &str) -> PathBuf {
|
fn construct_manifest_path(
|
||||||
|
base_path: &Path,
|
||||||
|
publisher: &str,
|
||||||
|
stem: &str,
|
||||||
|
version: &str,
|
||||||
|
) -> PathBuf {
|
||||||
let pkg_dir = base_path.join("pkg").join(publisher).join(stem);
|
let pkg_dir = base_path.join("pkg").join(publisher).join(stem);
|
||||||
let encoded_version = Self::url_encode(version);
|
let encoded_version = Self::url_encode(version);
|
||||||
pkg_dir.join(encoded_version)
|
pkg_dir.join(encoded_version)
|
||||||
|
|
@ -1253,26 +1302,36 @@ impl FileBackend {
|
||||||
let mut file = match fs::File::open(&path) {
|
let mut file = match fs::File::open(&path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("FileBackend::find_manifests_recursive: Error opening file {}: {}", path.display(), err);
|
error!(
|
||||||
|
"FileBackend::find_manifests_recursive: Error opening file {}: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer = [0; 1024];
|
let mut buffer = [0; 1024];
|
||||||
let bytes_read = match file.read(&mut buffer) {
|
let bytes_read = match file.read(&mut buffer) {
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("FileBackend::find_manifests_recursive: Error reading file {}: {}", path.display(), err);
|
error!(
|
||||||
|
"FileBackend::find_manifests_recursive: Error reading file {}: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the file starts with a valid manifest marker
|
// Check if the file starts with a valid manifest marker
|
||||||
// For example, if it's a JSON file, it should start with '{'
|
// For example, if it's a JSON file, it should start with '{'
|
||||||
if bytes_read == 0 || (buffer[0] != b'{' && buffer[0] != b'<' && buffer[0] != b's') {
|
if bytes_read == 0
|
||||||
|
|| (buffer[0] != b'{' && buffer[0] != b'<' && buffer[0] != b's')
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process manifest files
|
// Process manifest files
|
||||||
match Manifest::parse_file(&path) {
|
match Manifest::parse_file(&path) {
|
||||||
Ok(manifest) => {
|
Ok(manifest) => {
|
||||||
|
|
@ -1307,7 +1366,8 @@ impl FileBackend {
|
||||||
// If the publisher is not set in the FMRI, use the current publisher
|
// If the publisher is not set in the FMRI, use the current publisher
|
||||||
if parsed_fmri.publisher.is_none() {
|
if parsed_fmri.publisher.is_none() {
|
||||||
let mut fmri_with_publisher = parsed_fmri.clone();
|
let mut fmri_with_publisher = parsed_fmri.clone();
|
||||||
fmri_with_publisher.publisher = Some(publisher.to_string());
|
fmri_with_publisher.publisher =
|
||||||
|
Some(publisher.to_string());
|
||||||
|
|
||||||
// Create a PackageInfo struct and add it to the list
|
// Create a PackageInfo struct and add it to the list
|
||||||
packages.push(PackageInfo {
|
packages.push(PackageInfo {
|
||||||
|
|
@ -1360,76 +1420,83 @@ impl FileBackend {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rebuild catalog for a publisher
|
/// Rebuild catalog for a publisher
|
||||||
///
|
///
|
||||||
/// This method generates catalog files for a publisher and stores them in the publisher's
|
/// This method generates catalog files for a publisher and stores them in the publisher's
|
||||||
/// subdirectory within the catalog directory.
|
/// subdirectory within the catalog directory.
|
||||||
pub fn rebuild_catalog(&self, publisher: &str, create_update_log: bool) -> Result<()> {
|
pub fn rebuild_catalog(&self, publisher: &str, create_update_log: bool) -> Result<()> {
|
||||||
info!("Rebuilding catalog for publisher: {}", publisher);
|
info!("Rebuilding catalog for publisher: {}", publisher);
|
||||||
debug!("Catalog directory path: {}", self.path.join("catalog").display());
|
debug!(
|
||||||
|
"Catalog directory path: {}",
|
||||||
|
self.path.join("catalog").display()
|
||||||
|
);
|
||||||
|
|
||||||
// Create the catalog directory for the publisher if it doesn't exist
|
// Create the catalog directory for the publisher if it doesn't exist
|
||||||
let catalog_dir = self.path.join("catalog").join(publisher);
|
let catalog_dir = self.path.join("catalog").join(publisher);
|
||||||
debug!("Publisher catalog directory: {}", catalog_dir.display());
|
debug!("Publisher catalog directory: {}", catalog_dir.display());
|
||||||
fs::create_dir_all(&catalog_dir)?;
|
fs::create_dir_all(&catalog_dir)?;
|
||||||
debug!("Created publisher catalog directory");
|
debug!("Created publisher catalog directory");
|
||||||
|
|
||||||
// Collect package data
|
// Collect package data
|
||||||
let packages = self.list_packages(Some(publisher), None)?;
|
let packages = self.list_packages(Some(publisher), None)?;
|
||||||
|
|
||||||
// Prepare data structures for catalog parts
|
// Prepare data structures for catalog parts
|
||||||
let mut base_entries = Vec::new();
|
let mut base_entries = Vec::new();
|
||||||
let mut dependency_entries = Vec::new();
|
let mut dependency_entries = Vec::new();
|
||||||
let mut summary_entries = Vec::new();
|
let mut summary_entries = Vec::new();
|
||||||
let mut update_entries = Vec::new();
|
let mut update_entries = Vec::new();
|
||||||
|
|
||||||
// Track package counts
|
// Track package counts
|
||||||
let mut package_count = 0;
|
let mut package_count = 0;
|
||||||
let mut package_version_count = 0;
|
let mut package_version_count = 0;
|
||||||
|
|
||||||
// Process each package
|
// Process each package
|
||||||
for package in packages {
|
for package in packages {
|
||||||
let fmri = &package.fmri;
|
let fmri = &package.fmri;
|
||||||
let stem = fmri.stem();
|
let stem = fmri.stem();
|
||||||
|
|
||||||
// Skip if no version
|
// Skip if no version
|
||||||
if fmri.version().is_empty() {
|
if fmri.version().is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the package version
|
// Get the package version
|
||||||
let version = fmri.version();
|
let version = fmri.version();
|
||||||
|
|
||||||
// Construct the manifest path using the helper method
|
// Construct the manifest path using the helper method
|
||||||
let manifest_path = Self::construct_manifest_path(&self.path, publisher, stem, &version);
|
let manifest_path =
|
||||||
|
Self::construct_manifest_path(&self.path, publisher, stem, &version);
|
||||||
|
|
||||||
// Check if the package directory exists
|
// Check if the package directory exists
|
||||||
if let Some(pkg_dir) = manifest_path.parent() {
|
if let Some(pkg_dir) = manifest_path.parent() {
|
||||||
if !pkg_dir.exists() {
|
if !pkg_dir.exists() {
|
||||||
error!("Package directory {} does not exist skipping", pkg_dir.display());
|
error!(
|
||||||
|
"Package directory {} does not exist skipping",
|
||||||
|
pkg_dir.display()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the manifest content for hash calculation
|
// Read the manifest content for hash calculation
|
||||||
let manifest_content = fs::read_to_string(&manifest_path)?;
|
let manifest_content = fs::read_to_string(&manifest_path)?;
|
||||||
|
|
||||||
// Parse the manifest using parse_file which handles JSON correctly
|
// Parse the manifest using parse_file which handles JSON correctly
|
||||||
let manifest = Manifest::parse_file(&manifest_path)?;
|
let manifest = Manifest::parse_file(&manifest_path)?;
|
||||||
|
|
||||||
// Calculate SHA-256 hash of the manifest (as a substitute for SHA-1)
|
// Calculate SHA-256 hash of the manifest (as a substitute for SHA-1)
|
||||||
let mut hasher = sha2::Sha256::new();
|
let mut hasher = sha2::Sha256::new();
|
||||||
hasher.update(manifest_content.as_bytes());
|
hasher.update(manifest_content.as_bytes());
|
||||||
let signature = format!("{:x}", hasher.finalize());
|
let signature = format!("{:x}", hasher.finalize());
|
||||||
|
|
||||||
// Add to base entries
|
// Add to base entries
|
||||||
base_entries.push((fmri.clone(), None, signature.clone()));
|
base_entries.push((fmri.clone(), None, signature.clone()));
|
||||||
|
|
||||||
// Extract dependency actions
|
// Extract dependency actions
|
||||||
let mut dependency_actions = Vec::new();
|
let mut dependency_actions = Vec::new();
|
||||||
for dep in &manifest.dependencies {
|
for dep in &manifest.dependencies {
|
||||||
|
|
@ -1440,7 +1507,7 @@ impl FileBackend {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract variant and facet actions
|
// Extract variant and facet actions
|
||||||
for attr in &manifest.attributes {
|
for attr in &manifest.attributes {
|
||||||
if attr.key.starts_with("variant.") || attr.key.starts_with("facet.") {
|
if attr.key.starts_with("variant.") || attr.key.starts_with("facet.") {
|
||||||
|
|
@ -1448,7 +1515,7 @@ impl FileBackend {
|
||||||
dependency_actions.push(format!("set name={} value={}", attr.key, values_str));
|
dependency_actions.push(format!("set name={} value={}", attr.key, values_str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to dependency entries if there are dependency actions
|
// Add to dependency entries if there are dependency actions
|
||||||
if !dependency_actions.is_empty() {
|
if !dependency_actions.is_empty() {
|
||||||
dependency_entries.push((
|
dependency_entries.push((
|
||||||
|
|
@ -1457,7 +1524,7 @@ impl FileBackend {
|
||||||
signature.clone(),
|
signature.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract summary actions (set actions excluding variants and facets)
|
// Extract summary actions (set actions excluding variants and facets)
|
||||||
let mut summary_actions = Vec::new();
|
let mut summary_actions = Vec::new();
|
||||||
for attr in &manifest.attributes {
|
for attr in &manifest.attributes {
|
||||||
|
|
@ -1466,7 +1533,7 @@ impl FileBackend {
|
||||||
summary_actions.push(format!("set name={} value={}", attr.key, values_str));
|
summary_actions.push(format!("set name={} value={}", attr.key, values_str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to summary entries if there are summary actions
|
// Add to summary entries if there are summary actions
|
||||||
if !summary_actions.is_empty() {
|
if !summary_actions.is_empty() {
|
||||||
summary_entries.push((
|
summary_entries.push((
|
||||||
|
|
@ -1475,40 +1542,40 @@ impl FileBackend {
|
||||||
signature.clone(),
|
signature.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare update entry if needed
|
// Prepare update entry if needed
|
||||||
if create_update_log {
|
if create_update_log {
|
||||||
let mut catalog_parts = HashMap::new();
|
let mut catalog_parts = HashMap::new();
|
||||||
|
|
||||||
// Add dependency actions to update entry
|
// Add dependency actions to update entry
|
||||||
if !dependency_actions.is_empty() {
|
if !dependency_actions.is_empty() {
|
||||||
let mut actions = HashMap::new();
|
let mut actions = HashMap::new();
|
||||||
actions.insert("actions".to_string(), dependency_actions);
|
actions.insert("actions".to_string(), dependency_actions);
|
||||||
catalog_parts.insert("catalog.dependency.C".to_string(), actions);
|
catalog_parts.insert("catalog.dependency.C".to_string(), actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add summary actions to update entry
|
// Add summary actions to update entry
|
||||||
if !summary_actions.is_empty() {
|
if !summary_actions.is_empty() {
|
||||||
let mut actions = HashMap::new();
|
let mut actions = HashMap::new();
|
||||||
actions.insert("actions".to_string(), summary_actions);
|
actions.insert("actions".to_string(), summary_actions);
|
||||||
catalog_parts.insert("catalog.summary.C".to_string(), actions);
|
catalog_parts.insert("catalog.summary.C".to_string(), actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to update entries
|
// Add to update entries
|
||||||
update_entries.push((fmri.clone(), catalog_parts, signature));
|
update_entries.push((fmri.clone(), catalog_parts, signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update counts
|
// Update counts
|
||||||
package_count += 1;
|
package_count += 1;
|
||||||
package_version_count += 1;
|
package_version_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and save catalog parts
|
// Create and save catalog parts
|
||||||
|
|
||||||
// Create a catalog.attrs file
|
// Create a catalog.attrs file
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let timestamp = format_iso8601_timestamp(&now);
|
let timestamp = format_iso8601_timestamp(&now);
|
||||||
|
|
||||||
// Get the CatalogAttrs struct definition to see what fields it has
|
// Get the CatalogAttrs struct definition to see what fields it has
|
||||||
let mut attrs = crate::repository::catalog::CatalogAttrs {
|
let mut attrs = crate::repository::catalog::CatalogAttrs {
|
||||||
created: timestamp.clone(),
|
created: timestamp.clone(),
|
||||||
|
|
@ -1520,7 +1587,7 @@ impl FileBackend {
|
||||||
signature: None,
|
signature: None,
|
||||||
updates: HashMap::new(),
|
updates: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add part information
|
// Add part information
|
||||||
let base_part_name = "catalog.base.C";
|
let base_part_name = "catalog.base.C";
|
||||||
attrs.parts.insert(
|
attrs.parts.insert(
|
||||||
|
|
@ -1530,7 +1597,7 @@ impl FileBackend {
|
||||||
signature_sha1: None,
|
signature_sha1: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let dependency_part_name = "catalog.dependency.C";
|
let dependency_part_name = "catalog.dependency.C";
|
||||||
attrs.parts.insert(
|
attrs.parts.insert(
|
||||||
dependency_part_name.to_string(),
|
dependency_part_name.to_string(),
|
||||||
|
|
@ -1539,7 +1606,7 @@ impl FileBackend {
|
||||||
signature_sha1: None,
|
signature_sha1: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let summary_part_name = "catalog.summary.C";
|
let summary_part_name = "catalog.summary.C";
|
||||||
attrs.parts.insert(
|
attrs.parts.insert(
|
||||||
summary_part_name.to_string(),
|
summary_part_name.to_string(),
|
||||||
|
|
@ -1548,16 +1615,16 @@ impl FileBackend {
|
||||||
signature_sha1: None,
|
signature_sha1: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Save the catalog.attrs file
|
// Save the catalog.attrs file
|
||||||
let attrs_path = catalog_dir.join("catalog.attrs");
|
let attrs_path = catalog_dir.join("catalog.attrs");
|
||||||
debug!("Writing catalog.attrs to: {}", attrs_path.display());
|
debug!("Writing catalog.attrs to: {}", attrs_path.display());
|
||||||
let attrs_json = serde_json::to_string_pretty(&attrs)?;
|
let attrs_json = serde_json::to_string_pretty(&attrs)?;
|
||||||
fs::write(&attrs_path, attrs_json)?;
|
fs::write(&attrs_path, attrs_json)?;
|
||||||
debug!("Wrote catalog.attrs file");
|
debug!("Wrote catalog.attrs file");
|
||||||
|
|
||||||
// Create and save catalog parts
|
// Create and save catalog parts
|
||||||
|
|
||||||
// Base part
|
// Base part
|
||||||
let base_part_path = catalog_dir.join(base_part_name);
|
let base_part_path = catalog_dir.join(base_part_name);
|
||||||
debug!("Writing base part to: {}", base_part_path.display());
|
debug!("Writing base part to: {}", base_part_path.display());
|
||||||
|
|
@ -1568,10 +1635,13 @@ impl FileBackend {
|
||||||
let base_part_json = serde_json::to_string_pretty(&base_part)?;
|
let base_part_json = serde_json::to_string_pretty(&base_part)?;
|
||||||
fs::write(&base_part_path, base_part_json)?;
|
fs::write(&base_part_path, base_part_json)?;
|
||||||
debug!("Wrote base part file");
|
debug!("Wrote base part file");
|
||||||
|
|
||||||
// Dependency part
|
// Dependency part
|
||||||
let dependency_part_path = catalog_dir.join(dependency_part_name);
|
let dependency_part_path = catalog_dir.join(dependency_part_name);
|
||||||
debug!("Writing dependency part to: {}", dependency_part_path.display());
|
debug!(
|
||||||
|
"Writing dependency part to: {}",
|
||||||
|
dependency_part_path.display()
|
||||||
|
);
|
||||||
let mut dependency_part = crate::repository::catalog::CatalogPart::new();
|
let mut dependency_part = crate::repository::catalog::CatalogPart::new();
|
||||||
for (fmri, actions, signature) in dependency_entries {
|
for (fmri, actions, signature) in dependency_entries {
|
||||||
dependency_part.add_package(publisher, &fmri, actions, Some(signature));
|
dependency_part.add_package(publisher, &fmri, actions, Some(signature));
|
||||||
|
|
@ -1579,7 +1649,7 @@ impl FileBackend {
|
||||||
let dependency_part_json = serde_json::to_string_pretty(&dependency_part)?;
|
let dependency_part_json = serde_json::to_string_pretty(&dependency_part)?;
|
||||||
fs::write(&dependency_part_path, dependency_part_json)?;
|
fs::write(&dependency_part_path, dependency_part_json)?;
|
||||||
debug!("Wrote dependency part file");
|
debug!("Wrote dependency part file");
|
||||||
|
|
||||||
// Summary part
|
// Summary part
|
||||||
let summary_part_path = catalog_dir.join(summary_part_name);
|
let summary_part_path = catalog_dir.join(summary_part_name);
|
||||||
debug!("Writing summary part to: {}", summary_part_path.display());
|
debug!("Writing summary part to: {}", summary_part_path.display());
|
||||||
|
|
@ -1590,14 +1660,14 @@ impl FileBackend {
|
||||||
let summary_part_json = serde_json::to_string_pretty(&summary_part)?;
|
let summary_part_json = serde_json::to_string_pretty(&summary_part)?;
|
||||||
fs::write(&summary_part_path, summary_part_json)?;
|
fs::write(&summary_part_path, summary_part_json)?;
|
||||||
debug!("Wrote summary part file");
|
debug!("Wrote summary part file");
|
||||||
|
|
||||||
// Create and save the update log if needed
|
// Create and save the update log if needed
|
||||||
if create_update_log {
|
if create_update_log {
|
||||||
debug!("Creating update log");
|
debug!("Creating update log");
|
||||||
let update_log_name = format!("update.{}Z.C", timestamp.split('.').next().unwrap());
|
let update_log_name = format!("update.{}Z.C", timestamp.split('.').next().unwrap());
|
||||||
let update_log_path = catalog_dir.join(&update_log_name);
|
let update_log_path = catalog_dir.join(&update_log_name);
|
||||||
debug!("Update log path: {}", update_log_path.display());
|
debug!("Update log path: {}", update_log_path.display());
|
||||||
|
|
||||||
let mut update_log = crate::repository::catalog::UpdateLog::new();
|
let mut update_log = crate::repository::catalog::UpdateLog::new();
|
||||||
debug!("Adding {} updates to the log", update_entries.len());
|
debug!("Adding {} updates to the log", update_entries.len());
|
||||||
for (fmri, catalog_parts, signature) in update_entries {
|
for (fmri, catalog_parts, signature) in update_entries {
|
||||||
|
|
@ -1609,11 +1679,11 @@ impl FileBackend {
|
||||||
Some(signature),
|
Some(signature),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let update_log_json = serde_json::to_string_pretty(&update_log)?;
|
let update_log_json = serde_json::to_string_pretty(&update_log)?;
|
||||||
fs::write(&update_log_path, update_log_json)?;
|
fs::write(&update_log_path, update_log_json)?;
|
||||||
debug!("Wrote update log file");
|
debug!("Wrote update log file");
|
||||||
|
|
||||||
// Add an update log to catalog.attrs
|
// Add an update log to catalog.attrs
|
||||||
debug!("Adding update log to catalog.attrs");
|
debug!("Adding update log to catalog.attrs");
|
||||||
attrs.updates.insert(
|
attrs.updates.insert(
|
||||||
|
|
@ -1623,18 +1693,18 @@ impl FileBackend {
|
||||||
signature_sha1: None,
|
signature_sha1: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the catalog.attrs file with the new update log
|
// Update the catalog.attrs file with the new update log
|
||||||
debug!("Updating catalog.attrs file with new update log");
|
debug!("Updating catalog.attrs file with new update log");
|
||||||
let attrs_json = serde_json::to_string_pretty(&attrs)?;
|
let attrs_json = serde_json::to_string_pretty(&attrs)?;
|
||||||
fs::write(catalog_dir.join("catalog.attrs"), attrs_json)?;
|
fs::write(catalog_dir.join("catalog.attrs"), attrs_json)?;
|
||||||
debug!("Updated catalog.attrs file");
|
debug!("Updated catalog.attrs file");
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Catalog rebuilt for publisher: {}", publisher);
|
info!("Catalog rebuilt for publisher: {}", publisher);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the file path for a given hash using the new directory structure
|
/// Generate the file path for a given hash using the new directory structure
|
||||||
/// The path will be $REPO/file/XX/YY/XXYY... where XX and YY are the first four letters of the hash
|
/// The path will be $REPO/file/XX/YY/XXYY... where XX and YY are the first four letters of the hash
|
||||||
fn generate_file_path(&self, hash: &str) -> PathBuf {
|
fn generate_file_path(&self, hash: &str) -> PathBuf {
|
||||||
|
|
@ -1642,17 +1712,21 @@ impl FileBackend {
|
||||||
// Fallback for very short hashes (shouldn't happen with SHA256)
|
// Fallback for very short hashes (shouldn't happen with SHA256)
|
||||||
return self.path.join("file").join(hash);
|
return self.path.join("file").join(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the first two and next two characters from the hash
|
// Extract the first two and next two characters from the hash
|
||||||
let first_two = &hash[0..2];
|
let first_two = &hash[0..2];
|
||||||
let next_two = &hash[2..4];
|
let next_two = &hash[2..4];
|
||||||
|
|
||||||
// Create the path: $REPO/file/XX/YY/XXYY...
|
// Create the path: $REPO/file/XX/YY/XXYY...
|
||||||
self.path.join("file").join(first_two).join(next_two).join(hash)
|
self.path
|
||||||
|
.join("file")
|
||||||
|
.join(first_two)
|
||||||
|
.join(next_two)
|
||||||
|
.join(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get or initialize the catalog manager
|
/// Get or initialize the catalog manager
|
||||||
///
|
///
|
||||||
/// This method returns a mutable reference to the catalog manager.
|
/// This method returns a mutable reference to the catalog manager.
|
||||||
/// It uses interior mutability with RefCell to allow mutation through an immutable reference.
|
/// It uses interior mutability with RefCell to allow mutation through an immutable reference.
|
||||||
pub fn get_catalog_manager(
|
pub fn get_catalog_manager(
|
||||||
|
|
@ -1888,7 +1962,7 @@ impl FileBackend {
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
let mut transaction = self.begin_transaction()?;
|
let mut transaction = self.begin_transaction()?;
|
||||||
|
|
||||||
// Set the publisher for the transaction
|
// Set the publisher for the transaction
|
||||||
transaction.set_publisher(publisher);
|
transaction.set_publisher(publisher);
|
||||||
|
|
||||||
|
|
@ -1913,11 +1987,10 @@ impl FileBackend {
|
||||||
let actual_path = &transaction.manifest.files[0].path;
|
let actual_path = &transaction.manifest.files[0].path;
|
||||||
|
|
||||||
if actual_path != expected_path {
|
if actual_path != expected_path {
|
||||||
return Err(RepositoryError::Other(
|
return Err(RepositoryError::Other(format!(
|
||||||
format!("Path in FileAction is incorrect. Expected: {}, Actual: {}",
|
"Path in FileAction is incorrect. Expected: {}, Actual: {}",
|
||||||
expected_path,
|
expected_path, actual_path
|
||||||
actual_path)
|
)));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
|
|
@ -1928,12 +2001,18 @@ impl FileBackend {
|
||||||
let stored_file_path = self.generate_file_path(&hash);
|
let stored_file_path = self.generate_file_path(&hash);
|
||||||
|
|
||||||
if !stored_file_path.exists() {
|
if !stored_file_path.exists() {
|
||||||
return Err(RepositoryError::Other("File was not stored correctly".to_string()));
|
return Err(RepositoryError::Other(
|
||||||
|
"File was not stored correctly".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the manifest was updated in the publisher-specific directory
|
// Verify the manifest was updated in the publisher-specific directory
|
||||||
// The manifest should be named "unknown.manifest" since we didn't set a package name
|
// The manifest should be named "unknown.manifest" since we didn't set a package name
|
||||||
let manifest_path = self.path.join("pkg").join(publisher).join("unknown.manifest");
|
let manifest_path = self
|
||||||
|
.path
|
||||||
|
.join("pkg")
|
||||||
|
.join(publisher)
|
||||||
|
.join("unknown.manifest");
|
||||||
|
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
return Err(RepositoryError::Other(format!(
|
return Err(RepositoryError::Other(format!(
|
||||||
|
|
@ -1974,7 +2053,7 @@ impl FileBackend {
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
let mut transaction = self.begin_transaction()?;
|
let mut transaction = self.begin_transaction()?;
|
||||||
|
|
||||||
// Set the publisher for the transaction
|
// Set the publisher for the transaction
|
||||||
transaction.set_publisher(publisher);
|
transaction.set_publisher(publisher);
|
||||||
|
|
||||||
|
|
@ -1983,7 +2062,7 @@ impl FileBackend {
|
||||||
|
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
transaction.commit()?;
|
transaction.commit()?;
|
||||||
|
|
||||||
// Regenerate catalog and search index
|
// Regenerate catalog and search index
|
||||||
self.rebuild(Some(publisher), false, false)?;
|
self.rebuild(Some(publisher), false, false)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
// MPL was not distributed with this file, You can
|
// MPL was not distributed with this file, You can
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use miette::Diagnostic;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, StripPrefixError};
|
use std::path::{Path, StripPrefixError};
|
||||||
use miette::Diagnostic;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Result type for repository operations
|
/// Result type for repository operations
|
||||||
|
|
@ -149,25 +149,25 @@ impl From<DigestError> for RepositoryError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl From<StripPrefixError> for RepositoryError {
|
impl From<StripPrefixError> for RepositoryError {
|
||||||
fn from(err: StripPrefixError) -> Self {
|
fn from(err: StripPrefixError) -> Self {
|
||||||
RepositoryError::PathPrefixError(err.to_string())
|
RepositoryError::PathPrefixError(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mod catalog;
|
mod catalog;
|
||||||
mod file_backend;
|
mod file_backend;
|
||||||
mod rest_backend;
|
mod rest_backend;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use catalog::{CatalogAttrs, CatalogError, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog};
|
|
||||||
pub use file_backend::FileBackend;
|
|
||||||
pub use rest_backend::RestBackend;
|
|
||||||
use crate::actions::ActionError;
|
use crate::actions::ActionError;
|
||||||
use crate::digest::DigestError;
|
use crate::digest::DigestError;
|
||||||
|
pub use catalog::{
|
||||||
|
CatalogAttrs, CatalogError, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog,
|
||||||
|
};
|
||||||
|
pub use file_backend::FileBackend;
|
||||||
|
pub use rest_backend::RestBackend;
|
||||||
|
|
||||||
/// Repository configuration filename
|
/// Repository configuration filename
|
||||||
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";
|
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ mod tests {
|
||||||
use crate::actions::Manifest;
|
use crate::actions::Manifest;
|
||||||
use crate::fmri::Fmri;
|
use crate::fmri::Fmri;
|
||||||
use crate::repository::{
|
use crate::repository::{
|
||||||
CatalogManager, FileBackend, ReadableRepository, RepositoryError, RepositoryVersion, Result,
|
CatalogManager, FileBackend, ReadableRepository, RepositoryError, RepositoryVersion,
|
||||||
WritableRepository, REPOSITORY_CONFIG_FILENAME,
|
Result, WritableRepository, REPOSITORY_CONFIG_FILENAME,
|
||||||
};
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -83,17 +83,19 @@ mod tests {
|
||||||
// Check if the manifest file exists
|
// Check if the manifest file exists
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
println!("Error: Manifest file does not exist");
|
println!("Error: Manifest file does not exist");
|
||||||
return Err(RepositoryError::FileReadError(
|
return Err(RepositoryError::FileReadError(format!(
|
||||||
format!("Manifest file does not exist: {}", manifest_path.display())
|
"Manifest file does not exist: {}",
|
||||||
));
|
manifest_path.display()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the prototype directory exists
|
// Check if the prototype directory exists
|
||||||
if !prototype_dir.exists() {
|
if !prototype_dir.exists() {
|
||||||
println!("Error: Prototype directory does not exist");
|
println!("Error: Prototype directory does not exist");
|
||||||
return Err(RepositoryError::NotFound(
|
return Err(RepositoryError::NotFound(format!(
|
||||||
format!("Prototype directory does not exist: {}", prototype_dir.display())
|
"Prototype directory does not exist: {}",
|
||||||
));
|
prototype_dir.display()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the manifest file
|
// Parse the manifest file
|
||||||
|
|
@ -393,38 +395,50 @@ mod tests {
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_file_structure() {
|
fn test_file_structure() {
|
||||||
// Create a test directory
|
// Create a test directory
|
||||||
let test_dir = create_test_dir("file_structure");
|
let test_dir = create_test_dir("file_structure");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Add a publisher
|
// Add a publisher
|
||||||
repo.add_publisher("test").unwrap();
|
repo.add_publisher("test").unwrap();
|
||||||
|
|
||||||
// Create a test file
|
// Create a test file
|
||||||
let test_file_path = test_dir.join("test_file.txt");
|
let test_file_path = test_dir.join("test_file.txt");
|
||||||
fs::write(&test_file_path, "This is a test file").unwrap();
|
fs::write(&test_file_path, "This is a test file").unwrap();
|
||||||
|
|
||||||
// Store the file in the repository
|
// Store the file in the repository
|
||||||
let hash = repo.store_file(&test_file_path).unwrap();
|
let hash = repo.store_file(&test_file_path).unwrap();
|
||||||
|
|
||||||
// Check if the file was stored in the correct directory structure
|
// Check if the file was stored in the correct directory structure
|
||||||
let first_two = &hash[0..2];
|
let first_two = &hash[0..2];
|
||||||
let next_two = &hash[2..4];
|
let next_two = &hash[2..4];
|
||||||
let expected_path = repo_path.join("file").join(first_two).join(next_two).join(&hash);
|
let expected_path = repo_path
|
||||||
|
.join("file")
|
||||||
|
.join(first_two)
|
||||||
|
.join(next_two)
|
||||||
|
.join(&hash);
|
||||||
|
|
||||||
// Verify that the file exists at the expected path
|
// Verify that the file exists at the expected path
|
||||||
assert!(expected_path.exists(), "File was not stored at the expected path: {}", expected_path.display());
|
assert!(
|
||||||
|
expected_path.exists(),
|
||||||
|
"File was not stored at the expected path: {}",
|
||||||
|
expected_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
// Verify that the file does NOT exist at the old path
|
// Verify that the file does NOT exist at the old path
|
||||||
let old_path = repo_path.join("file").join(&hash);
|
let old_path = repo_path.join("file").join(&hash);
|
||||||
assert!(!old_path.exists(), "File was stored at the old path: {}", old_path.display());
|
assert!(
|
||||||
|
!old_path.exists(),
|
||||||
|
"File was stored at the old path: {}",
|
||||||
|
old_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ mod tests {
|
||||||
|
|
||||||
// Create a simple manifest
|
// Create a simple manifest
|
||||||
let mut manifest = Manifest::new();
|
let mut manifest = Manifest::new();
|
||||||
|
|
||||||
// Add some attributes
|
// Add some attributes
|
||||||
let mut attr = crate::actions::Attr::default();
|
let mut attr = crate::actions::Attr::default();
|
||||||
attr.key = "pkg.fmri".to_string();
|
attr.key = "pkg.fmri".to_string();
|
||||||
|
|
@ -33,7 +33,10 @@ mod tests {
|
||||||
// Verify that the parsed manifest matches the original
|
// Verify that the parsed manifest matches the original
|
||||||
assert_eq!(parsed_manifest.attributes.len(), 1);
|
assert_eq!(parsed_manifest.attributes.len(), 1);
|
||||||
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
|
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
|
||||||
assert_eq!(parsed_manifest.attributes[0].values[0], "pkg://test/example@1.0.0");
|
assert_eq!(
|
||||||
|
parsed_manifest.attributes[0].values[0],
|
||||||
|
"pkg://test/example@1.0.0"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -55,6 +58,9 @@ mod tests {
|
||||||
// Verify that the parsed manifest has the expected attributes
|
// Verify that the parsed manifest has the expected attributes
|
||||||
assert_eq!(parsed_manifest.attributes.len(), 1);
|
assert_eq!(parsed_manifest.attributes.len(), 1);
|
||||||
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
|
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
|
||||||
assert_eq!(parsed_manifest.attributes[0].values[0], "pkg://test/example@1.0.0");
|
assert_eq!(
|
||||||
|
parsed_manifest.attributes[0].values[0],
|
||||||
|
"pkg://test/example@1.0.0"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,54 @@ use std::path::Path;
|
||||||
fn test_parse_postgre_common_manifest() {
|
fn test_parse_postgre_common_manifest() {
|
||||||
let manifest_path = Path::new("/home/toasty/ws/illumos/ips/pkg6repo/postgre-common.manifest");
|
let manifest_path = Path::new("/home/toasty/ws/illumos/ips/pkg6repo/postgre-common.manifest");
|
||||||
let manifest = Manifest::parse_file(manifest_path).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_file(manifest_path).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Check that the manifest contains the expected actions
|
// Check that the manifest contains the expected actions
|
||||||
assert_eq!(manifest.attributes.len(), 11, "Expected 11 attributes");
|
assert_eq!(manifest.attributes.len(), 11, "Expected 11 attributes");
|
||||||
assert_eq!(manifest.directories.len(), 1, "Expected 1 directory");
|
assert_eq!(manifest.directories.len(), 1, "Expected 1 directory");
|
||||||
assert_eq!(manifest.groups.len(), 1, "Expected 1 group");
|
assert_eq!(manifest.groups.len(), 1, "Expected 1 group");
|
||||||
assert_eq!(manifest.users.len(), 1, "Expected 1 user");
|
assert_eq!(manifest.users.len(), 1, "Expected 1 user");
|
||||||
assert_eq!(manifest.licenses.len(), 1, "Expected 1 license");
|
assert_eq!(manifest.licenses.len(), 1, "Expected 1 license");
|
||||||
|
|
||||||
// Check the group action
|
// Check the group action
|
||||||
let group = &manifest.groups[0];
|
let group = &manifest.groups[0];
|
||||||
assert_eq!(group.groupname, "postgres", "Expected groupname to be 'postgres'");
|
assert_eq!(
|
||||||
|
group.groupname, "postgres",
|
||||||
|
"Expected groupname to be 'postgres'"
|
||||||
|
);
|
||||||
assert_eq!(group.gid, "90", "Expected gid to be '90'");
|
assert_eq!(group.gid, "90", "Expected gid to be '90'");
|
||||||
|
|
||||||
// Check the user action
|
// Check the user action
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
assert_eq!(user.username, "postgres", "Expected username to be 'postgres'");
|
assert_eq!(
|
||||||
|
user.username, "postgres",
|
||||||
|
"Expected username to be 'postgres'"
|
||||||
|
);
|
||||||
assert_eq!(user.uid, "90", "Expected uid to be '90'");
|
assert_eq!(user.uid, "90", "Expected uid to be '90'");
|
||||||
assert_eq!(user.group, "postgres", "Expected group to be 'postgres'");
|
assert_eq!(user.group, "postgres", "Expected group to be 'postgres'");
|
||||||
assert_eq!(user.home_dir, "/var/postgres", "Expected home_dir to be '/var/postgres'");
|
assert_eq!(
|
||||||
assert_eq!(user.login_shell, "/usr/bin/pfksh", "Expected login_shell to be '/usr/bin/pfksh'");
|
user.home_dir, "/var/postgres",
|
||||||
|
"Expected home_dir to be '/var/postgres'"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
user.login_shell, "/usr/bin/pfksh",
|
||||||
|
"Expected login_shell to be '/usr/bin/pfksh'"
|
||||||
|
);
|
||||||
assert_eq!(user.password, "NP", "Expected password to be 'NP'");
|
assert_eq!(user.password, "NP", "Expected password to be 'NP'");
|
||||||
assert!(user.services.is_empty(), "Expected no services for ftpuser=false");
|
assert!(
|
||||||
assert_eq!(user.gcos_field, "PostgreSQL Reserved UID", "Expected gcos_field to be 'PostgreSQL Reserved UID'");
|
user.services.is_empty(),
|
||||||
|
"Expected no services for ftpuser=false"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
user.gcos_field, "PostgreSQL Reserved UID",
|
||||||
|
"Expected gcos_field to be 'PostgreSQL Reserved UID'"
|
||||||
|
);
|
||||||
|
|
||||||
// Check the directory action
|
// Check the directory action
|
||||||
let dir = &manifest.directories[0];
|
let dir = &manifest.directories[0];
|
||||||
assert_eq!(dir.path, "var/postgres", "Expected path to be 'var/postgres'");
|
assert_eq!(
|
||||||
|
dir.path, "var/postgres",
|
||||||
|
"Expected path to be 'var/postgres'"
|
||||||
|
);
|
||||||
assert_eq!(dir.group, "postgres", "Expected group to be 'postgres'");
|
assert_eq!(dir.group, "postgres", "Expected group to be 'postgres'");
|
||||||
assert_eq!(dir.owner, "postgres", "Expected owner to be 'postgres'");
|
assert_eq!(dir.owner, "postgres", "Expected owner to be 'postgres'");
|
||||||
assert_eq!(dir.mode, "0755", "Expected mode to be '0755'");
|
assert_eq!(dir.mode, "0755", "Expected mode to be '0755'");
|
||||||
|
|
@ -43,17 +64,29 @@ fn test_parse_postgre_common_manifest() {
|
||||||
fn test_parse_pgadmin_manifest() {
|
fn test_parse_pgadmin_manifest() {
|
||||||
let manifest_path = Path::new("/home/toasty/ws/illumos/ips/pkg6repo/pgadmin.manifest");
|
let manifest_path = Path::new("/home/toasty/ws/illumos/ips/pkg6repo/pgadmin.manifest");
|
||||||
let manifest = Manifest::parse_file(manifest_path).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_file(manifest_path).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Check that the manifest contains the expected actions
|
// Check that the manifest contains the expected actions
|
||||||
assert!(manifest.attributes.len() > 0, "Expected attributes");
|
assert!(manifest.attributes.len() > 0, "Expected attributes");
|
||||||
assert!(manifest.files.len() > 0, "Expected files");
|
assert!(manifest.files.len() > 0, "Expected files");
|
||||||
assert_eq!(manifest.legacies.len(), 1, "Expected 1 legacy action");
|
assert_eq!(manifest.legacies.len(), 1, "Expected 1 legacy action");
|
||||||
|
|
||||||
// Check the legacy action
|
// Check the legacy action
|
||||||
let legacy = &manifest.legacies[0];
|
let legacy = &manifest.legacies[0];
|
||||||
assert_eq!(legacy.arch, "i386", "Expected arch to be 'i386'");
|
assert_eq!(legacy.arch, "i386", "Expected arch to be 'i386'");
|
||||||
assert_eq!(legacy.category, "system", "Expected category to be 'system'");
|
assert_eq!(
|
||||||
assert_eq!(legacy.pkg, "SUNWpgadmin3", "Expected pkg to be 'SUNWpgadmin3'");
|
legacy.category, "system",
|
||||||
assert_eq!(legacy.vendor, "Project OpenIndiana", "Expected vendor to be 'Project OpenIndiana'");
|
"Expected category to be 'system'"
|
||||||
assert_eq!(legacy.version, "11.11.0,REV=2010.05.25.01.00", "Expected version to be '11.11.0,REV=2010.05.25.01.00'");
|
);
|
||||||
}
|
assert_eq!(
|
||||||
|
legacy.pkg, "SUNWpgadmin3",
|
||||||
|
"Expected pkg to be 'SUNWpgadmin3'"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
legacy.vendor, "Project OpenIndiana",
|
||||||
|
"Expected vendor to be 'Project OpenIndiana'"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
legacy.version, "11.11.0,REV=2010.05.25.01.00",
|
||||||
|
"Expected version to be '11.11.0,REV=2010.05.25.01.00'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,18 @@ use libips::actions::Manifest;
|
||||||
fn test_ftpuser_boolean_true() {
|
fn test_ftpuser_boolean_true() {
|
||||||
// Create a manifest string with ftpuser=true
|
// Create a manifest string with ftpuser=true
|
||||||
let manifest_string = "user ftpuser=true".to_string();
|
let manifest_string = "user ftpuser=true".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that "ftp" service is added
|
// Check that "ftp" service is added
|
||||||
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added when ftpuser=true");
|
assert!(
|
||||||
|
user.services.contains("ftp"),
|
||||||
|
"Expected 'ftp' service to be added when ftpuser=true"
|
||||||
|
);
|
||||||
assert_eq!(user.services.len(), 1, "Expected exactly one service");
|
assert_eq!(user.services.len(), 1, "Expected exactly one service");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,32 +25,44 @@ fn test_ftpuser_boolean_true() {
|
||||||
fn test_ftpuser_boolean_false() {
|
fn test_ftpuser_boolean_false() {
|
||||||
// Create a manifest string with ftpuser=false
|
// Create a manifest string with ftpuser=false
|
||||||
let manifest_string = "user ftpuser=false".to_string();
|
let manifest_string = "user ftpuser=false".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that no services are added
|
// Check that no services are added
|
||||||
assert!(user.services.is_empty(), "Expected no services when ftpuser=false");
|
assert!(
|
||||||
|
user.services.is_empty(),
|
||||||
|
"Expected no services when ftpuser=false"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ftpuser_services_list() {
|
fn test_ftpuser_services_list() {
|
||||||
// Create a manifest string with ftpuser=ssh,ftp,http
|
// Create a manifest string with ftpuser=ssh,ftp,http
|
||||||
let manifest_string = "user ftpuser=ssh,ftp,http".to_string();
|
let manifest_string = "user ftpuser=ssh,ftp,http".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that all services are added
|
// Check that all services are added
|
||||||
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added");
|
assert!(
|
||||||
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added");
|
user.services.contains("ssh"),
|
||||||
assert!(user.services.contains("http"), "Expected 'http' service to be added");
|
"Expected 'ssh' service to be added"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
user.services.contains("ftp"),
|
||||||
|
"Expected 'ftp' service to be added"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
user.services.contains("http"),
|
||||||
|
"Expected 'http' service to be added"
|
||||||
|
);
|
||||||
assert_eq!(user.services.len(), 3, "Expected exactly three services");
|
assert_eq!(user.services.len(), 3, "Expected exactly three services");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,17 +70,26 @@ fn test_ftpuser_services_list() {
|
||||||
fn test_ftpuser_services_with_whitespace() {
|
fn test_ftpuser_services_with_whitespace() {
|
||||||
// Create a manifest string with ftpuser=ssh, ftp, http
|
// Create a manifest string with ftpuser=ssh, ftp, http
|
||||||
let manifest_string = "user ftpuser=\"ssh, ftp, http\"".to_string();
|
let manifest_string = "user ftpuser=\"ssh, ftp, http\"".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that all services are added with whitespace trimmed
|
// Check that all services are added with whitespace trimmed
|
||||||
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added");
|
assert!(
|
||||||
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added");
|
user.services.contains("ssh"),
|
||||||
assert!(user.services.contains("http"), "Expected 'http' service to be added");
|
"Expected 'ssh' service to be added"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
user.services.contains("ftp"),
|
||||||
|
"Expected 'ftp' service to be added"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
user.services.contains("http"),
|
||||||
|
"Expected 'http' service to be added"
|
||||||
|
);
|
||||||
assert_eq!(user.services.len(), 3, "Expected exactly three services");
|
assert_eq!(user.services.len(), 3, "Expected exactly three services");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,31 +97,40 @@ fn test_ftpuser_services_with_whitespace() {
|
||||||
fn test_ftpuser_empty_string() {
|
fn test_ftpuser_empty_string() {
|
||||||
// Create a manifest string with ftpuser=
|
// Create a manifest string with ftpuser=
|
||||||
let manifest_string = "user ftpuser=".to_string();
|
let manifest_string = "user ftpuser=".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that no services are added
|
// Check that no services are added
|
||||||
assert!(user.services.is_empty(), "Expected no services for empty string");
|
assert!(
|
||||||
|
user.services.is_empty(),
|
||||||
|
"Expected no services for empty string"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ftpuser_with_empty_elements() {
|
fn test_ftpuser_with_empty_elements() {
|
||||||
// Create a manifest string with ftpuser=ssh,,http
|
// Create a manifest string with ftpuser=ssh,,http
|
||||||
let manifest_string = "user ftpuser=ssh,,http".to_string();
|
let manifest_string = "user ftpuser=ssh,,http".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check that only non-empty services are added
|
// Check that only non-empty services are added
|
||||||
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added");
|
assert!(
|
||||||
assert!(user.services.contains("http"), "Expected 'http' service to be added");
|
user.services.contains("ssh"),
|
||||||
|
"Expected 'ssh' service to be added"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
user.services.contains("http"),
|
||||||
|
"Expected 'http' service to be added"
|
||||||
|
);
|
||||||
assert_eq!(user.services.len(), 2, "Expected exactly two services");
|
assert_eq!(user.services.len(), 2, "Expected exactly two services");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,13 +138,13 @@ fn test_ftpuser_with_empty_elements() {
|
||||||
fn test_real_world_example() {
|
fn test_real_world_example() {
|
||||||
// Create a manifest string similar to the one in postgre-common.manifest
|
// Create a manifest string similar to the one in postgre-common.manifest
|
||||||
let manifest_string = "user username=postgres uid=90 group=postgres home-dir=/var/postgres login-shell=/usr/bin/pfksh password=NP gcos-field=\"PostgreSQL Reserved UID\" ftpuser=false".to_string();
|
let manifest_string = "user username=postgres uid=90 group=postgres home-dir=/var/postgres login-shell=/usr/bin/pfksh password=NP gcos-field=\"PostgreSQL Reserved UID\" ftpuser=false".to_string();
|
||||||
|
|
||||||
// Parse the manifest
|
// Parse the manifest
|
||||||
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
let manifest = Manifest::parse_string(manifest_string).expect("Failed to parse manifest");
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
let user = &manifest.users[0];
|
let user = &manifest.users[0];
|
||||||
|
|
||||||
// Check user properties
|
// Check user properties
|
||||||
assert_eq!(user.username, "postgres");
|
assert_eq!(user.username, "postgres");
|
||||||
assert_eq!(user.uid, "90");
|
assert_eq!(user.uid, "90");
|
||||||
|
|
@ -120,5 +153,8 @@ fn test_real_world_example() {
|
||||||
assert_eq!(user.login_shell, "/usr/bin/pfksh");
|
assert_eq!(user.login_shell, "/usr/bin/pfksh");
|
||||||
assert_eq!(user.password, "NP");
|
assert_eq!(user.password, "NP");
|
||||||
assert_eq!(user.gcos_field, "PostgreSQL Reserved UID");
|
assert_eq!(user.gcos_field, "PostgreSQL Reserved UID");
|
||||||
assert!(user.services.is_empty(), "Expected no services for ftpuser=false");
|
assert!(
|
||||||
}
|
user.services.is_empty(),
|
||||||
|
"Expected no services for ftpuser=false"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,27 +41,21 @@ pub enum Pkg6DevError {
|
||||||
code(ips::pkg6dev::component_path_error),
|
code(ips::pkg6dev::component_path_error),
|
||||||
help("Ensure the component path exists and is a directory")
|
help("Ensure the component path exists and is a directory")
|
||||||
)]
|
)]
|
||||||
ComponentPathError {
|
ComponentPathError { path: PathBuf },
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("manifest not found: {path}")]
|
#[error("manifest not found: {path}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::pkg6dev::manifest_not_found),
|
code(ips::pkg6dev::manifest_not_found),
|
||||||
help("Ensure the manifest file exists at the specified path")
|
help("Ensure the manifest file exists at the specified path")
|
||||||
)]
|
)]
|
||||||
ManifestNotFoundError {
|
ManifestNotFoundError { path: PathBuf },
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("replacement format error: {value} is not in the format 'key:value'")]
|
#[error("replacement format error: {value} is not in the format 'key:value'")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::pkg6dev::replacement_format_error),
|
code(ips::pkg6dev::replacement_format_error),
|
||||||
help("Replacements must be in the format 'key:value'")
|
help("Replacements must be in the format 'key:value'")
|
||||||
)]
|
)]
|
||||||
ReplacementFormatError {
|
ReplacementFormatError { value: String },
|
||||||
value: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Makefile-related errors
|
// Makefile-related errors
|
||||||
#[error("makefile parse error: {message}")]
|
#[error("makefile parse error: {message}")]
|
||||||
|
|
@ -69,18 +63,14 @@ pub enum Pkg6DevError {
|
||||||
code(ips::pkg6dev::makefile_parse_error),
|
code(ips::pkg6dev::makefile_parse_error),
|
||||||
help("Check the Makefile syntax and try again")
|
help("Check the Makefile syntax and try again")
|
||||||
)]
|
)]
|
||||||
MakefileParseError {
|
MakefileParseError { message: String },
|
||||||
message: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("component info error: {message}")]
|
#[error("component info error: {message}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::pkg6dev::component_info_error),
|
code(ips::pkg6dev::component_info_error),
|
||||||
help("Check the component information and try again")
|
help("Check the component information and try again")
|
||||||
)]
|
)]
|
||||||
ComponentInfoError {
|
ComponentInfoError { message: String },
|
||||||
message: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Package publishing errors
|
// Package publishing errors
|
||||||
#[error("manifest file not found: {path}")]
|
#[error("manifest file not found: {path}")]
|
||||||
|
|
@ -88,27 +78,21 @@ pub enum Pkg6DevError {
|
||||||
code(ips::pkg6dev::manifest_file_not_found),
|
code(ips::pkg6dev::manifest_file_not_found),
|
||||||
help("Ensure the manifest file exists at the specified path")
|
help("Ensure the manifest file exists at the specified path")
|
||||||
)]
|
)]
|
||||||
ManifestFileNotFoundError {
|
ManifestFileNotFoundError { path: PathBuf },
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("prototype directory not found: {path}")]
|
#[error("prototype directory not found: {path}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::pkg6dev::prototype_dir_not_found),
|
code(ips::pkg6dev::prototype_dir_not_found),
|
||||||
help("Ensure the prototype directory exists at the specified path")
|
help("Ensure the prototype directory exists at the specified path")
|
||||||
)]
|
)]
|
||||||
PrototypeDirNotFoundError {
|
PrototypeDirNotFoundError { path: PathBuf },
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("publisher not found: {publisher}")]
|
#[error("publisher not found: {publisher}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(ips::pkg6dev::publisher_not_found),
|
code(ips::pkg6dev::publisher_not_found),
|
||||||
help("Add the publisher to the repository using pkg6repo add-publisher")
|
help("Add the publisher to the repository using pkg6repo add-publisher")
|
||||||
)]
|
)]
|
||||||
PublisherNotFoundError {
|
PublisherNotFoundError { publisher: String },
|
||||||
publisher: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("no default publisher set")]
|
#[error("no default publisher set")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
|
|
@ -122,9 +106,7 @@ pub enum Pkg6DevError {
|
||||||
code(ips::pkg6dev::file_not_found_in_prototype),
|
code(ips::pkg6dev::file_not_found_in_prototype),
|
||||||
help("Ensure the file exists in the prototype directory")
|
help("Ensure the file exists in the prototype directory")
|
||||||
)]
|
)]
|
||||||
FileNotFoundInPrototypeError {
|
FileNotFoundInPrototypeError { path: PathBuf },
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Logging environment error
|
// Logging environment error
|
||||||
#[error("logging environment setup error: {0}")]
|
#[error("logging environment setup error: {0}")]
|
||||||
|
|
@ -162,4 +144,4 @@ impl From<anyhow::Error> for Pkg6DevError {
|
||||||
fn from(e: anyhow::Error) -> Self {
|
fn from(e: anyhow::Error) -> Self {
|
||||||
Pkg6DevError::UserlandError(e.to_string())
|
Pkg6DevError::UserlandError(e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@ use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
|
||||||
|
|
||||||
use error::{Pkg6DevError, Result};
|
use error::{Pkg6DevError, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{read_dir, OpenOptions};
|
use std::fs::{OpenOptions, read_dir};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use tracing_subscriber::{fmt, EnvFilter};
|
|
||||||
use tracing_subscriber::filter::LevelFilter;
|
use tracing_subscriber::filter::LevelFilter;
|
||||||
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
use userland::repology::find_newest_version;
|
use userland::repology::find_newest_version;
|
||||||
use userland::{Component, Makefile};
|
use userland::{Component, Makefile};
|
||||||
|
|
||||||
|
|
@ -63,8 +63,10 @@ fn main() -> Result<()> {
|
||||||
let env_filter = EnvFilter::builder()
|
let env_filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::WARN.into())
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
.from_env()
|
.from_env()
|
||||||
.map_err(|e| Pkg6DevError::LoggingEnvError(format!("Failed to parse environment filter: {}", e)))?;
|
.map_err(|e| {
|
||||||
|
Pkg6DevError::LoggingEnvError(format!("Failed to parse environment filter: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
fmt::Subscriber::builder()
|
fmt::Subscriber::builder()
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
.with_max_level(tracing::Level::DEBUG)
|
||||||
.with_env_filter(env_filter)
|
.with_env_filter(env_filter)
|
||||||
|
|
@ -73,7 +75,7 @@ fn main() -> Result<()> {
|
||||||
.with_ansi(false)
|
.with_ansi(false)
|
||||||
.with_writer(std::io::stderr)
|
.with_writer(std::io::stderr)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let cli = App::parse();
|
let cli = App::parse();
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
|
|
@ -200,7 +202,8 @@ fn diff_component(
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(output_manifest).map_err(|e| Pkg6DevError::IoError(e))?;
|
.open(output_manifest)
|
||||||
|
.map_err(|e| Pkg6DevError::IoError(e))?;
|
||||||
for action in missing_files {
|
for action in missing_files {
|
||||||
writeln!(&mut f, "file path={}", action.path).map_err(|e| Pkg6DevError::IoError(e))?;
|
writeln!(&mut f, "file path={}", action.path).map_err(|e| Pkg6DevError::IoError(e))?;
|
||||||
}
|
}
|
||||||
|
|
@ -228,12 +231,14 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
|
||||||
|
|
||||||
// Parse Makefile
|
// Parse Makefile
|
||||||
// We'll wrap the anyhow errors with our more specific error types
|
// We'll wrap the anyhow errors with our more specific error types
|
||||||
let initial_makefile = Makefile::parse_single_file(&makefile_path)
|
let initial_makefile = Makefile::parse_single_file(&makefile_path).map_err(|e| {
|
||||||
.map_err(|e| Pkg6DevError::MakefileParseError {
|
Pkg6DevError::MakefileParseError {
|
||||||
message: format!("Failed to parse Makefile: {}", e),
|
message: format!("Failed to parse Makefile: {}", e),
|
||||||
})?;
|
}
|
||||||
|
})?;
|
||||||
let makefile = initial_makefile.parse_all()
|
|
||||||
|
let makefile = initial_makefile
|
||||||
|
.parse_all()
|
||||||
.map_err(|e| Pkg6DevError::MakefileParseError {
|
.map_err(|e| Pkg6DevError::MakefileParseError {
|
||||||
message: format!("Failed to parse all Makefiles: {}", e),
|
message: format!("Failed to parse all Makefiles: {}", e),
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -241,8 +246,8 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
|
|
||||||
// Get component information
|
// Get component information
|
||||||
let component = Component::new_from_makefile(&makefile)
|
let component =
|
||||||
.map_err(|e| Pkg6DevError::ComponentInfoError {
|
Component::new_from_makefile(&makefile).map_err(|e| Pkg6DevError::ComponentInfoError {
|
||||||
message: format!("Failed to get component information: {}", e),
|
message: format!("Failed to get component information: {}", e),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -259,9 +264,12 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
|
||||||
info!("Version: {}", var.replace('\n', "\n\t"));
|
info!("Version: {}", var.replace('\n', "\n\t"));
|
||||||
let latest_version = find_newest_version(&name);
|
let latest_version = find_newest_version(&name);
|
||||||
if latest_version.is_ok() {
|
if latest_version.is_ok() {
|
||||||
info!("Latest Version: {}", latest_version.map_err(|e| Pkg6DevError::ComponentInfoError {
|
info!(
|
||||||
message: format!("Failed to get latest version: {}", e),
|
"Latest Version: {}",
|
||||||
})?);
|
latest_version.map_err(|e| Pkg6DevError::ComponentInfoError {
|
||||||
|
message: format!("Failed to get latest version: {}", e),
|
||||||
|
})?
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"Could not get latest version info: {}",
|
"Could not get latest version info: {}",
|
||||||
|
|
@ -448,14 +456,14 @@ fn publish_package(
|
||||||
// Use the default publisher
|
// Use the default publisher
|
||||||
match &repo.config.default_publisher {
|
match &repo.config.default_publisher {
|
||||||
Some(default_pub) => default_pub.clone(),
|
Some(default_pub) => default_pub.clone(),
|
||||||
None => return Err(Pkg6DevError::NoDefaultPublisherError)
|
None => return Err(Pkg6DevError::NoDefaultPublisherError),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
info!("Beginning transaction for publisher: {}", publisher_name);
|
info!("Beginning transaction for publisher: {}", publisher_name);
|
||||||
let mut transaction = repo.begin_transaction()?;
|
let mut transaction = repo.begin_transaction()?;
|
||||||
|
|
||||||
// Set the publisher for the transaction
|
// Set the publisher for the transaction
|
||||||
transaction.set_publisher(&publisher_name);
|
transaction.set_publisher(&publisher_name);
|
||||||
|
|
||||||
|
|
@ -470,7 +478,9 @@ fn publish_package(
|
||||||
|
|
||||||
// Check if the file exists
|
// Check if the file exists
|
||||||
if !file_path.exists() {
|
if !file_path.exists() {
|
||||||
return Err(Pkg6DevError::FileNotFoundInPrototypeError{path: file_path.clone()})
|
return Err(Pkg6DevError::FileNotFoundInPrototypeError {
|
||||||
|
path: file_path.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the transaction
|
// Add the file to the transaction
|
||||||
|
|
@ -485,7 +495,7 @@ fn publish_package(
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
info!("Committing transaction...");
|
info!("Committing transaction...");
|
||||||
transaction.commit()?;
|
transaction.commit()?;
|
||||||
|
|
||||||
// Regenerate catalog and search index
|
// Regenerate catalog and search index
|
||||||
info!("Regenerating catalog and search index...");
|
info!("Regenerating catalog and search index...");
|
||||||
repo.rebuild(Some(&publisher_name), false, false)?;
|
repo.rebuild(Some(&publisher_name), false, false)?;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ mod e2e_tests {
|
||||||
|
|
||||||
// The base directory for all test repositories
|
// The base directory for all test repositories
|
||||||
const TEST_REPO_BASE_DIR: &str = "/tmp/pkg6repo_e2e_test";
|
const TEST_REPO_BASE_DIR: &str = "/tmp/pkg6repo_e2e_test";
|
||||||
|
|
||||||
// Get the path to the pre-built binaries
|
// Get the path to the pre-built binaries
|
||||||
fn get_bin_dir() -> PathBuf {
|
fn get_bin_dir() -> PathBuf {
|
||||||
match env::var("PKG6_TEST_BIN_DIR") {
|
match env::var("PKG6_TEST_BIN_DIR") {
|
||||||
|
|
@ -54,7 +54,7 @@ mod e2e_tests {
|
||||||
.args(["run", "-p", "xtask", "--", "setup-test-env"])
|
.args(["run", "-p", "xtask", "--", "setup-test-env"])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to run xtask setup-test-env");
|
.expect("Failed to run xtask setup-test-env");
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to set up test environment: {}",
|
"Failed to set up test environment: {}",
|
||||||
|
|
@ -73,7 +73,7 @@ mod e2e_tests {
|
||||||
fn run_pkg6repo(args: &[&str]) -> Result<String, String> {
|
fn run_pkg6repo(args: &[&str]) -> Result<String, String> {
|
||||||
let bin_dir = get_bin_dir();
|
let bin_dir = get_bin_dir();
|
||||||
let pkg6repo_bin = bin_dir.join("pkg6repo");
|
let pkg6repo_bin = bin_dir.join("pkg6repo");
|
||||||
|
|
||||||
// Check if the binary exists
|
// Check if the binary exists
|
||||||
if !pkg6repo_bin.exists() {
|
if !pkg6repo_bin.exists() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
|
@ -81,7 +81,7 @@ mod e2e_tests {
|
||||||
pkg6repo_bin.display()
|
pkg6repo_bin.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new(pkg6repo_bin)
|
let output = Command::new(pkg6repo_bin)
|
||||||
.args(args)
|
.args(args)
|
||||||
.output()
|
.output()
|
||||||
|
|
@ -98,7 +98,7 @@ mod e2e_tests {
|
||||||
fn run_pkg6dev(args: &[&str]) -> Result<String, String> {
|
fn run_pkg6dev(args: &[&str]) -> Result<String, String> {
|
||||||
let bin_dir = get_bin_dir();
|
let bin_dir = get_bin_dir();
|
||||||
let pkg6dev_bin = bin_dir.join("pkg6dev");
|
let pkg6dev_bin = bin_dir.join("pkg6dev");
|
||||||
|
|
||||||
// Check if the binary exists
|
// Check if the binary exists
|
||||||
if !pkg6dev_bin.exists() {
|
if !pkg6dev_bin.exists() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
|
@ -106,7 +106,7 @@ mod e2e_tests {
|
||||||
pkg6dev_bin.display()
|
pkg6dev_bin.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new(pkg6dev_bin)
|
let output = Command::new(pkg6dev_bin)
|
||||||
.args(args)
|
.args(args)
|
||||||
.output()
|
.output()
|
||||||
|
|
@ -161,7 +161,12 @@ mod e2e_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add a publisher using pkg6repo
|
// Add a publisher using pkg6repo
|
||||||
let result = run_pkg6repo(&["add-publisher", "-s", repo_path.to_str().unwrap(), "example.com"]);
|
let result = run_pkg6repo(&[
|
||||||
|
"add-publisher",
|
||||||
|
"-s",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
|
"example.com",
|
||||||
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
"Failed to add publisher: {:?}",
|
"Failed to add publisher: {:?}",
|
||||||
|
|
@ -205,9 +210,12 @@ mod e2e_tests {
|
||||||
let manifest_path = manifest_dir.join("example.p5m");
|
let manifest_path = manifest_dir.join("example.p5m");
|
||||||
let result = run_pkg6dev(&[
|
let result = run_pkg6dev(&[
|
||||||
"publish",
|
"publish",
|
||||||
"--manifest-path", manifest_path.to_str().unwrap(),
|
"--manifest-path",
|
||||||
"--prototype-dir", prototype_dir.to_str().unwrap(),
|
manifest_path.to_str().unwrap(),
|
||||||
"--repo-path", repo_path.to_str().unwrap(),
|
"--prototype-dir",
|
||||||
|
prototype_dir.to_str().unwrap(),
|
||||||
|
"--repo-path",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -262,9 +270,12 @@ mod e2e_tests {
|
||||||
let manifest_path = manifest_dir.join("example.p5m");
|
let manifest_path = manifest_dir.join("example.p5m");
|
||||||
let result = run_pkg6dev(&[
|
let result = run_pkg6dev(&[
|
||||||
"publish",
|
"publish",
|
||||||
"--manifest-path", manifest_path.to_str().unwrap(),
|
"--manifest-path",
|
||||||
"--prototype-dir", prototype_dir.to_str().unwrap(),
|
manifest_path.to_str().unwrap(),
|
||||||
"--repo-path", repo_path.to_str().unwrap(),
|
"--prototype-dir",
|
||||||
|
prototype_dir.to_str().unwrap(),
|
||||||
|
"--repo-path",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -327,9 +338,12 @@ mod e2e_tests {
|
||||||
let manifest_path1 = manifest_dir.join("example.p5m");
|
let manifest_path1 = manifest_dir.join("example.p5m");
|
||||||
let result = run_pkg6dev(&[
|
let result = run_pkg6dev(&[
|
||||||
"publish",
|
"publish",
|
||||||
"--manifest-path", manifest_path1.to_str().unwrap(),
|
"--manifest-path",
|
||||||
"--prototype-dir", prototype_dir.to_str().unwrap(),
|
manifest_path1.to_str().unwrap(),
|
||||||
"--repo-path", repo_path.to_str().unwrap(),
|
"--prototype-dir",
|
||||||
|
prototype_dir.to_str().unwrap(),
|
||||||
|
"--repo-path",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -341,9 +355,12 @@ mod e2e_tests {
|
||||||
let manifest_path2 = manifest_dir.join("example2.p5m");
|
let manifest_path2 = manifest_dir.join("example2.p5m");
|
||||||
let result = run_pkg6dev(&[
|
let result = run_pkg6dev(&[
|
||||||
"publish",
|
"publish",
|
||||||
"--manifest-path", manifest_path2.to_str().unwrap(),
|
"--manifest-path",
|
||||||
"--prototype-dir", prototype_dir.to_str().unwrap(),
|
manifest_path2.to_str().unwrap(),
|
||||||
"--repo-path", repo_path.to_str().unwrap(),
|
"--prototype-dir",
|
||||||
|
prototype_dir.to_str().unwrap(),
|
||||||
|
"--repo-path",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -379,10 +396,13 @@ mod e2e_tests {
|
||||||
let sample_repo_path = PathBuf::from(env::current_dir().unwrap())
|
let sample_repo_path = PathBuf::from(env::current_dir().unwrap())
|
||||||
.join("sample_data")
|
.join("sample_data")
|
||||||
.join("sample-repo");
|
.join("sample-repo");
|
||||||
|
|
||||||
// Check if the sample repository exists
|
// Check if the sample repository exists
|
||||||
if !sample_repo_path.exists() {
|
if !sample_repo_path.exists() {
|
||||||
println!("Sample pkg5 repository not found at {}, skipping test", sample_repo_path.display());
|
println!(
|
||||||
|
"Sample pkg5 repository not found at {}, skipping test",
|
||||||
|
sample_repo_path.display()
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,8 +413,10 @@ mod e2e_tests {
|
||||||
// Import the pkg5 repository using pkg6repo
|
// Import the pkg5 repository using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&[
|
||||||
"import-pkg5",
|
"import-pkg5",
|
||||||
"--source", sample_repo_path.to_str().unwrap(),
|
"--source",
|
||||||
"--destination", repo_path.to_str().unwrap(),
|
sample_repo_path.to_str().unwrap(),
|
||||||
|
"--destination",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -422,10 +444,7 @@ mod e2e_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
assert!(
|
assert!(!output.is_empty(), "No packages found in repository");
|
||||||
!output.is_empty(),
|
|
||||||
"No packages found in repository"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -437,10 +456,13 @@ mod e2e_tests {
|
||||||
let sample_p5p_path = PathBuf::from(env::current_dir().unwrap())
|
let sample_p5p_path = PathBuf::from(env::current_dir().unwrap())
|
||||||
.join("sample_data")
|
.join("sample_data")
|
||||||
.join("sample-repo.p5p");
|
.join("sample-repo.p5p");
|
||||||
|
|
||||||
// Check if the sample p5p archive exists
|
// Check if the sample p5p archive exists
|
||||||
if !sample_p5p_path.exists() {
|
if !sample_p5p_path.exists() {
|
||||||
println!("Sample pkg5 p5p archive not found at {}, skipping test", sample_p5p_path.display());
|
println!(
|
||||||
|
"Sample pkg5 p5p archive not found at {}, skipping test",
|
||||||
|
sample_p5p_path.display()
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,8 +473,10 @@ mod e2e_tests {
|
||||||
// Import the pkg5 p5p archive using pkg6repo
|
// Import the pkg5 p5p archive using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&[
|
||||||
"import-pkg5",
|
"import-pkg5",
|
||||||
"--source", sample_p5p_path.to_str().unwrap(),
|
"--source",
|
||||||
"--destination", repo_path.to_str().unwrap(),
|
sample_p5p_path.to_str().unwrap(),
|
||||||
|
"--destination",
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
|
@ -480,10 +504,7 @@ mod e2e_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
assert!(
|
assert!(!output.is_empty(), "No packages found in repository");
|
||||||
!output.is_empty(),
|
|
||||||
"No packages found in repository"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,7 @@ pub enum Pkg6RepoError {
|
||||||
LoggingEnvError(String),
|
LoggingEnvError(String),
|
||||||
|
|
||||||
#[error("other error: {0}")]
|
#[error("other error: {0}")]
|
||||||
#[diagnostic(
|
#[diagnostic(code(pkg6repo::other_error), help("See error message for details"))]
|
||||||
code(pkg6repo::other_error),
|
|
||||||
help("See error message for details")
|
|
||||||
)]
|
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,4 +72,4 @@ impl From<&str> for Pkg6RepoError {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
Pkg6RepoError::Other(s.to_string())
|
Pkg6RepoError::Other(s.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ use error::{Pkg6RepoError, Result};
|
||||||
use pkg5_import::Pkg5Importer;
|
use pkg5_import::Pkg5Importer;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use tracing_subscriber::{fmt, EnvFilter};
|
|
||||||
use tracing_subscriber::filter::LevelFilter;
|
use tracing_subscriber::filter::LevelFilter;
|
||||||
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository};
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod e2e_tests;
|
mod e2e_tests;
|
||||||
|
|
@ -326,8 +326,10 @@ fn main() -> Result<()> {
|
||||||
let env_filter = EnvFilter::builder()
|
let env_filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::WARN.into())
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
.from_env()
|
.from_env()
|
||||||
.map_err(|e| Pkg6RepoError::LoggingEnvError(format!("Failed to parse environment filter: {}", e)))?;
|
.map_err(|e| {
|
||||||
|
Pkg6RepoError::LoggingEnvError(format!("Failed to parse environment filter: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
fmt::Subscriber::builder()
|
fmt::Subscriber::builder()
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
.with_max_level(tracing::Level::DEBUG)
|
||||||
.with_env_filter(env_filter)
|
.with_env_filter(env_filter)
|
||||||
|
|
@ -336,7 +338,7 @@ fn main() -> Result<()> {
|
||||||
.with_ansi(false)
|
.with_ansi(false)
|
||||||
.with_writer(std::io::stderr)
|
.with_writer(std::io::stderr)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let cli = App::parse();
|
let cli = App::parse();
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
|
|
@ -525,7 +527,9 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string()));
|
return Err(Pkg6RepoError::UnsupportedOutputFormat(
|
||||||
|
output_format.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,7 +622,9 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string()));
|
return Err(Pkg6RepoError::UnsupportedOutputFormat(
|
||||||
|
output_format.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -727,7 +733,9 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string()));
|
return Err(Pkg6RepoError::UnsupportedOutputFormat(
|
||||||
|
output_format.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -933,7 +941,9 @@ fn main() -> Result<()> {
|
||||||
// Split the property=value string
|
// Split the property=value string
|
||||||
let parts: Vec<&str> = prop_val.split('=').collect();
|
let parts: Vec<&str> = prop_val.split('=').collect();
|
||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
return Err(Pkg6RepoError::InvalidPropertyValueFormat(prop_val.to_string()));
|
return Err(Pkg6RepoError::InvalidPropertyValueFormat(
|
||||||
|
prop_val.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let property = parts[0];
|
let property = parts[0];
|
||||||
|
|
@ -1052,7 +1062,9 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string()));
|
return Err(Pkg6RepoError::UnsupportedOutputFormat(
|
||||||
|
output_format.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1063,16 +1075,20 @@ fn main() -> Result<()> {
|
||||||
destination,
|
destination,
|
||||||
publisher,
|
publisher,
|
||||||
} => {
|
} => {
|
||||||
info!("Importing pkg5 repository from {} to {}", source.display(), destination.display());
|
info!(
|
||||||
|
"Importing pkg5 repository from {} to {}",
|
||||||
|
source.display(),
|
||||||
|
destination.display()
|
||||||
|
);
|
||||||
|
|
||||||
// Create a new Pkg5Importer
|
// Create a new Pkg5Importer
|
||||||
let mut importer = Pkg5Importer::new(source, destination)?;
|
let mut importer = Pkg5Importer::new(source, destination)?;
|
||||||
|
|
||||||
// Import the repository
|
// Import the repository
|
||||||
importer.import(publisher.as_deref())?;
|
importer.import(publisher.as_deref())?;
|
||||||
|
|
||||||
info!("Repository imported successfully");
|
info!("Repository imported successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::error::{Pkg6RepoError, Result};
|
use crate::error::{Pkg6RepoError, Result};
|
||||||
use libips::actions::{Manifest};
|
use libips::actions::Manifest;
|
||||||
use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
|
use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
@ -25,7 +25,11 @@ impl Pkg5Importer {
|
||||||
let source_path = source_path.as_ref().to_path_buf();
|
let source_path = source_path.as_ref().to_path_buf();
|
||||||
let dest_path = dest_path.as_ref().to_path_buf();
|
let dest_path = dest_path.as_ref().to_path_buf();
|
||||||
|
|
||||||
debug!("Creating Pkg5Importer with source: {}, destination: {}", source_path.display(), dest_path.display());
|
debug!(
|
||||||
|
"Creating Pkg5Importer with source: {}, destination: {}",
|
||||||
|
source_path.display(),
|
||||||
|
dest_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
// Check if a source path exists
|
// Check if a source path exists
|
||||||
if !source_path.exists() {
|
if !source_path.exists() {
|
||||||
|
|
@ -38,7 +42,8 @@ impl Pkg5Importer {
|
||||||
debug!("Source path exists: {}", source_path.display());
|
debug!("Source path exists: {}", source_path.display());
|
||||||
|
|
||||||
// Determine if a source is a p5p archive
|
// Determine if a source is a p5p archive
|
||||||
let is_p5p = source_path.is_file() && source_path.extension().map_or(false, |ext| ext == "p5p");
|
let is_p5p =
|
||||||
|
source_path.is_file() && source_path.extension().map_or(false, |ext| ext == "p5p");
|
||||||
debug!("Source is p5p archive: {}", is_p5p);
|
debug!("Source is p5p archive: {}", is_p5p);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -56,9 +61,12 @@ impl Pkg5Importer {
|
||||||
let temp_dir = tempdir().map_err(|e| {
|
let temp_dir = tempdir().map_err(|e| {
|
||||||
Pkg6RepoError::from(format!("Failed to create temporary directory: {}", e))
|
Pkg6RepoError::from(format!("Failed to create temporary directory: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
info!("Extracting p5p archive to temporary directory: {}", temp_dir.path().display());
|
info!(
|
||||||
|
"Extracting p5p archive to temporary directory: {}",
|
||||||
|
temp_dir.path().display()
|
||||||
|
);
|
||||||
|
|
||||||
// Extract the p5p archive to the temporary directory
|
// Extract the p5p archive to the temporary directory
|
||||||
let status = std::process::Command::new("tar")
|
let status = std::process::Command::new("tar")
|
||||||
.arg("-xf")
|
.arg("-xf")
|
||||||
|
|
@ -69,18 +77,18 @@ impl Pkg5Importer {
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
Pkg6RepoError::from(format!("Failed to extract p5p archive: {}", e))
|
Pkg6RepoError::from(format!("Failed to extract p5p archive: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
"Failed to extract p5p archive: {}",
|
"Failed to extract p5p archive: {}",
|
||||||
status
|
status
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the temporary directory
|
// Store the temporary directory
|
||||||
let source_path = temp_dir.path().to_path_buf();
|
let source_path = temp_dir.path().to_path_buf();
|
||||||
self.temp_dir = Some(temp_dir);
|
self.temp_dir = Some(temp_dir);
|
||||||
|
|
||||||
Ok(source_path)
|
Ok(source_path)
|
||||||
} else {
|
} else {
|
||||||
// Source is already a directory
|
// Source is already a directory
|
||||||
|
|
@ -91,61 +99,88 @@ impl Pkg5Importer {
|
||||||
/// Imports the pkg5 repository
|
/// Imports the pkg5 repository
|
||||||
pub fn import(&mut self, publisher: Option<&str>) -> Result<()> {
|
pub fn import(&mut self, publisher: Option<&str>) -> Result<()> {
|
||||||
debug!("Starting import with publisher: {:?}", publisher);
|
debug!("Starting import with publisher: {:?}", publisher);
|
||||||
|
|
||||||
// Prepare the source repository
|
// Prepare the source repository
|
||||||
debug!("Preparing source repository");
|
debug!("Preparing source repository");
|
||||||
let source_path = self.prepare_source()?;
|
let source_path = self.prepare_source()?;
|
||||||
debug!("Source repository prepared: {}", source_path.display());
|
debug!("Source repository prepared: {}", source_path.display());
|
||||||
|
|
||||||
// Check if this is a pkg5 repository
|
// Check if this is a pkg5 repository
|
||||||
let pkg5_repo_file = source_path.join("pkg5.repository");
|
let pkg5_repo_file = source_path.join("pkg5.repository");
|
||||||
let pkg5_index_file = source_path.join("pkg5.index.0.gz");
|
let pkg5_index_file = source_path.join("pkg5.index.0.gz");
|
||||||
|
|
||||||
debug!("Checking if pkg5.repository exists: {}", pkg5_repo_file.exists());
|
debug!(
|
||||||
debug!("Checking if pkg5.index.0.gz exists: {}", pkg5_index_file.exists());
|
"Checking if pkg5.repository exists: {}",
|
||||||
|
pkg5_repo_file.exists()
|
||||||
|
);
|
||||||
|
debug!(
|
||||||
|
"Checking if pkg5.index.0.gz exists: {}",
|
||||||
|
pkg5_index_file.exists()
|
||||||
|
);
|
||||||
|
|
||||||
if !pkg5_repo_file.exists() && !pkg5_index_file.exists() {
|
if !pkg5_repo_file.exists() && !pkg5_index_file.exists() {
|
||||||
debug!("Source does not appear to be a pkg5 repository: {}", source_path.display());
|
debug!(
|
||||||
|
"Source does not appear to be a pkg5 repository: {}",
|
||||||
|
source_path.display()
|
||||||
|
);
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
"Source does not appear to be a pkg5 repository: {}",
|
"Source does not appear to be a pkg5 repository: {}",
|
||||||
source_path.display()
|
source_path.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open or create the destination repository
|
// Open or create the destination repository
|
||||||
debug!("Checking if destination repository exists: {}", self.dest_path.exists());
|
debug!(
|
||||||
|
"Checking if destination repository exists: {}",
|
||||||
|
self.dest_path.exists()
|
||||||
|
);
|
||||||
let mut dest_repo = if self.dest_path.exists() {
|
let mut dest_repo = if self.dest_path.exists() {
|
||||||
// Check if it's a valid repository by looking for the pkg6.repository file
|
// Check if it's a valid repository by looking for the pkg6.repository file
|
||||||
let repo_config_file = self.dest_path.join("pkg6.repository");
|
let repo_config_file = self.dest_path.join("pkg6.repository");
|
||||||
debug!("Checking if repository config file exists: {}", repo_config_file.exists());
|
debug!(
|
||||||
|
"Checking if repository config file exists: {}",
|
||||||
|
repo_config_file.exists()
|
||||||
|
);
|
||||||
|
|
||||||
if repo_config_file.exists() {
|
if repo_config_file.exists() {
|
||||||
// It's a valid repository, open it
|
// It's a valid repository, open it
|
||||||
info!("Opening existing repository: {}", self.dest_path.display());
|
info!("Opening existing repository: {}", self.dest_path.display());
|
||||||
debug!("Attempting to open repository at: {}", self.dest_path.display());
|
debug!(
|
||||||
|
"Attempting to open repository at: {}",
|
||||||
|
self.dest_path.display()
|
||||||
|
);
|
||||||
FileBackend::open(&self.dest_path)?
|
FileBackend::open(&self.dest_path)?
|
||||||
} else {
|
} else {
|
||||||
// It's not a valid repository, create a new one
|
// It's not a valid repository, create a new one
|
||||||
info!("Destination exists but is not a valid repository, creating a new one: {}", self.dest_path.display());
|
info!(
|
||||||
debug!("Attempting to create repository at: {}", self.dest_path.display());
|
"Destination exists but is not a valid repository, creating a new one: {}",
|
||||||
|
self.dest_path.display()
|
||||||
|
);
|
||||||
|
debug!(
|
||||||
|
"Attempting to create repository at: {}",
|
||||||
|
self.dest_path.display()
|
||||||
|
);
|
||||||
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
|
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Destination doesn't exist, create a new repository
|
// Destination doesn't exist, create a new repository
|
||||||
info!("Creating new repository: {}", self.dest_path.display());
|
info!("Creating new repository: {}", self.dest_path.display());
|
||||||
debug!("Attempting to create repository at: {}", self.dest_path.display());
|
debug!(
|
||||||
|
"Attempting to create repository at: {}",
|
||||||
|
self.dest_path.display()
|
||||||
|
);
|
||||||
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
|
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find publishers in the source repository
|
// Find publishers in the source repository
|
||||||
let publishers = self.find_publishers(&source_path)?;
|
let publishers = self.find_publishers(&source_path)?;
|
||||||
|
|
||||||
if publishers.is_empty() {
|
if publishers.is_empty() {
|
||||||
return Err(Pkg6RepoError::from(
|
return Err(Pkg6RepoError::from(
|
||||||
"No publishers found in source repository".to_string()
|
"No publishers found in source repository".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine which publisher to import
|
// Determine which publisher to import
|
||||||
let publisher_to_import = match publisher {
|
let publisher_to_import = match publisher {
|
||||||
Some(pub_name) => {
|
Some(pub_name) => {
|
||||||
|
|
@ -156,34 +191,42 @@ impl Pkg5Importer {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
pub_name
|
pub_name
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
// Use the first publisher if none specified
|
// Use the first publisher if none specified
|
||||||
&publishers[0]
|
&publishers[0]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("Importing from publisher: {}", publisher_to_import);
|
info!("Importing from publisher: {}", publisher_to_import);
|
||||||
|
|
||||||
// Ensure the publisher exists in the destination repository
|
// Ensure the publisher exists in the destination repository
|
||||||
if !dest_repo.config.publishers.iter().any(|p| p == publisher_to_import) {
|
if !dest_repo
|
||||||
info!("Adding publisher to destination repository: {}", publisher_to_import);
|
.config
|
||||||
|
.publishers
|
||||||
|
.iter()
|
||||||
|
.any(|p| p == publisher_to_import)
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
"Adding publisher to destination repository: {}",
|
||||||
|
publisher_to_import
|
||||||
|
);
|
||||||
dest_repo.add_publisher(publisher_to_import)?;
|
dest_repo.add_publisher(publisher_to_import)?;
|
||||||
|
|
||||||
// Set as the default publisher if there isn't one already
|
// Set as the default publisher if there isn't one already
|
||||||
if dest_repo.config.default_publisher.is_none() {
|
if dest_repo.config.default_publisher.is_none() {
|
||||||
info!("Setting as default publisher: {}", publisher_to_import);
|
info!("Setting as default publisher: {}", publisher_to_import);
|
||||||
dest_repo.set_default_publisher(publisher_to_import)?;
|
dest_repo.set_default_publisher(publisher_to_import)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import packages
|
// Import packages
|
||||||
self.import_packages(&source_path, &mut dest_repo, publisher_to_import)?;
|
self.import_packages(&source_path, &mut dest_repo, publisher_to_import)?;
|
||||||
|
|
||||||
// Rebuild catalog and search index
|
// Rebuild catalog and search index
|
||||||
info!("Rebuilding catalog and search index...");
|
info!("Rebuilding catalog and search index...");
|
||||||
dest_repo.rebuild(Some(publisher_to_import), false, false)?;
|
dest_repo.rebuild(Some(publisher_to_import), false, false)?;
|
||||||
|
|
||||||
info!("Import completed successfully");
|
info!("Import completed successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -191,88 +234,87 @@ impl Pkg5Importer {
|
||||||
/// Finds publishers in the source repository
|
/// Finds publishers in the source repository
|
||||||
fn find_publishers(&self, source_path: &Path) -> Result<Vec<String>> {
|
fn find_publishers(&self, source_path: &Path) -> Result<Vec<String>> {
|
||||||
let publisher_dir = source_path.join("publisher");
|
let publisher_dir = source_path.join("publisher");
|
||||||
|
|
||||||
if !publisher_dir.exists() || !publisher_dir.is_dir() {
|
if !publisher_dir.exists() || !publisher_dir.is_dir() {
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
"Publisher directory not found: {}",
|
"Publisher directory not found: {}",
|
||||||
publisher_dir.display()
|
publisher_dir.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut publishers = Vec::new();
|
let mut publishers = Vec::new();
|
||||||
|
|
||||||
for entry in fs::read_dir(&publisher_dir).map_err(|e| {
|
for entry in fs::read_dir(&publisher_dir).map_err(|e| Pkg6RepoError::IoError(e))? {
|
||||||
Pkg6RepoError::IoError(e)
|
let entry = entry.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})? {
|
|
||||||
let entry = entry.map_err(|e| {
|
|
||||||
Pkg6RepoError::IoError(e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
let publisher = path.file_name().unwrap().to_string_lossy().to_string();
|
let publisher = path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
publishers.push(publisher);
|
publishers.push(publisher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(publishers)
|
Ok(publishers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Imports packages from the source repository
|
/// Imports packages from the source repository
|
||||||
fn import_packages(&self, source_path: &Path, dest_repo: &mut FileBackend, publisher: &str) -> Result<()> {
|
fn import_packages(
|
||||||
|
&self,
|
||||||
|
source_path: &Path,
|
||||||
|
dest_repo: &mut FileBackend,
|
||||||
|
publisher: &str,
|
||||||
|
) -> Result<()> {
|
||||||
let pkg_dir = source_path.join("publisher").join(publisher).join("pkg");
|
let pkg_dir = source_path.join("publisher").join(publisher).join("pkg");
|
||||||
|
|
||||||
if !pkg_dir.exists() || !pkg_dir.is_dir() {
|
if !pkg_dir.exists() || !pkg_dir.is_dir() {
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
"Package directory not found: {}",
|
"Package directory not found: {}",
|
||||||
pkg_dir.display()
|
pkg_dir.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary directory for extracted files
|
// Create a temporary directory for extracted files
|
||||||
let temp_proto_dir = tempdir().map_err(|e| {
|
let temp_proto_dir = tempdir().map_err(|e| {
|
||||||
Pkg6RepoError::from(format!("Failed to create temporary prototype directory: {}", e))
|
Pkg6RepoError::from(format!(
|
||||||
|
"Failed to create temporary prototype directory: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
info!("Created temporary prototype directory: {}", temp_proto_dir.path().display());
|
info!(
|
||||||
|
"Created temporary prototype directory: {}",
|
||||||
|
temp_proto_dir.path().display()
|
||||||
|
);
|
||||||
|
|
||||||
// Find package directories
|
// Find package directories
|
||||||
let mut package_count = 0;
|
let mut package_count = 0;
|
||||||
|
|
||||||
for pkg_entry in fs::read_dir(&pkg_dir).map_err(|e| {
|
for pkg_entry in fs::read_dir(&pkg_dir).map_err(|e| Pkg6RepoError::IoError(e))? {
|
||||||
Pkg6RepoError::IoError(e)
|
let pkg_entry = pkg_entry.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})? {
|
|
||||||
let pkg_entry = pkg_entry.map_err(|e| {
|
|
||||||
Pkg6RepoError::IoError(e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let pkg_path = pkg_entry.path();
|
let pkg_path = pkg_entry.path();
|
||||||
|
|
||||||
if pkg_path.is_dir() {
|
if pkg_path.is_dir() {
|
||||||
// This is a package directory
|
// This is a package directory
|
||||||
let pkg_name = pkg_path.file_name().unwrap().to_string_lossy().to_string();
|
let pkg_name = pkg_path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
let decoded_pkg_name = url_decode(&pkg_name);
|
let decoded_pkg_name = url_decode(&pkg_name);
|
||||||
|
|
||||||
debug!("Processing package: {}", decoded_pkg_name);
|
debug!("Processing package: {}", decoded_pkg_name);
|
||||||
|
|
||||||
// Find package versions
|
// Find package versions
|
||||||
for ver_entry in fs::read_dir(&pkg_path).map_err(|e| {
|
for ver_entry in fs::read_dir(&pkg_path).map_err(|e| Pkg6RepoError::IoError(e))? {
|
||||||
Pkg6RepoError::IoError(e)
|
let ver_entry = ver_entry.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})? {
|
|
||||||
let ver_entry = ver_entry.map_err(|e| {
|
|
||||||
Pkg6RepoError::IoError(e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let ver_path = ver_entry.path();
|
let ver_path = ver_entry.path();
|
||||||
|
|
||||||
if ver_path.is_file() {
|
if ver_path.is_file() {
|
||||||
// This is a package version
|
// This is a package version
|
||||||
let ver_name = ver_path.file_name().unwrap().to_string_lossy().to_string();
|
let ver_name = ver_path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
let decoded_ver_name = url_decode(&ver_name);
|
let decoded_ver_name = url_decode(&ver_name);
|
||||||
|
|
||||||
debug!("Processing version: {}", decoded_ver_name);
|
debug!("Processing version: {}", decoded_ver_name);
|
||||||
|
|
||||||
// Import this package version
|
// Import this package version
|
||||||
self.import_package_version(
|
self.import_package_version(
|
||||||
source_path,
|
source_path,
|
||||||
|
|
@ -283,13 +325,13 @@ impl Pkg5Importer {
|
||||||
&decoded_ver_name,
|
&decoded_ver_name,
|
||||||
temp_proto_dir.path(),
|
temp_proto_dir.path(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
package_count += 1;
|
package_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Imported {} packages", package_count);
|
info!("Imported {} packages", package_count);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -306,115 +348,118 @@ impl Pkg5Importer {
|
||||||
proto_dir: &Path,
|
proto_dir: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
debug!("Importing package version from {}", manifest_path.display());
|
debug!("Importing package version from {}", manifest_path.display());
|
||||||
|
|
||||||
// Extract package name from FMRI
|
// Extract package name from FMRI
|
||||||
debug!("Extracted package name from FMRI: {}", pkg_name);
|
debug!("Extracted package name from FMRI: {}", pkg_name);
|
||||||
|
|
||||||
// Read the manifest file content
|
// Read the manifest file content
|
||||||
debug!("Reading manifest file content from {}", manifest_path.display());
|
debug!(
|
||||||
|
"Reading manifest file content from {}",
|
||||||
|
manifest_path.display()
|
||||||
|
);
|
||||||
let manifest_content = fs::read_to_string(manifest_path).map_err(|e| {
|
let manifest_content = fs::read_to_string(manifest_path).map_err(|e| {
|
||||||
debug!("Error reading manifest file: {}", e);
|
debug!("Error reading manifest file: {}", e);
|
||||||
Pkg6RepoError::IoError(e)
|
Pkg6RepoError::IoError(e)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Parse the manifest using parse_string
|
// Parse the manifest using parse_string
|
||||||
debug!("Parsing manifest content");
|
debug!("Parsing manifest content");
|
||||||
let manifest = Manifest::parse_string(manifest_content)?;
|
let manifest = Manifest::parse_string(manifest_content)?;
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
debug!("Beginning transaction");
|
debug!("Beginning transaction");
|
||||||
let mut transaction = dest_repo.begin_transaction()?;
|
let mut transaction = dest_repo.begin_transaction()?;
|
||||||
|
|
||||||
// Set the publisher for the transaction
|
// Set the publisher for the transaction
|
||||||
debug!("Using specified publisher: {}", publisher);
|
debug!("Using specified publisher: {}", publisher);
|
||||||
transaction.set_publisher(publisher);
|
transaction.set_publisher(publisher);
|
||||||
|
|
||||||
// Debug the repository structure
|
// Debug the repository structure
|
||||||
debug!("Publisher directory: {}", dest_repo.path.join("pkg").join(publisher).display());
|
debug!(
|
||||||
|
"Publisher directory: {}",
|
||||||
|
dest_repo.path.join("pkg").join(publisher).display()
|
||||||
|
);
|
||||||
|
|
||||||
// Extract files referenced in the manifest
|
// Extract files referenced in the manifest
|
||||||
let file_dir = source_path.join("publisher").join(publisher).join("file");
|
let file_dir = source_path.join("publisher").join(publisher).join("file");
|
||||||
|
|
||||||
if !file_dir.exists() || !file_dir.is_dir() {
|
if !file_dir.exists() || !file_dir.is_dir() {
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
"File directory not found: {}",
|
"File directory not found: {}",
|
||||||
file_dir.display()
|
file_dir.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process file actions
|
// Process file actions
|
||||||
for file_action in manifest.files.iter() {
|
for file_action in manifest.files.iter() {
|
||||||
// Extract the hash from the file action's payload
|
// Extract the hash from the file action's payload
|
||||||
if let Some(payload) = &file_action.payload {
|
if let Some(payload) = &file_action.payload {
|
||||||
let hash = payload.primary_identifier.hash.clone();
|
let hash = payload.primary_identifier.hash.clone();
|
||||||
|
|
||||||
// Determine the file path in the source repository
|
// Determine the file path in the source repository
|
||||||
let hash_prefix = &hash[0..2];
|
let hash_prefix = &hash[0..2];
|
||||||
let file_path = file_dir.join(hash_prefix).join(&hash);
|
let file_path = file_dir.join(hash_prefix).join(&hash);
|
||||||
|
|
||||||
if !file_path.exists() {
|
if !file_path.exists() {
|
||||||
warn!("File not found in source repository: {}", file_path.display());
|
warn!(
|
||||||
|
"File not found in source repository: {}",
|
||||||
|
file_path.display()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the file to the prototype directory
|
// Extract the file to the prototype directory
|
||||||
let proto_file_path = proto_dir.join(&file_action.path);
|
let proto_file_path = proto_dir.join(&file_action.path);
|
||||||
|
|
||||||
// Create parent directories if they don't exist
|
// Create parent directories if they don't exist
|
||||||
if let Some(parent) = proto_file_path.parent() {
|
if let Some(parent) = proto_file_path.parent() {
|
||||||
fs::create_dir_all(parent).map_err(|e| {
|
fs::create_dir_all(parent).map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
Pkg6RepoError::IoError(e)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the gzipped file
|
// Extract the gzipped file
|
||||||
let mut source_file = File::open(&file_path).map_err(|e| {
|
let mut source_file =
|
||||||
Pkg6RepoError::IoError(e)
|
File::open(&file_path).map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})?;
|
|
||||||
|
let mut dest_file =
|
||||||
let mut dest_file = File::create(&proto_file_path).map_err(|e| {
|
File::create(&proto_file_path).map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
Pkg6RepoError::IoError(e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Check if the file is gzipped
|
// Check if the file is gzipped
|
||||||
let mut header = [0; 2];
|
let mut header = [0; 2];
|
||||||
source_file.read_exact(&mut header).map_err(|e| {
|
source_file
|
||||||
Pkg6RepoError::IoError(e)
|
.read_exact(&mut header)
|
||||||
})?;
|
.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
|
|
||||||
// Reset file position
|
// Reset file position
|
||||||
source_file.seek(std::io::SeekFrom::Start(0)).map_err(|e| {
|
source_file
|
||||||
Pkg6RepoError::IoError(e)
|
.seek(std::io::SeekFrom::Start(0))
|
||||||
})?;
|
.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
|
|
||||||
if header[0] == 0x1f && header[1] == 0x8b {
|
if header[0] == 0x1f && header[1] == 0x8b {
|
||||||
// File is gzipped, decompress it
|
// File is gzipped, decompress it
|
||||||
let mut decoder = flate2::read::GzDecoder::new(source_file);
|
let mut decoder = flate2::read::GzDecoder::new(source_file);
|
||||||
std::io::copy(&mut decoder, &mut dest_file).map_err(|e| {
|
std::io::copy(&mut decoder, &mut dest_file)
|
||||||
Pkg6RepoError::IoError(e)
|
.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})?;
|
|
||||||
} else {
|
} else {
|
||||||
// File is not gzipped, copy it as is
|
// File is not gzipped, copy it as is
|
||||||
std::io::copy(&mut source_file, &mut dest_file).map_err(|e| {
|
std::io::copy(&mut source_file, &mut dest_file)
|
||||||
Pkg6RepoError::IoError(e)
|
.map_err(|e| Pkg6RepoError::IoError(e))?;
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the transaction
|
// Add the file to the transaction
|
||||||
transaction.add_file(file_action.clone(), &proto_file_path)?;
|
transaction.add_file(file_action.clone(), &proto_file_path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the manifest in the transaction
|
// Update the manifest in the transaction
|
||||||
transaction.update_manifest(manifest);
|
transaction.update_manifest(manifest);
|
||||||
|
|
||||||
// The Transaction.commit() method will handle creating necessary directories
|
// The Transaction.commit() method will handle creating necessary directories
|
||||||
// and storing the manifest in the correct location, so we don't need to create
|
// and storing the manifest in the correct location, so we don't need to create
|
||||||
// package-specific directories here.
|
// package-specific directories here.
|
||||||
|
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
transaction.commit()?;
|
transaction.commit()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -423,10 +468,10 @@ impl Pkg5Importer {
|
||||||
fn url_decode(s: &str) -> String {
|
fn url_decode(s: &str) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
||||||
while i < s.len() {
|
while i < s.len() {
|
||||||
if s[i..].starts_with("%") && i + 2 < s.len() {
|
if s[i..].starts_with("%") && i + 2 < s.len() {
|
||||||
if let Ok(hex) = u8::from_str_radix(&s[i+1..i+3], 16) {
|
if let Ok(hex) = u8::from_str_radix(&s[i + 1..i + 3], 16) {
|
||||||
result.push(hex as char);
|
result.push(hex as char);
|
||||||
i += 3;
|
i += 3;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -438,14 +483,14 @@ fn url_decode(s: &str) -> String {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_decode() {
|
fn test_url_decode() {
|
||||||
assert_eq!(url_decode("test"), "test");
|
assert_eq!(url_decode("test"), "test");
|
||||||
|
|
@ -454,4 +499,4 @@ mod tests {
|
||||||
assert_eq!(url_decode("test%2Ctest"), "test,test");
|
assert_eq!(url_decode("test%2Ctest"), "test,test");
|
||||||
assert_eq!(url_decode("test%3Atest"), "test:test");
|
assert_eq!(url_decode("test%3Atest"), "test:test");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use libips::repository::{
|
use libips::repository::{
|
||||||
FileBackend, ReadableRepository, RepositoryVersion,
|
FileBackend, REPOSITORY_CONFIG_FILENAME, ReadableRepository, RepositoryVersion,
|
||||||
WritableRepository, REPOSITORY_CONFIG_FILENAME,
|
WritableRepository,
|
||||||
};
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
|
||||||
|
|
@ -22,45 +22,45 @@ struct Cli {
|
||||||
enum Commands {
|
enum Commands {
|
||||||
/// Set up the test environment for repository tests
|
/// Set up the test environment for repository tests
|
||||||
SetupTestEnv,
|
SetupTestEnv,
|
||||||
|
|
||||||
/// Build the project
|
/// Build the project
|
||||||
Build {
|
Build {
|
||||||
/// Build with release optimizations
|
/// Build with release optimizations
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
release: bool,
|
release: bool,
|
||||||
|
|
||||||
/// Specific crate to build
|
/// Specific crate to build
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
package: Option<String>,
|
package: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Run tests
|
/// Run tests
|
||||||
Test {
|
Test {
|
||||||
/// Run tests with release optimizations
|
/// Run tests with release optimizations
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
release: bool,
|
release: bool,
|
||||||
|
|
||||||
/// Specific crate to test
|
/// Specific crate to test
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
package: Option<String>,
|
package: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Build binaries for end-to-end tests
|
/// Build binaries for end-to-end tests
|
||||||
BuildE2E,
|
BuildE2E,
|
||||||
|
|
||||||
/// Run end-to-end tests using pre-built binaries
|
/// Run end-to-end tests using pre-built binaries
|
||||||
RunE2E {
|
RunE2E {
|
||||||
/// Specific test to run (runs all e2e tests if not specified)
|
/// Specific test to run (runs all e2e tests if not specified)
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
test: Option<String>,
|
test: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Format code
|
/// Format code
|
||||||
Fmt,
|
Fmt,
|
||||||
|
|
||||||
/// Run clippy
|
/// Run clippy
|
||||||
Clippy,
|
Clippy,
|
||||||
|
|
||||||
/// Clean build artifacts
|
/// Clean build artifacts
|
||||||
Clean,
|
Clean,
|
||||||
}
|
}
|
||||||
|
|
@ -83,20 +83,21 @@ fn main() -> Result<()> {
|
||||||
/// Set up the test environment for repository tests
|
/// Set up the test environment for repository tests
|
||||||
fn setup_test_env() -> Result<()> {
|
fn setup_test_env() -> Result<()> {
|
||||||
println!("Setting up test environment...");
|
println!("Setting up test environment...");
|
||||||
|
|
||||||
// Clean up any existing test directories except the bin directory
|
// Clean up any existing test directories except the bin directory
|
||||||
if Path::new(TEST_BASE_DIR).exists() {
|
if Path::new(TEST_BASE_DIR).exists() {
|
||||||
println!("Cleaning up existing test directory...");
|
println!("Cleaning up existing test directory...");
|
||||||
|
|
||||||
// Remove subdirectories individually, preserving the bin directory
|
// Remove subdirectories individually, preserving the bin directory
|
||||||
let entries = fs::read_dir(TEST_BASE_DIR).context("Failed to read test directory")?;
|
let entries = fs::read_dir(TEST_BASE_DIR).context("Failed to read test directory")?;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let entry = entry.context("Failed to read directory entry")?;
|
let entry = entry.context("Failed to read directory entry")?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
// Skip the bin directory
|
// Skip the bin directory
|
||||||
if path.is_dir() && path.file_name().unwrap_or_default() != "bin" {
|
if path.is_dir() && path.file_name().unwrap_or_default() != "bin" {
|
||||||
fs::remove_dir_all(&path).context(format!("Failed to remove directory: {:?}", path))?;
|
fs::remove_dir_all(&path)
|
||||||
|
.context(format!("Failed to remove directory: {:?}", path))?;
|
||||||
} else if path.is_file() {
|
} else if path.is_file() {
|
||||||
fs::remove_file(&path).context(format!("Failed to remove file: {:?}", path))?;
|
fs::remove_file(&path).context(format!("Failed to remove file: {:?}", path))?;
|
||||||
}
|
}
|
||||||
|
|
@ -105,32 +106,38 @@ fn setup_test_env() -> Result<()> {
|
||||||
// Create the base directory if it doesn't exist
|
// Create the base directory if it doesn't exist
|
||||||
fs::create_dir_all(TEST_BASE_DIR).context("Failed to create test base directory")?;
|
fs::create_dir_all(TEST_BASE_DIR).context("Failed to create test base directory")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create test directories
|
// Create test directories
|
||||||
println!("Creating test directories...");
|
println!("Creating test directories...");
|
||||||
fs::create_dir_all(PROTOTYPE_DIR).context("Failed to create prototype directory")?;
|
fs::create_dir_all(PROTOTYPE_DIR).context("Failed to create prototype directory")?;
|
||||||
fs::create_dir_all(MANIFEST_DIR).context("Failed to create manifest directory")?;
|
fs::create_dir_all(MANIFEST_DIR).context("Failed to create manifest directory")?;
|
||||||
|
|
||||||
// Compile the applications
|
// Compile the applications
|
||||||
println!("Compiling applications...");
|
println!("Compiling applications...");
|
||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.arg("build")
|
.arg("build")
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to compile applications")?;
|
.context("Failed to compile applications")?;
|
||||||
|
|
||||||
// Create a simple prototype directory structure with some files
|
// Create a simple prototype directory structure with some files
|
||||||
println!("Creating prototype directory structure...");
|
println!("Creating prototype directory structure...");
|
||||||
|
|
||||||
// Create some directories
|
// Create some directories
|
||||||
fs::create_dir_all(format!("{}/usr/bin", PROTOTYPE_DIR)).context("Failed to create usr/bin directory")?;
|
fs::create_dir_all(format!("{}/usr/bin", PROTOTYPE_DIR))
|
||||||
fs::create_dir_all(format!("{}/usr/share/doc/example", PROTOTYPE_DIR)).context("Failed to create usr/share/doc/example directory")?;
|
.context("Failed to create usr/bin directory")?;
|
||||||
fs::create_dir_all(format!("{}/etc/config", PROTOTYPE_DIR)).context("Failed to create etc/config directory")?;
|
fs::create_dir_all(format!("{}/usr/share/doc/example", PROTOTYPE_DIR))
|
||||||
|
.context("Failed to create usr/share/doc/example directory")?;
|
||||||
|
fs::create_dir_all(format!("{}/etc/config", PROTOTYPE_DIR))
|
||||||
|
.context("Failed to create etc/config directory")?;
|
||||||
|
|
||||||
// Create some files
|
// Create some files
|
||||||
let hello_script = "#!/bin/sh\necho 'Hello, World!'";
|
let hello_script = "#!/bin/sh\necho 'Hello, World!'";
|
||||||
let mut hello_file = File::create(format!("{}/usr/bin/hello", PROTOTYPE_DIR)).context("Failed to create hello script")?;
|
let mut hello_file = File::create(format!("{}/usr/bin/hello", PROTOTYPE_DIR))
|
||||||
hello_file.write_all(hello_script.as_bytes()).context("Failed to write hello script")?;
|
.context("Failed to create hello script")?;
|
||||||
|
hello_file
|
||||||
|
.write_all(hello_script.as_bytes())
|
||||||
|
.context("Failed to write hello script")?;
|
||||||
|
|
||||||
// Make the script executable
|
// Make the script executable
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
|
@ -142,17 +149,24 @@ fn setup_test_env() -> Result<()> {
|
||||||
fs::set_permissions(format!("{}/usr/bin/hello", PROTOTYPE_DIR), perms)
|
fs::set_permissions(format!("{}/usr/bin/hello", PROTOTYPE_DIR), perms)
|
||||||
.context("Failed to set hello script permissions")?;
|
.context("Failed to set hello script permissions")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let readme_content = "This is an example document.";
|
let readme_content = "This is an example document.";
|
||||||
let mut readme_file = File::create(format!("{}/usr/share/doc/example/README.txt", PROTOTYPE_DIR))
|
let mut readme_file = File::create(format!(
|
||||||
.context("Failed to create README.txt")?;
|
"{}/usr/share/doc/example/README.txt",
|
||||||
readme_file.write_all(readme_content.as_bytes()).context("Failed to write README.txt")?;
|
PROTOTYPE_DIR
|
||||||
|
))
|
||||||
|
.context("Failed to create README.txt")?;
|
||||||
|
readme_file
|
||||||
|
.write_all(readme_content.as_bytes())
|
||||||
|
.context("Failed to write README.txt")?;
|
||||||
|
|
||||||
let config_content = "# Example configuration file\nvalue=42";
|
let config_content = "# Example configuration file\nvalue=42";
|
||||||
let mut config_file = File::create(format!("{}/etc/config/example.conf", PROTOTYPE_DIR))
|
let mut config_file = File::create(format!("{}/etc/config/example.conf", PROTOTYPE_DIR))
|
||||||
.context("Failed to create example.conf")?;
|
.context("Failed to create example.conf")?;
|
||||||
config_file.write_all(config_content.as_bytes()).context("Failed to write example.conf")?;
|
config_file
|
||||||
|
.write_all(config_content.as_bytes())
|
||||||
|
.context("Failed to write example.conf")?;
|
||||||
|
|
||||||
// Create a simple manifest
|
// Create a simple manifest
|
||||||
println!("Creating package manifest...");
|
println!("Creating package manifest...");
|
||||||
let example_manifest = r#"set name=pkg.fmri value=pkg://test/example@1.0.0
|
let example_manifest = r#"set name=pkg.fmri value=pkg://test/example@1.0.0
|
||||||
|
|
@ -167,11 +181,13 @@ dir path=usr/bin mode=0755 owner=root group=bin
|
||||||
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
||||||
dir path=etc/config mode=0755 owner=root group=sys
|
dir path=etc/config mode=0755 owner=root group=sys
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut example_file = File::create(format!("{}/example.p5m", MANIFEST_DIR))
|
let mut example_file = File::create(format!("{}/example.p5m", MANIFEST_DIR))
|
||||||
.context("Failed to create example.p5m")?;
|
.context("Failed to create example.p5m")?;
|
||||||
example_file.write_all(example_manifest.as_bytes()).context("Failed to write example.p5m")?;
|
example_file
|
||||||
|
.write_all(example_manifest.as_bytes())
|
||||||
|
.context("Failed to write example.p5m")?;
|
||||||
|
|
||||||
// Create a second manifest for testing multiple packages
|
// Create a second manifest for testing multiple packages
|
||||||
let example2_manifest = r#"set name=pkg.fmri value=pkg://test/example2@1.0.0
|
let example2_manifest = r#"set name=pkg.fmri value=pkg://test/example2@1.0.0
|
||||||
set name=pkg.summary value="Second example package for testing"
|
set name=pkg.summary value="Second example package for testing"
|
||||||
|
|
@ -183,15 +199,17 @@ file path=usr/share/doc/example/README.txt mode=0644 owner=root group=bin
|
||||||
dir path=usr/bin mode=0755 owner=root group=bin
|
dir path=usr/bin mode=0755 owner=root group=bin
|
||||||
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut example2_file = File::create(format!("{}/example2.p5m", MANIFEST_DIR))
|
let mut example2_file = File::create(format!("{}/example2.p5m", MANIFEST_DIR))
|
||||||
.context("Failed to create example2.p5m")?;
|
.context("Failed to create example2.p5m")?;
|
||||||
example2_file.write_all(example2_manifest.as_bytes()).context("Failed to write example2.p5m")?;
|
example2_file
|
||||||
|
.write_all(example2_manifest.as_bytes())
|
||||||
|
.context("Failed to write example2.p5m")?;
|
||||||
|
|
||||||
println!("Test environment setup complete!");
|
println!("Test environment setup complete!");
|
||||||
println!("Prototype directory: {}", PROTOTYPE_DIR);
|
println!("Prototype directory: {}", PROTOTYPE_DIR);
|
||||||
println!("Manifest directory: {}", MANIFEST_DIR);
|
println!("Manifest directory: {}", MANIFEST_DIR);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,17 +217,17 @@ dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
||||||
fn build(release: &bool, package: &Option<String>) -> Result<()> {
|
fn build(release: &bool, package: &Option<String>) -> Result<()> {
|
||||||
let mut cmd = Command::new("cargo");
|
let mut cmd = Command::new("cargo");
|
||||||
cmd.arg("build");
|
cmd.arg("build");
|
||||||
|
|
||||||
if *release {
|
if *release {
|
||||||
cmd.arg("--release");
|
cmd.arg("--release");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pkg) = package {
|
if let Some(pkg) = package {
|
||||||
cmd.args(["--package", pkg]);
|
cmd.args(["--package", pkg]);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.status().context("Failed to build project")?;
|
cmd.status().context("Failed to build project")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,17 +235,17 @@ fn build(release: &bool, package: &Option<String>) -> Result<()> {
|
||||||
fn test(release: &bool, package: &Option<String>) -> Result<()> {
|
fn test(release: &bool, package: &Option<String>) -> Result<()> {
|
||||||
let mut cmd = Command::new("cargo");
|
let mut cmd = Command::new("cargo");
|
||||||
cmd.arg("test");
|
cmd.arg("test");
|
||||||
|
|
||||||
if *release {
|
if *release {
|
||||||
cmd.arg("--release");
|
cmd.arg("--release");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pkg) = package {
|
if let Some(pkg) = package {
|
||||||
cmd.args(["--package", pkg]);
|
cmd.args(["--package", pkg]);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.status().context("Failed to run tests")?;
|
cmd.status().context("Failed to run tests")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,17 +255,24 @@ fn fmt() -> Result<()> {
|
||||||
.arg("fmt")
|
.arg("fmt")
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to format code")?;
|
.context("Failed to format code")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run clippy
|
/// Run clippy
|
||||||
fn clippy() -> Result<()> {
|
fn clippy() -> Result<()> {
|
||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.args(["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"])
|
.args([
|
||||||
|
"clippy",
|
||||||
|
"--all-targets",
|
||||||
|
"--all-features",
|
||||||
|
"--",
|
||||||
|
"-D",
|
||||||
|
"warnings",
|
||||||
|
])
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to run clippy")?;
|
.context("Failed to run clippy")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,85 +282,85 @@ fn clean() -> Result<()> {
|
||||||
.arg("clean")
|
.arg("clean")
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to clean build artifacts")?;
|
.context("Failed to clean build artifacts")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build binaries for end-to-end tests
|
/// Build binaries for end-to-end tests
|
||||||
fn build_e2e() -> Result<()> {
|
fn build_e2e() -> Result<()> {
|
||||||
println!("Building binaries for end-to-end tests...");
|
println!("Building binaries for end-to-end tests...");
|
||||||
|
|
||||||
// Create the bin directory if it doesn't exist
|
// Create the bin directory if it doesn't exist
|
||||||
fs::create_dir_all(E2E_TEST_BIN_DIR).context("Failed to create bin directory")?;
|
fs::create_dir_all(E2E_TEST_BIN_DIR).context("Failed to create bin directory")?;
|
||||||
|
|
||||||
// Build pkg6repo in release mode
|
// Build pkg6repo in release mode
|
||||||
println!("Building pkg6repo...");
|
println!("Building pkg6repo...");
|
||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.args(["build", "--release", "--package", "pkg6repo"])
|
.args(["build", "--release", "--package", "pkg6repo"])
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to build pkg6repo")?;
|
.context("Failed to build pkg6repo")?;
|
||||||
|
|
||||||
// Build pkg6dev in release mode
|
// Build pkg6dev in release mode
|
||||||
println!("Building pkg6dev...");
|
println!("Building pkg6dev...");
|
||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.args(["build", "--release", "--package", "pkg6dev"])
|
.args(["build", "--release", "--package", "pkg6dev"])
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to build pkg6dev")?;
|
.context("Failed to build pkg6dev")?;
|
||||||
|
|
||||||
// Copy the binaries to the bin directory
|
// Copy the binaries to the bin directory
|
||||||
let target_dir = PathBuf::from("target/release");
|
let target_dir = PathBuf::from("target/release");
|
||||||
|
|
||||||
println!("Copying binaries to test directory...");
|
println!("Copying binaries to test directory...");
|
||||||
fs::copy(
|
fs::copy(
|
||||||
target_dir.join("pkg6repo"),
|
target_dir.join("pkg6repo"),
|
||||||
PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6repo"),
|
PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6repo"),
|
||||||
)
|
)
|
||||||
.context("Failed to copy pkg6repo binary")?;
|
.context("Failed to copy pkg6repo binary")?;
|
||||||
|
|
||||||
fs::copy(
|
fs::copy(
|
||||||
target_dir.join("pkg6dev"),
|
target_dir.join("pkg6dev"),
|
||||||
PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6dev"),
|
PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6dev"),
|
||||||
)
|
)
|
||||||
.context("Failed to copy pkg6dev binary")?;
|
.context("Failed to copy pkg6dev binary")?;
|
||||||
|
|
||||||
println!("End-to-end test binaries built successfully!");
|
println!("End-to-end test binaries built successfully!");
|
||||||
println!("Binaries are located at: {}", E2E_TEST_BIN_DIR);
|
println!("Binaries are located at: {}", E2E_TEST_BIN_DIR);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run end-to-end tests using pre-built binaries
|
/// Run end-to-end tests using pre-built binaries
|
||||||
fn run_e2e(test: &Option<String>) -> Result<()> {
|
fn run_e2e(test: &Option<String>) -> Result<()> {
|
||||||
println!("Running end-to-end tests...");
|
println!("Running end-to-end tests...");
|
||||||
|
|
||||||
// Check if the binaries exist
|
// Check if the binaries exist
|
||||||
let pkg6repo_bin = PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6repo");
|
let pkg6repo_bin = PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6repo");
|
||||||
let pkg6dev_bin = PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6dev");
|
let pkg6dev_bin = PathBuf::from(E2E_TEST_BIN_DIR).join("pkg6dev");
|
||||||
|
|
||||||
if !pkg6repo_bin.exists() || !pkg6dev_bin.exists() {
|
if !pkg6repo_bin.exists() || !pkg6dev_bin.exists() {
|
||||||
println!("Pre-built binaries not found. Building them first...");
|
println!("Pre-built binaries not found. Building them first...");
|
||||||
build_e2e()?;
|
build_e2e()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the test environment
|
// Set up the test environment
|
||||||
setup_test_env()?;
|
setup_test_env()?;
|
||||||
|
|
||||||
// Run the tests
|
// Run the tests
|
||||||
let mut cmd = Command::new("cargo");
|
let mut cmd = Command::new("cargo");
|
||||||
cmd.arg("test");
|
cmd.arg("test");
|
||||||
|
|
||||||
if let Some(test_name) = test {
|
if let Some(test_name) = test {
|
||||||
cmd.args(["--package", "pkg6repo", "--test", "e2e_tests", test_name]);
|
cmd.args(["--package", "pkg6repo", "--test", "e2e_tests", test_name]);
|
||||||
} else {
|
} else {
|
||||||
cmd.args(["--package", "pkg6repo", "--test", "e2e_tests"]);
|
cmd.args(["--package", "pkg6repo", "--test", "e2e_tests"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the environment variable for the test binaries
|
// Set the environment variable for the test binaries
|
||||||
cmd.env("PKG6_TEST_BIN_DIR", E2E_TEST_BIN_DIR);
|
cmd.env("PKG6_TEST_BIN_DIR", E2E_TEST_BIN_DIR);
|
||||||
|
|
||||||
cmd.status().context("Failed to run end-to-end tests")?;
|
cmd.status().context("Failed to run end-to-end tests")?;
|
||||||
|
|
||||||
println!("End-to-end tests completed!");
|
println!("End-to-end tests completed!");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue