cli: Implement `rad cob show` with new meaning
This commit is contained in:
parent
6147a8a2fb
commit
6d11ea17c8
|
|
@ -0,0 +1,183 @@
|
|||
Well known COBs, for example issues and patches, can not only be showed via porcelain commands such as
|
||||
`rad issue show` and `rad patch show`, but also using the plumbing command `rad cob show`.
|
||||
While humans likely prefer to use `rad issue show` and `rad patch show`, this command makes integration
|
||||
with other software components easier.
|
||||
|
||||
First create an issue.
|
||||
|
||||
```
|
||||
$ rad issue open --title "spice harvester broken" --description "Fremen have attacked, maybe we went too far?" --no-announce
|
||||
╭──────────────────────────────────────────────────╮
|
||||
│ Title spice harvester broken │
|
||||
│ Issue 9de644864342d7a505eb8d58d1ef20e5bb05de2e │
|
||||
│ Author z6MknSL…StBU8Vi (you) │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ Fremen have attacked, maybe we went too far? │
|
||||
╰──────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
The issue is now listed under our project.
|
||||
|
||||
```
|
||||
$ rad issue list
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Labels Assignees Opened │
|
||||
├──────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 9de6448 spice harvester broken z6MknSL…StBU8Vi (you) now │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Let's create a patch, too.
|
||||
|
||||
```
|
||||
$ git checkout -b spice-harvester-broken
|
||||
$ touch TREATY.md
|
||||
$ git add TREATY.md
|
||||
$ git commit -v -m "Start drafting peace treaty"
|
||||
[spice-harvester-broken 575ed68] Start drafting peace treaty
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 TREATY.md
|
||||
$ git push rad -o patch.message="Start drafting peace treaty" -o patch.message="See details." HEAD:refs/patches
|
||||
```
|
||||
|
||||
Patch can be listed.
|
||||
|
||||
```
|
||||
$ rad patch
|
||||
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Reviews Head + - Updated │
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● d1f7f86 Start drafting peace treaty z6MknSL…StBU8Vi (you) - 575ed68 +0 -0 now │
|
||||
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Both issue and patch COBs can be listed.
|
||||
|
||||
```
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue
|
||||
9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch
|
||||
d1f7f869fde9fac19c1779c4c2e77e8361333f91
|
||||
```
|
||||
|
||||
We can show the issue COB.
|
||||
|
||||
```
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object 9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
{
|
||||
"assignees": [],
|
||||
"title": "spice harvester broken",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"labels": [],
|
||||
"thread": {
|
||||
"comments": {
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e": {
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"reactions": [],
|
||||
"resolved": false,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"edits": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"embeds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can show the patch COB too.
|
||||
|
||||
```
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch --object d1f7f869fde9fac19c1779c4c2e77e8361333f91
|
||||
{
|
||||
"title": "Start drafting peace treaty",
|
||||
"author": {
|
||||
"id": "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"target": "delegates",
|
||||
"labels": [],
|
||||
"merges": {},
|
||||
"revisions": {
|
||||
"d1f7f869fde9fac19c1779c4c2e77e8361333f91": {
|
||||
"author": {
|
||||
"id": "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"description": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "See details.",
|
||||
"embeds": []
|
||||
}
|
||||
],
|
||||
"base": "f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354",
|
||||
"oid": "575ed68c716d6aae81ea6b718fd9ac66a8eae532",
|
||||
"discussion": {
|
||||
"comments": {},
|
||||
"timeline": []
|
||||
},
|
||||
"reviews": {},
|
||||
"timestamp": 1671125284000,
|
||||
"resolves": [],
|
||||
"reactions": []
|
||||
}
|
||||
},
|
||||
"assignees": [],
|
||||
"timeline": [
|
||||
"d1f7f869fde9fac19c1779c4c2e77e8361333f91"
|
||||
],
|
||||
"reviews": {}
|
||||
}
|
||||
```
|
||||
|
||||
Finally let's update the issue and see the output of `rad cob show` also changes.
|
||||
|
||||
```
|
||||
$ rad issue label 9de6448 --add bug --no-announce
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object 9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
{
|
||||
"assignees": [],
|
||||
"title": "spice harvester broken",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"labels": [
|
||||
"bug"
|
||||
],
|
||||
"thread": {
|
||||
"comments": {
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e": {
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"reactions": [],
|
||||
"resolved": false,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"edits": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"embeds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -6,6 +6,9 @@ use chrono::prelude::*;
|
|||
use nonempty::NonEmpty;
|
||||
use radicle::cob;
|
||||
use radicle::cob::Op;
|
||||
use radicle::identity::Identity;
|
||||
use radicle::issue::cache::Issues;
|
||||
use radicle::patch::cache::Patches;
|
||||
use radicle::prelude::RepoId;
|
||||
use radicle::storage::ReadStorage;
|
||||
use radicle_cob::object::collaboration::list;
|
||||
|
|
@ -24,7 +27,8 @@ Usage
|
|||
|
||||
rad cob <command> [<option>...]
|
||||
rad cob list --repo <rid> --type <typename>
|
||||
rad cob log --repo <rid> --type <typename> --object <oid> [<option>..]
|
||||
rad cob log --repo <rid> --type <typename> --object <oid> [<option>...]
|
||||
rad cob show --repo <rid> --type <typename> --object <oid> [<option>...]
|
||||
|
||||
Commands
|
||||
|
||||
|
|
@ -35,6 +39,10 @@ Log options
|
|||
|
||||
--format (pretty | json) Desired output format (default: pretty)
|
||||
|
||||
Show options
|
||||
|
||||
--format json Desired output format (default: json)
|
||||
|
||||
Other options
|
||||
|
||||
--help Print help
|
||||
|
|
@ -45,11 +53,13 @@ Other options
|
|||
enum OperationName {
|
||||
List,
|
||||
Log,
|
||||
Show,
|
||||
}
|
||||
|
||||
enum Operation {
|
||||
List,
|
||||
Log(Rev),
|
||||
Show(Rev),
|
||||
}
|
||||
|
||||
enum Format {
|
||||
|
|
@ -80,6 +90,7 @@ impl Args for Options {
|
|||
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
||||
"list" => op = Some(OperationName::List),
|
||||
"log" => op = Some(OperationName::Log),
|
||||
"show" => op = Some(OperationName::Show),
|
||||
unknown => anyhow::bail!("unknown operation '{unknown}'"),
|
||||
},
|
||||
Long("type") | Short('t') => {
|
||||
|
|
@ -104,10 +115,12 @@ impl Args for Options {
|
|||
Long("help") | Short('h') => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
Long("format") if op == Some(OperationName::Log) => {
|
||||
Long("format")
|
||||
if op == Some(OperationName::Log) || op == Some(OperationName::Show) =>
|
||||
{
|
||||
let v: String = term::args::string(&parser.value()?);
|
||||
match v.as_ref() {
|
||||
"pretty" => format = Some(Format::Pretty),
|
||||
"pretty" if op == Some(OperationName::Log) => format = Some(Format::Pretty),
|
||||
"json" => format = Some(Format::Json),
|
||||
unknown => anyhow::bail!("unknown format '{unknown}'"),
|
||||
}
|
||||
|
|
@ -122,7 +135,10 @@ impl Args for Options {
|
|||
match op.ok_or_else(|| anyhow!("a command must be specified"))? {
|
||||
OperationName::List => Operation::List,
|
||||
OperationName::Log => Operation::Log(oid.ok_or_else(|| {
|
||||
anyhow!("an object id must be specified with `--object")
|
||||
anyhow!("an object id must be specified with `--object`")
|
||||
})?),
|
||||
OperationName::Show => Operation::Show(oid.ok_or_else(|| {
|
||||
anyhow!("an object id must be specified with `--object`")
|
||||
})?),
|
||||
}
|
||||
},
|
||||
|
|
@ -160,6 +176,31 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Operation::Show(oid) => {
|
||||
let oid = &oid.resolve(&repo.backend)?;
|
||||
|
||||
if options.type_name == cob::patch::TYPENAME.clone() {
|
||||
let patches = profile.patches(&repo)?;
|
||||
let Some(patch) = patches.get(oid)? else {
|
||||
anyhow::bail!(cob::store::Error::NotFound(options.type_name, *oid))
|
||||
};
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &patch)?
|
||||
} else if options.type_name == cob::issue::TYPENAME.clone() {
|
||||
let issues = profile.issues(&repo)?;
|
||||
let Some(issue) = issues.get(oid)? else {
|
||||
anyhow::bail!(cob::store::Error::NotFound(options.type_name, *oid))
|
||||
};
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &issue)?
|
||||
} else if options.type_name == cob::identity::TYPENAME.clone() {
|
||||
let Some(cob) = cob::get::<Identity, _>(&repo, &options.type_name, oid)? else {
|
||||
anyhow::bail!(cob::store::Error::NotFound(options.type_name, *oid))
|
||||
};
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &cob.object)?
|
||||
} else {
|
||||
anyhow::bail!("the type name '{}' is unknown", options.type_name);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ fn rad_issue() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn rad_cob() {
|
||||
fn rad_cob_log() {
|
||||
let mut environment = Environment::new();
|
||||
let profile = environment.profile(config::profile("alice"));
|
||||
let home = &profile.home;
|
||||
|
|
@ -169,6 +169,20 @@ fn rad_cob() {
|
|||
test("examples/rad-cob-log.md", &working, Some(home), []).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_cob_show() {
|
||||
let mut environment = Environment::new();
|
||||
let profile = environment.profile(config::profile("alice"));
|
||||
let home = &profile.home;
|
||||
let working = environment.tmp().join("working");
|
||||
|
||||
// Setup a test repository.
|
||||
fixtures::repository(&working);
|
||||
|
||||
test("examples/rad-init.md", &working, Some(home), []).unwrap();
|
||||
test("examples/rad-cob-show.md", &working, Some(home), []).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_init() {
|
||||
let mut environment = Environment::new();
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ pub enum Error {
|
|||
}
|
||||
|
||||
/// An evolving identity document.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Identity {
|
||||
/// The canonical identifier for this identity.
|
||||
/// This is the object id of the initial document blob.
|
||||
|
|
@ -612,7 +613,7 @@ impl<R: ReadRepository> cob::Evaluate<R> for Identity {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub enum Verdict {
|
||||
/// An accepting verdict must supply the [`Signature`] over the
|
||||
/// new proposed [`Doc`].
|
||||
|
|
@ -656,7 +657,7 @@ 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)]
|
||||
pub struct Revision {
|
||||
/// The id of this revision. Points to a commit.
|
||||
pub id: RevisionId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue