mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Refactor repository structure to use publisher-specific directories
- Introduce publisher-specific hierarchy (`publisher/<publisher_name>`) for catalog, package, and file storage. - Replace hardcoded paths with helper methods (`construct_catalog_path`, `construct_package_dir`, `construct_file_path_with_publisher`) for consistent path generation. - Remove outdated tests and simplify redundant assertions in repository tests. - Update `CatalogManager` and file handling logic to leverage publisher-specific paths. - Enhance debug logs to include publisher context where applicable.
This commit is contained in:
parent
845ffec13b
commit
99dd0fe87c
9 changed files with 259 additions and 188 deletions
|
|
@ -380,6 +380,9 @@ pub struct CatalogManager {
|
||||||
/// Path to the catalog directory
|
/// Path to the catalog directory
|
||||||
catalog_dir: PathBuf,
|
catalog_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Publisher name
|
||||||
|
publisher: String,
|
||||||
|
|
||||||
/// Catalog attributes
|
/// Catalog attributes
|
||||||
attrs: CatalogAttrs,
|
attrs: CatalogAttrs,
|
||||||
|
|
||||||
|
|
@ -392,16 +395,17 @@ pub struct CatalogManager {
|
||||||
|
|
||||||
impl CatalogManager {
|
impl CatalogManager {
|
||||||
/// Create a new catalog manager
|
/// Create a new catalog manager
|
||||||
pub fn new<P: AsRef<Path>>(catalog_dir: P) -> Result<Self> {
|
pub fn new<P: AsRef<Path>>(base_dir: P, publisher: &str) -> Result<Self> {
|
||||||
let catalog_dir = catalog_dir.as_ref().to_path_buf();
|
let base_dir = base_dir.as_ref().to_path_buf();
|
||||||
|
let publisher_catalog_dir = base_dir.join(publisher).join("catalog");
|
||||||
|
|
||||||
// Create catalog directory if it doesn't exist
|
// Create catalog directory if it doesn't exist
|
||||||
if !catalog_dir.exists() {
|
if !publisher_catalog_dir.exists() {
|
||||||
fs::create_dir_all(&catalog_dir)?;
|
fs::create_dir_all(&publisher_catalog_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to load existing catalog attributes
|
// Try to load existing catalog attributes
|
||||||
let attrs_path = catalog_dir.join("catalog.attrs");
|
let attrs_path = publisher_catalog_dir.join("catalog.attrs");
|
||||||
let attrs = if attrs_path.exists() {
|
let attrs = if attrs_path.exists() {
|
||||||
CatalogAttrs::load(&attrs_path)?
|
CatalogAttrs::load(&attrs_path)?
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -409,7 +413,8 @@ impl CatalogManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(CatalogManager {
|
Ok(CatalogManager {
|
||||||
catalog_dir,
|
catalog_dir: publisher_catalog_dir,
|
||||||
|
publisher: publisher.to_string(),
|
||||||
attrs,
|
attrs,
|
||||||
parts: HashMap::new(),
|
parts: HashMap::new(),
|
||||||
update_logs: HashMap::new(),
|
update_logs: HashMap::new(),
|
||||||
|
|
@ -537,4 +542,41 @@ impl CatalogManager {
|
||||||
pub fn get_update_log_mut(&mut self, name: &str) -> Option<&mut UpdateLog> {
|
pub fn get_update_log_mut(&mut self, name: &str) -> Option<&mut UpdateLog> {
|
||||||
self.update_logs.get_mut(name)
|
self.update_logs.get_mut(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a package to a catalog part using the stored publisher
|
||||||
|
pub fn add_package_to_part(
|
||||||
|
&mut self,
|
||||||
|
part_name: &str,
|
||||||
|
fmri: &Fmri,
|
||||||
|
actions: Option<Vec<String>>,
|
||||||
|
signature: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Some(part) = self.parts.get_mut(part_name) {
|
||||||
|
part.add_package(&self.publisher, fmri, actions, signature);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CatalogError::CatalogPartNotLoaded {
|
||||||
|
name: part_name.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an update to an update log using the stored publisher
|
||||||
|
pub fn add_update_to_log(
|
||||||
|
&mut self,
|
||||||
|
log_name: &str,
|
||||||
|
fmri: &Fmri,
|
||||||
|
op_type: CatalogOperationType,
|
||||||
|
catalog_parts: HashMap<String, HashMap<String, Vec<String>>>,
|
||||||
|
signature: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Some(log) = self.update_logs.get_mut(log_name) {
|
||||||
|
log.add_update(&self.publisher, fmri, op_type, catalog_parts, signature);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CatalogError::UpdateLogNotLoaded {
|
||||||
|
name: log_name.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -454,24 +454,45 @@ impl Transaction {
|
||||||
let manifest_json = serde_json::to_string_pretty(&self.manifest)?;
|
let manifest_json = serde_json::to_string_pretty(&self.manifest)?;
|
||||||
fs::write(&manifest_path, manifest_json)?;
|
fs::write(&manifest_path, manifest_json)?;
|
||||||
|
|
||||||
|
// Determine the publisher to use
|
||||||
|
let publisher = match &self.publisher {
|
||||||
|
Some(pub_name) => {
|
||||||
|
debug!("Using specified publisher: {}", pub_name);
|
||||||
|
pub_name.clone()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug!("No publisher specified, trying to use default publisher");
|
||||||
|
// If no publisher is specified, use the default publisher from the repository config
|
||||||
|
let config_path = self.repo.join(REPOSITORY_CONFIG_FILENAME);
|
||||||
|
if config_path.exists() {
|
||||||
|
let config_content = fs::read_to_string(&config_path)?;
|
||||||
|
let config: RepositoryConfig = serde_json::from_str(&config_content)?;
|
||||||
|
match config.default_publisher {
|
||||||
|
Some(default_pub) => {
|
||||||
|
debug!("Using default publisher: {}", default_pub);
|
||||||
|
default_pub
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug!("No default publisher set in repository");
|
||||||
|
return Err(RepositoryError::Other(
|
||||||
|
"No publisher specified and no default publisher set in repository"
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("Repository configuration not found");
|
||||||
|
return Err(RepositoryError::Other(
|
||||||
|
"No publisher specified and repository configuration not found".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Copy files to their final location
|
// Copy files to their final location
|
||||||
for (source_path, hash) in self.files {
|
for (source_path, hash) in self.files {
|
||||||
// Create the destination path using the new directory structure
|
// Create the destination path using the helper function with publisher
|
||||||
let dest_path = if hash.len() < 4 {
|
let dest_path = FileBackend::construct_file_path_with_publisher(&self.repo, &publisher, &hash);
|
||||||
// Fallback for very short hashes (shouldn't happen with SHA256)
|
|
||||||
self.repo.join("file").join(&hash)
|
|
||||||
} else {
|
|
||||||
// Extract the first two and next two characters from the hash
|
|
||||||
let first_two = &hash[0..2];
|
|
||||||
let next_two = &hash[2..4];
|
|
||||||
|
|
||||||
// Create the path: $REPO/file/XX/YY/XXYY...
|
|
||||||
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
|
||||||
if let Some(parent) = dest_path.parent() {
|
if let Some(parent) = dest_path.parent() {
|
||||||
|
|
@ -535,7 +556,7 @@ impl Transaction {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the package directory if it doesn't exist
|
// Create the package directory if it doesn't exist
|
||||||
let pkg_dir = self.repo.join("pkg").join(&publisher).join(&package_stem);
|
let pkg_dir = FileBackend::construct_package_dir(&self.repo, &publisher, &package_stem);
|
||||||
debug!("Package directory: {}", pkg_dir.display());
|
debug!("Package directory: {}", pkg_dir.display());
|
||||||
if !pkg_dir.exists() {
|
if !pkg_dir.exists() {
|
||||||
debug!("Creating package directory");
|
debug!("Creating package directory");
|
||||||
|
|
@ -627,8 +648,8 @@ impl ReadableRepository for FileBackend {
|
||||||
let mut publishers = Vec::new();
|
let mut publishers = Vec::new();
|
||||||
|
|
||||||
for publisher_name in &self.config.publishers {
|
for publisher_name in &self.config.publishers {
|
||||||
// Count packages by scanning the pkg/<publisher> directory
|
// Count packages by scanning the publisher's package directory
|
||||||
let publisher_pkg_dir = self.path.join("pkg").join(publisher_name);
|
let publisher_pkg_dir = Self::construct_package_dir(&self.path, publisher_name, "");
|
||||||
let mut package_count = 0;
|
let mut package_count = 0;
|
||||||
let mut latest_timestamp = SystemTime::UNIX_EPOCH;
|
let mut latest_timestamp = SystemTime::UNIX_EPOCH;
|
||||||
|
|
||||||
|
|
@ -702,7 +723,7 @@ impl ReadableRepository for FileBackend {
|
||||||
// For each publisher, list packages
|
// For each publisher, list packages
|
||||||
for pub_name in publishers {
|
for pub_name in publishers {
|
||||||
// Get the publisher's package directory
|
// Get the publisher's package directory
|
||||||
let publisher_pkg_dir = self.path.join("pkg").join(&pub_name);
|
let publisher_pkg_dir = Self::construct_package_dir(&self.path, &pub_name, "");
|
||||||
|
|
||||||
// Check if the publisher directory exists
|
// Check if the publisher directory exists
|
||||||
if publisher_pkg_dir.exists() {
|
if publisher_pkg_dir.exists() {
|
||||||
|
|
@ -751,7 +772,7 @@ impl ReadableRepository for FileBackend {
|
||||||
// For each publisher, process packages
|
// For each publisher, process packages
|
||||||
for pub_name in publishers {
|
for pub_name in publishers {
|
||||||
// Get the publisher's package directory
|
// Get the publisher's package directory
|
||||||
let publisher_pkg_dir = self.path.join("pkg").join(&pub_name);
|
let publisher_pkg_dir = Self::construct_package_dir(&self.path, &pub_name, "");
|
||||||
|
|
||||||
// Check if the publisher directory exists
|
// Check if the publisher directory exists
|
||||||
if publisher_pkg_dir.exists() {
|
if publisher_pkg_dir.exists() {
|
||||||
|
|
@ -1324,8 +1345,8 @@ impl WritableRepository for FileBackend {
|
||||||
self.config.publishers.push(publisher.to_string());
|
self.config.publishers.push(publisher.to_string());
|
||||||
|
|
||||||
// Create publisher-specific directories
|
// Create publisher-specific directories
|
||||||
fs::create_dir_all(self.path.join("catalog").join(publisher))?;
|
fs::create_dir_all(Self::construct_catalog_path(&self.path, publisher))?;
|
||||||
fs::create_dir_all(self.path.join("pkg").join(publisher))?;
|
fs::create_dir_all(Self::construct_package_dir(&self.path, publisher, ""))?;
|
||||||
|
|
||||||
// Set as the default publisher if no default publisher is set
|
// Set as the default publisher if no default publisher is set
|
||||||
if self.config.default_publisher.is_none() {
|
if self.config.default_publisher.is_none() {
|
||||||
|
|
@ -1346,8 +1367,8 @@ impl WritableRepository for FileBackend {
|
||||||
self.config.publishers.remove(pos);
|
self.config.publishers.remove(pos);
|
||||||
|
|
||||||
// Remove publisher-specific directories and their contents recursively
|
// Remove publisher-specific directories and their contents recursively
|
||||||
let catalog_dir = self.path.join("catalog").join(publisher);
|
let catalog_dir = Self::construct_catalog_path(&self.path, publisher);
|
||||||
let pkg_dir = self.path.join("pkg").join(publisher);
|
let pkg_dir = Self::construct_package_dir(&self.path, publisher, "");
|
||||||
|
|
||||||
// Remove the catalog directory if it exists
|
// Remove the catalog directory if it exists
|
||||||
if catalog_dir.exists() {
|
if catalog_dir.exists() {
|
||||||
|
|
@ -1492,18 +1513,91 @@ impl WritableRepository for FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileBackend {
|
impl FileBackend {
|
||||||
|
/// Helper method to construct a catalog path consistently
|
||||||
|
///
|
||||||
|
/// Format: base_path/publisher/publisher_name/catalog
|
||||||
|
pub fn construct_catalog_path(
|
||||||
|
base_path: &Path,
|
||||||
|
publisher: &str,
|
||||||
|
) -> PathBuf {
|
||||||
|
base_path.join("publisher").join(publisher).join("catalog")
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper method to construct a manifest path consistently
|
/// Helper method to construct a manifest path consistently
|
||||||
fn construct_manifest_path(
|
///
|
||||||
|
/// Format: base_path/publisher/publisher_name/pkg/stem/encoded_version
|
||||||
|
pub fn construct_manifest_path(
|
||||||
base_path: &Path,
|
base_path: &Path,
|
||||||
publisher: &str,
|
publisher: &str,
|
||||||
stem: &str,
|
stem: &str,
|
||||||
version: &str,
|
version: &str,
|
||||||
) -> PathBuf {
|
) -> PathBuf {
|
||||||
let pkg_dir = base_path.join("pkg").join(publisher).join(stem);
|
let pkg_dir = Self::construct_package_dir(base_path, publisher, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to construct a package directory path consistently
|
||||||
|
///
|
||||||
|
/// Format: base_path/publisher/publisher_name/pkg/url_encoded_stem
|
||||||
|
pub fn construct_package_dir(
|
||||||
|
base_path: &Path,
|
||||||
|
publisher: &str,
|
||||||
|
stem: &str,
|
||||||
|
) -> PathBuf {
|
||||||
|
let encoded_stem = Self::url_encode(stem);
|
||||||
|
base_path.join("publisher").join(publisher).join("pkg").join(encoded_stem)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to construct a file path consistently
|
||||||
|
///
|
||||||
|
/// Format: base_path/file/XX/hash
|
||||||
|
/// Where XX is the first two characters of the hash
|
||||||
|
pub fn construct_file_path(
|
||||||
|
base_path: &Path,
|
||||||
|
hash: &str,
|
||||||
|
) -> PathBuf {
|
||||||
|
if hash.len() < 2 {
|
||||||
|
// Fallback for very short hashes (shouldn't happen with SHA256)
|
||||||
|
base_path.join("file").join(hash)
|
||||||
|
} else {
|
||||||
|
// Extract the first two characters from the hash
|
||||||
|
let first_two = &hash[0..2];
|
||||||
|
|
||||||
|
// Create the path: $REPO/file/XX/XXYY...
|
||||||
|
base_path
|
||||||
|
.join("file")
|
||||||
|
.join(first_two)
|
||||||
|
.join(hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to construct a file path consistently with publisher
|
||||||
|
///
|
||||||
|
/// Format: base_path/publisher/publisher_name/file/XX/hash
|
||||||
|
/// Where XX is the first two characters of the hash
|
||||||
|
pub fn construct_file_path_with_publisher(
|
||||||
|
base_path: &Path,
|
||||||
|
publisher: &str,
|
||||||
|
hash: &str,
|
||||||
|
) -> PathBuf {
|
||||||
|
if hash.len() < 2 {
|
||||||
|
// Fallback for very short hashes (shouldn't happen with SHA256)
|
||||||
|
base_path.join("publisher").join(publisher).join("file").join(hash)
|
||||||
|
} else {
|
||||||
|
// Extract the first two characters from the hash
|
||||||
|
let first_two = &hash[0..2];
|
||||||
|
|
||||||
|
// Create the path: $REPO/publisher/publisher_name/file/XX/XXYY...
|
||||||
|
base_path
|
||||||
|
.join("publisher")
|
||||||
|
.join(publisher)
|
||||||
|
.join("file")
|
||||||
|
.join(first_two)
|
||||||
|
.join(hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Recursively find manifest files in a directory and its subdirectories
|
/// Recursively find manifest files in a directory and its subdirectories
|
||||||
fn find_manifests_recursive(
|
fn find_manifests_recursive(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -1642,10 +1736,9 @@ impl FileBackend {
|
||||||
/// Create the repository directories
|
/// Create the repository directories
|
||||||
fn create_directories(&self) -> Result<()> {
|
fn create_directories(&self) -> Result<()> {
|
||||||
// Create the main repository directories
|
// Create the main repository directories
|
||||||
fs::create_dir_all(self.path.join("catalog"))?;
|
fs::create_dir_all(self.path.join("publisher"))?;
|
||||||
fs::create_dir_all(self.path.join("file"))?;
|
fs::create_dir_all(self.path.join("file"))?;
|
||||||
fs::create_dir_all(self.path.join("index"))?;
|
fs::create_dir_all(self.path.join("index"))?;
|
||||||
fs::create_dir_all(self.path.join("pkg"))?;
|
|
||||||
fs::create_dir_all(self.path.join("trans"))?;
|
fs::create_dir_all(self.path.join("trans"))?;
|
||||||
fs::create_dir_all(self.path.join("obsoleted"))?;
|
fs::create_dir_all(self.path.join("obsoleted"))?;
|
||||||
|
|
||||||
|
|
@ -1655,16 +1748,12 @@ impl FileBackend {
|
||||||
/// 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.
|
/// 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()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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::construct_catalog_path(&self.path, 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");
|
||||||
|
|
@ -1936,36 +2025,25 @@ impl FileBackend {
|
||||||
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 with publisher
|
||||||
/// The path will be $REPO/file/XX/YY/XXYY... where XX and YY are the first four letters of the hash
|
/// This is a wrapper around the construct_file_path_with_publisher helper method
|
||||||
fn generate_file_path(&self, hash: &str) -> PathBuf {
|
fn generate_file_path_with_publisher(&self, publisher: &str, hash: &str) -> PathBuf {
|
||||||
if hash.len() < 4 {
|
Self::construct_file_path_with_publisher(&self.path, publisher, hash)
|
||||||
// Fallback for very short hashes (shouldn't happen with SHA256)
|
|
||||||
return self.path.join("file").join(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first two and next two characters from the hash
|
|
||||||
let first_two = &hash[0..2];
|
|
||||||
let next_two = &hash[2..4];
|
|
||||||
|
|
||||||
// Create the path: $REPO/file/XX/YY/XXYY...
|
|
||||||
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.
|
||||||
|
///
|
||||||
|
/// The catalog manager is specific to the given publisher.
|
||||||
pub fn get_catalog_manager(
|
pub fn get_catalog_manager(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
publisher: &str,
|
||||||
) -> Result<std::cell::RefMut<crate::repository::catalog::CatalogManager>> {
|
) -> Result<std::cell::RefMut<crate::repository::catalog::CatalogManager>> {
|
||||||
if self.catalog_manager.is_none() {
|
if self.catalog_manager.is_none() {
|
||||||
let catalog_dir = self.path.join("catalog");
|
let publisher_dir = self.path.join("publisher");
|
||||||
let manager = crate::repository::catalog::CatalogManager::new(&catalog_dir)?;
|
let manager = crate::repository::catalog::CatalogManager::new(&publisher_dir, publisher)?;
|
||||||
let refcell = std::cell::RefCell::new(manager);
|
let refcell = std::cell::RefCell::new(manager);
|
||||||
self.catalog_manager = Some(refcell);
|
self.catalog_manager = Some(refcell);
|
||||||
}
|
}
|
||||||
|
|
@ -2015,7 +2093,7 @@ impl FileBackend {
|
||||||
let mut index = SearchIndex::new();
|
let mut index = SearchIndex::new();
|
||||||
|
|
||||||
// Get the publisher's package directory
|
// Get the publisher's package directory
|
||||||
let publisher_pkg_dir = self.path.join("pkg").join(publisher);
|
let publisher_pkg_dir = Self::construct_package_dir(&self.path, publisher, "");
|
||||||
|
|
||||||
// Check if the publisher directory exists
|
// Check if the publisher directory exists
|
||||||
if publisher_pkg_dir.exists() {
|
if publisher_pkg_dir.exists() {
|
||||||
|
|
@ -2278,7 +2356,8 @@ impl FileBackend {
|
||||||
|
|
||||||
// Verify the file was stored
|
// Verify the file was stored
|
||||||
let hash = Transaction::calculate_file_hash(&test_file_path)?;
|
let hash = Transaction::calculate_file_hash(&test_file_path)?;
|
||||||
let stored_file_path = self.generate_file_path(&hash);
|
// Use the new method with publisher
|
||||||
|
let stored_file_path = self.generate_file_path_with_publisher(publisher, &hash);
|
||||||
|
|
||||||
if !stored_file_path.exists() {
|
if !stored_file_path.exists() {
|
||||||
return Err(RepositoryError::Other(
|
return Err(RepositoryError::Other(
|
||||||
|
|
@ -2288,11 +2367,9 @@ impl FileBackend {
|
||||||
|
|
||||||
// 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
|
// Use the construct_package_dir helper to get the base directory, then join with the manifest name
|
||||||
.path
|
let pkg_dir = Self::construct_package_dir(&self.path, publisher, "unknown");
|
||||||
.join("pkg")
|
let manifest_path = pkg_dir.join("manifest");
|
||||||
.join(publisher)
|
|
||||||
.join("unknown.manifest");
|
|
||||||
|
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
return Err(RepositoryError::Other(format!(
|
return Err(RepositoryError::Other(format!(
|
||||||
|
|
@ -2383,14 +2460,14 @@ impl FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a file in the repository
|
/// Store a file in the repository
|
||||||
pub fn store_file<P: AsRef<Path>>(&self, file_path: P) -> Result<String> {
|
pub fn store_file<P: AsRef<Path>>(&self, file_path: P, publisher: &str) -> Result<String> {
|
||||||
let file_path = file_path.as_ref();
|
let file_path = file_path.as_ref();
|
||||||
|
|
||||||
// Calculate the SHA256 hash of the file
|
// Calculate the SHA256 hash of the file
|
||||||
let hash = Transaction::calculate_file_hash(file_path)?;
|
let hash = Transaction::calculate_file_hash(file_path)?;
|
||||||
|
|
||||||
// Create the destination path using the new directory structure
|
// Create the destination path using the new directory structure with publisher
|
||||||
let dest_path = self.generate_file_path(&hash);
|
let dest_path = self.generate_file_path_with_publisher(publisher, &hash);
|
||||||
|
|
||||||
// Create parent directories if they don't exist
|
// Create parent directories if they don't exist
|
||||||
if let Some(parent) = dest_path.parent() {
|
if let Some(parent) = dest_path.parent() {
|
||||||
|
|
|
||||||
|
|
@ -2513,20 +2513,20 @@ impl ObsoletedPackageManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// URL encode a string
|
/// URL encode a string for use in a filename
|
||||||
fn url_encode(s: &str) -> String {
|
fn url_encode(s: &str) -> String {
|
||||||
let mut encoded = String::new();
|
let mut result = String::new();
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
match c {
|
match c {
|
||||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => encoded.push(c),
|
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => result.push(c),
|
||||||
|
' ' => result.push('+'),
|
||||||
_ => {
|
_ => {
|
||||||
for b in c.to_string().as_bytes() {
|
result.push('%');
|
||||||
encoded.push_str(&format!("%{:02X}", b));
|
result.push_str(&format!("{:02X}", c as u8));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
encoded
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ mod tests {
|
||||||
println!("Transaction committed successfully");
|
println!("Transaction committed successfully");
|
||||||
|
|
||||||
// Debug: Check if the package manifest was stored in the correct location
|
// Debug: Check if the package manifest was stored in the correct location
|
||||||
let publisher_pkg_dir = repo.path.join("pkg").join(publisher);
|
let publisher_pkg_dir = FileBackend::construct_package_dir(&repo.path, publisher, "");
|
||||||
println!(
|
println!(
|
||||||
"Publisher package directory: {}",
|
"Publisher package directory: {}",
|
||||||
publisher_pkg_dir.display()
|
publisher_pkg_dir.display()
|
||||||
|
|
@ -181,10 +181,9 @@ mod tests {
|
||||||
|
|
||||||
// Check that the repository was created
|
// Check that the repository was created
|
||||||
assert!(repo_path.exists());
|
assert!(repo_path.exists());
|
||||||
assert!(repo_path.join("catalog").exists());
|
assert!(repo_path.join("publisher").exists());
|
||||||
assert!(repo_path.join("file").exists());
|
assert!(repo_path.join("file").exists());
|
||||||
assert!(repo_path.join("index").exists());
|
assert!(repo_path.join("index").exists());
|
||||||
assert!(repo_path.join("pkg").exists());
|
|
||||||
assert!(repo_path.join("trans").exists());
|
assert!(repo_path.join("trans").exists());
|
||||||
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
||||||
|
|
||||||
|
|
@ -206,8 +205,8 @@ mod tests {
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
assert!(FileBackend::construct_catalog_path(&repo_path, "example.com").exists());
|
||||||
assert!(repo_path.join("pkg").join("example.com").exists());
|
assert!(FileBackend::construct_package_dir(&repo_path, "example.com", "").exists());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -217,20 +216,22 @@ mod tests {
|
||||||
fn test_catalog_manager() {
|
fn test_catalog_manager() {
|
||||||
// Create a test directory
|
// Create a test directory
|
||||||
let test_dir = create_test_dir("catalog_manager");
|
let test_dir = create_test_dir("catalog_manager");
|
||||||
let catalog_dir = test_dir.join("catalog");
|
let publisher_dir = test_dir.join("publisher");
|
||||||
|
let publisher_name = "test";
|
||||||
|
let catalog_dir = publisher_dir.join(publisher_name).join("catalog");
|
||||||
|
|
||||||
// Create the catalog directory
|
// Create the catalog directory
|
||||||
fs::create_dir_all(&catalog_dir).unwrap();
|
fs::create_dir_all(&catalog_dir).unwrap();
|
||||||
|
|
||||||
// Create a catalog manager
|
// Create a catalog manager with the publisher parameter
|
||||||
let mut catalog_manager = CatalogManager::new(&catalog_dir).unwrap();
|
let mut catalog_manager = CatalogManager::new(&publisher_dir, publisher_name).unwrap();
|
||||||
|
|
||||||
// Create a catalog part
|
// Create a catalog part
|
||||||
let part = catalog_manager.create_part("test_part");
|
catalog_manager.create_part("test_part");
|
||||||
|
|
||||||
// Add a package to the part
|
// Add a package to the part using the stored publisher
|
||||||
let fmri = Fmri::parse("pkg://test/example@1.0.0").unwrap();
|
let fmri = Fmri::parse("pkg://test/example@1.0.0").unwrap();
|
||||||
part.add_package("test", &fmri, None, None);
|
catalog_manager.add_package_to_part("test_part", &fmri, None, None).unwrap();
|
||||||
|
|
||||||
// Save the part
|
// Save the part
|
||||||
catalog_manager.save_part("test_part").unwrap();
|
catalog_manager.save_part("test_part").unwrap();
|
||||||
|
|
@ -239,7 +240,7 @@ mod tests {
|
||||||
assert!(catalog_dir.join("test_part").exists());
|
assert!(catalog_dir.join("test_part").exists());
|
||||||
|
|
||||||
// Create a new catalog manager and load the part
|
// Create a new catalog manager and load the part
|
||||||
let mut new_catalog_manager = CatalogManager::new(&catalog_dir).unwrap();
|
let mut new_catalog_manager = CatalogManager::new(&publisher_dir, publisher_name).unwrap();
|
||||||
new_catalog_manager.load_part("test_part").unwrap();
|
new_catalog_manager.load_part("test_part").unwrap();
|
||||||
|
|
||||||
// Check that the part was loaded
|
// Check that the part was loaded
|
||||||
|
|
@ -409,16 +410,10 @@ mod tests {
|
||||||
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, "test").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 expected_path = FileBackend::construct_file_path_with_publisher(&repo_path, "test", &hash);
|
||||||
let next_two = &hash[2..4];
|
|
||||||
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!(
|
assert!(
|
||||||
|
|
@ -427,7 +422,7 @@ mod tests {
|
||||||
expected_path.display()
|
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 (with no directory prefixing)
|
||||||
let old_path = repo_path.join("file").join(&hash);
|
let old_path = repo_path.join("file").join(&hash);
|
||||||
assert!(
|
assert!(
|
||||||
!old_path.exists(),
|
!old_path.exists(),
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Check that the repository was created
|
// Check that the repository was created
|
||||||
assert!(repo_path.exists());
|
assert!(repo_path.exists());
|
||||||
assert!(repo_path.join("catalog").exists());
|
assert!(repo_path.join("publisher").exists());
|
||||||
assert!(repo_path.join("file").exists());
|
assert!(repo_path.join("file").exists());
|
||||||
assert!(repo_path.join("index").exists());
|
assert!(repo_path.join("index").exists());
|
||||||
assert!(repo_path.join("pkg").exists());
|
assert!(repo_path.join("pkg").exists());
|
||||||
|
|
@ -174,8 +174,9 @@ mod e2e_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
assert!(repo_path.join("publisher").join("example.com").exists());
|
||||||
assert!(repo_path.join("pkg").join("example.com").exists());
|
assert!(repo_path.join("publisher").join("example.com").join("catalog").exists());
|
||||||
|
assert!(repo_path.join("publisher").join("example.com").join("pkg").exists());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -462,6 +463,24 @@ mod e2e_tests {
|
||||||
fmri
|
fmri
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Print the FMRI and repo path for debugging
|
||||||
|
println!("FMRI: {}", fmri);
|
||||||
|
println!("Repo path: {}", repo_path.display());
|
||||||
|
|
||||||
|
// Check if the package exists in the repository
|
||||||
|
let pkg_dir = repo_path.join("publisher").join("test").join("pkg").join("example");
|
||||||
|
println!("Package directory: {}", pkg_dir.display());
|
||||||
|
println!("Package directory exists: {}", pkg_dir.exists());
|
||||||
|
|
||||||
|
// List files in the package directory if it exists
|
||||||
|
if pkg_dir.exists() {
|
||||||
|
println!("Files in package directory:");
|
||||||
|
for entry in std::fs::read_dir(&pkg_dir).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
println!(" {}", entry.path().display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mark the package as obsoleted
|
// Mark the package as obsoleted
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&[
|
||||||
"obsolete-package",
|
"obsolete-package",
|
||||||
|
|
@ -471,6 +490,10 @@ mod e2e_tests {
|
||||||
"-m", "This package is obsoleted for testing purposes",
|
"-m", "This package is obsoleted for testing purposes",
|
||||||
"-r", "pkg://test/example2@1.0"
|
"-r", "pkg://test/example2@1.0"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Print the result for debugging
|
||||||
|
println!("Result: {:?}", result);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
"Failed to mark package as obsoleted: {:?}",
|
"Failed to mark package as obsoleted: {:?}",
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,6 @@ mod pkg5_import;
|
||||||
use error::{Pkg6RepoError, Result};
|
use error::{Pkg6RepoError, Result};
|
||||||
use pkg5_import::Pkg5Importer;
|
use pkg5_import::Pkg5Importer;
|
||||||
|
|
||||||
/// URL encode a string for use in a filename
|
|
||||||
fn url_encode(s: &str) -> String {
|
|
||||||
let mut result = String::new();
|
|
||||||
for c in s.chars() {
|
|
||||||
match c {
|
|
||||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => result.push(c),
|
|
||||||
' ' => result.push('+'),
|
|
||||||
_ => {
|
|
||||||
result.push('%');
|
|
||||||
result.push_str(&format!("{:02X}", c as u8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository};
|
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
@ -1306,10 +1290,18 @@ fn main() -> Result<()> {
|
||||||
// Parse the FMRI
|
// Parse the FMRI
|
||||||
let parsed_fmri = libips::fmri::Fmri::parse(fmri)?;
|
let parsed_fmri = libips::fmri::Fmri::parse(fmri)?;
|
||||||
|
|
||||||
// Get the manifest for the package
|
// Get the manifest for the package using the helper method
|
||||||
let pkg_dir = repo.path.join("pkg").join(publisher).join(parsed_fmri.stem());
|
let manifest_path = FileBackend::construct_manifest_path(
|
||||||
let encoded_version = url_encode(&parsed_fmri.version());
|
&repo.path,
|
||||||
let manifest_path = pkg_dir.join(&encoded_version);
|
publisher,
|
||||||
|
parsed_fmri.stem(),
|
||||||
|
&parsed_fmri.version()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Looking for manifest at: {}", manifest_path.display());
|
||||||
|
println!("Publisher: {}", publisher);
|
||||||
|
println!("Stem: {}", parsed_fmri.stem());
|
||||||
|
println!("Version: {}", parsed_fmri.version());
|
||||||
|
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
return Err(Pkg6RepoError::from(format!(
|
return Err(Pkg6RepoError::from(format!(
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ impl Pkg5Importer {
|
||||||
// Debug the repository structure
|
// Debug the repository structure
|
||||||
debug!(
|
debug!(
|
||||||
"Publisher directory: {}",
|
"Publisher directory: {}",
|
||||||
dest_repo.path.join("pkg").join(publisher).display()
|
libips::repository::FileBackend::construct_package_dir(&dest_repo.path, publisher, "").display()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract files referenced in the manifest
|
// Extract files referenced in the manifest
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,9 @@ mod tests {
|
||||||
|
|
||||||
// Check that the repository was created
|
// Check that the repository was created
|
||||||
assert!(repo_path.exists());
|
assert!(repo_path.exists());
|
||||||
assert!(repo_path.join("catalog").exists());
|
assert!(repo_path.join("publisher").exists());
|
||||||
assert!(repo_path.join("file").exists());
|
assert!(repo_path.join("file").exists());
|
||||||
assert!(repo_path.join("index").exists());
|
assert!(repo_path.join("index").exists());
|
||||||
assert!(repo_path.join("pkg").exists());
|
|
||||||
assert!(repo_path.join("trans").exists());
|
assert!(repo_path.join("trans").exists());
|
||||||
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
||||||
|
|
||||||
|
|
@ -72,8 +71,8 @@ mod tests {
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
assert!(FileBackend::construct_catalog_path(&repo_path, "example.com").exists());
|
||||||
assert!(repo_path.join("pkg").join("example.com").exists());
|
assert!(FileBackend::construct_package_dir(&repo_path, "example.com", "").exists());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -95,8 +94,8 @@ mod tests {
|
||||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
|
|
||||||
// Check that the publisher directories were created
|
// Check that the publisher directories were created
|
||||||
let catalog_dir = repo_path.join("catalog").join("example.com");
|
let catalog_dir = FileBackend::construct_catalog_path(&repo_path, "example.com");
|
||||||
let pkg_dir = repo_path.join("pkg").join("example.com");
|
let pkg_dir = FileBackend::construct_package_dir(&repo_path, "example.com", "");
|
||||||
assert!(
|
assert!(
|
||||||
catalog_dir.exists(),
|
catalog_dir.exists(),
|
||||||
"Catalog directory should exist after adding publisher"
|
"Catalog directory should exist after adding publisher"
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use libips::repository::{FileBackend, WritableRepository, ReadableRepository, RepositoryVersion};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// Create a temporary directory for the test
|
|
||||||
let test_dir = Path::new("/tmp/pkg6_file_structure_test");
|
|
||||||
if test_dir.exists() {
|
|
||||||
fs::remove_dir_all(test_dir)?;
|
|
||||||
}
|
|
||||||
fs::create_dir_all(test_dir)?;
|
|
||||||
|
|
||||||
println!("Created test directory: {}", test_dir.display());
|
|
||||||
|
|
||||||
// Create a new repository
|
|
||||||
let mut repo = FileBackend::create(test_dir, RepositoryVersion::V1)?;
|
|
||||||
|
|
||||||
// Add a publisher
|
|
||||||
repo.add_publisher("test")?;
|
|
||||||
|
|
||||||
println!("Created repository with publisher 'test'");
|
|
||||||
|
|
||||||
// Create a test file
|
|
||||||
let test_file_path = test_dir.join("test_file.txt");
|
|
||||||
fs::write(&test_file_path, "This is a test file")?;
|
|
||||||
|
|
||||||
println!("Created test file: {}", test_file_path.display());
|
|
||||||
|
|
||||||
// Store the file in the repository
|
|
||||||
let hash = repo.store_file(&test_file_path)?;
|
|
||||||
|
|
||||||
println!("Stored file with hash: {}", hash);
|
|
||||||
|
|
||||||
// Check if the file was stored in the correct directory structure
|
|
||||||
let first_two = &hash[0..2];
|
|
||||||
let next_two = &hash[2..4];
|
|
||||||
let expected_path = test_dir.join("file").join(first_two).join(next_two).join(&hash);
|
|
||||||
|
|
||||||
if expected_path.exists() {
|
|
||||||
println!("SUCCESS: File was stored at the correct path: {}", expected_path.display());
|
|
||||||
} else {
|
|
||||||
println!("ERROR: File was not stored at the expected path: {}", expected_path.display());
|
|
||||||
|
|
||||||
// Check if the file was stored in the old location
|
|
||||||
let old_path = test_dir.join("file").join(&hash);
|
|
||||||
if old_path.exists() {
|
|
||||||
println!("File was stored at the old path: {}", old_path.display());
|
|
||||||
} else {
|
|
||||||
println!("File was not stored at the old path either");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
fs::remove_dir_all(test_dir)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue