radicle: add clean method

Add a `clean` method to remove the remotes of a repository that are
not the local peer nor the delegates.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2023-11-22 14:13:20 +00:00
parent 53013f306f
commit d23120ca8b
No known key found for this signature in database
GPG Key ID: 582959CEFC92CB4E
3 changed files with 67 additions and 1 deletions

View File

@ -348,6 +348,9 @@ pub trait WriteStorage: ReadStorage {
fn create(&self, rid: Id) -> Result<Self::RepositoryMut, Error>; fn create(&self, rid: Id) -> Result<Self::RepositoryMut, Error>;
/// Delete a repository. /// Delete a repository.
fn remove(&self, rid: Id) -> Result<(), Error>; fn remove(&self, rid: Id) -> Result<(), Error>;
/// Delete all remote namespaces apart from the local node's and
/// delegates' namespace.
fn clean(&self, rid: Id) -> Result<Vec<RemoteId>, RepositoryError>;
} }
/// Allows read-only access to a repository. /// Allows read-only access to a repository.
@ -592,6 +595,10 @@ where
fn remove(&self, rid: Id) -> Result<(), Error> { fn remove(&self, rid: Id) -> Result<(), Error> {
self.deref().remove(rid) self.deref().remove(rid)
} }
fn clean(&self, rid: Id) -> Result<Vec<RemoteId>, RepositoryError> {
self.deref().clean(rid)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@
pub mod cob; pub mod cob;
pub mod transport; pub mod transport;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io};
@ -142,6 +142,11 @@ impl WriteStorage for Storage {
fn remove(&self, rid: Id) -> Result<(), Error> { fn remove(&self, rid: Id) -> Result<(), Error> {
self.repository(rid)?.remove() self.repository(rid)?.remove()
} }
fn clean(&self, rid: Id) -> Result<Vec<RemoteId>, RepositoryError> {
let repo = self.repository(rid)?;
repo.clean(&self.info.key)
}
} }
impl Storage { impl Storage {
@ -357,6 +362,56 @@ impl Repository {
Ok(()) Ok(())
} }
/// Remove all the remotes of a repository that are not the
/// `local` remote or a delegate of the repository.
///
/// N.b. failure to delete remotes or references will not result
/// in an early exit. Instead, this method continues to delete the
/// next available remote or reference.
pub fn clean(&self, local: &RemoteId) -> Result<Vec<RemoteId>, RepositoryError> {
let delegates = self
.delegates()?
.into_iter()
.map(RemoteId::from)
.collect::<BTreeSet<_>>();
let mut deleted = Vec::new();
for id in self.remote_ids()? {
let id = match id {
Ok(id) => id,
Err(e) => {
log::error!(target: "storage", "Failed to clean up remote: {e}");
continue;
}
};
// N.b. it is fatal to delete local or delegates
if *local == id || delegates.contains(&id) {
continue;
}
let glob = git::refname!("refs/namespaces")
.join(git::Component::from(&id))
.with_pattern(git::refspec::STAR);
let refs = match self.references_glob(&glob) {
Ok(refs) => refs,
Err(e) => {
log::error!(target: "storage", "Failed to clean up remote '{id}': {e}");
continue;
}
};
for (refname, _) in refs {
if let Ok(mut r) = self.backend.find_reference(refname.as_str()) {
if let Err(e) = r.delete() {
log::error!(target: "storage", "Failed to clean up reference '{refname}': {e}");
}
} else {
log::error!(target: "storage", "Failed to clean up reference '{refname}'");
}
}
deleted.push(id);
}
Ok(deleted)
}
/// Create the repository's identity branch. /// Create the repository's identity branch.
pub fn init<G: Signer, S: WriteStorage>( pub fn init<G: Signer, S: WriteStorage>(
doc: &Doc<Verified>, doc: &Doc<Verified>,

View File

@ -99,6 +99,10 @@ impl WriteStorage for MockStorage {
fn remove(&self, _rid: Id) -> Result<(), Error> { fn remove(&self, _rid: Id) -> Result<(), Error> {
todo!() todo!()
} }
fn clean(&self, _rid: Id) -> Result<Vec<RemoteId>, RepositoryError> {
todo!()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]