radicle: canoncial refs/rad/id
Introduce a canonical reference for refs/rad/id to aid in discovering information about a repository without knowing a peer -- e.g. if a peer wants to clone a new repository it needs to find out which peers are delegates for that project, but does not know what their NIDs are up front. The approach mirrors the canonical `head` for a repository and introduces useful constructors for `Doc`/`DocAt`. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
b030e9ea80
commit
a32101ab75
|
|
@ -18,6 +18,7 @@ use crate::crypto;
|
|||
use crate::crypto::{Signature, Unverified, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::{project::Project, Did};
|
||||
use crate::storage;
|
||||
use crate::storage::git::trailers;
|
||||
use crate::storage::{ReadRepository, RemoteId};
|
||||
|
||||
|
|
@ -142,6 +143,13 @@ pub struct Doc<V> {
|
|||
}
|
||||
|
||||
impl<V> Doc<V> {
|
||||
pub fn canonical_head(repo: &storage::git::Repository) -> Result<Oid, DocError> {
|
||||
repo.backend
|
||||
.refname_to_id(storage::git::CANONICAL_IDENTITY.as_str())
|
||||
.map(Oid::from)
|
||||
.map_err(DocError::from)
|
||||
}
|
||||
|
||||
pub fn head<R: ReadRepository>(remote: &RemoteId, repo: &R) -> Result<Oid, DocError> {
|
||||
repo.reference_oid(remote, &git::refs::storage::IDENTITY_BRANCH)
|
||||
.map_err(DocError::from)
|
||||
|
|
@ -215,6 +223,11 @@ impl Doc<Verified> {
|
|||
Ok((oid, sig))
|
||||
}
|
||||
|
||||
pub fn canonical(repo: &storage::git::Repository) -> Result<DocAt, DocError> {
|
||||
let oid = Self::canonical_head(repo)?;
|
||||
Self::load_at(oid, repo)
|
||||
}
|
||||
|
||||
pub fn load_at<R: ReadRepository>(oid: Oid, repo: &R) -> Result<DocAt, DocError> {
|
||||
let blob = Self::blob_at(oid, repo)?;
|
||||
let doc = Doc::from_json(blob.content())?.verified()?;
|
||||
|
|
|
|||
|
|
@ -353,6 +353,17 @@ pub trait ReadRepository {
|
|||
/// Returns the [`Oid`] as well as the qualified reference name.
|
||||
fn canonical_head(&self) -> Result<(Qualified, Oid), IdentityError>;
|
||||
|
||||
/// Get the head of the `rad/id` reference in this repository.
|
||||
///
|
||||
/// Returns the reference pointed to by `rad/id` if it is set. Otherwise, computes the canonical
|
||||
/// `rad/id` using [`ReadRepository::canonical_identity_head`].
|
||||
fn identity_head(&self) -> Result<Oid, IdentityError>;
|
||||
|
||||
/// Compute the canonical `rad/id` of this repository.
|
||||
///
|
||||
/// Ignores any existing `rad/id` reference.
|
||||
fn canonical_identity_head(&self) -> Result<Oid, IdentityError>;
|
||||
|
||||
/// Get the `reference` for the given `remote`.
|
||||
///
|
||||
/// Returns `None` is the reference did not exist.
|
||||
|
|
@ -403,6 +414,8 @@ pub trait WriteRepository: ReadRepository {
|
|||
/// Set the repository head to the canonical branch.
|
||||
/// This computes the head based on the delegate set.
|
||||
fn set_head(&self) -> Result<Oid, IdentityError>;
|
||||
/// Set the repository 'rad/id' to the canonical commit, agreed by quorum.
|
||||
fn set_identity_head(&self) -> Result<Oid, IdentityError>;
|
||||
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
|
||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
|
||||
/// Get the underlying git repository.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,13 @@ pub static NAMESPACES_GLOB: Lazy<refspec::PatternString> =
|
|||
Lazy::new(|| refspec::pattern!("refs/namespaces/*"));
|
||||
pub static SIGREFS_GLOB: Lazy<refspec::PatternString> =
|
||||
Lazy::new(|| refspec::pattern!("refs/namespaces/*/rad/sigrefs"));
|
||||
pub static CANONICAL_IDENTITY: Lazy<git::Qualified> = Lazy::new(|| {
|
||||
git::Qualified::from_components(
|
||||
git::name::component!("rad"),
|
||||
git::name::component!("id"),
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
/// A parsed Git reference.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -584,6 +591,18 @@ impl ReadRepository for Repository {
|
|||
|
||||
Ok((branch_ref, oid.into()))
|
||||
}
|
||||
|
||||
fn identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
match Doc::<Verified>::canonical_head(self) {
|
||||
Ok(oid) => Ok(oid),
|
||||
Err(err) if err.is_not_found() => self.canonical_identity_head(),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn canonical_identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
Repository::identity_head(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteRepository for Repository {
|
||||
|
|
@ -602,6 +621,20 @@ impl WriteRepository for Repository {
|
|||
Ok(head)
|
||||
}
|
||||
|
||||
fn set_identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
let head = self.canonical_identity_head()?;
|
||||
|
||||
log::debug!(target: "storage", "Setting ref: {} -> {}", *CANONICAL_IDENTITY, head);
|
||||
self.raw().reference(
|
||||
CANONICAL_IDENTITY.as_str(),
|
||||
*head,
|
||||
true,
|
||||
"set-local-branch (radicle)",
|
||||
)?;
|
||||
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error> {
|
||||
let remote = signer.public_key();
|
||||
let refs = self.references_of(remote)?;
|
||||
|
|
|
|||
|
|
@ -194,6 +194,14 @@ impl ReadRepository for MockRepository {
|
|||
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
|
||||
Ok((git2::Oid::zero().into(), self.doc.clone().unverified()))
|
||||
}
|
||||
|
||||
fn identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn canonical_identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteRepository for MockRepository {
|
||||
|
|
@ -211,4 +219,8 @@ impl WriteRepository for MockRepository {
|
|||
) -> Result<crate::storage::refs::SignedRefs<Verified>, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_identity_head(&self) -> Result<Oid, IdentityError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue