Skip to content

Commit

Permalink
Include more refs categories in signed_refs
Browse files Browse the repository at this point in the history
Namely:

    * refs/rad
    * refs/tags
    * refs/notes

The first one is necessary for implementing "radicle applications",
while the other two are more or less for the giggles for now.

Closes #484
Signed-off-by: Kim Altintop <[email protected]>
Co-authored-by: Fintan Halpenny <[email protected]>
  • Loading branch information
kim and FintanH committed Jan 19, 2021
1 parent 3e56f98 commit 1f8e66c
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 111 deletions.
208 changes: 131 additions & 77 deletions librad/src/git/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ use super::{
p2p::url::GitUrl,
refs::Refs,
storage::{self, Storage},
types::{reference::Reference, AsRemote, Fetchspec, Force, Namespace, Refspec},
types::{
reference::{Reference, RefsCategory},
AsRemote,
Fetchspec,
Force,
Namespace,
Refspec,
},
};
use crate::{
identities::{
Expand Down Expand Up @@ -157,85 +164,18 @@ pub mod refspecs {
R: HasProtocol + Clone + 'static,
for<'a> &'a R: Into<Multihash>,
{
let namespace = Namespace::from(urn);
let mut signed = tracked_sigrefs
.iter()
.map(|(tracked_peer, tracked_sigrefs)| {
let namespace = Namespace::from(urn);
tracked_sigrefs
.heads
.iter()
.filter_map(move |(name, target)| {
let name_namespaced =
// Either the signed ref is in the "owned" section of
// `remote_peer`'s repo...
if tracked_peer == remote_peer {
reflike!("refs/namespaces")
.join(&namespace)
.join(ext::Qualified::from(name.clone()))
// .. or `remote_peer` is tracking `tracked_peer`, in
// which case it is in the remotes section.
} else {
reflike!("refs/namespaces")
.join(&namespace)
.join(reflike!("refs/remotes"))
.join(tracked_peer)
// Nb.: `name` is `OneLevel`, but we are in the
// remote tracking branches, so we need `heads`
// Like `Qualified::from(name).strip_prefix("refs")`
.join(reflike!("heads")).join(name.clone())
};

// Only include the advertised ref if its target OID
// is the same as the signed one.
let targets_match = {
let found = remote_heads.get(&name_namespaced);
match found {
None => {
tracing::debug!(
"{} not found in remote heads",
name_namespaced
);
false
},

Some(remote_target) => {
if remote_target == &*target {
true
} else {
tracing::warn!(
"{} target mismatch: expected {}, got {}",
name_namespaced,
target,
remote_target
);
false
}
},
}
};

targets_match.then_some({
let dst = Reference::head(
namespace.clone(),
tracked_peer.clone(),
name.clone().into(),
);
let src = if tracked_peer == remote_peer {
dst.clone().with_remote(None)
} else {
dst.clone()
};

Refspec {
src,
dst,
force: Force::True,
}
.into_fetchspec()
})
})
.flat_map(|(tracked_peer, refs)| {
sigrefs(
namespace.clone(),
remote_peer,
remote_heads,
tracked_peer,
refs,
)
})
.flatten()
.collect::<Vec<_>>();

// Peek at the remote peer
Expand Down Expand Up @@ -264,6 +204,114 @@ pub mod refspecs {
signed.append(&mut delegates);
signed
}

fn sigrefs<'a, P, R>(
namespace: Namespace<R>,
remote_peer: &'a P,
remote_heads: &'a RemoteHeads,
tracked_peer: &'a P,
refs: &'a Refs,
) -> impl Iterator<Item = Fetchspec> + 'a
where
P: Clone + PartialEq,
for<'b> &'b P: AsRemote + Into<ext::RefLike>,

R: HasProtocol + Clone + 'a,
for<'b> &'b R: Into<Multihash>,
{
refs.iter_categorised()
.map({
let namespace = namespace.clone();
move |(x, category)| {
(
x,
namespaced(&namespace, remote_peer, tracked_peer, x.0, category),
)
}
})
.filter_map(move |((name, target), namespaced_name)| {
// Only include the advertised ref if its target OID
// is the same as the signed one.
let targets_match = {
let found = remote_heads.get(&namespaced_name);
match found {
None => {
tracing::debug!("{} not found in remote heads", namespaced_name);
false
},

Some(remote_target) => {
if remote_target == &*target {
true
} else {
tracing::warn!(
"{} target mismatch: expected {}, got {}",
namespaced_name,
target,
remote_target
);
false
}
},
}
};

targets_match.then_some({
let dst = Reference::head(
namespace.clone(),
tracked_peer.clone(),
name.clone().into(),
);
let src = if tracked_peer == remote_peer {
dst.clone().with_remote(None)
} else {
dst.clone()
};

Refspec {
src,
dst,
force: Force::True,
}
.into_fetchspec()
})
})
}

fn namespaced<'a, P, R>(
namespace: &'a Namespace<R>,
remote_peer: &'a P,
tracked_peer: &'a P,
name: &'a ext::OneLevel,
cat: RefsCategory,
) -> ext::RefLike
where
P: PartialEq,
for<'b> &'b P: AsRemote + Into<ext::RefLike>,

R: HasProtocol,
for<'b> &'b R: Into<Multihash>,
{
// Either the signed ref is in the "owned" section of
// `remote_peer`'s repo...
if tracked_peer == remote_peer {
reflike!("refs/namespaces")
.join(namespace)
.join(ext::Qualified::from(name.clone()))
// .. or `remote_peer` is tracking `tracked_peer`, in
// which case it is in the remotes section.
} else {
reflike!("refs/namespaces")
.join(namespace)
.join(reflike!("refs/remotes"))
.join(tracked_peer)
// Nb.: `name` is `OneLevel`, but we are in the
// remote tracking branches, so we need `heads`
// Like `Qualified::from(name).strip_prefix("refs")`
.join(ext::RefLike::from(cat))
.join(name.clone())
}
}
}

#[derive(Default)]
Expand Down Expand Up @@ -571,6 +619,9 @@ mod tests {
.iter()
.cloned()
.collect(),
rad: Default::default(),
tags: Default::default(),
notes: Default::default(),
remotes: Remotes::new(),
},
),
Expand All @@ -584,6 +635,9 @@ mod tests {
.iter()
.cloned()
.collect(),
rad: Default::default(),
tags: Default::default(),
notes: Default::default(),
remotes: Remotes::new(),
},
),
Expand Down
5 changes: 4 additions & 1 deletion librad/src/git/identities/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use thiserror::Error;

use super::{
super::{storage, types::reference},
super::{refs, storage, types::reference},
local,
};
use crate::identities::{
Expand All @@ -23,6 +23,9 @@ pub enum Error {
#[error("malformed URN")]
Ref(#[from] reference::FromUrnError),

#[error("update of signed_refs failed")]
Sigrefs(#[from] refs::stored::Error),

#[error(transparent)]
LocalId(#[from] local::ValidationError),

Expand Down
4 changes: 4 additions & 0 deletions librad/src/git/identities/person.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use radicle_git_ext::{is_not_found_err, OneLevel};

use super::{
super::{
refs::Refs,
storage::{self, Storage},
types::Reference,
},
Expand Down Expand Up @@ -103,6 +104,7 @@ where
let urn = person.urn();
common::IdRef::from(&urn).create(storage, person.content_id)?;
person.link(storage, &urn)?;
Refs::update(storage, &urn)?;

Ok(person.into_inner().into_inner())
}
Expand All @@ -129,6 +131,7 @@ where
if let Some(local_id) = whoami.into() {
local_id.link(storage, urn)?;
}
Refs::update(storage, urn)?;

Ok(next)
}
Expand All @@ -152,6 +155,7 @@ pub fn merge(storage: &Storage, urn: &Urn, from: PeerId) -> Result<Person, Error
let next = identities(storage).update_from(ours, theirs, storage.signer())?;

common::IdRef::from(urn).update(storage, next.content_id, &format!("merge from {}", from))?;
Refs::update(storage, urn)?;

Ok(next)
}
Expand Down
17 changes: 11 additions & 6 deletions librad/src/git/identities/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use git_ext::{is_not_found_err, OneLevel};

use super::{
super::{
refs::Refs as Sigrefs,
storage::{self, Storage},
types::{namespace, reference, Force, Reference, Single, SymbolicRef},
},
Expand Down Expand Up @@ -90,8 +91,10 @@ where
P: Into<ProjectPayload> + Debug,
{
let project = identities(storage).create(payload.into(), delegations, storage.signer())?;
Refs::Create(&project).apply(storage)?;
whoami.link(storage, &project.urn())?;
let urn = project.urn();
ProjectRefs::Create(&project).apply(storage)?;
whoami.link(storage, &urn)?;
Sigrefs::update(storage, &urn)?;

Ok(project)
}
Expand All @@ -114,10 +117,11 @@ where
let prev = Verifying::from(prev).signed()?;
let next = identities(storage).update(prev, payload, delegations, storage.signer())?;

Refs::Update(&next, "update").apply(storage)?;
ProjectRefs::Update(&next, "update").apply(storage)?;
if let Some(local_id) = whoami.into() {
local_id.link(storage, urn)?;
}
Sigrefs::update(storage, urn)?;

Ok(next)
}
Expand All @@ -140,17 +144,18 @@ pub fn merge(storage: &Storage, urn: &Urn, from: PeerId) -> Result<Project, Erro
let theirs = Verifying::from(theirs).signed()?;
let next = identities(storage).update_from(ours, theirs, storage.signer())?;

Refs::Update(&next, &format!("merge from {}", from)).apply(storage)?;
ProjectRefs::Update(&next, &format!("merge from {}", from)).apply(storage)?;
Sigrefs::update(storage, urn)?;

Ok(next)
}

enum Refs<'a> {
enum ProjectRefs<'a> {
Create(&'a Project),
Update(&'a Project, &'a str),
}

impl<'a> Refs<'a> {
impl<'a> ProjectRefs<'a> {
pub fn apply(&self, storage: &Storage) -> Result<(), Error> {
for symref in self.delegates() {
symref.create(storage.as_raw())?;
Expand Down
Loading

0 comments on commit 1f8e66c

Please sign in to comment.