rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood8811e9bedba54eaf8ad0953c1ff824e14e46211e
{
"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": "28748764fd8e2ee0856418cf6c94920ca5119e80",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"title": "cli: clear by notification state",
"state": {
"status": "archived",
"conflicts": []
},
"before": "6f8d75a00daac96ee4b375b4a7e93cfb8988820c",
"after": "8811e9bedba54eaf8ad0953c1ff824e14e46211e",
"commits": [
"8811e9bedba54eaf8ad0953c1ff824e14e46211e"
],
"target": "d285eccb23f3986bfa88fc1af033e6dcb0dc3a65",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "28748764fd8e2ee0856418cf6c94920ca5119e80",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Add the ability to clear a set of `rad inbox` items by a notifications state.\n\nThis is achieved by introducing a field to the `Clear` operation to optionally\nclear by a state. When `rad inbox clear` is called, it will check if the\n`--by-state` option is used, if not then the behaviour is as before, otherwise\nit will get all notification ids matching the given state (and `Mode`)\nbefore removing all those notifications.",
"base": "484cf0220cca07cea1863d0e20d44827683da901",
"oid": "12d3772079ff6981ec46bcba8292766643f197db",
"timestamp": 1718704477
},
{
"id": "61a6333d3481e568ecd2c1e9676c6322721e3b41",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Changes:\n- Rebased\n- Avoid collecting into a `Vec` and then pushing to another `Vec` by using\n closures and `filter_map`.",
"base": "6f8d75a00daac96ee4b375b4a7e93cfb8988820c",
"oid": "8811e9bedba54eaf8ad0953c1ff824e14e46211e",
"timestamp": 1721641050
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "1e74e5b4-7ad9-4778-9951-0da2ded78f41"
},
"info_url": "https://cci.rad.levitte.org//1e74e5b4-7ad9-4778-9951-0da2ded78f41.html"
}
Started at: 2026-09-07 20:00:53.286350+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/1e74e5b4-7ad9-4778-9951-0da2ded78f41/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 189 issues · 38 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 28748764fd8e2ee0856418cf6c94920ca5119e80
✓ Switched to branch patch/2874876 at revision 61a6333
✓ Branch patch/2874876 setup to track rad/patches/28748764fd8e2ee0856418cf6c94920ca5119e80
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 8811e9bedba54eaf8ad0953c1ff824e14e46211e
HEAD is now at 8811e9be cli: clear by notification state
Exit code: 0
$ rad patch show 28748764fd8e2ee0856418cf6c94920ca5119e80 -p
╭────────────────────────────────────────────────────────────────────────────────╮
│ Title cli: clear by notification state │
│ Patch 28748764fd8e2ee0856418cf6c94920ca5119e80 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head 8811e9bedba54eaf8ad0953c1ff824e14e46211e │
│ Base 6f8d75a00daac96ee4b375b4a7e93cfb8988820c │
│ Target master │
│ Branches patch/2874876 │
│ Commits ahead 1, behind 1241 │
│ Status archived │
│ │
│ Add the ability to clear a set of `rad inbox` items by a notifications state. │
│ │
│ This is achieved by introducing a field to the `Clear` operation to optionally │
│ clear by a state. When `rad inbox clear` is called, it will check if the │
│ `--by-state` option is used, if not then the behaviour is as before, otherwise │
│ it will get all notification ids matching the given state (and `Mode`) │
│ before removing all those notifications. │
├────────────────────────────────────────────────────────────────────────────────┤
│ 8811e9b cli: clear by notification state │
├────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision 2874876 @ 484cf02..12d3772 by fintohaps z6Mkire…SQZ3voM 2 years ago │
│ ↑ Revision 61a6333 @ 6f8d75a..8811e9b by fintohaps z6Mkire…SQZ3voM 2 years ago │
╰────────────────────────────────────────────────────────────────────────────────╯
commit 8811e9bedba54eaf8ad0953c1ff824e14e46211e
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Tue Jun 18 11:50:30 2024 +0200
cli: clear by notification state
Add the ability to clear a set of `rad inbox` items by a notifications state.
This is achieved by introducing a field to the `Clear` operation to optionally
clear by a state. When `rad inbox clear` is called, it will check if the
`--by-state` option is used, if not then the behaviour is as before, otherwise
it will get all notification ids matching the given state (and `Mode`)
before removing all those notifications.
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
diff --git a/radicle-cli/examples/rad-inbox.md b/radicle-cli/examples/rad-inbox.md
index 13a714a8c..6ef8c3ff2 100644
--- a/radicle-cli/examples/rad-inbox.md
+++ b/radicle-cli/examples/rad-inbox.md
@@ -138,3 +138,12 @@ $ rad inbox show 1
"threshold": 1
}
```
+
+We can also clear any items by the given state of the notification. The state
+values are dependent on each category of notification items. In this case, we
+will clear by the `accepted` state to remove the identity update.
+
+``` ~bob
+$ rad inbox clear --all --by-state accepted
+✓ Cleared 1 item(s) from your inbox
+```
diff --git a/radicle-cli/src/commands/inbox.rs b/radicle-cli/src/commands/inbox.rs
index f313d14fc..40972241f 100644
--- a/radicle-cli/src/commands/inbox.rs
+++ b/radicle-cli/src/commands/inbox.rs
@@ -32,7 +32,7 @@ Usage
rad inbox [<option>...]
rad inbox list [<option>...]
rad inbox show <id> [<option>...]
- rad inbox clear [<option>...]
+ rad inbox clear [--by-state <state>] [<option>...]
By default, this command lists all items in your inbox.
If your working directory is a Radicle repository, it only shows item
@@ -43,6 +43,9 @@ Usage
notification. This will mark the notification as read.
The `rad inbox clear` command will delete all notifications in the inbox.
+ You can also clear all items that are in a certain state, depending on the
+ kind of item you are clearing, using `--by-state`. For example, to clear all
+ patches that have been merged, you can use `--by-state merged`.
Options
@@ -55,8 +58,15 @@ Options
"#,
};
-#[derive(Debug, Default, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
enum Operation {
+ List,
+ Show,
+ Clear { state: Option<String> },
+}
+
+#[derive(Debug, Default, PartialEq, Eq)]
+enum OperationName {
#[default]
List,
Show,
@@ -90,12 +100,13 @@ impl Args for Options {
use lexopt::prelude::*;
let mut parser = lexopt::Parser::from_args(args);
- let mut op: Option<Operation> = None;
+ let mut op: Option<OperationName> = None;
let mut mode = None;
let mut ids = Vec::new();
let mut reverse = None;
let mut field = None;
let mut show_unknown = false;
+ let mut state: Option<String> = None;
while let Some(arg) = parser.next()? {
match arg {
@@ -124,6 +135,10 @@ impl Args for Options {
}
}
}
+ Long("by-state") if matches!(op, Some(OperationName::Clear)) => {
+ let val = parser.value()?;
+ state = Some(term::args::string(&val));
+ }
Long("repo") if mode.is_none() => {
let val = parser.value()?;
let repo = args::rid(&val)?;
@@ -131,9 +146,9 @@ impl Args for Options {
mode = Some(Mode::ByRepo(repo));
}
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
- "list" => op = Some(Operation::List),
- "show" => op = Some(Operation::Show),
- "clear" => op = Some(Operation::Clear),
+ "list" => op = Some(OperationName::List),
+ "show" => op = Some(OperationName::Show),
+ "clear" => op = Some(OperationName::Clear),
cmd => return Err(anyhow!("unknown command `{cmd}`, see `rad inbox --help`")),
},
Value(val) if op.is_some() && mode.is_none() => {
@@ -148,7 +163,11 @@ impl Args for Options {
} else {
Mode::ById(ids)
};
- let op = op.unwrap_or_default();
+ let op = match op.unwrap_or_default() {
+ OperationName::List => Operation::List,
+ OperationName::Show => Operation::Show,
+ OperationName::Clear => Operation::Clear { state },
+ };
let sort_by = if let Some(field) = field {
SortBy {
@@ -194,7 +213,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
storage,
&profile,
),
- Operation::Clear => clear(mode, &mut notifs),
+ Operation::Clear { state } => clear(mode, state, &mut notifs, storage, &profile),
Operation::Show => show(mode, &mut notifs, storage, &profile),
}
}
@@ -344,6 +363,73 @@ where
}
}
+fn notifications_ids<T, R>(
+ filter_state: &str,
+ notifs: ¬ifications::Store<T>,
+ storage: &R,
+ profile: &Profile,
+) -> anyhow::Result<Vec<u32>>
+where
+ R: ReadStorage,
+ R::Repository: cob::Store,
+{
+ let mut repos = storage.repositories()?;
+ repos.sort_by_key(|r| r.rid);
+
+ let mut ids = Vec::new();
+ for repo in repos {
+ ids.extend(notification_ids_by_repo(
+ repo.rid,
+ filter_state,
+ notifs,
+ storage,
+ profile,
+ )?)
+ }
+ Ok(ids)
+}
+
+fn notification_ids_by_repo<R, T>(
+ rid: RepoId,
+ filter_state: &str,
+ notifs: ¬ifications::Store<T>,
+ storage: &R,
+ profile: &Profile,
+) -> anyhow::Result<Vec<u32>>
+where
+ R: ReadStorage,
+ R::Repository: cob::Store,
+{
+ let repo = storage.repository(rid)?;
+ let (_, head) = repo.head()?;
+ let issues = profile.issues(&repo)?;
+ let patches = profile.patches(&repo)?;
+
+ let matches_state = |id: NotificationId, row: NotificationRow| -> Option<NotificationId> {
+ (row.state.inner() == filter_state).then_some(id)
+ };
+ let filter_row =
+ |n: Result<Notification, notifications::Error>| -> anyhow::Result<Option<NotificationId>> {
+ let n = n?;
+ match &n.kind {
+ NotificationKind::Branch { name } => Ok(matches_state(
+ n.id,
+ NotificationRow::branch(name, head, &n, &repo)?,
+ )),
+ NotificationKind::Cob { typed_id } => Ok(NotificationRow::cob(
+ typed_id, &n, &issues, &patches, &repo,
+ )?
+ .and_then(|row| matches_state(n.id, row))),
+ NotificationKind::Unknown { .. } => Ok(None),
+ }
+ };
+ let notifications = notifs
+ .by_repo(&rid, "repo")?
+ .filter_map(|n| filter_row(n).transpose())
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(notifications)
+}
+
struct NotificationRow {
category: term::Paint<String>,
summary: term::Paint<String>,
@@ -487,14 +573,42 @@ impl NotificationRow {
}
}
-fn clear(mode: Mode, notifs: &mut notifications::StoreWriter) -> anyhow::Result<()> {
+fn clear<R>(
+ mode: Mode,
+ state: Option<String>,
+ notifs: &mut notifications::StoreWriter,
+ storage: &R,
+ profile: &Profile,
+) -> anyhow::Result<()>
+where
+ R: ReadStorage,
+ <R as ReadStorage>::Repository: cob::Store,
+{
let cleared = match mode {
- Mode::All => notifs.clear_all()?,
+ Mode::All => match state {
+ Some(filter) => {
+ let ids = notifications_ids(&filter, notifs, storage, profile)?;
+ notifs.clear(&ids)?
+ }
+ None => notifs.clear_all()?,
+ },
Mode::ById(ids) => notifs.clear(&ids)?,
- Mode::ByRepo(rid) => notifs.clear_by_repo(&rid)?,
+ Mode::ByRepo(rid) => match state {
+ Some(filter) => {
+ let ids = notification_ids_by_repo(rid, &filter, notifs, storage, profile)?;
+ notifs.clear(&ids)?
+ }
+ None => notifs.clear_by_repo(&rid)?,
+ },
Mode::Contextual => {
if let Ok((_, rid)) = radicle::rad::cwd() {
- notifs.clear_by_repo(&rid)?
+ match state {
+ Some(filter) => {
+ let ids = notification_ids_by_repo(rid, &filter, notifs, storage, profile)?;
+ notifs.clear(&ids)?
+ }
+ None => notifs.clear_by_repo(&rid)?,
+ }
} else {
return Err(Error::WithHint {
err: anyhow!("not a radicle repository"),
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 1e74e5b4-7ad9-4778-9951-0da2ded78f41 -v /opt/radcis/ci.rad.levitte.org/cci/state/1e74e5b4-7ad9-4778-9951-0da2ded78f41/s:/1e74e5b4-7ad9-4778-9951-0da2ded78f41/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/1e74e5b4-7ad9-4778-9951-0da2ded78f41/w:/1e74e5b4-7ad9-4778-9951-0da2ded78f41/w -w /1e74e5b4-7ad9-4778-9951-0da2ded78f41/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /1e74e5b4-7ad9-4778-9951-0da2ded78f41/s/script.sh
+ cargo --version
info: syncing channel updates for '1.77-x86_64-unknown-linux-gnu'
info: latest update on 2024-04-09, rust version 1.77.2 (25ef9e3d8 2024-04-09)
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.77.2 (e52e36006 2024-03-26)
+ rustc --version
rustc 1.77.2 (25ef9e3d8 2024-04-09)
+ cargo fmt --check
error: 'cargo-fmt' is not installed for the toolchain '1.77-x86_64-unknown-linux-gnu'.
To install, run `rustup component add --toolchain 1.77-x86_64-unknown-linux-gnu rustfmt`
Exit code: 1
{
"response": "finished",
"result": "failure"
}