rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood18b0f3895cd70fa1949176679346f4d25a906c98
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Created",
"patch": {
"id": "a510cdd818d1495e89914cc7f5ff91a72aad48a8",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"title": "radicle: identity caching",
"state": {
"status": "archived",
"conflicts": []
},
"before": "6569449fff99b251b2cea96cb76eaa0ea430c63e",
"after": "18b0f3895cd70fa1949176679346f4d25a906c98",
"commits": [
"18b0f3895cd70fa1949176679346f4d25a906c98"
],
"target": "3fc92f83fe089263c470b328414d923227cf0e4a",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "a510cdd818d1495e89914cc7f5ff91a72aad48a8",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Add caching for the `Identity` COB.\n\nA migration file is added for the sqlite COB cache, which inserts the\nnew `identities` table.\n\nA new module `identity::cache` is added, similar to `patch::cache` and\n`issue::cache`.\n\nTo achieve the same pattern as patches and issues, it was necessary to\nintroduce a new type `Identities` in the `identity` module, which\noffers the same functionality as methods that existed on the\n`Identity` type.\n\nThe caching is implemented using write-through semantics, again\nsimilar to patches and issues.",
"base": "6569449fff99b251b2cea96cb76eaa0ea430c63e",
"oid": "18b0f3895cd70fa1949176679346f4d25a906c98",
"timestamp": 1710863546
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "6e4c8612-2892-48ac-86fb-13451e355e72"
},
"info_url": "https://cci.rad.levitte.org//6e4c8612-2892-48ac-86fb-13451e355e72.html"
}
Started at: 2026-09-08 02:01:14.774616+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/6e4c8612-2892-48ac-86fb-13451e355e72/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 189 issues · 38 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout a510cdd818d1495e89914cc7f5ff91a72aad48a8
✓ Switched to branch patch/a510cdd at revision a510cdd
✓ Branch patch/a510cdd setup to track rad/patches/a510cdd818d1495e89914cc7f5ff91a72aad48a8
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 18b0f3895cd70fa1949176679346f4d25a906c98
HEAD is now at 18b0f389 radicle: identity caching
Exit code: 0
$ rad patch show a510cdd818d1495e89914cc7f5ff91a72aad48a8 -p
╭────────────────────────────────────────────────────────────────────────────────╮
│ Title radicle: identity caching │
│ Patch a510cdd818d1495e89914cc7f5ff91a72aad48a8 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head 18b0f3895cd70fa1949176679346f4d25a906c98 │
│ Base 6569449fff99b251b2cea96cb76eaa0ea430c63e │
│ Target master │
│ Branches patch/a510cdd │
│ Commits ahead 1, behind 1519 │
│ Status archived │
│ │
│ Add caching for the `Identity` COB. │
│ │
│ A migration file is added for the sqlite COB cache, which inserts the │
│ new `identities` table. │
│ │
│ A new module `identity::cache` is added, similar to `patch::cache` and │
│ `issue::cache`. │
│ │
│ To achieve the same pattern as patches and issues, it was necessary to │
│ introduce a new type `Identities` in the `identity` module, which │
│ offers the same functionality as methods that existed on the │
│ `Identity` type. │
│ │
│ The caching is implemented using write-through semantics, again │
│ similar to patches and issues. │
├────────────────────────────────────────────────────────────────────────────────┤
│ 18b0f38 radicle: identity caching │
├────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision a510cdd @ 6569449..18b0f38 by fintohaps z6Mkire…SQZ3voM 2 years ago │
╰────────────────────────────────────────────────────────────────────────────────╯
commit 18b0f3895cd70fa1949176679346f4d25a906c98
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Tue Mar 19 15:39:49 2024 +0000
radicle: identity caching
Add caching for the `Identity` COB.
A migration file is added for the sqlite COB cache, which inserts the
new `identities` table.
A new module `identity::cache` is added, similar to `patch::cache` and
`issue::cache`.
To achieve the same pattern as patches and issues, it was necessary to
introduce a new type `Identities` in the `identity` module, which
offers the same functionality as methods that existed on the
`Identity` type.
The caching is implemented using write-through semantics, again
similar to patches and issues.
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
diff --git a/radicle-cli/src/commands/id.rs b/radicle-cli/src/commands/id.rs
index de726f804..ebd726d02 100644
--- a/radicle-cli/src/commands/id.rs
+++ b/radicle-cli/src/commands/id.rs
@@ -272,7 +272,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let repo = storage
.repository(rid)
.context(anyhow!("repository `{rid}` not found in local storage"))?;
- let mut identity = Identity::load_mut(&repo)?;
+ let mut identities = profile.identities_mut(&repo)?;
+ let mut identity = identities.load_mut()?;
let current = identity.current().clone();
match options.op {
@@ -613,13 +614,18 @@ and description.
Ok(result)
}
-fn update<R: WriteRepository + cob::Store, G: Signer>(
+fn update<R, C, G>(
title: Option<String>,
description: Option<String>,
doc: Doc<Verified>,
- current: &mut IdentityMut<R>,
+ current: &mut IdentityMut<R, C>,
signer: &G,
-) -> anyhow::Result<Revision> {
+) -> anyhow::Result<Revision>
+where
+ R: WriteRepository + cob::Store,
+ C: cob::cache::Update<Identity>,
+ G: Signer,
+{
if let Some((title, description)) = edit_title_description(title, description)? {
let id = current.update(title, description, &doc, signer)?;
let revision = current
diff --git a/radicle-cli/src/commands/inbox.rs b/radicle-cli/src/commands/inbox.rs
index b6e589a4d..f0764a328 100644
--- a/radicle-cli/src/commands/inbox.rs
+++ b/radicle-cli/src/commands/inbox.rs
@@ -6,8 +6,8 @@ use anyhow::anyhow;
use git_ref_format::Qualified;
use localtime::LocalTime;
+use radicle::cob::identity::cache::Identities as _;
use radicle::cob::TypedId;
-use radicle::identity::Identity;
use radicle::issue::cache::Issues as _;
use radicle::node::notifications;
use radicle::node::notifications::*;
@@ -273,6 +273,7 @@ where
let proj = doc.project()?;
let issues = profile.issues(&repo)?;
let patches = profile.patches(&repo)?;
+ let identities = profile.identities(&repo)?;
let mut notifs = notifs.by_repo(&rid, sort_by.field)?.collect::<Vec<_>>();
if !sort_by.reverse {
@@ -306,7 +307,7 @@ where
} = match &n.kind {
NotificationKind::Branch { name } => NotificationRow::branch(name, head, &n, &repo)?,
NotificationKind::Cob { typed_id } => {
- match NotificationRow::cob(typed_id, &n, &issues, &patches, &repo)? {
+ match NotificationRow::cob(typed_id, &n, &issues, &patches, &identities)? {
Some(row) => row,
None => continue,
}
@@ -401,17 +402,17 @@ impl NotificationRow {
))
}
- fn cob<S, I, P>(
+ fn cob<I, P, Ids>(
typed_id: &TypedId,
n: &Notification,
issues: &I,
patches: &P,
- repo: &S,
+ identities: &Ids,
) -> anyhow::Result<Option<Self>>
where
- S: ReadRepository + cob::Store,
I: cob::issue::cache::Issues,
P: cob::patch::cache::Patches,
+ Ids: cob::identity::cache::Identities,
{
let TypedId { id, .. } = typed_id;
let (category, summary, state) = if typed_id.is_issue() {
@@ -435,7 +436,7 @@ impl NotificationRow {
term::format::patch::state(patch.state()),
)
} else if typed_id.is_identity() {
- let Ok(identity) = Identity::get(id, repo) else {
+ let Ok(identity) = identities.get(id) else {
log::error!(
target: "cli",
"Error retrieving identity {id} for notification {}", n.id
@@ -548,7 +549,8 @@ fn show(
term::patch::show(&patch, &typed_id.id, false, &repo, None, profile)?;
}
NotificationKind::Cob { typed_id } if typed_id.is_identity() => {
- let identity = Identity::get(&typed_id.id, &repo)?;
+ let identities = profile.identities(&repo)?;
+ let identity = identities.get(&typed_id.id)?;
term::json::to_pretty(&identity.doc, Path::new("radicle.json"))?.print();
}
diff --git a/radicle-cli/src/commands/init.rs b/radicle-cli/src/commands/init.rs
index 71fcf0da0..148c6cbd9 100644
--- a/radicle-cli/src/commands/init.rs
+++ b/radicle-cli/src/commands/init.rs
@@ -17,7 +17,7 @@ use radicle::identity::{RepoId, Visibility};
use radicle::node::policy::Scope;
use radicle::node::{Event, Handle, NodeId};
use radicle::prelude::Doc;
-use radicle::{profile, Node};
+use radicle::{cob, profile, Node};
use crate as cli;
use crate::commands;
@@ -265,8 +265,13 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
let mut spinner = term::spinner("Initializing...");
let mut push_cmd = String::from("git push");
+ // N.b. we cannot use `profile.identities_mut` here because the
+ // Repository has not been initialised.
+ let mut cache = cob::cache::Store::open(profile.cobs().join(cob::cache::COBS_DB_FILE))?;
+
match radicle::rad::init(
&repo,
+ &mut cache,
&name,
&description,
branch.clone(),
diff --git a/radicle-cli/src/commands/inspect.rs b/radicle-cli/src/commands/inspect.rs
index afe4c7cdd..6da927274 100644
--- a/radicle-cli/src/commands/inspect.rs
+++ b/radicle-cli/src/commands/inspect.rs
@@ -7,8 +7,9 @@ use std::str::FromStr;
use anyhow::{anyhow, Context as _};
use chrono::prelude::*;
+use radicle::cob::identity::cache::Identities as _;
+use radicle::identity::DocAt;
use radicle::identity::RepoId;
-use radicle::identity::{DocAt, Identity};
use radicle::node::policy::Policy;
use radicle::node::AliasStore as _;
use radicle::storage::git::{Repository, Storage};
@@ -213,7 +214,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
}
Target::History => {
let (repo, _) = repo(rid, storage)?;
- let identity = Identity::load(&repo)?;
+ let identities = profile.identities(&repo)?;
+ let identity = identities.load()?;
let head = repo.identity_head()?;
let history = repo.revwalk(head)?;
diff --git a/radicle-cli/src/commands/publish.rs b/radicle-cli/src/commands/publish.rs
index 85f02109b..eb4591028 100644
--- a/radicle-cli/src/commands/publish.rs
+++ b/radicle-cli/src/commands/publish.rs
@@ -2,7 +2,7 @@ use std::ffi::OsString;
use anyhow::{anyhow, Context as _};
-use radicle::identity::{Identity, Visibility};
+use radicle::identity::Visibility;
use radicle::node::Handle as _;
use radicle::prelude::RepoId;
use radicle::storage::{SignRepository, ValidateRepository, WriteRepository, WriteStorage};
@@ -76,7 +76,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
};
let repo = profile.storage.repository_mut(rid)?;
- let mut identity = Identity::load_mut(&repo)?;
+ let mut identities = profile.identities_mut(&repo)?;
+ let mut identity = identities.load_mut()?;
let mut doc = identity.doc().clone();
if doc.visibility.is_public() {
diff --git a/radicle-httpd/src/test.rs b/radicle-httpd/src/test.rs
index 43d8fb274..b3aa1ab4c 100644
--- a/radicle-httpd/src/test.rs
+++ b/radicle-httpd/src/test.rs
@@ -19,7 +19,7 @@ use radicle::git::{raw as git2, RefString};
use radicle::identity::Visibility;
use radicle::profile::Home;
use radicle::storage::ReadStorage;
-use radicle::Storage;
+use radicle::{cob, Storage};
use radicle::{node, profile};
use radicle_crypto::test::signer::MockSigner;
@@ -129,8 +129,10 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
let visibility = Visibility::Private {
allow: BTreeSet::default(),
};
+ let mut cache = cob::cache::NoCache;
let (rid, _, _) = radicle::rad::init(
&repo,
+ &mut cache,
&name,
&description,
branch,
@@ -221,8 +223,10 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
let description = "Rad repository for tests".to_string();
let branch = RefString::try_from(DEFAULT_BRANCH).unwrap();
let visibility = Visibility::default();
+ let mut cache = cob::cache::NoCache;
let (rid, _, _) = radicle::rad::init(
&repo,
+ &mut cache,
&name,
&description,
branch,
diff --git a/radicle-node/src/test/environment.rs b/radicle-node/src/test/environment.rs
index d89809f94..959fb4176 100644
--- a/radicle-node/src/test/environment.rs
+++ b/radicle-node/src/test/environment.rs
@@ -153,6 +153,7 @@ pub struct Node<G> {
pub config: Config,
pub db: service::Stores<Database>,
pub policies: policy::Store<policy::Write>,
+ pub cache: cob::cache::StoreWriter,
}
impl Node<MemorySigner> {
@@ -163,6 +164,8 @@ impl Node<MemorySigner> {
let policies = policy::Store::open(policies_db).unwrap();
let db = profile.database_mut().unwrap();
let db = service::Stores::from(db);
+ let cache = profile.home.cobs().join(COBS_DB_FILE);
+ let cache = cob::cache::Store::open(cache).unwrap();
Node {
id,
@@ -172,6 +175,7 @@ impl Node<MemorySigner> {
db,
policies,
storage: profile.storage,
+ cache,
}
}
}
@@ -412,6 +416,8 @@ impl Node<MockSigner> {
let policies = home.policies_mut().unwrap();
let db = home.database_mut().unwrap();
let db = service::Stores::from(db);
+ let cache = home.cobs().join(COBS_DB_FILE);
+ let cache = cob::cache::Store::open(cache).unwrap();
log::debug!(target: "test", "Node::init {}: {}", config.alias, signer.public_key());
Self {
@@ -422,6 +428,7 @@ impl Node<MockSigner> {
config,
db,
policies,
+ cache,
}
}
}
@@ -469,6 +476,7 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
let branch = refname!("master");
let id = rad::init(
repo,
+ &mut self.cache,
name,
description,
branch.clone(),
diff --git a/radicle-node/src/test/peer.rs b/radicle-node/src/test/peer.rs
index 82b02ba67..57d387a17 100644
--- a/radicle-node/src/test/peer.rs
+++ b/radicle-node/src/test/peer.rs
@@ -6,6 +6,7 @@ use std::str::FromStr;
use log::*;
+use radicle::cob;
use radicle::identity::Visibility;
use radicle::node::address::Store as _;
use radicle::node::Database;
@@ -136,8 +137,10 @@ impl<G: Signer> Peer<Storage, G> {
radicle::storage::git::transport::local::register(self.storage().clone());
let (repo, _) = fixtures::repository(self.tempdir.path().join(name));
+ let mut cache = cob::cache::NoCache;
let (rid, _, _) = rad::init(
&repo,
+ &mut cache,
name,
description,
radicle::git::refname!("master"),
diff --git a/radicle-node/src/tests.rs b/radicle-node/src/tests.rs
index e3a7988b5..944f6e1e3 100644
--- a/radicle-node/src/tests.rs
+++ b/radicle-node/src/tests.rs
@@ -8,6 +8,7 @@ use std::time;
use crossbeam_channel as chan;
use netservices::Direction as Link;
+use radicle::cob;
use radicle::identity::Visibility;
use radicle::node::address::Store;
use radicle::node::routing::Store as _;
@@ -1570,10 +1571,11 @@ fn test_push_and_pull() {
eve.address(),
ConnectOptions::default(),
));
-
+ let mut cache = cob::cache::NoCache;
// Alice creates a new project.
let (proj_id, _, _) = rad::init(
&repo,
+ &mut cache,
"alice",
"alice's repo",
git::refname!("master"),
diff --git a/radicle-node/src/worker/fetch.rs b/radicle-node/src/worker/fetch.rs
index aeceebfbc..03dbea3be 100644
--- a/radicle-node/src/worker/fetch.rs
+++ b/radicle-node/src/worker/fetch.rs
@@ -210,11 +210,14 @@ fn cache_cobs<S, C>(
) -> Result<(), error::Cache>
where
S: ReadRepository + cob::Store,
- C: cob::cache::Update<cob::issue::Issue> + cob::cache::Update<cob::patch::Patch>,
+ C: cob::cache::Update<cob::issue::Issue>
+ + cob::cache::Update<cob::patch::Patch>
+ + cob::cache::Update<cob::identity::Identity>,
C: cob::cache::Remove<cob::issue::Issue> + cob::cache::Remove<cob::patch::Patch>,
{
let issues = cob::issue::Issues::open(storage)?;
let patches = cob::patch::Patches::open(storage)?;
+ let identities = cob::identity::Identities::open(storage)?;
for update in refs {
match update {
RefUpdate::Updated { name, .. }
@@ -246,6 +249,17 @@ where
err: e.into(),
})?;
}
+ } else if identifier.is_identity() {
+ if let Ok(identity) = identities.get(&identifier.id) {
+ cache
+ .update(rid, &identifier.id, &identity)
+ .map(|_| ())
+ .map_err(|e| error::Cache::Update {
+ id: identifier.id,
+ type_name: identifier.type_name,
+ err: e.into(),
+ })?;
+ }
}
}
None => continue,
diff --git a/radicle-tools/src/rad-init.rs b/radicle-tools/src/rad-init.rs
index b1ab54109..c351f47f8 100644
--- a/radicle-tools/src/rad-init.rs
+++ b/radicle-tools/src/rad-init.rs
@@ -1,6 +1,6 @@
use std::path::Path;
-use radicle::{git, identity::Visibility, Profile};
+use radicle::{cob, git, identity::Visibility, Profile};
fn main() -> anyhow::Result<()> {
let cwd = Path::new(".").canonicalize()?;
@@ -8,8 +8,10 @@ fn main() -> anyhow::Result<()> {
let repo = radicle::git::raw::Repository::open(cwd)?;
let profile = Profile::load()?;
let signer = profile.signer()?;
+ let mut cache = cob::cache::Store::open(profile.cobs().join(cob::cache::COBS_DB_FILE))?;
let (id, _, _) = radicle::rad::init(
&repo,
+ &mut cache,
&name,
"",
git::refname!("master"),
diff --git a/radicle/src/cob/cache.rs b/radicle/src/cob/cache.rs
index 407a69439..b8d96f700 100644
--- a/radicle/src/cob/cache.rs
+++ b/radicle/src/cob/cache.rs
@@ -23,7 +23,10 @@ const DB_WRITE_TIMEOUT: time::Duration = time::Duration::from_secs(6);
/// Database migrations.
/// The first migration is the creation of the initial tables.
-const MIGRATIONS: &[&str] = &[include_str!("cache/migrations/1.sql")];
+const MIGRATIONS: &[&str] = &[
+ include_str!("cache/migrations/1.sql"),
+ include_str!("cache/migrations/2.sql"),
+];
#[derive(Error, Debug)]
pub enum Error {
@@ -247,6 +250,7 @@ where
///
/// The intention is for this to be used in tests that do not expect
/// any cache reads.
+#[derive(Clone, Copy)]
pub struct NoCache;
impl<T> Update<T> for NoCache {
diff --git a/radicle/src/cob/identity.rs b/radicle/src/cob/identity.rs
index 191d2871c..7faa1aa9d 100644
--- a/radicle/src/cob/identity.rs
+++ b/radicle/src/cob/identity.rs
@@ -1,3 +1,5 @@
+pub mod cache;
+
use std::collections::BTreeMap;
use std::{fmt, ops::Deref, str::FromStr};
@@ -21,11 +23,13 @@ use crate::{
doc::{Doc, DocError, RepoId},
Did,
},
- storage::{ReadRepository, RepositoryError, WriteRepository},
+ storage::{HasRepoId, ReadRepository, RepositoryError, WriteRepository},
};
use super::{Author, EntryId};
+pub use cache::Cache;
+
/// Type name of an identity proposal.
pub static TYPENAME: Lazy<TypeName> =
Lazy::new(|| FromStr::from_str("xyz.radicle.id").expect("type name is valid"));
@@ -130,10 +134,23 @@ pub enum Error {
Doc(#[from] DocError),
#[error("revision {0} was not found")]
NotFound(RevisionId),
+ #[error("failed to update identity {id} in cache: {err}")]
+ CacheUpdate {
+ id: ObjectId,
+ #[source]
+ err: Box<dyn std::error::Error + Send + Sync + 'static>,
+ },
+ #[error("failed to remove identity {id} from cache : {err}")]
+ CacheRemove {
+ id: ObjectId,
+ #[source]
+ err: Box<dyn std::error::Error + Send + Sync + 'static>,
+ },
}
/// An evolving identity document.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct Identity {
/// The canonical identifier for this identity.
/// This is the object id of the initial document blob.
@@ -179,64 +196,6 @@ impl Identity {
timeline: vec![root],
}
}
-
- pub fn initialize<'a, R: WriteRepository + cob::Store, G: Signer>(
- doc: &Doc<Verified>,
- store: &'a R,
- signer: &G,
- ) -> Result<IdentityMut<'a, R>, cob::store::Error> {
- let mut store = cob::store::Store::open(store)?;
- let (id, identity) =
- Transaction::<Identity, _>::initial("Initialize identity", &mut store, signer, |tx| {
- tx.revision("Initial revision", "", doc, None, signer)
- })?;
-
- Ok(IdentityMut {
- id,
- identity,
- store,
- })
- }
-
- pub fn get<R: ReadRepository + cob::Store>(
- object: &ObjectId,
- repo: &R,
- ) -> Result<Identity, store::Error> {
- cob::get::<Self, _>(repo, Self::type_name(), object)
- .map(|r| r.map(|cob| cob.object))?
- .ok_or_else(move || store::Error::NotFound(TYPENAME.clone(), *object))
- }
-
- /// Get a proposal mutably.
- pub fn get_mut<'a, R: WriteRepository + cob::Store>(
- id: &ObjectId,
- repo: &'a R,
- ) -> Result<IdentityMut<'a, R>, store::Error> {
- let obj = Self::get(id, repo)?;
- let store = cob::store::Store::open(repo)?;
-
- Ok(IdentityMut {
- id: *id,
- identity: obj,
- store,
- })
- }
-
- pub fn load<R: ReadRepository + cob::Store>(repo: &R) -> Result<Identity, RepositoryError> {
- let oid = repo.identity_root()?;
- let oid = ObjectId::from(oid);
-
- Self::get(&oid, repo).map_err(RepositoryError::from)
- }
-
- pub fn load_mut<R: WriteRepository + cob::Store>(
- repo: &R,
- ) -> Result<IdentityMut<R>, RepositoryError> {
- let oid = repo.identity_root()?;
- let oid = ObjectId::from(oid);
-
- Self::get_mut(&oid, repo).map_err(RepositoryError::from)
- }
}
impl Identity {
@@ -612,11 +571,12 @@ impl<R: ReadRepository> cob::Evaluate<R> for Identity {
}
}
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(tag = "verdict")]
pub enum Verdict {
/// An accepting verdict must supply the [`Signature`] over the
/// new proposed [`Doc`].
- Accept(Signature),
+ Accept { signature: Signature },
/// Rejecting the proposed [`Doc`].
Reject,
}
@@ -656,7 +616,8 @@ impl std::fmt::Display for State {
///
/// Once a revision has reached the quorum threshold of the previous
/// [`Identity`] it is then adopted as the current identity.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct Revision {
/// The id of this revision. Points to a commit.
pub id: RevisionId,
@@ -692,7 +653,7 @@ impl std::ops::Deref for Revision {
impl Revision {
pub fn signatures(&self) -> impl Iterator<Item = (&PublicKey, Signature)> {
self.verdicts().filter_map(|(key, verdict)| match verdict {
- Verdict::Accept(sig) => Some((key, *sig)),
+ Verdict::Accept { signature } => Some((key, *signature)),
Verdict::Reject => None,
})
}
@@ -715,7 +676,7 @@ impl Revision {
pub fn rejected(&self) -> impl Iterator<Item = Did> + '_ {
self.verdicts().filter_map(|(key, v)| match v {
- Verdict::Accept(_) => None,
+ Verdict::Accept { .. } => None,
Verdict::Reject => Some(key.into()),
})
}
@@ -739,7 +700,7 @@ impl Revision {
parent: Option<RevisionId>,
timestamp: Timestamp,
) -> Self {
- let verdicts = BTreeMap::from_iter([(*author.public_key(), Verdict::Accept(signature))]);
+ let verdicts = BTreeMap::from_iter([(*author.public_key(), Verdict::Accept { signature })]);
Self {
id,
@@ -770,7 +731,7 @@ impl Revision {
}
if self
.verdicts
- .insert(author, Verdict::Accept(signature))
+ .insert(author, Verdict::Accept { signature })
.is_some()
{
return Err(ApplyError::DuplicateVerdict);
@@ -852,14 +813,15 @@ impl<R: ReadRepository> store::Transaction<Identity, R> {
}
}
-pub struct IdentityMut<'a, R> {
+pub struct IdentityMut<'a, 'g, R, C> {
pub id: ObjectId,
identity: Identity,
- store: store::Store<'a, Identity, R>,
+ store: &'g mut Identities<'a, R>,
+ cache: &'g mut C,
}
-impl<'a, R> fmt::Debug for IdentityMut<'a, R> {
+impl<'a, 'g, R, C> fmt::Debug for IdentityMut<'a, 'g, R, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IdentityMut")
.field("id", &self.id)
@@ -868,16 +830,14 @@ impl<'a, R> fmt::Debug for IdentityMut<'a, R> {
}
}
-impl<'a, R> IdentityMut<'a, R>
+impl<'a, 'g, R, C> IdentityMut<'a, 'g, R, C>
where
R: WriteRepository + cob::Store,
+ C: cob::cache::Update<Identity>,
{
/// Reload the identity data from storage.
pub fn reload(&mut self) -> Result<(), store::Error> {
- self.identity = self
- .store
- .get(&self.id)?
- .ok_or_else(|| store::Error::NotFound(TYPENAME.clone(), self.id))?;
+ self.identity = self.store.get(&self.id)?;
Ok(())
}
@@ -895,7 +855,13 @@ where
let mut tx = Transaction::default();
operations(&mut tx)?;
- let (doc, commit) = tx.commit(message, self.id, &mut self.store, signer)?;
+ let (doc, commit) = tx.commit(message, self.id, &mut self.store.raw, signer)?;
+ self.cache
+ .update(&self.store.rid(), &self.id, &doc)
+ .map_err(|e| Error::CacheUpdate {
+ id: self.id,
+ err: e.into(),
+ })?;
self.identity = doc;
Ok(commit)
@@ -963,7 +929,7 @@ where
}
}
-impl<'a, R> Deref for IdentityMut<'a, R> {
+impl<'a, 'g, R, C> Deref for IdentityMut<'a, 'g, R, C> {
type Target = Identity;
fn deref(&self) -> &Self::Target {
@@ -971,6 +937,99 @@ impl<'a, R> Deref for IdentityMut<'a, R> {
}
}
+pub struct Identities<'a, R> {
+ raw: store::Store<'a, Identity, R>,
+}
+
+impl<'a, R> HasRepoId for Identities<'a, R>
+where
+ R: ReadRepository,
+{
+ fn rid(&self) -> RepoId {
+ self.raw.as_ref().id()
+ }
+}
+
+impl<'a, R> Identities<'a, R>
+where
+ R: ReadRepository + cob::Store,
+{
+ /// Open an issues store.
+ pub fn open(repository: &'a R) -> Result<Self, RepositoryError> {
+ let raw = store::Store::open(repository)?;
+
+ Ok(Self { raw })
+ }
+
+ pub fn initialize<'g, G, C>(
+ &'g mut self,
+ doc: &Doc<Verified>,
+ cache: &'g mut C,
+ signer: &G,
+ ) -> Result<IdentityMut<'a, 'g, R, C>, Error>
+ where
+ R: WriteRepository,
+ C: cob::cache::Update<Identity>,
+ G: Signer,
+ {
+ let (id, identity) = Transaction::<Identity, _>::initial(
+ "Initialize identity",
+ &mut self.raw,
+ signer,
+ |tx| tx.revision("Initial revision", "", doc, None, signer),
+ )?;
+ cache
+ .update(&self.rid(), &id, &identity)
+ .map_err(|e| Error::CacheUpdate { id, err: e.into() })?;
+
+ Ok(IdentityMut {
+ id,
+ identity,
+ store: self,
+ cache,
+ })
+ }
+
+ pub fn get(&self, object: &ObjectId) -> Result<Identity, store::Error> {
+ self.raw
+ .get(object)?
+ .ok_or_else(move || store::Error::NotFound(TYPENAME.clone(), *object))
+ }
+
+ /// Get a proposal mutably.
+ pub fn get_mut<'g, C>(
+ &'g mut self,
+ id: &ObjectId,
+ cache: &'g mut C,
+ ) -> Result<IdentityMut<'a, 'g, R, C>, store::Error> {
+ let obj = self.get(id)?;
+
+ Ok(IdentityMut {
+ id: *id,
+ identity: obj,
+ store: self,
+ cache,
+ })
+ }
+
+ pub fn load(&self) -> Result<Identity, RepositoryError> {
+ let oid = self.raw.as_ref().identity_root()?;
+ let oid = ObjectId::from(oid);
+
+ self.get(&oid).map_err(RepositoryError::from)
+ }
+
+ pub fn load_mut<'g, C>(
+ &'g mut self,
+ cache: &'g mut C,
+ ) -> Result<IdentityMut<'a, 'g, R, C>, RepositoryError> {
+ let oid = self.raw.as_ref().identity_root()?;
+ let oid = ObjectId::from(oid);
+
+ self.get_mut(&oid, cache).map_err(RepositoryError::from)
+ }
+}
+
mod lookup {
use super::*;
@@ -1037,7 +1096,9 @@ mod test {
let NodeWithRepo { node, repo } = NodeWithRepo::default();
let bob = MockSigner::default();
let signer = &node.signer;
- let mut identity = Identity::load_mut(&*repo).unwrap();
+ let mut identities = Identities::open(&*repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+ let mut identity = identities.load_mut(&mut cache).unwrap();
let mut doc = identity.doc().clone();
let title = "Identity update";
let description = "";
@@ -1093,7 +1154,9 @@ mod test {
let bob = MockSigner::default();
let eve = MockSigner::default();
let signer = &node.signer;
- let mut identity = Identity::load_mut(&*repo).unwrap();
+ let mut identities = Identities::open(&*repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+ let mut identity = identities.load_mut(&mut cache).unwrap();
let mut doc = identity.doc().clone();
let title = "Identity update";
let description = "";
@@ -1143,7 +1206,12 @@ mod test {
let alice = &network.alice;
let bob = &network.bob;
- let mut alice_identity = Identity::load_mut(&*alice.repo).unwrap();
+ let mut alice_identities = Identities::open(&*alice.repo).unwrap();
+ let mut bob_identities = Identities::open(&*bob.repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+
+ let mut alice_cache = cache;
+ let mut alice_identity = alice_identities.load_mut(&mut alice_cache).unwrap();
let mut alice_doc = alice_identity.doc().clone();
alice_doc.delegate(bob.signer.public_key());
@@ -1153,7 +1221,7 @@ mod test {
bob.repo.fetch(alice);
- let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
+ let mut bob_identity = bob_identities.load_mut(&mut cache).unwrap();
let bob_doc = bob_identity.doc().clone();
assert!(bob_doc.is_delegate(bob.signer.public_key()));
@@ -1195,7 +1263,11 @@ mod test {
let bob = &network.bob;
let eve = &network.eve;
- let mut alice_identity = Identity::load_mut(&*alice.repo).unwrap();
+ let mut alice_identities = Identities::open(&*alice.repo).unwrap();
+ let mut bob_identities = Identities::open(&*bob.repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+
+ let mut alice_identity = alice_identities.load_mut(&mut cache).unwrap();
let mut alice_doc = alice_identity.doc().clone();
alice_doc.delegate(bob.signer.public_key());
@@ -1214,7 +1286,7 @@ mod test {
assert!(alice_identity.revision(&a1).is_some());
assert_eq!(alice_identity.timeline, vec![a0, a1, a2, a3]);
- let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
+ let mut bob_identity = bob_identities.load_mut(&mut cache).unwrap();
let b1 = bob_identity.accept(&a2, &bob.signer).unwrap();
assert_eq!(bob_identity.timeline, vec![a0, a1, a2, b1]);
@@ -1234,7 +1306,12 @@ mod test {
let bob = &network.bob;
let eve = &network.eve;
- let mut alice_identity = Identity::load_mut(&*alice.repo).unwrap();
+ let mut alice_identities = Identities::open(&*alice.repo).unwrap();
+ let mut bob_identities = Identities::open(&*bob.repo).unwrap();
+ let mut eve_identities = Identities::open(&*eve.repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+
+ let mut alice_identity = alice_identities.load_mut(&mut cache).unwrap();
let mut alice_doc = alice_identity.doc().clone();
alice_doc.delegate(bob.signer.public_key());
@@ -1253,11 +1330,11 @@ mod test {
bob.repo.fetch(alice);
eve.repo.fetch(bob);
- let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
+ let mut bob_identity = bob_identities.load_mut(&mut cache).unwrap();
let b1 = bob_identity.accept(&a2, &bob.signer).unwrap();
assert_eq!(bob_identity.current, a2);
- let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
+ let mut eve_identity = eve_identities.load_mut(&mut cache).unwrap();
let mut eve_doc = eve_identity.doc().clone();
eve_doc.visibility = Visibility::private([eve.signer.public_key().into()]);
let e1 = eve_identity
@@ -1291,7 +1368,12 @@ mod test {
let bob = &network.bob;
let eve = &network.eve;
- let mut alice_identity = Identity::load_mut(&*alice.repo).unwrap();
+ let mut alice_identities = Identities::open(&*alice.repo).unwrap();
+ let mut bob_identities = Identities::open(&*bob.repo).unwrap();
+ let mut eve_identities = Identities::open(&*eve.repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+
+ let mut alice_identity = alice_identities.load_mut(&mut cache).unwrap();
let mut alice_doc = alice_identity.doc().clone();
alice_doc.delegate(bob.signer.public_key());
@@ -1311,11 +1393,11 @@ mod test {
eve.repo.fetch(bob);
// Bob accepts alice's revision.
- let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
+ let mut bob_identity = bob_identities.load_mut(&mut cache).unwrap();
let b1 = bob_identity.accept(&a2, &bob.signer).unwrap();
// Eve rejects the revision, not knowing.
- let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
+ let mut eve_identity = eve_identities.load_mut(&mut cache).unwrap();
let e1 = eve_identity.reject(a2, &eve.signer).unwrap();
assert!(eve_identity.revision(&a2).unwrap().is_active());
@@ -1360,7 +1442,13 @@ mod test {
let bob = &network.bob;
let eve = &network.eve;
- let mut alice_identity = Identity::load_mut(&*alice.repo).unwrap();
+ let mut alice_identities = Identities::open(&*alice.repo).unwrap();
+ let mut bob_identities = Identities::open(&*bob.repo).unwrap();
+ let mut eve_identities = Identities::open(&*eve.repo).unwrap();
+ let mut cache = cob::cache::NoCache;
+
+ let mut alice_cache = cache;
+ let mut alice_identity = alice_identities.load_mut(&mut alice_cache).unwrap();
let mut alice_doc = alice_identity.doc().clone();
alice.repo.fetch(bob);
@@ -1375,7 +1463,7 @@ mod test {
bob.repo.fetch(alice);
eve.repo.fetch(alice);
- let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
+ let mut bob_identity = bob_identities.load_mut(&mut cache).unwrap();
let mut bob_doc = bob_identity.doc().clone();
assert!(bob_doc.is_delegate(bob.signer.public_key()));
@@ -1396,7 +1484,7 @@ mod test {
eve.repo.fetch(bob);
// In the meantime, Eve does the same thing on her side.
- let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
+ let mut eve_identity = eve_identities.load_mut(&mut cache).unwrap();
let mut eve_doc = eve_identity.doc().clone();
eve_doc.visibility = Visibility::private([]);
let e1 = eve_identity
@@ -1424,6 +1512,7 @@ mod test {
let bob = MockSigner::new(&mut rng);
let eve = MockSigner::new(&mut rng);
+ let mut cache = cob::cache::InMemory::default();
let storage = Storage::open(tempdir.path().join("storage"), fixtures::user()).unwrap();
let (id, _, _, _) =
fixtures::project(tempdir.path().join("copy"), &storage, &alice).unwrap();
@@ -1433,7 +1522,8 @@ mod test {
rad::fork_remote(id, alice.public_key(), &eve, &storage).unwrap();
let repo = storage.repository(id).unwrap();
- let mut identity = Identity::load_mut(&repo).unwrap();
+ let mut identities = Identities::open(&repo).unwrap();
+ let mut identity = identities.load_mut(&mut cache).unwrap();
let mut doc = identity.doc().clone();
let prj = doc.project().unwrap();
@@ -1467,7 +1557,7 @@ mod test {
.unwrap();
identity.accept(&revision, &eve).unwrap();
- let identity: Identity = Identity::load(&repo).unwrap();
+ let identity: Identity = identities.load().unwrap();
let root = repo.identity_root().unwrap();
let doc = repo.identity_doc_at(revision).unwrap();
diff --git a/radicle/src/identity/doc.rs b/radicle/src/identity/doc.rs
index e1c1670ee..20d892593 100644
--- a/radicle/src/identity/doc.rs
+++ b/radicle/src/identity/doc.rs
@@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::canonical::formatter::CanonicalFormatter;
+use crate::cob;
use crate::cob::identity;
use crate::crypto;
use crate::crypto::{Signature, Unverified, Verified};
@@ -354,12 +355,18 @@ impl Doc<Verified> {
Doc::from_json(blob.content())?.verified()
}
- pub fn init<G: crypto::Signer>(
+ pub fn init<G, C>(
&self,
repo: &storage::git::Repository,
+ cache: &mut C,
signer: &G,
- ) -> Result<git::Oid, RepositoryError> {
- let cob = identity::Identity::initialize(self, repo, signer)?;
+ ) -> Result<git::Oid, RepositoryError>
+ where
+ C: cob::cache::Update<identity::Identity>,
+ G: crypto::Signer,
+ {
+ let mut store = identity::Identities::open(repo)?;
+ let cob = store.initialize(self, cache, signer)?;
let id_ref = git::refs::storage::id(signer.public_key());
let cob_ref = git::refs::storage::cob(
signer.public_key(),
@@ -462,6 +469,7 @@ mod test {
let (repo, _) = fixtures::repository(tempdir.path().join("working"));
let (id, _, _) = rad::init(
&repo,
+ &mut cob::cache::NoCache,
"heartwood",
"Radicle Heartwood Protocol & Stack",
git::refname!("master"),
@@ -509,6 +517,7 @@ mod test {
let delegate = MockSigner::from_seed([0xff; 32]);
let (rid, doc, _) = rad::init(
&working,
+ &mut cob::cache::NoCache,
"heartwood",
"Radicle Heartwood Protocol & Stack",
git::refname!("master"),
diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs
index dd0c4faee..bd2c35cf7 100644
--- a/radicle/src/profile.rs
+++ b/radicle/src/profile.rs
@@ -543,6 +543,40 @@ impl Home {
let store = cob::patch::Patches::open(repository)?;
Ok(cob::patch::Cache::open(store, db))
}
+
+ /// Return a read-only handle for the identities cache.
+ pub fn identities<'a, R>(
+ &self,
+ repository: &'a R,
+ ) -> Result<
+ cob::identity::Cache<cob::identity::Identities<'a, R>, cob::cache::StoreReader>,
+ Error,
+ >
+ where
+ R: ReadRepository + cob::Store,
+ {
+ let path = self.cobs().join(cob::cache::COBS_DB_FILE);
+ let db = cob::cache::Store::reader(path)?;
+ let store = cob::identity::Identities::open(repository)?;
+ Ok(cob::identity::Cache::reader(store, db))
+ }
+
+ /// Return a read-write handle for the identities cache.
+ pub fn identities_mut<'a, R>(
+ &self,
+ repository: &'a R,
+ ) -> Result<
+ cob::identity::Cache<cob::identity::Identities<'a, R>, cob::cache::StoreWriter>,
+ Error,
+ >
+ where
+ R: ReadRepository + cob::Store,
+ {
+ let path = self.cobs().join(cob::cache::COBS_DB_FILE);
+ let db = cob::cache::Store::open(path)?;
+ let store = cob::identity::Identities::open(repository)?;
+ Ok(cob::identity::Cache::open(store, db))
+ }
}
// Private methods.
diff --git a/radicle/src/rad.rs b/radicle/src/rad.rs
index 889a897b7..fd0e8e470 100644
--- a/radicle/src/rad.rs
+++ b/radicle/src/rad.rs
@@ -6,7 +6,7 @@ use std::str::FromStr;
use once_cell::sync::Lazy;
use thiserror::Error;
-use crate::cob::ObjectId;
+use crate::cob::{cache, ObjectId};
use crate::crypto::{Signer, Verified};
use crate::git;
use crate::identity::doc;
@@ -44,15 +44,21 @@ pub enum InitError {
}
/// Initialize a new radicle project from a git repository.
-pub fn init<G: Signer, S: WriteStorage>(
+pub fn init<G, S, C>(
repo: &git2::Repository,
+ cache: &mut C,
name: &str,
description: &str,
default_branch: BranchName,
visibility: Visibility,
signer: &G,
storage: S,
-) -> Result<(RepoId, identity::Doc<Verified>, SignedRefs<Verified>), InitError> {
+) -> Result<(RepoId, identity::Doc<Verified>, SignedRefs<Verified>), InitError>
+where
+ G: Signer,
+ S: WriteStorage,
+ C: cache::Update<identity::Identity>,
+{
// TODO: Better error when project id already exists in storage, but remote doesn't.
let pk = signer.public_key();
let delegate = identity::Did::from(*pk);
@@ -70,7 +76,7 @@ pub fn init<G: Signer, S: WriteStorage>(
)
})?;
let doc = identity::Doc::initial(proj, delegate, visibility).verified()?;
- let (project, _) = Repository::init(&doc, &storage, signer)?;
+ let (project, _) = Repository::init(&doc, &storage, cache, signer)?;
let url = git::Url::from(project.id);
match init_configure(repo, &project, pk, &default_branch, &url, signer) {
@@ -388,6 +394,7 @@ mod tests {
use pretty_assertions::assert_eq;
use radicle_crypto::test::signer::MockSigner;
+ use crate::cob;
use crate::git::{name::component, qualified};
use crate::identity::Did;
use crate::storage::git::transport;
@@ -409,6 +416,7 @@ mod tests {
let (repo, _) = fixtures::repository(tempdir.path().join("working"));
let (proj, _, refs) = init(
&repo,
+ &mut cob::cache::NoCache,
"acme",
"Acme's repo",
git::refname!("master"),
@@ -464,6 +472,7 @@ mod tests {
let (original, _) = fixtures::repository(tempdir.path().join("original"));
let (id, _, alice_refs) = init(
&original,
+ &mut cob::cache::NoCache,
"acme",
"Acme's repo",
git::refname!("master"),
@@ -500,6 +509,7 @@ mod tests {
let (original, _) = fixtures::repository(tempdir.path().join("original"));
let (id, _, _) = init(
&original,
+ &mut cob::cache::NoCache,
"acme",
"Acme's repo",
git::refname!("master"),
diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs
index a6308138a..f7f92df8e 100644
--- a/radicle/src/storage.rs
+++ b/radicle/src/storage.rs
@@ -14,7 +14,8 @@ use crypto::{PublicKey, Signer, Unverified, Verified};
pub use git::{Validation, Validations};
pub use radicle_git_ext::Oid;
-use crate::cob;
+use crate::cob::identity::Identities;
+use crate::cob::{self, identity};
use crate::collections::RandomMap;
use crate::git::ext as git_ext;
use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefStr, RefString};
@@ -103,6 +104,8 @@ pub enum RepositoryError {
Quorum(#[from] git::QuorumError),
#[error(transparent)]
Refs(#[from] refs::Error),
+ #[error(transparent)]
+ Identity(#[from] identity::Error),
}
/// Storage error.
@@ -467,7 +470,7 @@ pub trait ReadRepository: Sized + ValidateRepository {
where
Self: cob::Store,
{
- Identity::load(self)
+ Identities::open(self)?.load()
}
/// Compute the canonical `rad/id` of this repository.
diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs
index b4e048067..d4655d8db 100644
--- a/radicle/src/storage/git.rs
+++ b/radicle/src/storage/git.rs
@@ -11,6 +11,7 @@ use crypto::{Signer, Verified};
use once_cell::sync::Lazy;
use tempfile::TempDir;
+use crate::cob::{cache, identity};
use crate::crypto::Unverified;
use crate::git;
use crate::identity::doc::DocError;
@@ -438,16 +439,21 @@ impl Repository {
}
/// Create the repository's identity branch.
- pub fn init<G: Signer, S: WriteStorage>(
+ pub fn init<G, S, C>(
doc: &Doc<Verified>,
storage: &S,
+ cache: &mut C,
signer: &G,
- ) -> Result<(Self, git::Oid), RepositoryError> {
+ ) -> Result<(Self, git::Oid), RepositoryError>
+ where
+ G: Signer,
+ S: WriteStorage,
+ C: cache::Update<Identity>,
+ {
let (doc_oid, _) = doc.encode()?;
let id = RepoId::from(doc_oid);
let repo = Self::create(paths::repository(storage, &id), id, storage.info())?;
- let commit = doc.init(&repo, signer)?;
-
+ let commit = doc.init(&repo, cache, signer)?;
Ok((repo, commit))
}
@@ -800,7 +806,8 @@ impl ReadRepository for Repository {
// We've got an identity that goes back to the correct root.
if blob.id() == **self.id {
- let identity = Identity::get(&root.into(), self)?;
+ let store = identity::Identities::open(self)?;
+ let identity = store.get(&root.into())?;
return Ok(identity.head());
}
diff --git a/radicle/src/test/fixtures.rs b/radicle/src/test/fixtures.rs
index 3b1beef54..010fdc7be 100644
--- a/radicle/src/test/fixtures.rs
+++ b/radicle/src/test/fixtures.rs
@@ -2,7 +2,6 @@ use std::path::Path;
use std::str::FromStr;
use crate::crypto::{PublicKey, Signer, Verified};
-use crate::git;
use crate::identity::doc::Visibility;
use crate::identity::RepoId;
use crate::node::Alias;
@@ -10,6 +9,7 @@ use crate::rad;
use crate::storage::git::transport;
use crate::storage::git::Storage;
use crate::storage::refs::SignedRefs;
+use crate::{cob, git};
/// The birth of the radicle project, January 1st, 2018.
pub const RADICLE_EPOCH: i64 = 1514817556;
@@ -38,6 +38,7 @@ pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage
let (repo, _) = repository(path.join("workdir").join(name));
rad::init(
&repo,
+ &mut cob::cache::NoCache,
name,
desc,
git::refname!("master"),
@@ -61,6 +62,7 @@ pub fn project<P: AsRef<Path>, G: Signer>(
let (working, head) = repository(path);
let (id, _, refs) = rad::init(
&working,
+ &mut cob::cache::NoCache,
"acme",
"Acme's repository",
git::refname!("master"),
Exit code: 0
shell: 'cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny clippy::all cargo build --all-targets --workspace cargo doc --workspace cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 6e4c8612-2892-48ac-86fb-13451e355e72 -v /opt/radcis/ci.rad.levitte.org/cci/state/6e4c8612-2892-48ac-86fb-13451e355e72/s:/6e4c8612-2892-48ac-86fb-13451e355e72/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/6e4c8612-2892-48ac-86fb-13451e355e72/w:/6e4c8612-2892-48ac-86fb-13451e355e72/w -w /6e4c8612-2892-48ac-86fb-13451e355e72/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /6e4c8612-2892-48ac-86fb-13451e355e72/s/script.sh
+ cargo --version
info: syncing channel updates for '1.74-x86_64-unknown-linux-gnu'
info: latest update on 2023-12-07, rust version 1.74.1 (a28077b28 2023-12-04)
info: downloading component 'cargo'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-std'
info: installing component 'rustc'
cargo 1.74.1 (ecb9851af 2023-10-18)
+ rustc --version
rustc 1.74.1 (a28077b28 2023-12-04)
+ cargo fmt --check
error: 'cargo-fmt' is not installed for the toolchain '1.74-x86_64-unknown-linux-gnu'.
To install, run `rustup component add --toolchain 1.74-x86_64-unknown-linux-gnu rustfmt`
Exit code: 1
{
"response": "finished",
"result": "failure"
}