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>;
/// Delete a repository.
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.
@ -592,6 +595,10 @@ where
fn remove(&self, rid: Id) -> Result<(), Error> {
self.deref().remove(rid)
}
fn clean(&self, rid: Id) -> Result<Vec<RemoteId>, RepositoryError> {
self.deref().clean(rid)
}
}
#[cfg(test)]

View File

@ -2,7 +2,7 @@
pub mod cob;
pub mod transport;
use std::collections::{BTreeMap, HashMap};
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::{fs, io};
@ -142,6 +142,11 @@ impl WriteStorage for Storage {
fn remove(&self, rid: Id) -> Result<(), Error> {
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 {
@ -357,6 +362,56 @@ impl Repository {
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.
pub fn init<G: Signer, S: WriteStorage>(
doc: &Doc<Verified>,

View File

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