From d23120ca8bf082e7dc7171a3b53829593cbf7701 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 22 Nov 2023 14:13:20 +0000 Subject: [PATCH] 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 X-Clacks-Overhead: GNU Terry Pratchett --- radicle/src/storage.rs | 7 +++++ radicle/src/storage/git.rs | 57 ++++++++++++++++++++++++++++++++++++- radicle/src/test/storage.rs | 4 +++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs index da48793b..4f9ca006 100644 --- a/radicle/src/storage.rs +++ b/radicle/src/storage.rs @@ -348,6 +348,9 @@ pub trait WriteStorage: ReadStorage { fn create(&self, rid: Id) -> Result; /// 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, 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, RepositoryError> { + self.deref().clean(rid) + } } #[cfg(test)] diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index dbc79447..bc17abe8 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -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, 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, RepositoryError> { + let delegates = self + .delegates()? + .into_iter() + .map(RemoteId::from) + .collect::>(); + 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( doc: &Doc, diff --git a/radicle/src/test/storage.rs b/radicle/src/test/storage.rs index b01e1e88..9f9b2d8c 100644 --- a/radicle/src/test/storage.rs +++ b/radicle/src/test/storage.rs @@ -99,6 +99,10 @@ impl WriteStorage for MockStorage { fn remove(&self, _rid: Id) -> Result<(), Error> { todo!() } + + fn clean(&self, _rid: Id) -> Result, RepositoryError> { + todo!() + } } #[derive(Clone, Debug)]