cli: Move label commands to `issue` and `patch`
It is more natural for the label and unlabel commands to exist as subcommands of the object they are labelling, i.e. issues and patches. Remove the `rad label` and `rad unlabel` commands, moving their functionality to the `rad issue` and `rad patch` commands. The `rad-issue.md` and `rad-patch.md` tests are modified to include a label test. The `rad patch show` command did not support showing labels as part of its output. This was also included to confirm the test above was working. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
511165bfc5
commit
1af9480b08
|
|
@ -24,6 +24,8 @@ rad-patch - Manage radicle patches.
|
||||||
*rad patch* _edit_ <patch-id> [<option>...] +
|
*rad patch* _edit_ <patch-id> [<option>...] +
|
||||||
*rad patch* _set_ <patch-id> [<option>...] +
|
*rad patch* _set_ <patch-id> [<option>...] +
|
||||||
*rad patch* _comment_ <revision-id> [<option>...] +
|
*rad patch* _comment_ <revision-id> [<option>...] +
|
||||||
|
*rad patch* _label_ <patch-id> --label <label> [<option>...] +
|
||||||
|
*rad patch* _unlabel_ <patch-id> --label <label> [<option>...] +
|
||||||
|
|
||||||
== Description
|
== Description
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,24 +43,27 @@ $ rad issue show d185ee1
|
||||||
|
|
||||||
Great! Now we've documented the issue for ourselves and others.
|
Great! Now we've documented the issue for ourselves and others.
|
||||||
|
|
||||||
Just like with other project management systems, the issue can be assigned to
|
Just like with other project management systems, the issue can be
|
||||||
others to work on. This is to ensure work is not duplicated.
|
labeled and assigned to others to work on. This is to ensure work is
|
||||||
|
not duplicated.
|
||||||
|
|
||||||
Let's assign ourselves to this one.
|
Let's assign ourselves to this one, this is to ensure work is not
|
||||||
|
duplicated. While we're at it, let's add a label.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad assign d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --to did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
$ rad assign d185ee16a00bac874c0bcbc2a8ad80fdce5e1e6 --to did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
$ rad issue label d185ee1 -l good-first-issue
|
||||||
```
|
```
|
||||||
|
|
||||||
It will now show in the list of issues assigned to us.
|
It will now show in the list of issues assigned to us, along with the new label.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad issue list --assigned
|
$ rad issue list --assigned
|
||||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ ● ID Title Author Labels Assignees Opened │
|
│ ● ID Title Author Labels Assignees Opened │
|
||||||
├─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||||
│ ● d185ee1 flux capacitor underpowered z6MknSL…StBU8Vi (you) z6MknSL…StBU8Vi now │
|
│ ● d185ee1 flux capacitor underpowered z6MknSL…StBU8Vi (you) good-first-issue z6MknSL…StBU8Vi now │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: this can always be undone with the `unassign` subcommand.
|
Note: this can always be undone with the `unassign` subcommand.
|
||||||
|
|
@ -76,9 +79,9 @@ It will help whoever works on a fix.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad issue comment d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --message 'The flux capacitor needs 1.21 Gigawatts' -q
|
$ rad issue comment d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --message 'The flux capacitor needs 1.21 Gigawatts' -q
|
||||||
14019611935fd1c66458111a5b49f0a7350c226f
|
80ef590710edb64dfa57e8e940d6e4d0b0ae4217
|
||||||
$ rad issue comment d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --reply-to 14019611935fd1c66458111a5b49f0a7350c226f --message 'More power!' -q
|
$ rad issue comment d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --reply-to 80ef590710edb64dfa57e8e940d6e4d0b0ae4217 --message 'More power!' -q
|
||||||
e342e47b7dd93451d47c806a0faeb0b5e957da2c
|
91009820ca0996d93b9afd5739a4d2158a2ec898
|
||||||
```
|
```
|
||||||
|
|
||||||
We can see our comments by showing the issue:
|
We can see our comments by showing the issue:
|
||||||
|
|
@ -89,14 +92,15 @@ $ rad issue show d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61
|
||||||
│ Title flux capacitor underpowered │
|
│ Title flux capacitor underpowered │
|
||||||
│ Issue d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 │
|
│ Issue d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 │
|
||||||
│ Author z6MknSL…StBU8Vi (you) │
|
│ Author z6MknSL…StBU8Vi (you) │
|
||||||
|
│ Labels good-first-issue │
|
||||||
│ Status open │
|
│ Status open │
|
||||||
│ │
|
│ │
|
||||||
│ Flux capacitor power requirements exceed current supply │
|
│ Flux capacitor power requirements exceed current supply │
|
||||||
├─────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────┤
|
||||||
│ z6MknSL…StBU8Vi (you) now 1401961 │
|
│ z6MknSL…StBU8Vi (you) now 80ef590 │
|
||||||
│ The flux capacitor needs 1.21 Gigawatts │
|
│ The flux capacitor needs 1.21 Gigawatts │
|
||||||
├─────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────┤
|
||||||
│ z6MknSL…StBU8Vi (you) now e342e47 │
|
│ z6MknSL…StBU8Vi (you) now 9100982 │
|
||||||
│ More power! │
|
│ More power! │
|
||||||
╰─────────────────────────────────────────────────────────╯
|
╰─────────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
Labeling an issue is easy, let's add the `bug` and `good-first-issue` labels to
|
|
||||||
some issue:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rad label d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 bug good-first-issue
|
|
||||||
```
|
|
||||||
|
|
||||||
We can now show the issue to check whether those labels were added:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rad issue show d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --format header
|
|
||||||
╭─────────────────────────────────────────────────────────╮
|
|
||||||
│ Title flux capacitor underpowered │
|
|
||||||
│ Issue d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 │
|
|
||||||
│ Author z6MknSL…StBU8Vi (you) │
|
|
||||||
│ Labels bug, good-first-issue │
|
|
||||||
│ Status open │
|
|
||||||
│ │
|
|
||||||
│ Flux capacitor power requirements exceed current supply │
|
|
||||||
╰─────────────────────────────────────────────────────────╯
|
|
||||||
```
|
|
||||||
|
|
||||||
Untagging an issue is very similar:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rad unlabel d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 good-first-issue
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that the `good-first-issue` label has disappeared:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ rad issue show d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 --format header
|
|
||||||
╭─────────────────────────────────────────────────────────╮
|
|
||||||
│ Title flux capacitor underpowered │
|
|
||||||
│ Issue d185ee16a00bac874c0bcbc2a8ad80fdce5e1e61 │
|
|
||||||
│ Author z6MknSL…StBU8Vi (you) │
|
|
||||||
│ Labels bug │
|
|
||||||
│ Status open │
|
|
||||||
│ │
|
|
||||||
│ Flux capacitor power requirements exceed current supply │
|
|
||||||
╰─────────────────────────────────────────────────────────╯
|
|
||||||
```
|
|
||||||
|
|
@ -89,6 +89,28 @@ $ git branch -vv
|
||||||
master f2de534 [rad/master] Second commit
|
master f2de534 [rad/master] Second commit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We also want to label the patch after we've created it:
|
||||||
|
```
|
||||||
|
$ rad patch label 6ff4f09c1b5a81347981f59b02ef43a31a07cdae -l fun
|
||||||
|
$ rad patch show 6ff4f09c1b5a81347981f59b02ef43a31a07cdae
|
||||||
|
╭────────────────────────────────────────────────────╮
|
||||||
|
│ Title Define power requirements │
|
||||||
|
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
||||||
|
│ Author z6MknSL…StBU8Vi (you) │
|
||||||
|
│ Labels fun │
|
||||||
|
│ Head 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
|
||||||
|
│ Branches flux-capacitor-power │
|
||||||
|
│ Commits ahead 1, behind 0 │
|
||||||
|
│ Status open │
|
||||||
|
│ │
|
||||||
|
│ See details. │
|
||||||
|
├────────────────────────────────────────────────────┤
|
||||||
|
│ 3e674d1 Define power requirements │
|
||||||
|
├────────────────────────────────────────────────────┤
|
||||||
|
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
||||||
|
╰────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
Wait, let's add a README too! Just for fun.
|
Wait, let's add a README too! Just for fun.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -101,7 +123,7 @@ $ git commit --message "Add README, just for the fun"
|
||||||
```
|
```
|
||||||
``` (stderr)
|
``` (stderr)
|
||||||
$ git push rad -o patch.message="Add README, just for the fun"
|
$ git push rad -o patch.message="Add README, just for the fun"
|
||||||
✓ Patch 6ff4f09 updated to 0c0942e2ff2488617d950ede15567ca39a29972e
|
✓ Patch 6ff4f09 updated to 873e637a66be511c45f4ef7b04fddc9def8f072c
|
||||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
3e674d1..27857ec flux-capacitor-power -> patches/6ff4f09c1b5a81347981f59b02ef43a31a07cdae
|
3e674d1..27857ec flux-capacitor-power -> patches/6ff4f09c1b5a81347981f59b02ef43a31a07cdae
|
||||||
```
|
```
|
||||||
|
|
@ -111,11 +133,11 @@ And let's leave a quick comment for our team:
|
||||||
```
|
```
|
||||||
$ rad patch comment 6ff4f09c1b5a81347981f59b02ef43a31a07cdae --message 'I cannot wait to get back to the 90s!'
|
$ rad patch comment 6ff4f09c1b5a81347981f59b02ef43a31a07cdae --message 'I cannot wait to get back to the 90s!'
|
||||||
╭───────────────────────────────────────╮
|
╭───────────────────────────────────────╮
|
||||||
│ z6MknSL…StBU8Vi (you) now cd811db │
|
│ z6MknSL…StBU8Vi (you) now efaf6fb │
|
||||||
│ I cannot wait to get back to the 90s! │
|
│ I cannot wait to get back to the 90s! │
|
||||||
╰───────────────────────────────────────╯
|
╰───────────────────────────────────────╯
|
||||||
$ rad patch comment 6ff4f09c1b5a81347981f59b02ef43a31a07cdae --message 'My favorite decade!' --reply-to cd811db -q
|
$ rad patch comment 6ff4f09c1b5a81347981f59b02ef43a31a07cdae --message 'My favorite decade!' --reply-to efaf6fb -q
|
||||||
b6a76fe394de87eb34cbc3823a0edc80ff98cb97
|
2cb22a1c87af86c25368c7be9fc385720fd6086f
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, let's checkout the patch that we just created:
|
Now, let's checkout the patch that we just created:
|
||||||
|
|
@ -141,6 +163,7 @@ $ rad patch show 6ff4f09
|
||||||
│ Title Define power requirements │
|
│ Title Define power requirements │
|
||||||
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
||||||
│ Author z6MknSL…StBU8Vi (you) │
|
│ Author z6MknSL…StBU8Vi (you) │
|
||||||
|
│ Labels fun │
|
||||||
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
||||||
│ Branches flux-capacitor-power, patch/6ff4f09 │
|
│ Branches flux-capacitor-power, patch/6ff4f09 │
|
||||||
│ Commits ahead 2, behind 0 │
|
│ Commits ahead 2, behind 0 │
|
||||||
|
|
@ -152,7 +175,7 @@ $ rad patch show 6ff4f09
|
||||||
│ 3e674d1 Define power requirements │
|
│ 3e674d1 Define power requirements │
|
||||||
├─────────────────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────────────────┤
|
||||||
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
||||||
│ ↑ updated to 0c0942e2ff2488617d950ede15567ca39a29972e (27857ec) now │
|
│ ↑ updated to 873e637a66be511c45f4ef7b04fddc9def8f072c (27857ec) now │
|
||||||
│ ✓ accepted by z6MknSL…StBU8Vi (you) now │
|
│ ✓ accepted by z6MknSL…StBU8Vi (you) now │
|
||||||
╰─────────────────────────────────────────────────────────────────────╯
|
╰─────────────────────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
@ -166,6 +189,7 @@ $ rad patch show 6ff4f09
|
||||||
│ Title Define power requirements │
|
│ Title Define power requirements │
|
||||||
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
||||||
│ Author z6MknSL…StBU8Vi (you) │
|
│ Author z6MknSL…StBU8Vi (you) │
|
||||||
|
│ Labels fun │
|
||||||
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
||||||
│ Branches flux-capacitor-power, patch/6ff4f09 │
|
│ Branches flux-capacitor-power, patch/6ff4f09 │
|
||||||
│ Commits ahead 2, behind 0 │
|
│ Commits ahead 2, behind 0 │
|
||||||
|
|
@ -177,7 +201,7 @@ $ rad patch show 6ff4f09
|
||||||
│ 3e674d1 Define power requirements │
|
│ 3e674d1 Define power requirements │
|
||||||
├─────────────────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────────────────┤
|
||||||
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
||||||
│ ↑ updated to 0c0942e2ff2488617d950ede15567ca39a29972e (27857ec) now │
|
│ ↑ updated to 873e637a66be511c45f4ef7b04fddc9def8f072c (27857ec) now │
|
||||||
│ ✓ accepted by z6MknSL…StBU8Vi (you) now │
|
│ ✓ accepted by z6MknSL…StBU8Vi (you) now │
|
||||||
╰─────────────────────────────────────────────────────────────────────╯
|
╰─────────────────────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ pub mod rad_init;
|
||||||
pub mod rad_inspect;
|
pub mod rad_inspect;
|
||||||
#[path = "commands/issue.rs"]
|
#[path = "commands/issue.rs"]
|
||||||
pub mod rad_issue;
|
pub mod rad_issue;
|
||||||
#[path = "commands/label.rs"]
|
|
||||||
pub mod rad_label;
|
|
||||||
#[path = "commands/ls.rs"]
|
#[path = "commands/ls.rs"]
|
||||||
pub mod rad_ls;
|
pub mod rad_ls;
|
||||||
#[path = "commands/node.rs"]
|
#[path = "commands/node.rs"]
|
||||||
|
|
@ -50,7 +48,5 @@ pub mod rad_sync;
|
||||||
pub mod rad_track;
|
pub mod rad_track;
|
||||||
#[path = "commands/unassign.rs"]
|
#[path = "commands/unassign.rs"]
|
||||||
pub mod rad_unassign;
|
pub mod rad_unassign;
|
||||||
#[path = "commands/unlabel.rs"]
|
|
||||||
pub mod rad_unlabel;
|
|
||||||
#[path = "commands/untrack.rs"]
|
#[path = "commands/untrack.rs"]
|
||||||
pub mod rad_untrack;
|
pub mod rad_untrack;
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,8 @@ const COMMANDS: &[Help] = &[
|
||||||
rad_review::HELP,
|
rad_review::HELP,
|
||||||
rad_clean::HELP,
|
rad_clean::HELP,
|
||||||
rad_self::HELP,
|
rad_self::HELP,
|
||||||
rad_label::HELP,
|
|
||||||
rad_track::HELP,
|
rad_track::HELP,
|
||||||
rad_unassign::HELP,
|
rad_unassign::HELP,
|
||||||
rad_unlabel::HELP,
|
|
||||||
rad_untrack::HELP,
|
rad_untrack::HELP,
|
||||||
rad_remote::HELP,
|
rad_remote::HELP,
|
||||||
rad_sync::HELP,
|
rad_sync::HELP,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context as _};
|
use anyhow::{anyhow, Context as _};
|
||||||
|
|
||||||
|
use nonempty::NonEmpty;
|
||||||
use radicle::cob::common::{Label, Reaction};
|
use radicle::cob::common::{Label, Reaction};
|
||||||
use radicle::cob::issue;
|
use radicle::cob::issue;
|
||||||
use radicle::cob::issue::{CloseReason, Issues, State};
|
use radicle::cob::issue::{CloseReason, Issues, State};
|
||||||
|
|
@ -38,10 +39,20 @@ Usage
|
||||||
rad issue list [--assigned <did>] [--all | --closed | --open | --solved] [<option>...]
|
rad issue list [--assigned <did>] [--all | --closed | --open | --solved] [<option>...]
|
||||||
rad issue open [--title <title>] [--description <text>] [--label <label>] [<option>...]
|
rad issue open [--title <title>] [--description <text>] [--label <label>] [<option>...]
|
||||||
rad issue react <issue-id> [--emoji <char>] [--to <comment>] [<option>...]
|
rad issue react <issue-id> [--emoji <char>] [--to <comment>] [<option>...]
|
||||||
|
rad issue label <issue-id> --label <label> [<option>...]
|
||||||
|
rad issue unlabel <issue-id> --label <label> [<option>...]
|
||||||
rad issue comment <issue-id> [--message <message>] [--reply-to <comment-id>] [<option>...]
|
rad issue comment <issue-id> [--message <message>] [--reply-to <comment-id>] [<option>...]
|
||||||
rad issue show <issue-id> [<option>...]
|
rad issue show <issue-id> [<option>...]
|
||||||
rad issue state <issue-id> [--closed | --open | --solved] [<option>...]
|
rad issue state <issue-id> [--closed | --open | --solved] [<option>...]
|
||||||
|
|
||||||
|
Label options
|
||||||
|
|
||||||
|
-l, --label Label the issue with the provided label (may be specified multiple times)
|
||||||
|
|
||||||
|
Unlabel options
|
||||||
|
|
||||||
|
-l, --label Remove the provided label from the issue (may be specified multiple times)
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
--no-announce Don't announce issue to peers
|
--no-announce Don't announce issue to peers
|
||||||
|
|
@ -57,11 +68,13 @@ pub enum OperationName {
|
||||||
Open,
|
Open,
|
||||||
Comment,
|
Comment,
|
||||||
Delete,
|
Delete,
|
||||||
|
Label,
|
||||||
#[default]
|
#[default]
|
||||||
List,
|
List,
|
||||||
React,
|
React,
|
||||||
Show,
|
Show,
|
||||||
State,
|
State,
|
||||||
|
Unlabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command line Peer argument.
|
/// Command line Peer argument.
|
||||||
|
|
@ -106,6 +119,14 @@ pub enum Operation {
|
||||||
reaction: Reaction,
|
reaction: Reaction,
|
||||||
comment_id: Option<thread::CommentId>,
|
comment_id: Option<thread::CommentId>,
|
||||||
},
|
},
|
||||||
|
Label {
|
||||||
|
id: Rev,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
},
|
||||||
|
Unlabel {
|
||||||
|
id: Rev,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
},
|
||||||
List {
|
List {
|
||||||
assigned: Option<Assigned>,
|
assigned: Option<Assigned>,
|
||||||
state: Option<State>,
|
state: Option<State>,
|
||||||
|
|
@ -164,7 +185,12 @@ impl Args for Options {
|
||||||
Long("title") if op == Some(OperationName::Open) => {
|
Long("title") if op == Some(OperationName::Open) => {
|
||||||
title = Some(parser.value()?.to_string_lossy().into());
|
title = Some(parser.value()?.to_string_lossy().into());
|
||||||
}
|
}
|
||||||
Long("label") if op == Some(OperationName::Open) => {
|
Short('l') | Long("label")
|
||||||
|
if matches!(
|
||||||
|
op,
|
||||||
|
Some(OperationName::Open | OperationName::Label | OperationName::Unlabel)
|
||||||
|
) =>
|
||||||
|
{
|
||||||
let val = parser.value()?;
|
let val = parser.value()?;
|
||||||
let name = term::args::string(&val);
|
let name = term::args::string(&val);
|
||||||
let label = Label::new(name)?;
|
let label = Label::new(name)?;
|
||||||
|
|
@ -248,6 +274,8 @@ impl Args for Options {
|
||||||
"o" | "open" => op = Some(OperationName::Open),
|
"o" | "open" => op = Some(OperationName::Open),
|
||||||
"r" | "react" => op = Some(OperationName::React),
|
"r" | "react" => op = Some(OperationName::React),
|
||||||
"s" | "state" => op = Some(OperationName::State),
|
"s" | "state" => op = Some(OperationName::State),
|
||||||
|
"label" => op = Some(OperationName::Label),
|
||||||
|
"unlabel" => op = Some(OperationName::Unlabel),
|
||||||
|
|
||||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||||
},
|
},
|
||||||
|
|
@ -294,6 +322,16 @@ impl Args for Options {
|
||||||
OperationName::Delete => Operation::Delete {
|
OperationName::Delete => Operation::Delete {
|
||||||
id: id.ok_or_else(|| anyhow!("an issue to remove must be provided"))?,
|
id: id.ok_or_else(|| anyhow!("an issue to remove must be provided"))?,
|
||||||
},
|
},
|
||||||
|
OperationName::Label => Operation::Label {
|
||||||
|
id: id.ok_or_else(|| anyhow!("an issue to label must be provided"))?,
|
||||||
|
labels: NonEmpty::from_vec(labels)
|
||||||
|
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
||||||
|
},
|
||||||
|
OperationName::Unlabel => Operation::Unlabel {
|
||||||
|
id: id.ok_or_else(|| anyhow!("an issue to label must be provided"))?,
|
||||||
|
labels: NonEmpty::from_vec(labels)
|
||||||
|
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
||||||
|
},
|
||||||
OperationName::List => Operation::List { assigned, state },
|
OperationName::List => Operation::List { assigned, state },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -407,6 +445,26 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
&profile,
|
&profile,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
Operation::Label { id, labels } => {
|
||||||
|
let id = id.resolve(&repo.backend)?;
|
||||||
|
let Ok(mut issue) = issues.get_mut(&id) else {
|
||||||
|
anyhow::bail!("Issue `{id}` not found");
|
||||||
|
};
|
||||||
|
let labels = issue.labels().cloned().chain(labels).collect::<Vec<_>>();
|
||||||
|
issue.label(labels, &signer)?;
|
||||||
|
}
|
||||||
|
Operation::Unlabel { id, labels } => {
|
||||||
|
let id = id.resolve(&repo.backend)?;
|
||||||
|
let Ok(mut issue) = issues.get_mut(&id) else {
|
||||||
|
anyhow::bail!("Issue `{id}` not found");
|
||||||
|
};
|
||||||
|
let labels = issue
|
||||||
|
.labels()
|
||||||
|
.filter(|&l| !labels.contains(l))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
issue.label(labels, &signer)?;
|
||||||
|
}
|
||||||
Operation::List { assigned, state } => {
|
Operation::List { assigned, state } => {
|
||||||
list(&issues, &assigned, &state, &profile)?;
|
list(&issues, &assigned, &state, &profile)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use nonempty::NonEmpty;
|
|
||||||
|
|
||||||
use radicle::cob;
|
|
||||||
use radicle::cob::common::Label;
|
|
||||||
use radicle::cob::{issue, patch, store};
|
|
||||||
use radicle::crypto::Signer;
|
|
||||||
use radicle::storage::{self, WriteStorage};
|
|
||||||
|
|
||||||
use crate::terminal as term;
|
|
||||||
use crate::terminal::args::{Args, Error, Help};
|
|
||||||
|
|
||||||
pub const HELP: Help = Help {
|
|
||||||
name: "label",
|
|
||||||
description: "Label an issue or patch",
|
|
||||||
version: env!("CARGO_PKG_VERSION"),
|
|
||||||
usage: r#"
|
|
||||||
Usage
|
|
||||||
|
|
||||||
rad label <issue-id> <label>... [<option>...]
|
|
||||||
|
|
||||||
Adds the given labels to the patch or issue.
|
|
||||||
|
|
||||||
Options
|
|
||||||
|
|
||||||
--help Print help
|
|
||||||
"#,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Options {
|
|
||||||
pub id: cob::ObjectId,
|
|
||||||
pub labels: NonEmpty<Label>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args for Options {
|
|
||||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
|
||||||
use lexopt::prelude::*;
|
|
||||||
|
|
||||||
let mut parser = lexopt::Parser::from_args(args);
|
|
||||||
let mut id: Option<cob::ObjectId> = None;
|
|
||||||
let mut labels: Vec<Label> = Vec::new();
|
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
|
||||||
match arg {
|
|
||||||
Long("help") | Short('h') => {
|
|
||||||
return Err(Error::Help.into());
|
|
||||||
}
|
|
||||||
Value(ref val) if id.is_none() => {
|
|
||||||
id = Some(term::args::cob(val)?);
|
|
||||||
}
|
|
||||||
Value(ref val) if id.is_some() => {
|
|
||||||
let s: String = val.parse()?;
|
|
||||||
let label = Label::from_str(&s)?;
|
|
||||||
|
|
||||||
labels.push(label);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(anyhow!(arg.unexpected()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
Options {
|
|
||||||
id: id.ok_or_else(|| anyhow!("an issue or patch must be specified"))?,
|
|
||||||
labels: NonEmpty::from_vec(labels)
|
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn label(
|
|
||||||
options: Options,
|
|
||||||
repo: &storage::git::Repository,
|
|
||||||
signer: impl Signer,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut issues = issue::Issues::open(repo)?;
|
|
||||||
match issues.get_mut(&options.id) {
|
|
||||||
Ok(mut issue) => {
|
|
||||||
let labels = issue
|
|
||||||
.labels()
|
|
||||||
.cloned()
|
|
||||||
.chain(options.labels)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
issue.label(labels, &signer)?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(store::Error::NotFound(_, _)) => {}
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut patches = patch::Patches::open(repo)?;
|
|
||||||
match patches.get_mut(&options.id) {
|
|
||||||
Ok(mut patch) => {
|
|
||||||
let labels = patch
|
|
||||||
.labels()
|
|
||||||
.cloned()
|
|
||||||
.chain(options.labels)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
patch.label(labels, &signer)?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(store::Error::NotFound(_, _)) => {}
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
anyhow::bail!("Couldn't find issue or patch {}", options.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|
||||||
let profile = ctx.profile()?;
|
|
||||||
let (_, id) = radicle::rad::cwd()?;
|
|
||||||
let repo = profile.storage.repository_mut(id)?;
|
|
||||||
let signer = term::signer(&profile)?;
|
|
||||||
|
|
||||||
label(options, &repo, signer)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -10,6 +10,8 @@ mod common;
|
||||||
mod delete;
|
mod delete;
|
||||||
#[path = "patch/edit.rs"]
|
#[path = "patch/edit.rs"]
|
||||||
mod edit;
|
mod edit;
|
||||||
|
#[path = "patch/label.rs"]
|
||||||
|
mod label;
|
||||||
#[path = "patch/list.rs"]
|
#[path = "patch/list.rs"]
|
||||||
mod list;
|
mod list;
|
||||||
#[path = "patch/ready.rs"]
|
#[path = "patch/ready.rs"]
|
||||||
|
|
@ -26,8 +28,9 @@ use std::ffi::OsString;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use radicle::cob::patch;
|
use nonempty::NonEmpty;
|
||||||
use radicle::cob::patch::PatchId;
|
use radicle::cob::patch::PatchId;
|
||||||
|
use radicle::cob::{patch, Label};
|
||||||
use radicle::prelude::*;
|
use radicle::prelude::*;
|
||||||
use radicle::storage::git::transport;
|
use radicle::storage::git::transport;
|
||||||
|
|
||||||
|
|
@ -51,6 +54,8 @@ Usage
|
||||||
rad patch checkout <patch-id> [<option>...]
|
rad patch checkout <patch-id> [<option>...]
|
||||||
rad patch delete <patch-id> [<option>...]
|
rad patch delete <patch-id> [<option>...]
|
||||||
rad patch redact <revision-id> [<option>...]
|
rad patch redact <revision-id> [<option>...]
|
||||||
|
rad patch label <patch-id> --label <label> [<option>...]
|
||||||
|
rad patch unlabel <patch-id> --label <label> [<option>...]
|
||||||
rad patch ready <patch-id> [--undo] [<option>...]
|
rad patch ready <patch-id> [--undo] [<option>...]
|
||||||
rad patch edit <patch-id> [<option>...]
|
rad patch edit <patch-id> [<option>...]
|
||||||
rad patch set <patch-id> [<option>...]
|
rad patch set <patch-id> [<option>...]
|
||||||
|
|
@ -69,6 +74,14 @@ Edit options
|
||||||
|
|
||||||
-m, --message [<string>] Provide a comment message to the patch or revision (default: prompt)
|
-m, --message [<string>] Provide a comment message to the patch or revision (default: prompt)
|
||||||
|
|
||||||
|
Label options
|
||||||
|
|
||||||
|
-l, --label Label the patch with the provided label (may be specified multiple times)
|
||||||
|
|
||||||
|
Unlabel options
|
||||||
|
|
||||||
|
-l, --label Remove the provided label from the patch (may be specified multiple times)
|
||||||
|
|
||||||
Update options
|
Update options
|
||||||
|
|
||||||
-m, --message [<string>] Provide a comment message to the patch or revision (default: prompt)
|
-m, --message [<string>] Provide a comment message to the patch or revision (default: prompt)
|
||||||
|
|
@ -111,6 +124,8 @@ pub enum OperationName {
|
||||||
Checkout,
|
Checkout,
|
||||||
Comment,
|
Comment,
|
||||||
Ready,
|
Ready,
|
||||||
|
Label,
|
||||||
|
Unlabel,
|
||||||
#[default]
|
#[default]
|
||||||
List,
|
List,
|
||||||
Edit,
|
Edit,
|
||||||
|
|
@ -169,6 +184,14 @@ pub enum Operation {
|
||||||
message: Message,
|
message: Message,
|
||||||
reply_to: Option<Rev>,
|
reply_to: Option<Rev>,
|
||||||
},
|
},
|
||||||
|
Label {
|
||||||
|
patch_id: Rev,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
},
|
||||||
|
Unlabel {
|
||||||
|
patch_id: Rev,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
},
|
||||||
List {
|
List {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
},
|
},
|
||||||
|
|
@ -215,6 +238,7 @@ impl Args for Options {
|
||||||
let mut undo = false;
|
let mut undo = false;
|
||||||
let mut reply_to: Option<Rev> = None;
|
let mut reply_to: Option<Rev> = None;
|
||||||
let mut checkout_opts = checkout::Options::default();
|
let mut checkout_opts = checkout::Options::default();
|
||||||
|
let mut labels = Vec::new();
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -285,6 +309,17 @@ impl Args for Options {
|
||||||
checkout_opts.name = Some(term::args::refstring("name", val)?);
|
checkout_opts.name = Some(term::args::refstring("name", val)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Un/Label options.
|
||||||
|
Short('l') | Long("label")
|
||||||
|
if matches!(op, Some(OperationName::Label | OperationName::Unlabel)) =>
|
||||||
|
{
|
||||||
|
let val = parser.value()?;
|
||||||
|
let name = term::args::string(&val);
|
||||||
|
let label = Label::new(name)?;
|
||||||
|
|
||||||
|
labels.push(label);
|
||||||
|
}
|
||||||
|
|
||||||
// List options.
|
// List options.
|
||||||
Long("all") => {
|
Long("all") => {
|
||||||
filter = Filter::all();
|
filter = Filter::all();
|
||||||
|
|
@ -332,6 +367,8 @@ impl Args for Options {
|
||||||
"y" | "ready" => op = Some(OperationName::Ready),
|
"y" | "ready" => op = Some(OperationName::Ready),
|
||||||
"e" | "edit" => op = Some(OperationName::Edit),
|
"e" | "edit" => op = Some(OperationName::Edit),
|
||||||
"r" | "redact" => op = Some(OperationName::Redact),
|
"r" | "redact" => op = Some(OperationName::Redact),
|
||||||
|
"label" => op = Some(OperationName::Label),
|
||||||
|
"unlabel" => op = Some(OperationName::Unlabel),
|
||||||
"comment" => op = Some(OperationName::Comment),
|
"comment" => op = Some(OperationName::Comment),
|
||||||
"set" => op = Some(OperationName::Set),
|
"set" => op = Some(OperationName::Set),
|
||||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||||
|
|
@ -352,6 +389,8 @@ impl Args for Options {
|
||||||
Some(OperationName::Comment),
|
Some(OperationName::Comment),
|
||||||
Some(OperationName::Edit),
|
Some(OperationName::Edit),
|
||||||
Some(OperationName::Set),
|
Some(OperationName::Set),
|
||||||
|
Some(OperationName::Label),
|
||||||
|
Some(OperationName::Unlabel),
|
||||||
]
|
]
|
||||||
.contains(&op) =>
|
.contains(&op) =>
|
||||||
{
|
{
|
||||||
|
|
@ -400,6 +439,16 @@ impl Args for Options {
|
||||||
OperationName::Redact => Operation::Redact {
|
OperationName::Redact => Operation::Redact {
|
||||||
revision_id: revision_id.ok_or_else(|| anyhow!("a revision must be provided"))?,
|
revision_id: revision_id.ok_or_else(|| anyhow!("a revision must be provided"))?,
|
||||||
},
|
},
|
||||||
|
OperationName::Label => Operation::Label {
|
||||||
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
|
labels: NonEmpty::from_vec(labels)
|
||||||
|
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
||||||
|
},
|
||||||
|
OperationName::Unlabel => Operation::Unlabel {
|
||||||
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
|
labels: NonEmpty::from_vec(labels)
|
||||||
|
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
||||||
|
},
|
||||||
OperationName::Set => Operation::Set {
|
OperationName::Set => Operation::Set {
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
},
|
},
|
||||||
|
|
@ -506,6 +555,14 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
Operation::Redact { revision_id } => {
|
Operation::Redact { revision_id } => {
|
||||||
redact::run(&revision_id, &profile, &repository)?;
|
redact::run(&revision_id, &profile, &repository)?;
|
||||||
}
|
}
|
||||||
|
Operation::Label { patch_id, labels } => {
|
||||||
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
|
label::add(&patch_id, labels, &profile, &repository)?;
|
||||||
|
}
|
||||||
|
Operation::Unlabel { patch_id, labels } => {
|
||||||
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
|
label::remove(&patch_id, labels, &profile, &repository)?;
|
||||||
|
}
|
||||||
Operation::Set { patch_id } => {
|
Operation::Set { patch_id } => {
|
||||||
let patches = radicle::cob::patch::Patches::open(&repository)?;
|
let patches = radicle::cob::patch::Patches::open(&repository)?;
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use nonempty::NonEmpty;
|
||||||
|
use radicle::storage::git::Repository;
|
||||||
|
|
||||||
|
use crate::terminal as term;
|
||||||
|
|
||||||
|
pub fn add(
|
||||||
|
patch_id: &PatchId,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
profile: &Profile,
|
||||||
|
repository: &Repository,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let signer = term::signer(profile)?;
|
||||||
|
let mut patches = radicle::cob::patch::Patches::open(repository)?;
|
||||||
|
let Ok(mut patch) = patches.get_mut(patch_id) else {
|
||||||
|
anyhow::bail!("Patch `{patch_id}` not found");
|
||||||
|
};
|
||||||
|
let labels = patch.labels().cloned().chain(labels).collect::<Vec<_>>();
|
||||||
|
patch.label(labels, &signer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(
|
||||||
|
patch_id: &PatchId,
|
||||||
|
labels: NonEmpty<Label>,
|
||||||
|
profile: &Profile,
|
||||||
|
repository: &Repository,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let signer = term::signer(profile)?;
|
||||||
|
let mut patches = radicle::cob::patch::Patches::open(repository)?;
|
||||||
|
let Ok(mut patch) = patches.get_mut(patch_id) else {
|
||||||
|
anyhow::bail!("Patch `{patch_id}` not found");
|
||||||
|
};
|
||||||
|
let labels = patch
|
||||||
|
.labels()
|
||||||
|
.filter(|&l| !labels.contains(l))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
patch.label(labels, &signer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -76,6 +76,7 @@ pub fn run(
|
||||||
)?;
|
)?;
|
||||||
let author = patch.author();
|
let author = patch.author();
|
||||||
let author = term::format::Author::new(author.id(), profile);
|
let author = term::format::Author::new(author.id(), profile);
|
||||||
|
let labels = patch.labels().map(|l| l.to_string()).collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut attrs = Table::<2, term::Line>::new(TableOptions {
|
let mut attrs = Table::<2, term::Line>::new(TableOptions {
|
||||||
spacing: 2,
|
spacing: 2,
|
||||||
|
|
@ -93,6 +94,12 @@ pub fn run(
|
||||||
term::format::tertiary("Author".to_owned()).into(),
|
term::format::tertiary("Author".to_owned()).into(),
|
||||||
author.line(),
|
author.line(),
|
||||||
]);
|
]);
|
||||||
|
if !labels.is_empty() {
|
||||||
|
attrs.push([
|
||||||
|
term::format::tertiary("Labels".to_owned()).into(),
|
||||||
|
term::format::secondary(labels.join(", ")).into(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
attrs.push([
|
attrs.push([
|
||||||
term::format::tertiary("Head".to_owned()).into(),
|
term::format::tertiary("Head".to_owned()).into(),
|
||||||
term::format::secondary(revision.head().to_string()).into(),
|
term::format::secondary(revision.head().to_string()).into(),
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use nonempty::NonEmpty;
|
|
||||||
|
|
||||||
use radicle::cob;
|
|
||||||
use radicle::cob::common::Label;
|
|
||||||
use radicle::cob::{issue, patch, store};
|
|
||||||
use radicle::crypto::Signer;
|
|
||||||
use radicle::storage::{self, WriteStorage};
|
|
||||||
|
|
||||||
use crate::terminal as term;
|
|
||||||
use crate::terminal::args::{Args, Error, Help};
|
|
||||||
|
|
||||||
pub const HELP: Help = Help {
|
|
||||||
name: "untag",
|
|
||||||
description: "Untag an issue or patch",
|
|
||||||
version: env!("CARGO_PKG_VERSION"),
|
|
||||||
usage: r#"
|
|
||||||
Usage
|
|
||||||
|
|
||||||
rad untag <cob-id> <tag>... [<option>...]
|
|
||||||
|
|
||||||
Options
|
|
||||||
|
|
||||||
--help Print help
|
|
||||||
"#,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Options {
|
|
||||||
pub id: cob::ObjectId,
|
|
||||||
pub labels: NonEmpty<Label>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args for Options {
|
|
||||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
|
||||||
use lexopt::prelude::*;
|
|
||||||
|
|
||||||
let mut parser = lexopt::Parser::from_args(args);
|
|
||||||
let mut id: Option<cob::ObjectId> = None;
|
|
||||||
let mut labels: Vec<Label> = Vec::new();
|
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
|
||||||
match arg {
|
|
||||||
Long("help") | Short('h') => {
|
|
||||||
return Err(Error::Help.into());
|
|
||||||
}
|
|
||||||
Value(ref val) if id.is_none() => {
|
|
||||||
id = Some(term::args::cob(val)?);
|
|
||||||
}
|
|
||||||
Value(ref val) if id.is_some() => {
|
|
||||||
let s: String = val.parse()?;
|
|
||||||
let label = Label::from_str(&s)?;
|
|
||||||
|
|
||||||
labels.push(label);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(anyhow!(arg.unexpected()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
Options {
|
|
||||||
id: id.ok_or_else(|| anyhow!("an issue or patch must be specified"))?,
|
|
||||||
labels: NonEmpty::from_vec(labels)
|
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unlabel(
|
|
||||||
options: Options,
|
|
||||||
repo: &storage::git::Repository,
|
|
||||||
signer: impl Signer,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut issues = issue::Issues::open(repo)?;
|
|
||||||
match issues.get_mut(&options.id) {
|
|
||||||
Ok(mut issue) => {
|
|
||||||
let labels = issue
|
|
||||||
.labels()
|
|
||||||
.filter(|&l| !options.labels.contains(l))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
issue.label(labels, &signer)?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(store::Error::NotFound(_, _)) => {}
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut patches = patch::Patches::open(repo)?;
|
|
||||||
match patches.get_mut(&options.id) {
|
|
||||||
Ok(mut patch) => {
|
|
||||||
let labels = patch
|
|
||||||
.labels()
|
|
||||||
.filter(|&l| !options.labels.contains(l))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
patch.label(labels, &signer)?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(store::Error::NotFound(_, _)) => {}
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
anyhow::bail!("Couldn't find issue or patch {}", options.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|
||||||
let profile = ctx.profile()?;
|
|
||||||
let (_, id) = radicle::rad::cwd()?;
|
|
||||||
let repo = profile.storage.repository_mut(id)?;
|
|
||||||
let signer = term::signer(&profile)?;
|
|
||||||
|
|
||||||
unlabel(options, &repo, signer)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -243,13 +243,6 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
||||||
args.to_vec(),
|
args.to_vec(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"label" => {
|
|
||||||
term::run_command_args::<rad_label::Options, _>(
|
|
||||||
rad_label::HELP,
|
|
||||||
rad_label::run,
|
|
||||||
args.to_vec(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
"track" => {
|
"track" => {
|
||||||
term::run_command_args::<rad_track::Options, _>(
|
term::run_command_args::<rad_track::Options, _>(
|
||||||
rad_track::HELP,
|
rad_track::HELP,
|
||||||
|
|
@ -264,13 +257,6 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
||||||
args.to_vec(),
|
args.to_vec(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"unlabel" => {
|
|
||||||
term::run_command_args::<rad_unlabel::Options, _>(
|
|
||||||
rad_unlabel::HELP,
|
|
||||||
rad_unlabel::run,
|
|
||||||
args.to_vec(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
"untrack" => {
|
"untrack" => {
|
||||||
term::run_command_args::<rad_untrack::Options, _>(
|
term::run_command_args::<rad_untrack::Options, _>(
|
||||||
rad_untrack::HELP,
|
rad_untrack::HELP,
|
||||||
|
|
|
||||||
|
|
@ -117,21 +117,6 @@ fn rad_cob() {
|
||||||
test("examples/rad-cob.md", &working, Some(home), []).unwrap();
|
test("examples/rad-cob.md", &working, Some(home), []).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rad_label() {
|
|
||||||
let mut environment = Environment::new();
|
|
||||||
let profile = environment.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-issue.md", &working, Some(home), []).unwrap();
|
|
||||||
test("examples/rad-label.md", &working, Some(home), []).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_init() {
|
fn rad_init() {
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue