radicle: ensure clean up on rad::init error

It is possible that there can be errors while configuring a Radicle
`Repository` during `rad::init`, for example:
- configuring working copy remote fails
- signing references fails
- setting the identity head fails
- setting the project head fails

If any of these occur, a best effort is made to remove the
`Repository` and the working copy remote, so that the caller may fix
any issues and retry.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2024-01-11 14:52:28 +00:00 committed by cloudhead
parent f03c734e85
commit 5dbd9bb2a3
No known key found for this signature in database
3 changed files with 34 additions and 10 deletions

View File

@ -70,17 +70,44 @@ pub fn init<G: Signer, S: WriteStorage>(
) )
})?; })?;
let doc = identity::Doc::initial(proj, delegate, visibility).verified()?; let doc = identity::Doc::initial(proj, delegate, visibility).verified()?;
let (project, _) = Repository::init(&doc, storage, signer)?; let (project, _) = Repository::init(&doc, &storage, signer)?;
let url = git::Url::from(project.id); let url = git::Url::from(project.id);
match init_configure(repo, &project, pk, &default_branch, &url, signer) {
Ok(signed) => Ok((project.id, doc, signed)),
Err(err) => {
if let Err(e) = project.remove() {
log::warn!(target: "radicle", "Failed to remove project during `rad::init` cleanup: {e}");
}
if repo.find_remote(&REMOTE_NAME).is_ok() {
if let Err(e) = repo.remote_delete(&REMOTE_NAME) {
log::warn!(target: "radicle", "Failed to remove remote during `rad::init` cleanup: {e}");
}
}
Err(err)
}
}
}
fn init_configure<G>(
repo: &git2::Repository,
project: &Repository,
pk: &crypto::PublicKey,
default_branch: &BranchName,
url: &git::Url,
signer: &G,
) -> Result<SignedRefs<Verified>, InitError>
where
G: crypto::Signer,
{
git::configure_repository(repo)?; git::configure_repository(repo)?;
git::configure_remote(repo, &REMOTE_NAME, &url, &url.clone().with_namespace(*pk))?; git::configure_remote(repo, &REMOTE_NAME, url, &url.clone().with_namespace(*pk))?;
git::push( git::push(
repo, repo,
&REMOTE_NAME, &REMOTE_NAME,
[( [(
&git::fmt::lit::refs_heads(&default_branch).into(), &git::fmt::lit::refs_heads(default_branch).into(),
&git::fmt::lit::refs_heads(&default_branch).into(), &git::fmt::lit::refs_heads(default_branch).into(),
)], )],
)?; )?;
@ -88,7 +115,7 @@ pub fn init<G: Signer, S: WriteStorage>(
let _head = project.set_identity_head()?; let _head = project.set_identity_head()?;
let _head = project.set_head()?; let _head = project.set_head()?;
Ok((project.id, doc, signed)) Ok(signed)
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@ -371,9 +371,6 @@ pub trait WriteStorage: ReadStorage {
/// Create a read-write repository. /// Create a read-write repository.
fn create(&self, rid: Id) -> Result<Self::RepositoryMut, Error>; fn create(&self, rid: Id) -> Result<Self::RepositoryMut, Error>;
/// Delete all remote namespaces apart from the local node's and
/// delegates' namespace.
/// Clean the repository found at `rid`. /// Clean the repository found at `rid`.
/// ///
/// If the local peer has initialised `rad/sigrefs` by forking or /// If the local peer has initialised `rad/sigrefs` by forking or

View File

@ -422,12 +422,12 @@ impl Repository {
/// 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>,
storage: S, storage: &S,
signer: &G, signer: &G,
) -> Result<(Self, git::Oid), RepositoryError> { ) -> Result<(Self, git::Oid), RepositoryError> {
let (doc_oid, _) = doc.encode()?; let (doc_oid, _) = doc.encode()?;
let id = Id::from(doc_oid); let id = Id::from(doc_oid);
let repo = Self::create(paths::repository(&storage, &id), id, storage.info())?; let repo = Self::create(paths::repository(storage, &id), id, storage.info())?;
let commit = doc.init(&repo, signer)?; let commit = doc.init(&repo, signer)?;
Ok((repo, commit)) Ok((repo, commit))