mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 13:20:42 +00:00
chore(format): format code
This commit is contained in:
parent
e7dc9d5a4c
commit
4ab529f4c7
11 changed files with 210 additions and 107 deletions
|
|
@ -635,7 +635,10 @@ impl Resolver {
|
||||||
// Helper: extract the package FMRI from a manifest's attributes
|
// Helper: extract the package FMRI from a manifest's attributes
|
||||||
fn manifest_fmri(manifest: &Manifest) -> Option<Fmri> {
|
fn manifest_fmri(manifest: &Manifest) -> Option<Fmri> {
|
||||||
for attr in &manifest.attributes {
|
for attr in &manifest.attributes {
|
||||||
if attr.key == "pkg.fmri" && let Some(val) = attr.values.first() && let Ok(f) = Fmri::parse(val) {
|
if attr.key == "pkg.fmri"
|
||||||
|
&& let Some(val) = attr.values.first()
|
||||||
|
&& let Ok(f) = Fmri::parse(val)
|
||||||
|
{
|
||||||
return Some(f);
|
return Some(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
// 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 crate::repository::{ReadableRepository, FileBackend, RepositoryError, Result, WritableRepository, ProgressReporter, ProgressInfo, NoopProgressReporter};
|
|
||||||
use crate::fmri::Fmri;
|
|
||||||
use crate::actions::Manifest;
|
use crate::actions::Manifest;
|
||||||
|
use crate::fmri::Fmri;
|
||||||
|
use crate::repository::{
|
||||||
|
FileBackend, NoopProgressReporter, ProgressInfo, ProgressReporter, ReadableRepository,
|
||||||
|
RepositoryError, Result, WritableRepository,
|
||||||
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tracing::{info, debug};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
/// PackageReceiver handles downloading packages from a source repository
|
/// PackageReceiver handles downloading packages from a source repository
|
||||||
/// and storing them in a destination repository.
|
/// and storing them in a destination repository.
|
||||||
|
|
@ -21,7 +24,11 @@ pub struct PackageReceiver<'a, S: ReadableRepository> {
|
||||||
impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
/// Create a new PackageReceiver
|
/// Create a new PackageReceiver
|
||||||
pub fn new(source: &'a mut S, dest: FileBackend) -> Self {
|
pub fn new(source: &'a mut S, dest: FileBackend) -> Self {
|
||||||
Self { source, dest, progress: None }
|
Self {
|
||||||
|
source,
|
||||||
|
dest,
|
||||||
|
progress: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the progress reporter
|
/// Set the progress reporter
|
||||||
|
|
@ -37,7 +44,12 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
/// * `default_publisher` - The default publisher name if not specified in FMRI
|
/// * `default_publisher` - The default publisher name if not specified in FMRI
|
||||||
/// * `fmris` - List of FMRIs to receive
|
/// * `fmris` - List of FMRIs to receive
|
||||||
/// * `recursive` - Whether to receive dependencies recursively
|
/// * `recursive` - Whether to receive dependencies recursively
|
||||||
pub fn receive(&mut self, default_publisher: Option<&str>, fmris: &[Fmri], recursive: bool) -> Result<()> {
|
pub fn receive(
|
||||||
|
&mut self,
|
||||||
|
default_publisher: Option<&str>,
|
||||||
|
fmris: &[Fmri],
|
||||||
|
recursive: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let mut processed = HashSet::new();
|
let mut processed = HashSet::new();
|
||||||
let mut queue: Vec<Fmri> = fmris.to_vec();
|
let mut queue: Vec<Fmri> = fmris.to_vec();
|
||||||
let mut updated_publishers = HashSet::new();
|
let mut updated_publishers = HashSet::new();
|
||||||
|
|
@ -53,36 +65,55 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
while let Some(fmri) = queue.pop() {
|
while let Some(fmri) = queue.pop() {
|
||||||
// If the FMRI doesn't have a version, we need to find the newest one
|
// If the FMRI doesn't have a version, we need to find the newest one
|
||||||
let fmris_to_fetch = if fmri.version.is_none() {
|
let fmris_to_fetch = if fmri.version.is_none() {
|
||||||
let publisher = fmri.publisher.as_deref().or(default_publisher).ok_or_else(|| {
|
let publisher =
|
||||||
RepositoryError::Other(format!("No publisher specified for package {}", fmri.name))
|
fmri.publisher
|
||||||
})?;
|
.as_deref()
|
||||||
|
.or(default_publisher)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
RepositoryError::Other(format!(
|
||||||
|
"No publisher specified for package {}",
|
||||||
|
fmri.name
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
overall_progress = overall_progress.with_context(format!("Looking up newest version for {}", fmri.name));
|
overall_progress = overall_progress
|
||||||
|
.with_context(format!("Looking up newest version for {}", fmri.name));
|
||||||
progress.update(&overall_progress);
|
progress.update(&overall_progress);
|
||||||
|
|
||||||
debug!("No version specified for {}, looking up newest", fmri.name);
|
debug!("No version specified for {}, looking up newest", fmri.name);
|
||||||
let pkgs = self.source.list_packages(Some(publisher), Some(&fmri.name))?;
|
let pkgs = self
|
||||||
|
.source
|
||||||
|
.list_packages(Some(publisher), Some(&fmri.name))?;
|
||||||
|
|
||||||
// Group by package name to find the newest version for each
|
// Group by package name to find the newest version for each
|
||||||
let mut by_name: std::collections::HashMap<String, Vec<crate::repository::PackageInfo>> = std::collections::HashMap::new();
|
let mut by_name: std::collections::HashMap<
|
||||||
|
String,
|
||||||
|
Vec<crate::repository::PackageInfo>,
|
||||||
|
> = std::collections::HashMap::new();
|
||||||
for pi in pkgs {
|
for pi in pkgs {
|
||||||
by_name.entry(pi.fmri.name.clone()).or_default().push(pi);
|
by_name.entry(pi.fmri.name.clone()).or_default().push(pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for (name, versions) in by_name {
|
for (name, versions) in by_name {
|
||||||
let newest = versions.into_iter().max_by(|a, b| {
|
let newest = versions
|
||||||
a.fmri.to_string().cmp(&b.fmri.to_string())
|
.into_iter()
|
||||||
});
|
.max_by(|a, b| a.fmri.to_string().cmp(&b.fmri.to_string()));
|
||||||
if let Some(pi) = newest {
|
if let Some(pi) = newest {
|
||||||
results.push(pi.fmri);
|
results.push(pi.fmri);
|
||||||
} else {
|
} else {
|
||||||
info!("Package {} not found in source for publisher {}", name, publisher);
|
info!(
|
||||||
|
"Package {} not found in source for publisher {}",
|
||||||
|
name, publisher
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if results.is_empty() {
|
if results.is_empty() {
|
||||||
info!("Package {} not found in source for publisher {}", fmri.name, publisher);
|
info!(
|
||||||
|
"Package {} not found in source for publisher {}",
|
||||||
|
fmri.name, publisher
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Update total_packages: remove the wildcard FMRI we just popped, and add actual results
|
// Update total_packages: remove the wildcard FMRI we just popped, and add actual results
|
||||||
|
|
@ -93,9 +124,17 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for fmri_to_fetch in fmris_to_fetch {
|
for fmri_to_fetch in fmris_to_fetch {
|
||||||
let publisher_name = fmri_to_fetch.publisher.as_deref().or(default_publisher).ok_or_else(|| {
|
let publisher_name = fmri_to_fetch
|
||||||
RepositoryError::Other(format!("No publisher specified for package {}", fmri_to_fetch.name))
|
.publisher
|
||||||
})?.to_string();
|
.as_deref()
|
||||||
|
.or(default_publisher)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
RepositoryError::Other(format!(
|
||||||
|
"No publisher specified for package {}",
|
||||||
|
fmri_to_fetch.name
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
if !processed.insert(fmri_to_fetch.clone()) {
|
if !processed.insert(fmri_to_fetch.clone()) {
|
||||||
// If we already processed it (possibly as a dependency), don't count it again
|
// If we already processed it (possibly as a dependency), don't count it again
|
||||||
|
|
@ -110,7 +149,10 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
.with_context(format!("Receiving {}", fmri_to_fetch));
|
.with_context(format!("Receiving {}", fmri_to_fetch));
|
||||||
progress.update(&overall_progress);
|
progress.update(&overall_progress);
|
||||||
|
|
||||||
info!("Receiving package {} from publisher {}", fmri_to_fetch, publisher_name);
|
info!(
|
||||||
|
"Receiving package {} from publisher {}",
|
||||||
|
fmri_to_fetch, publisher_name
|
||||||
|
);
|
||||||
let manifest = self.receive_one(&publisher_name, &fmri_to_fetch)?;
|
let manifest = self.receive_one(&publisher_name, &fmri_to_fetch)?;
|
||||||
updated_publishers.insert(publisher_name.clone());
|
updated_publishers.insert(publisher_name.clone());
|
||||||
|
|
||||||
|
|
@ -121,10 +163,10 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
if dep_fmri.publisher.is_none() {
|
if dep_fmri.publisher.is_none() {
|
||||||
dep_fmri.publisher = Some(publisher_name.clone());
|
dep_fmri.publisher = Some(publisher_name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !processed.contains(&dep_fmri) && queued.insert(dep_fmri.clone()) {
|
if !processed.contains(&dep_fmri) && queued.insert(dep_fmri.clone()) {
|
||||||
total_packages += 1;
|
total_packages += 1;
|
||||||
queue.push(dep_fmri);
|
queue.push(dep_fmri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +176,8 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
|
|
||||||
for pub_name in updated_publishers {
|
for pub_name in updated_publishers {
|
||||||
info!("Rebuilding metadata for publisher {}", pub_name);
|
info!("Rebuilding metadata for publisher {}", pub_name);
|
||||||
overall_progress = overall_progress.with_context(format!("Rebuilding metadata for {}", pub_name));
|
overall_progress =
|
||||||
|
overall_progress.with_context(format!("Rebuilding metadata for {}", pub_name));
|
||||||
progress.update(&overall_progress);
|
progress.update(&overall_progress);
|
||||||
self.dest.rebuild(Some(&pub_name), false, false)?;
|
self.dest.rebuild(Some(&pub_name), false, false)?;
|
||||||
}
|
}
|
||||||
|
|
@ -147,10 +190,11 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
/// Receive a single package
|
/// Receive a single package
|
||||||
fn receive_one(&mut self, publisher: &str, fmri: &Fmri) -> Result<Manifest> {
|
fn receive_one(&mut self, publisher: &str, fmri: &Fmri) -> Result<Manifest> {
|
||||||
let progress = self.progress.unwrap_or(&NoopProgressReporter);
|
let progress = self.progress.unwrap_or(&NoopProgressReporter);
|
||||||
|
|
||||||
let manifest_text = self.source.fetch_manifest_text(publisher, fmri)?;
|
let manifest_text = self.source.fetch_manifest_text(publisher, fmri)?;
|
||||||
let manifest = Manifest::parse_string(manifest_text.clone()).map_err(RepositoryError::from)?;
|
let manifest =
|
||||||
|
Manifest::parse_string(manifest_text.clone()).map_err(RepositoryError::from)?;
|
||||||
|
|
||||||
// Ensure publisher exists in destination
|
// Ensure publisher exists in destination
|
||||||
let dest_info = self.dest.get_info()?;
|
let dest_info = self.dest.get_info()?;
|
||||||
if !dest_info.publishers.iter().any(|p| p.name == publisher) {
|
if !dest_info.publishers.iter().any(|p| p.name == publisher) {
|
||||||
|
|
@ -163,30 +207,41 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
txn.set_legacy_manifest(manifest_text);
|
txn.set_legacy_manifest(manifest_text);
|
||||||
|
|
||||||
let temp_dir = tempdir().map_err(RepositoryError::IoError)?;
|
let temp_dir = tempdir().map_err(RepositoryError::IoError)?;
|
||||||
|
|
||||||
let payload_files: Vec<_> = manifest.files.iter().filter(|f| f.payload.is_some()).collect();
|
let payload_files: Vec<_> = manifest
|
||||||
|
.files
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.payload.is_some())
|
||||||
|
.collect();
|
||||||
let total_files = payload_files.len() as u64;
|
let total_files = payload_files.len() as u64;
|
||||||
|
|
||||||
for (i, file) in payload_files.into_iter().enumerate() {
|
for (i, file) in payload_files.into_iter().enumerate() {
|
||||||
if let Some(payload) = &file.payload {
|
if let Some(payload) = &file.payload {
|
||||||
let files_done = (i + 1) as u64;
|
let files_done = (i + 1) as u64;
|
||||||
let digest = &payload.primary_identifier.hash;
|
let digest = &payload.primary_identifier.hash;
|
||||||
|
|
||||||
progress.update(&ProgressInfo::new(format!("Receiving payloads for {}", fmri.name))
|
progress.update(
|
||||||
.with_total(total_files)
|
&ProgressInfo::new(format!("Receiving payloads for {}", fmri.name))
|
||||||
.with_current(files_done)
|
.with_total(total_files)
|
||||||
.with_context(format!("Payload: {}", digest)));
|
.with_current(files_done)
|
||||||
|
.with_context(format!("Payload: {}", digest)),
|
||||||
|
);
|
||||||
|
|
||||||
let temp_file_path = temp_dir.path().join(digest);
|
let temp_file_path = temp_dir.path().join(digest);
|
||||||
debug!("Fetching payload {} to {}", digest, temp_file_path.display());
|
debug!(
|
||||||
self.source.fetch_payload(publisher, digest, &temp_file_path)?;
|
"Fetching payload {} to {}",
|
||||||
|
digest,
|
||||||
|
temp_file_path.display()
|
||||||
|
);
|
||||||
|
self.source
|
||||||
|
.fetch_payload(publisher, digest, &temp_file_path)?;
|
||||||
txn.add_file(file.clone(), &temp_file_path)?;
|
txn.add_file(file.clone(), &temp_file_path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.update_manifest(manifest.clone());
|
txn.update_manifest(manifest.clone());
|
||||||
txn.commit()?;
|
txn.commit()?;
|
||||||
|
|
||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -194,8 +249,8 @@ impl<'a, S: ReadableRepository> PackageReceiver<'a, S> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::repository::{FileBackend, RepositoryVersion};
|
|
||||||
use crate::actions::Attr;
|
use crate::actions::Attr;
|
||||||
|
use crate::repository::{FileBackend, RepositoryVersion};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -206,7 +261,7 @@ mod tests {
|
||||||
// Create source repo with one package
|
// Create source repo with one package
|
||||||
let mut source_repo = FileBackend::create(source_dir.path(), RepositoryVersion::V4)?;
|
let mut source_repo = FileBackend::create(source_dir.path(), RepositoryVersion::V4)?;
|
||||||
source_repo.add_publisher("test")?;
|
source_repo.add_publisher("test")?;
|
||||||
|
|
||||||
let fmri = Fmri::parse("pkg://test/pkgA@1.0").unwrap();
|
let fmri = Fmri::parse("pkg://test/pkgA@1.0").unwrap();
|
||||||
let mut manifest = Manifest::new();
|
let mut manifest = Manifest::new();
|
||||||
manifest.attributes.push(Attr {
|
manifest.attributes.push(Attr {
|
||||||
|
|
@ -245,15 +300,18 @@ mod tests {
|
||||||
// Create source repo
|
// Create source repo
|
||||||
let mut source_repo = FileBackend::create(source_dir.path(), RepositoryVersion::V4)?;
|
let mut source_repo = FileBackend::create(source_dir.path(), RepositoryVersion::V4)?;
|
||||||
source_repo.add_publisher("test")?;
|
source_repo.add_publisher("test")?;
|
||||||
|
|
||||||
let _fmri = Fmri::parse("pkg://test/pkgA@1.0").unwrap();
|
let _fmri = Fmri::parse("pkg://test/pkgA@1.0").unwrap();
|
||||||
let manifest_content = "set name=pkg.fmri value=pkg://test/pkgA@1.0\nset name=pkg.summary value=test\n";
|
let manifest_content =
|
||||||
|
"set name=pkg.fmri value=pkg://test/pkgA@1.0\nset name=pkg.summary value=test\n";
|
||||||
|
|
||||||
// Manually write the manifest in IPS format to the source repo
|
// Manually write the manifest in IPS format to the source repo
|
||||||
let manifest_path = FileBackend::construct_manifest_path(source_dir.path(), "test", "pkgA", "1.0");
|
let manifest_path =
|
||||||
std::fs::create_dir_all(manifest_path.parent().unwrap()).map_err(RepositoryError::IoError)?;
|
FileBackend::construct_manifest_path(source_dir.path(), "test", "pkgA", "1.0");
|
||||||
|
std::fs::create_dir_all(manifest_path.parent().unwrap())
|
||||||
|
.map_err(RepositoryError::IoError)?;
|
||||||
std::fs::write(&manifest_path, manifest_content).map_err(RepositoryError::IoError)?;
|
std::fs::write(&manifest_path, manifest_content).map_err(RepositoryError::IoError)?;
|
||||||
|
|
||||||
// Rebuild source repo to recognize the package
|
// Rebuild source repo to recognize the package
|
||||||
source_repo.rebuild(Some("test"), false, false)?;
|
source_repo.rebuild(Some("test"), false, false)?;
|
||||||
|
|
||||||
|
|
@ -264,9 +322,11 @@ mod tests {
|
||||||
receiver.receive(Some("test"), &[Fmri::new("pkgA")], false)?;
|
receiver.receive(Some("test"), &[Fmri::new("pkgA")], false)?;
|
||||||
|
|
||||||
// Verify dest repo has the package and the manifest is in IPS format
|
// Verify dest repo has the package and the manifest is in IPS format
|
||||||
let dest_manifest_path = FileBackend::construct_manifest_path(dest_dir.path(), "test", "pkgA", "1.0");
|
let dest_manifest_path =
|
||||||
let content = std::fs::read_to_string(&dest_manifest_path).map_err(RepositoryError::IoError)?;
|
FileBackend::construct_manifest_path(dest_dir.path(), "test", "pkgA", "1.0");
|
||||||
|
let content =
|
||||||
|
std::fs::read_to_string(&dest_manifest_path).map_err(RepositoryError::IoError)?;
|
||||||
|
|
||||||
assert_eq!(content, manifest_content);
|
assert_eq!(content, manifest_content);
|
||||||
assert!(!content.starts_with('{'), "Manifest should not be JSON");
|
assert!(!content.starts_with('{'), "Manifest should not be JSON");
|
||||||
|
|
||||||
|
|
@ -275,9 +335,16 @@ mod tests {
|
||||||
let mut filename = json_path.file_name().unwrap().to_os_string();
|
let mut filename = json_path.file_name().unwrap().to_os_string();
|
||||||
filename.push(".json");
|
filename.push(".json");
|
||||||
json_path.set_file_name(filename);
|
json_path.set_file_name(filename);
|
||||||
assert!(json_path.exists(), "JSON manifest should exist at {}", json_path.display());
|
assert!(
|
||||||
|
json_path.exists(),
|
||||||
|
"JSON manifest should exist at {}",
|
||||||
|
json_path.display()
|
||||||
|
);
|
||||||
let json_content = std::fs::read_to_string(&json_path).map_err(RepositoryError::IoError)?;
|
let json_content = std::fs::read_to_string(&json_path).map_err(RepositoryError::IoError)?;
|
||||||
assert!(json_content.starts_with('{'), "JSON manifest should be JSON");
|
assert!(
|
||||||
|
json_content.starts_with('{'),
|
||||||
|
"JSON manifest should be JSON"
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1593,11 +1593,7 @@ impl ReadableRepository for FileBackend {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_manifest_text(
|
fn fetch_manifest_text(&mut self, publisher: &str, fmri: &Fmri) -> Result<String> {
|
||||||
&mut self,
|
|
||||||
publisher: &str,
|
|
||||||
fmri: &Fmri,
|
|
||||||
) -> Result<String> {
|
|
||||||
// Require a concrete version
|
// Require a concrete version
|
||||||
let version = fmri.version();
|
let version = fmri.version();
|
||||||
if version.is_empty() {
|
if version.is_empty() {
|
||||||
|
|
@ -1609,10 +1605,7 @@ impl ReadableRepository for FileBackend {
|
||||||
let path = Self::construct_manifest_path(&self.path, publisher, fmri.stem(), &version);
|
let path = Self::construct_manifest_path(&self.path, publisher, fmri.stem(), &version);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
return std::fs::read_to_string(&path)
|
return std::fs::read_to_string(&path)
|
||||||
.map_err(|e| RepositoryError::FileReadError {
|
.map_err(|e| RepositoryError::FileReadError { path, source: e });
|
||||||
path,
|
|
||||||
source: e,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Fallbacks: global pkg layout without publisher
|
// Fallbacks: global pkg layout without publisher
|
||||||
let encoded_stem = Self::url_encode(fmri.stem());
|
let encoded_stem = Self::url_encode(fmri.stem());
|
||||||
|
|
|
||||||
|
|
@ -385,11 +385,7 @@ pub trait ReadableRepository {
|
||||||
) -> Result<crate::actions::Manifest>;
|
) -> Result<crate::actions::Manifest>;
|
||||||
|
|
||||||
/// Fetch a package manifest as raw text by FMRI from the repository.
|
/// Fetch a package manifest as raw text by FMRI from the repository.
|
||||||
fn fetch_manifest_text(
|
fn fetch_manifest_text(&mut self, publisher: &str, fmri: &crate::fmri::Fmri) -> Result<String>;
|
||||||
&mut self,
|
|
||||||
publisher: &str,
|
|
||||||
fmri: &crate::fmri::Fmri,
|
|
||||||
) -> Result<String>;
|
|
||||||
|
|
||||||
/// Search for packages in the repository
|
/// Search for packages in the repository
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,11 @@ impl WritableRepository for RestBackend {
|
||||||
// This is a stub implementation
|
// This is a stub implementation
|
||||||
// In a real implementation, we would make a REST API call to create the repository
|
// In a real implementation, we would make a REST API call to create the repository
|
||||||
|
|
||||||
let uri_str = uri.as_ref().to_string_lossy().trim_end_matches('/').to_string();
|
let uri_str = uri
|
||||||
|
.as_ref()
|
||||||
|
.to_string_lossy()
|
||||||
|
.trim_end_matches('/')
|
||||||
|
.to_string();
|
||||||
|
|
||||||
// Create the repository configuration
|
// Create the repository configuration
|
||||||
let config = RepositoryConfig {
|
let config = RepositoryConfig {
|
||||||
|
|
@ -323,7 +327,11 @@ impl WritableRepository for RestBackend {
|
||||||
impl ReadableRepository for RestBackend {
|
impl ReadableRepository for RestBackend {
|
||||||
/// Open an existing repository
|
/// Open an existing repository
|
||||||
fn open<P: AsRef<Path>>(uri: P) -> Result<Self> {
|
fn open<P: AsRef<Path>>(uri: P) -> Result<Self> {
|
||||||
let uri_str = uri.as_ref().to_string_lossy().trim_end_matches('/').to_string();
|
let uri_str = uri
|
||||||
|
.as_ref()
|
||||||
|
.to_string_lossy()
|
||||||
|
.trim_end_matches('/')
|
||||||
|
.to_string();
|
||||||
|
|
||||||
// Create an HTTP client
|
// Create an HTTP client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
@ -444,14 +452,20 @@ impl ReadableRepository for RestBackend {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(RepositoryError::Other(format!("Search API error: {} for {}", e, url)));
|
return Err(RepositoryError::Other(format!(
|
||||||
|
"Search API error: {} for {}",
|
||||||
|
e, url
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let reader = BufReader::new(resp);
|
let reader = BufReader::new(resp);
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
let line = line.map_err(|e| {
|
let line = line.map_err(|e| {
|
||||||
RepositoryError::Other(format!("Failed to read search response line: {}", e))
|
RepositoryError::Other(format!(
|
||||||
|
"Failed to read search response line: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
// Line format: <attr> <fmri> <value_type> <value>
|
// Line format: <attr> <fmri> <value_type> <value>
|
||||||
// Example: pkg.fmri pkg:/system/rsyslog@8.2508.0,5.11-151056.0:20251023T180542Z set omnios/system/rsyslog
|
// Example: pkg.fmri pkg:/system/rsyslog@8.2508.0,5.11-151056.0:20251023T180542Z set omnios/system/rsyslog
|
||||||
|
|
@ -621,7 +635,7 @@ impl ReadableRepository for RestBackend {
|
||||||
// Write atomically
|
// Write atomically
|
||||||
let tmp_path = dest.with_extension("tmp");
|
let tmp_path = dest.with_extension("tmp");
|
||||||
let mut tmp_file = File::create(&tmp_path)?;
|
let mut tmp_file = File::create(&tmp_path)?;
|
||||||
|
|
||||||
std::io::copy(&mut resp, &mut tmp_file).map_err(|e| {
|
std::io::copy(&mut resp, &mut tmp_file).map_err(|e| {
|
||||||
RepositoryError::Other(format!("Failed to download payload: {}", e))
|
RepositoryError::Other(format!("Failed to download payload: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -681,11 +695,7 @@ impl ReadableRepository for RestBackend {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_manifest_text(
|
fn fetch_manifest_text(&mut self, publisher: &str, fmri: &crate::fmri::Fmri) -> Result<String> {
|
||||||
&mut self,
|
|
||||||
publisher: &str,
|
|
||||||
fmri: &crate::fmri::Fmri,
|
|
||||||
) -> Result<String> {
|
|
||||||
// Require versioned FMRI
|
// Require versioned FMRI
|
||||||
let version = fmri.version();
|
let version = fmri.version();
|
||||||
if version.is_empty() {
|
if version.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ async fn get_publisher_response(
|
||||||
}],
|
}],
|
||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
let json =
|
let json = serde_json::to_string_pretty(&p5i)
|
||||||
serde_json::to_string_pretty(&p5i).map_err(|e| DepotError::Server(e.to_string()))?;
|
.map_err(|e| DepotError::Server(e.to_string()))?;
|
||||||
Ok(([(header::CONTENT_TYPE, "application/vnd.pkg5.info")], json).into_response())
|
Ok(([(header::CONTENT_TYPE, "application/vnd.pkg5.info")], json).into_response())
|
||||||
} else {
|
} else {
|
||||||
Err(DepotError::Repo(
|
Err(DepotError::Repo(
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,15 @@ pub fn app_router(state: Arc<DepotRepo>) -> Router {
|
||||||
)
|
)
|
||||||
.route("/{publisher}/info/0/{fmri}", get(info::get_info))
|
.route("/{publisher}/info/0/{fmri}", get(info::get_info))
|
||||||
.route("/{publisher}/publisher/0", get(publisher::get_publisher_v0))
|
.route("/{publisher}/publisher/0", get(publisher::get_publisher_v0))
|
||||||
.route("/{publisher}/publisher/0/", get(publisher::get_publisher_v0))
|
.route(
|
||||||
|
"/{publisher}/publisher/0/",
|
||||||
|
get(publisher::get_publisher_v0),
|
||||||
|
)
|
||||||
.route("/{publisher}/publisher/1", get(publisher::get_publisher_v1))
|
.route("/{publisher}/publisher/1", get(publisher::get_publisher_v1))
|
||||||
.route("/{publisher}/publisher/1/", get(publisher::get_publisher_v1))
|
.route(
|
||||||
|
"/{publisher}/publisher/1/",
|
||||||
|
get(publisher::get_publisher_v1),
|
||||||
|
)
|
||||||
.route("/publisher/0", get(publisher::get_default_publisher_v0))
|
.route("/publisher/0", get(publisher::get_default_publisher_v0))
|
||||||
.route("/publisher/0/", get(publisher::get_default_publisher_v0))
|
.route("/publisher/0/", get(publisher::get_default_publisher_v0))
|
||||||
.route("/publisher/1", get(publisher::get_default_publisher_v1))
|
.route("/publisher/1", get(publisher::get_default_publisher_v1))
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ async fn test_depot_server() {
|
||||||
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
||||||
assert_eq!(pub_json["version"], 1);
|
assert_eq!(pub_json["version"], 1);
|
||||||
assert_eq!(pub_json["publishers"][0]["name"], "test");
|
assert_eq!(pub_json["publishers"][0]["name"], "test");
|
||||||
|
|
||||||
// Test Default Publisher Route v1
|
// Test Default Publisher Route v1
|
||||||
let def_pub_url = format!("{}/publisher/1", base_url);
|
let def_pub_url = format!("{}/publisher/1", base_url);
|
||||||
let resp = client.get(&def_pub_url).send().await.unwrap();
|
let resp = client.get(&def_pub_url).send().await.unwrap();
|
||||||
|
|
@ -427,10 +427,10 @@ async fn test_multiple_publishers_default_route() {
|
||||||
let temp_dir = TempDir::new().unwrap();
|
let temp_dir = TempDir::new().unwrap();
|
||||||
let repo_path = temp_dir.path().join("repo_multi");
|
let repo_path = temp_dir.path().join("repo_multi");
|
||||||
let mut backend = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut backend = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
backend.add_publisher("pub1").unwrap();
|
backend.add_publisher("pub1").unwrap();
|
||||||
backend.add_publisher("pub2").unwrap();
|
backend.add_publisher("pub2").unwrap();
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
server: ServerConfig {
|
server: ServerConfig {
|
||||||
bind: vec!["127.0.0.1:0".to_string()],
|
bind: vec!["127.0.0.1:0".to_string()],
|
||||||
|
|
@ -471,12 +471,15 @@ async fn test_multiple_publishers_default_route() {
|
||||||
|
|
||||||
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
||||||
let pubs = pub_json["publishers"].as_array().unwrap();
|
let pubs = pub_json["publishers"].as_array().unwrap();
|
||||||
|
|
||||||
// CURRENT BEHAVIOR: returns 1
|
// CURRENT BEHAVIOR: returns 1
|
||||||
// DESIRED BEHAVIOR: returns 2
|
// DESIRED BEHAVIOR: returns 2
|
||||||
assert_eq!(pubs.len(), 2, "Should return all publishers");
|
assert_eq!(pubs.len(), 2, "Should return all publishers");
|
||||||
|
|
||||||
let names: Vec<String> = pubs.iter().map(|p| p["name"].as_str().unwrap().to_string()).collect();
|
let names: Vec<String> = pubs
|
||||||
|
.iter()
|
||||||
|
.map(|p| p["name"].as_str().unwrap().to_string())
|
||||||
|
.collect();
|
||||||
assert!(names.contains(&"pub1".to_string()));
|
assert!(names.contains(&"pub1".to_string()));
|
||||||
assert!(names.contains(&"pub2".to_string()));
|
assert!(names.contains(&"pub2".to_string()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use miette::{IntoDiagnostic, Result};
|
|
||||||
use libips::repository::{FileBackend, RestBackend, ReadableRepository, ProgressReporter, ProgressInfo};
|
|
||||||
use libips::recv::PackageReceiver;
|
|
||||||
use libips::fmri::Fmri;
|
use libips::fmri::Fmri;
|
||||||
|
use libips::recv::PackageReceiver;
|
||||||
|
use libips::repository::{
|
||||||
|
FileBackend, ProgressInfo, ProgressReporter, ReadableRepository, RestBackend,
|
||||||
|
};
|
||||||
|
use miette::{IntoDiagnostic, Result};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing_subscriber::{EnvFilter, fmt};
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
|
|
@ -56,7 +58,9 @@ fn main() -> Result<()> {
|
||||||
// Open destination repository
|
// Open destination repository
|
||||||
// We'll open it inside each branch to avoid borrow checker issues with moves
|
// We'll open it inside each branch to avoid borrow checker issues with moves
|
||||||
|
|
||||||
let fmris: Vec<Fmri> = cli.packages.iter()
|
let fmris: Vec<Fmri> = cli
|
||||||
|
.packages
|
||||||
|
.iter()
|
||||||
.map(|s| Fmri::parse(s))
|
.map(|s| Fmri::parse(s))
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
.into_diagnostic()?;
|
.into_diagnostic()?;
|
||||||
|
|
@ -69,13 +73,17 @@ fn main() -> Result<()> {
|
||||||
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
||||||
let mut receiver = PackageReceiver::new(&mut source_repo, dest_repo);
|
let mut receiver = PackageReceiver::new(&mut source_repo, dest_repo);
|
||||||
receiver = receiver.with_progress(&progress);
|
receiver = receiver.with_progress(&progress);
|
||||||
receiver.receive(cli.publisher.as_deref(), &fmris, cli.recursive).into_diagnostic()?;
|
receiver
|
||||||
|
.receive(cli.publisher.as_deref(), &fmris, cli.recursive)
|
||||||
|
.into_diagnostic()?;
|
||||||
} else {
|
} else {
|
||||||
let mut source_repo = FileBackend::open(&cli.source).into_diagnostic()?;
|
let mut source_repo = FileBackend::open(&cli.source).into_diagnostic()?;
|
||||||
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
let dest_repo = FileBackend::open(&cli.dest).into_diagnostic()?;
|
||||||
let mut receiver = PackageReceiver::new(&mut source_repo, dest_repo);
|
let mut receiver = PackageReceiver::new(&mut source_repo, dest_repo);
|
||||||
receiver = receiver.with_progress(&progress);
|
receiver = receiver.with_progress(&progress);
|
||||||
receiver.receive(cli.publisher.as_deref(), &fmris, cli.recursive).into_diagnostic()?;
|
receiver
|
||||||
|
.receive(cli.publisher.as_deref(), &fmris, cli.recursive)
|
||||||
|
.into_diagnostic()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Package receive complete.");
|
info!("Package receive complete.");
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,12 @@ fn main() -> Result<()> {
|
||||||
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
||||||
|
|
||||||
// Load image
|
// Load image
|
||||||
let image = Image::load(&cli.image_path).map_err(|e| PkgTreeError(format!("Failed to load image at {:?}: {}", cli.image_path, e)))?;
|
let image = Image::load(&cli.image_path).map_err(|e| {
|
||||||
|
PkgTreeError(format!(
|
||||||
|
"Failed to load image at {:?}: {}",
|
||||||
|
cli.image_path, e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Targeted analysis of solver error file has top priority if provided
|
// Targeted analysis of solver error file has top priority if provided
|
||||||
if let Some(err_path) = &cli.solver_error_file {
|
if let Some(err_path) = &cli.solver_error_file {
|
||||||
|
|
@ -164,7 +169,9 @@ fn main() -> Result<()> {
|
||||||
.query_catalog(Some(needle.as_str()))
|
.query_catalog(Some(needle.as_str()))
|
||||||
.map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?
|
.map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?
|
||||||
} else {
|
} else {
|
||||||
image.query_catalog(None).map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?
|
image
|
||||||
|
.query_catalog(None)
|
||||||
|
.map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter by publisher if specified
|
// Filter by publisher if specified
|
||||||
|
|
@ -765,7 +772,10 @@ fn query_catalog_cached_mut(
|
||||||
return Ok(v.clone());
|
return Ok(v.clone());
|
||||||
}
|
}
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for p in image.query_catalog(Some(stem)).map_err(|e| PkgTreeError(format!("Failed to query catalog for {}: {}", stem, e)))? {
|
for p in image
|
||||||
|
.query_catalog(Some(stem))
|
||||||
|
.map_err(|e| PkgTreeError(format!("Failed to query catalog for {}: {}", stem, e)))?
|
||||||
|
{
|
||||||
out.push((p.publisher, p.fmri));
|
out.push((p.publisher, p.fmri));
|
||||||
}
|
}
|
||||||
ctx.catalog_cache.insert(stem.to_string(), out.clone());
|
ctx.catalog_cache.insert(stem.to_string(), out.clone());
|
||||||
|
|
@ -781,9 +791,13 @@ fn get_manifest_cached(
|
||||||
if let Some(m) = ctx.manifest_cache.get(&key) {
|
if let Some(m) = ctx.manifest_cache.get(&key) {
|
||||||
return Ok(m.clone());
|
return Ok(m.clone());
|
||||||
}
|
}
|
||||||
let manifest_opt = image
|
let manifest_opt = image.get_manifest_from_catalog(fmri).map_err(|e| {
|
||||||
.get_manifest_from_catalog(fmri)
|
PkgTreeError(format!(
|
||||||
.map_err(|e| PkgTreeError(format!("Failed to load manifest for {}: {}", fmri.to_string(), e)))?;
|
"Failed to load manifest for {}: {}",
|
||||||
|
fmri.to_string(),
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
let manifest = manifest_opt.unwrap_or_else(|| libips::actions::Manifest::new());
|
let manifest = manifest_opt.unwrap_or_else(|| libips::actions::Manifest::new());
|
||||||
ctx.manifest_cache.insert(key, manifest.clone());
|
ctx.manifest_cache.insert(key, manifest.clone());
|
||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
|
|
@ -1018,7 +1032,9 @@ fn run_dangling_scan(
|
||||||
format: OutputFormat,
|
format: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Query full catalog once
|
// Query full catalog once
|
||||||
let mut pkgs = image.query_catalog(None).map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?;
|
let mut pkgs = image
|
||||||
|
.query_catalog(None)
|
||||||
|
.map_err(|e| PkgTreeError(format!("Failed to query catalog: {}", e)))?;
|
||||||
|
|
||||||
// Build set of available non-obsolete stems AND an index of available (release, branch) pairs per stem,
|
// Build set of available non-obsolete stems AND an index of available (release, branch) pairs per stem,
|
||||||
// honoring publisher filter
|
// honoring publisher filter
|
||||||
|
|
@ -1188,7 +1204,12 @@ fn run_dangling_scan(
|
||||||
|
|
||||||
// ---------- Targeted analysis: parse pkg6 solver error text ----------
|
// ---------- Targeted analysis: parse pkg6 solver error text ----------
|
||||||
fn analyze_solver_error(image: &Image, publisher: Option<&str>, err_path: &PathBuf) -> Result<()> {
|
fn analyze_solver_error(image: &Image, publisher: Option<&str>, err_path: &PathBuf) -> Result<()> {
|
||||||
let text = std::fs::read_to_string(err_path).map_err(|e| PkgTreeError(format!("Failed to read solver error file {:?}: {}", err_path, e)))?;
|
let text = std::fs::read_to_string(err_path).map_err(|e| {
|
||||||
|
PkgTreeError(format!(
|
||||||
|
"Failed to read solver error file {:?}: {}",
|
||||||
|
err_path, e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Build a stack based on indentation before the tree bullet "└─".
|
// Build a stack based on indentation before the tree bullet "└─".
|
||||||
let mut stack: Vec<String> = Vec::new();
|
let mut stack: Vec<String> = Vec::new();
|
||||||
|
|
|
||||||
|
|
@ -262,11 +262,7 @@ fn fmt() -> Result<()> {
|
||||||
/// Run clippy
|
/// Run clippy
|
||||||
fn clippy() -> Result<()> {
|
fn clippy() -> Result<()> {
|
||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.args([
|
.args(["clippy", "--all-targets", "--all-features"])
|
||||||
"clippy",
|
|
||||||
"--all-targets",
|
|
||||||
"--all-features",
|
|
||||||
])
|
|
||||||
.status()
|
.status()
|
||||||
.context("Failed to run clippy")?;
|
.context("Failed to run clippy")?;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue