~radicle-link/dev

radicle-link: lnk identities list delegates v1 PROPOSED

Adding a subcommand to person/project to list the delegates of the
identity.

Published-as: https://github.com/FintanH/radicle-link/tree/patches/cli%2Flnk-identities-delegates%2Fv1
Published-as:
  urn: rad:git:hnrkxafojjsz4m55qxbwigh1z8sdt7mai81gy
  peer: hyytgji3qmjmq86ti4b3idgi1wjexowp9jfxmdosreu7qn4t77589e
  ref: patches/cli/lnk-identities-delegates/v1

Fintan Halpenny (2):
  lnk-identities: person/project delegates
  lnk-identities: remove placeholder review function

 cli/lnk-identities/src/cli/args.rs         | 26 ++++++++++++++++++++++
 cli/lnk-identities/src/cli/eval/person.rs  |  8 +++++++
 cli/lnk-identities/src/cli/eval/project.rs | 24 ++++++++++++++++++++
 cli/lnk-identities/src/person.rs           | 16 +++++++++----
 cli/lnk-identities/src/project.rs          | 20 +++++++++++++++++
 5 files changed, 90 insertions(+), 4 deletions(-)

-- 
2.31.1
#797373 nixos-latest.yml failed
radicle-link/patches/nixos-latest.yml: FAILED in 21m2s

[lnk identities list delegates][0] from [Fintan Halpenny][1]

[0]: https://lists.sr.ht/~radicle-link/dev/patches/33671
[1]: mailto:fintan.halpenny@gmail.com

✗ #797373 FAILED radicle-link/patches/nixos-latest.yml https://builds.sr.ht/~radicle-link/job/797373
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~radicle-link/dev/patches/33671/mbox | git am -3
Learn more about email & git

[PATCH radicle-link v1 1/2] lnk-identities: person/project delegates Export this patch

A feature lacking from the `lnk identities` CLI is the ability to list
delegates of an identity. This *could* be derived from the output of
`tracked`. A dedicated subcommand is more convenient for this specific
output and is easier to remember.

Add a `delegates` subcommand to `person` and `project` that outputs
the delegates of a specific, verified identity. In the case of a
project it outputs an object with `"direct"` and `"indirect"` keys --
corresponding to `PeerId`s and `Urn`s respectively. In the case of a
person it outputs a list of `PeerId`s.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
---
 cli/lnk-identities/src/cli/args.rs         | 26 ++++++++++++++++++++++
 cli/lnk-identities/src/cli/eval/person.rs  |  8 +++++++
 cli/lnk-identities/src/cli/eval/project.rs | 24 ++++++++++++++++++++
 cli/lnk-identities/src/person.rs           | 12 ++++++++++
 cli/lnk-identities/src/project.rs          | 20 +++++++++++++++++
 5 files changed, 90 insertions(+)

diff --git a/cli/lnk-identities/src/cli/args.rs b/cli/lnk-identities/src/cli/args.rs
index d790f53e..67a86dd6 100644
--- a/cli/lnk-identities/src/cli/args.rs
+++ b/cli/lnk-identities/src/cli/args.rs
@@ -110,6 +110,7 @@ pub mod project {
        List(List),
        Update(Update),
        Checkout(Checkout),
        Delegates(Delegates),
        Diff(Diff),
        Accept(Accept),
        Tracked(Tracked),
@@ -267,6 +268,18 @@ pub mod project {
        pub peer: Option<PeerId>,
    }

    /// list the delegates of the Radicle project
    #[derive(Debug, Parser)]
    pub struct Delegates {
        /// the Radicle URN of the project
        #[clap(long)]
        pub urn: Urn,

        /// the peer's version of the project
        #[clap(long)]
        pub peer: Option<PeerId>,
    }

    /// review the difference between the local Radicle project and a peer's
    #[derive(Debug, Parser)]
    pub struct Diff {
@@ -322,6 +335,7 @@ pub mod person {
        List(List),
        Update(Update),
        Checkout(Checkout),
        Delegates(Delegates),
        Diff(Diff),
        Accept(Accept),
        Tracked(Tracked),
@@ -455,6 +469,18 @@ pub mod person {
        pub peer: Option<PeerId>,
    }

    /// list the delegates of the Radicle person
    #[derive(Debug, Parser)]
    pub struct Delegates {
        /// the Radicle URN of the person
        #[clap(long)]
        pub urn: Urn,

        /// the peer's version of the person
        #[clap(long)]
        pub peer: Option<PeerId>,
    }

    /// review the difference between the local Radicle person and a peer's
    #[derive(Debug, Parser)]
    pub struct Diff {
diff --git a/cli/lnk-identities/src/cli/eval/person.rs b/cli/lnk-identities/src/cli/eval/person.rs
index 1bc39b00..43046f59 100644
--- a/cli/lnk-identities/src/cli/eval/person.rs
+++ b/cli/lnk-identities/src/cli/eval/person.rs
@@ -46,6 +46,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu
            eval_accept(profile, sock, urn, peer, force)?
        },
        Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?,
        Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?,
    }

    Ok(())
@@ -159,6 +160,13 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> {
    Ok(())
}

fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> {
    let storage = storage::read_only(profile)?;
    let delegates = person::delegates(&storage, &urn, peer)?;
    println!("{}", serde_json::to_string(&delegates)?);
    Ok(())
}

fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> {
    let storage = storage::read_only(profile)?;
    diff(&storage, urn, peer)?;
diff --git a/cli/lnk-identities/src/cli/eval/project.rs b/cli/lnk-identities/src/cli/eval/project.rs
index b36d7b17..2a4e5a65 100644
--- a/cli/lnk-identities/src/cli/eval/project.rs
+++ b/cli/lnk-identities/src/cli/eval/project.rs
@@ -20,6 +20,7 @@ use librad::{
    },
    profile::Profile,
    PeerId,
    PublicKey,
};
use lnk_clib::{
    keys::ssh::SshAuthSock,
@@ -48,6 +49,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu
            eval_accept(profile, sock, urn, peer, force)?
        },
        Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?,
        Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?,
    }

    Ok(())
@@ -164,6 +166,28 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> {
    Ok(())
}

fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> {
    #[derive(serde::Serialize)]
    struct Delegates {
        indirect: Vec<Urn>,
        direct: Vec<PublicKey>,
    }

    let storage = storage::read_only(profile)?;
    let mut delegates = Delegates {
        indirect: Vec::new(),
        direct: Vec::new(),
    };
    for del in project::delegates(&storage, &urn, peer)? {
        match del {
            either::Either::Left(key) => delegates.direct.push(key),
            either::Either::Right(urn) => delegates.indirect.push(urn),
        }
    }
    println!("{}", serde_json::to_string(&delegates)?);
    Ok(())
}

fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> {
    let storage = storage::read_only(profile)?;
    diff(&storage, urn, peer)?;
diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs
index 7e409018..f00d58a1 100644
--- a/cli/lnk-identities/src/person.rs
+++ b/cli/lnk-identities/src/person.rs
@@ -217,3 +217,15 @@ where
    let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    Ok(identities::relations::tracked(storage, urn)?)
}

pub fn delegates<S, P>(storage: &S, urn: &Urn, peer: P) -> Result<Vec<PublicKey>, Error>
where
    S: AsRef<ReadOnly>,
    P: Into<Option<PeerId>>,
{
    let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap();
    let project = identities::person::verify(storage, &urn)?
        .ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    let delegations = project.delegations();
    Ok(delegations.iter().copied().collect())
}
diff --git a/cli/lnk-identities/src/project.rs b/cli/lnk-identities/src/project.rs
index 2469d65a..dcff2cd5 100644
--- a/cli/lnk-identities/src/project.rs
+++ b/cli/lnk-identities/src/project.rs
@@ -25,6 +25,7 @@ use librad::{
    },
    paths::Paths,
    PeerId,
    PublicKey,
};

use crate::{
@@ -282,3 +283,22 @@ where
    let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    Ok(identities::relations::tracked(storage, urn)?)
}

pub fn delegates<S, P>(
    storage: &S,
    urn: &Urn,
    peer: P,
) -> Result<Vec<Either<PublicKey, Urn>>, Error>
where
    S: AsRef<ReadOnly>,
    P: Into<Option<PeerId>>,
{
    let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap();
    let project = identities::project::verify(storage, &urn)?
        .ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    let delegations = project.delegations();
    Ok(delegations
        .iter()
        .map(|del| del.map_left(|key| *key).map_right(|id| id.urn()))
        .collect())
}
-- 
2.31.1

[PATCH radicle-link v2 1/2] lnk-identities: person/project delegates Export this patch

A feature lacking from the `lnk identities` CLI is the ability to list
delegates of an identity. This *could* be derived from the output of
`tracked`. A dedicated subcommand is more convenient for this specific
output and is easier to remember.

Add a `delegates` subcommand to `person` and `project` that outputs
the delegates of the specified, verified identity. The output format
is a JSON list, each entry containing a JSON object:

    [ { "type": <type>, "payload": <payload> } ]

Currently, the type can be either "direct" or "indirect" -- the
payloads being a public key or an URN, respectively. This makes the
output of both commands uniform while also leaving room for new types
if they are ever added.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
---
 cli/lnk-identities/src/cli.rs              |  1 +
 cli/lnk-identities/src/cli/args.rs         | 26 +++++++++++++++
 cli/lnk-identities/src/cli/eval/person.rs  | 13 ++++++++
 cli/lnk-identities/src/cli/eval/project.rs | 20 ++++++++++-
 cli/lnk-identities/src/cli/fmt.rs          |  7 ++++
 cli/lnk-identities/src/cli/fmt/delegate.rs | 39 ++++++++++++++++++++++
 cli/lnk-identities/src/person.rs           | 12 +++++++
 cli/lnk-identities/src/project.rs          | 20 +++++++++++
 8 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 cli/lnk-identities/src/cli/fmt.rs
 create mode 100644 cli/lnk-identities/src/cli/fmt/delegate.rs

diff --git a/cli/lnk-identities/src/cli.rs b/cli/lnk-identities/src/cli.rs
index c4a9afc4..acbea1d1 100644
--- a/cli/lnk-identities/src/cli.rs
+++ b/cli/lnk-identities/src/cli.rs
@@ -8,3 +8,4 @@ mod main;
pub use main::main;

pub mod eval;
mod fmt;
diff --git a/cli/lnk-identities/src/cli/args.rs b/cli/lnk-identities/src/cli/args.rs
index d790f53e..67a86dd6 100644
--- a/cli/lnk-identities/src/cli/args.rs
+++ b/cli/lnk-identities/src/cli/args.rs
@@ -110,6 +110,7 @@ pub mod project {
        List(List),
        Update(Update),
        Checkout(Checkout),
        Delegates(Delegates),
        Diff(Diff),
        Accept(Accept),
        Tracked(Tracked),
@@ -267,6 +268,18 @@ pub mod project {
        pub peer: Option<PeerId>,
    }

    /// list the delegates of the Radicle project
    #[derive(Debug, Parser)]
    pub struct Delegates {
        /// the Radicle URN of the project
        #[clap(long)]
        pub urn: Urn,

        /// the peer's version of the project
        #[clap(long)]
        pub peer: Option<PeerId>,
    }

    /// review the difference between the local Radicle project and a peer's
    #[derive(Debug, Parser)]
    pub struct Diff {
@@ -322,6 +335,7 @@ pub mod person {
        List(List),
        Update(Update),
        Checkout(Checkout),
        Delegates(Delegates),
        Diff(Diff),
        Accept(Accept),
        Tracked(Tracked),
@@ -455,6 +469,18 @@ pub mod person {
        pub peer: Option<PeerId>,
    }

    /// list the delegates of the Radicle person
    #[derive(Debug, Parser)]
    pub struct Delegates {
        /// the Radicle URN of the person
        #[clap(long)]
        pub urn: Urn,

        /// the peer's version of the person
        #[clap(long)]
        pub peer: Option<PeerId>,
    }

    /// review the difference between the local Radicle person and a peer's
    #[derive(Debug, Parser)]
    pub struct Diff {
diff --git a/cli/lnk-identities/src/cli/eval/person.rs b/cli/lnk-identities/src/cli/eval/person.rs
index 1bc39b00..fde873ac 100644
--- a/cli/lnk-identities/src/cli/eval/person.rs
+++ b/cli/lnk-identities/src/cli/eval/person.rs
@@ -46,6 +46,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu
            eval_accept(profile, sock, urn, peer, force)?
        },
        Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?,
        Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?,
    }

    Ok(())
@@ -159,6 +160,18 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> {
    Ok(())
}

fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> {
    use crate::cli::fmt::Delegate;

    let storage = storage::read_only(profile)?;
    let delegates = person::delegates(&storage, &urn, peer)?
        .into_iter()
        .map(Delegate::direct)
        .collect::<Vec<_>>();
    println!("{}", serde_json::to_string(&delegates)?);
    Ok(())
}

fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> {
    let storage = storage::read_only(profile)?;
    diff(&storage, urn, peer)?;
diff --git a/cli/lnk-identities/src/cli/eval/project.rs b/cli/lnk-identities/src/cli/eval/project.rs
index b36d7b17..9eb76a61 100644
--- a/cli/lnk-identities/src/cli/eval/project.rs
+++ b/cli/lnk-identities/src/cli/eval/project.rs
@@ -26,7 +26,12 @@ use lnk_clib::{
    storage::{self, ssh},
};

use crate::{cli::args::project::*, display, project, working_copy_dir::WorkingCopyDir};
use crate::{
    cli::{self, args::project::*},
    display,
    project,
    working_copy_dir::WorkingCopyDir,
};

pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Result<()> {
    match opts {
@@ -48,6 +53,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu
            eval_accept(profile, sock, urn, peer, force)?
        },
        Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?,
        Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?,
    }

    Ok(())
@@ -164,6 +170,18 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> {
    Ok(())
}

fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> {
    use cli::fmt::Delegate;

    let storage = storage::read_only(profile)?;
    let delegates = project::delegates(&storage, &urn, peer)?
        .into_iter()
        .map(|del| del.either(Delegate::direct, Delegate::indirect))
        .collect::<Vec<_>>();
    println!("{}", serde_json::to_string(&delegates)?);
    Ok(())
}

fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> {
    let storage = storage::read_only(profile)?;
    diff(&storage, urn, peer)?;
diff --git a/cli/lnk-identities/src/cli/fmt.rs b/cli/lnk-identities/src/cli/fmt.rs
new file mode 100644
index 00000000..fad6a961
--- /dev/null
+++ b/cli/lnk-identities/src/cli/fmt.rs
@@ -0,0 +1,7 @@
// Copyright © 2022 The Radicle Link Contributors
//
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.

pub mod delegate;
pub use delegate::Delegate;
diff --git a/cli/lnk-identities/src/cli/fmt/delegate.rs b/cli/lnk-identities/src/cli/fmt/delegate.rs
new file mode 100644
index 00000000..93cd48a6
--- /dev/null
+++ b/cli/lnk-identities/src/cli/fmt/delegate.rs
@@ -0,0 +1,39 @@
// Copyright © 2022 The Radicle Link Contributors
//
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.

use librad::{git::Urn, PublicKey};
use serde::Serialize;

#[non_exhaustive]
#[derive(Clone, Debug, Serialize)]
#[serde(tag = "type", content = "payload", rename_all = "camelCase")]
pub enum Delegate {
    #[serde(rename = "direct")]
    Key(PublicKey),
    #[serde(rename = "indirect")]
    Urn(Urn),
}

impl From<PublicKey> for Delegate {
    fn from(key: PublicKey) -> Self {
        Self::Key(key)
    }
}

impl From<Urn> for Delegate {
    fn from(urn: Urn) -> Self {
        Self::Urn(urn)
    }
}

impl Delegate {
    pub fn direct(key: PublicKey) -> Self {
        key.into()
    }

    pub fn indirect(urn: Urn) -> Self {
        urn.into()
    }
}
diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs
index 7e409018..f00d58a1 100644
--- a/cli/lnk-identities/src/person.rs
+++ b/cli/lnk-identities/src/person.rs
@@ -217,3 +217,15 @@ where
    let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    Ok(identities::relations::tracked(storage, urn)?)
}

pub fn delegates<S, P>(storage: &S, urn: &Urn, peer: P) -> Result<Vec<PublicKey>, Error>
where
    S: AsRef<ReadOnly>,
    P: Into<Option<PeerId>>,
{
    let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap();
    let project = identities::person::verify(storage, &urn)?
        .ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    let delegations = project.delegations();
    Ok(delegations.iter().copied().collect())
}
diff --git a/cli/lnk-identities/src/project.rs b/cli/lnk-identities/src/project.rs
index 2469d65a..dcff2cd5 100644
--- a/cli/lnk-identities/src/project.rs
+++ b/cli/lnk-identities/src/project.rs
@@ -25,6 +25,7 @@ use librad::{
    },
    paths::Paths,
    PeerId,
    PublicKey,
};

use crate::{
@@ -282,3 +283,22 @@ where
    let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    Ok(identities::relations::tracked(storage, urn)?)
}

pub fn delegates<S, P>(
    storage: &S,
    urn: &Urn,
    peer: P,
) -> Result<Vec<Either<PublicKey, Urn>>, Error>
where
    S: AsRef<ReadOnly>,
    P: Into<Option<PeerId>>,
{
    let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap();
    let project = identities::project::verify(storage, &urn)?
        .ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
    let delegations = project.delegations();
    Ok(delegations
        .iter()
        .map(|del| del.map_left(|key| *key).map_right(|id| id.urn()))
        .collect())
}
-- 
2.31.1

[PATCH radicle-link v1 2/2] lnk-identities: remove placeholder review function Export this patch

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
---
 cli/lnk-identities/src/person.rs | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs
index f00d58a1..c4ae5a5b 100644
--- a/cli/lnk-identities/src/person.rs
+++ b/cli/lnk-identities/src/person.rs
@@ -205,10 +205,6 @@ where
    Ok(repo)
}

pub fn review() {
    todo!()
}

pub fn tracked<S>(storage: &S, urn: &Urn) -> Result<relations::Tracked, Error>
where
    S: AsRef<ReadOnly>,
-- 
2.31.1
radicle-link/patches/nixos-latest.yml: FAILED in 21m2s

[lnk identities list delegates][0] from [Fintan Halpenny][1]

[0]: https://lists.sr.ht/~radicle-link/dev/patches/33671
[1]: mailto:fintan.halpenny@gmail.com

✗ #797373 FAILED radicle-link/patches/nixos-latest.yml https://builds.sr.ht/~radicle-link/job/797373

[PATCH radicle-link v2 2/2] lnk-identities: remove placeholder review function Export this patch

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
---
 cli/lnk-identities/src/person.rs | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs
index f00d58a1..c4ae5a5b 100644
--- a/cli/lnk-identities/src/person.rs
+++ b/cli/lnk-identities/src/person.rs
@@ -205,10 +205,6 @@ where
    Ok(repo)
}

pub fn review() {
    todo!()
}

pub fn tracked<S>(storage: &S, urn: &Urn) -> Result<relations::Tracked, Error>
where
    S: AsRef<ReadOnly>,
-- 
2.31.1