rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwoodb276ae03ee8b33932ad2d968f2e3e5420a2dd73d
{
"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:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Updated",
"patch": {
"id": "01fde0e26aa0c735f89f78ed50efb1a284360038",
"author": {
"id": "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"alias": "erikli"
},
"title": "cli/unfollow: Use clap",
"state": {
"status": "open",
"conflicts": []
},
"before": "4787b53b1e85d8052744fc77e4160e4d90e46d0f",
"after": "b276ae03ee8b33932ad2d968f2e3e5420a2dd73d",
"commits": [
"b276ae03ee8b33932ad2d968f2e3e5420a2dd73d"
],
"target": "4787b53b1e85d8052744fc77e4160e4d90e46d0f",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "01fde0e26aa0c735f89f78ed50efb1a284360038",
"author": {
"id": "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"alias": "erikli"
},
"description": "See `issue/7fb03f234030b91c38cc4f5b48bd30cf5fd6a1de`.",
"base": "f1c7c9860716e5db88f657c61d39d6081fb5645f",
"oid": "6eefef57110c58ee745fbc366e327f8e958c925b",
"timestamp": 1759130208
},
{
"id": "03bf7bbb77c88f57e6d24880f3c33b75cef97b82",
"author": {
"id": "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"alias": "erikli"
},
"description": "- move args to module",
"base": "f1c7c9860716e5db88f657c61d39d6081fb5645f",
"oid": "88a1f452723974f45903e84688bdca36c96a3e37",
"timestamp": 1759227984
},
{
"id": "4ffe80ce96ae80dbb37c5fb13b8cba84478c38d9",
"author": {
"id": "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"alias": "erikli"
},
"description": "Squash review.",
"base": "f1c7c9860716e5db88f657c61d39d6081fb5645f",
"oid": "ecfb218064a46fc6a08f2e030e55b15b120c8ee4",
"timestamp": 1759231315
},
{
"id": "9730c38b952278097ed2fd44832ff62b1876b871",
"author": {
"id": "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"alias": "erikli"
},
"description": "Rebased.",
"base": "4787b53b1e85d8052744fc77e4160e4d90e46d0f",
"oid": "b276ae03ee8b33932ad2d968f2e3e5420a2dd73d",
"timestamp": 1759313827
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "e0a9069f-2a71-4d21-a269-dbf3c347fe9d"
},
"info_url": "https://cci.rad.levitte.org//e0a9069f-2a71-4d21-a269-dbf3c347fe9d.html"
}
Started at: 2025-10-01 12:17:11.843097+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW added
✓ Remote-tracking branch cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW/master created for z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz added
✓ Remote-tracking branch erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz/master created for z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz
✓ 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/e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 123 issues · 18 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 01fde0e26aa0c735f89f78ed50efb1a284360038
✓ Switched to branch patch/01fde0e at revision 4ffe80c
✓ Branch patch/01fde0e setup to track rad/patches/01fde0e26aa0c735f89f78ed50efb1a284360038
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout b276ae03ee8b33932ad2d968f2e3e5420a2dd73d
HEAD is now at b276ae03 cli/unfollow: Use clap
Exit code: 0
$ git show b276ae03ee8b33932ad2d968f2e3e5420a2dd73d
commit b276ae03ee8b33932ad2d968f2e3e5420a2dd73d
Author: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Date: Mon Sep 29 09:04:50 2025 +0200
cli/unfollow: Use clap
diff --git a/crates/radicle-cli/src/commands/help.rs b/crates/radicle-cli/src/commands/help.rs
index 99d5d814..0348d371 100644
--- a/crates/radicle-cli/src/commands/help.rs
+++ b/crates/radicle-cli/src/commands/help.rs
@@ -61,11 +61,19 @@ const COMMANDS: &[CommandItem] = &[
CommandItem::Lexopt(crate::commands::seed::HELP),
CommandItem::Lexopt(crate::commands::follow::HELP),
CommandItem::Lexopt(crate::commands::unblock::HELP),
+<<<<<<< HEAD
CommandItem::Lexopt(crate::commands::unfollow::HELP),
CommandItem::Clap {
name: "unseed",
about: crate::commands::unseed::ABOUT,
},
+=======
+ CommandItem::Clap {
+ name: "unfollow",
+ about: crate::commands::unfollow::ABOUT,
+ },
+ CommandItem::Lexopt(crate::commands::unseed::HELP),
+>>>>>>> ecfb21806 (cli/unfollow: Use clap)
CommandItem::Lexopt(crate::commands::remote::HELP),
CommandItem::Clap {
name: "stats",
diff --git a/crates/radicle-cli/src/commands/unfollow.rs b/crates/radicle-cli/src/commands/unfollow.rs
index d821a125..0932ed87 100644
--- a/crates/radicle-cli/src/commands/unfollow.rs
+++ b/crates/radicle-cli/src/commands/unfollow.rs
@@ -1,77 +1,13 @@
-use std::ffi::OsString;
+mod args;
-use anyhow::anyhow;
-
-use radicle::node::{Handle, NodeId};
+use radicle::node::Handle;
use crate::terminal as term;
-use crate::terminal::args::{Args, Error, Help};
-
-pub const HELP: Help = Help {
- name: "unfollow",
- description: "Unfollow a peer",
- version: env!("RADICLE_VERSION"),
- usage: r#"
-Usage
-
- rad unfollow <nid> [<option>...]
-
- The `unfollow` command takes a Node ID (<nid>), optionally in DID format,
- and removes the follow policy for that peer.
-
-Options
-
- --verbose, -v Verbose output
- --help Print help
-"#,
-};
-
-#[derive(Debug)]
-pub struct Options {
- pub nid: NodeId,
- pub verbose: bool,
-}
-
-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 nid: Option<NodeId> = None;
- let mut verbose = false;
-
- while let Some(arg) = parser.next()? {
- match &arg {
- Value(val) if nid.is_none() => {
- if let Ok(did) = term::args::did(val) {
- nid = Some(did.into());
- } else if let Ok(val) = term::args::nid(val) {
- nid = Some(val);
- } else {
- anyhow::bail!("invalid Node ID `{}` specified", val.to_string_lossy());
- }
- }
- Long("verbose") | Short('v') => verbose = true,
- Long("help") | Short('h') => {
- return Err(Error::Help.into());
- }
- _ => {
- return Err(anyhow!(arg.unexpected()));
- }
- }
- }
-
- Ok((
- Options {
- nid: nid.ok_or_else(|| anyhow!("a Node ID must be specified"))?,
- verbose,
- },
- vec![],
- ))
- }
-}
+pub use args::Args;
+pub(crate) use args::ABOUT;
-pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+pub fn run(options: Args, ctx: impl term::Context) -> anyhow::Result<()> {
let profile = ctx.profile()?;
let mut node = radicle::Node::new(profile.socket());
let nid = options.nid;
diff --git a/crates/radicle-cli/src/commands/unfollow/args.rs b/crates/radicle-cli/src/commands/unfollow/args.rs
new file mode 100644
index 00000000..2822a923
--- /dev/null
+++ b/crates/radicle-cli/src/commands/unfollow/args.rs
@@ -0,0 +1,39 @@
+use clap::Parser;
+
+use thiserror::Error;
+
+use radicle::node::NodeId;
+use radicle::prelude::Did;
+
+pub(crate) const ABOUT: &str = "Unfollow a peer";
+
+const LONG_ABOUT: &str = r#"
+The `unfollow` command takes a Node ID, optionally in DID format,
+and removes the follow policy for that peer."#;
+
+#[derive(Debug, Error)]
+#[error("invalid Node ID specified (Node ID parsing failed with: '{nid}', DID parsing failed with: '{did}'))")]
+struct NodeIdParseError {
+ did: radicle::identity::did::DidError,
+ nid: radicle::crypto::PublicKeyError,
+}
+
+fn parse_nid(value: &str) -> Result<NodeId, NodeIdParseError> {
+ value.parse::<Did>().map(NodeId::from).or_else(|did| {
+ value
+ .parse::<NodeId>()
+ .map_err(|nid| NodeIdParseError { nid, did })
+ })
+}
+
+#[derive(Debug, Parser)]
+#[command(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
+pub struct Args {
+ /// Node ID (optionally in DID format) of the peer to unfollow
+ #[arg(value_name = "NID", value_parser = parse_nid)]
+ pub nid: NodeId,
+
+ /// Verbose output
+ #[arg(short, long)]
+ pub verbose: bool,
+}
diff --git a/crates/radicle-cli/src/main.rs b/crates/radicle-cli/src/main.rs
index 2bd6e543..f71c98d1 100644
--- a/crates/radicle-cli/src/main.rs
+++ b/crates/radicle-cli/src/main.rs
@@ -47,6 +47,7 @@ struct CliArgs {
enum Commands {
Issue(issue::Args),
Stats(stats::Args),
+ Unfollow(unfollow::Args),
Unseed(unseed::Args),
}
@@ -272,11 +273,9 @@ pub(crate) fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyho
);
}
"unfollow" => {
- term::run_command_args::<unfollow::Options, _>(
- unfollow::HELP,
- unfollow::run,
- args.to_vec(),
- );
+ if let Some(Commands::Unfollow(args)) = CliArgs::parse().command {
+ term::run_command_fn(unfollow::run, args);
+ }
}
"unseed" => {
if let Some(Commands::Unseed(args)) = CliArgs::parse().command {
Exit code: 0
shell: 'export RUSTDOCFLAGS=''-D warnings'' cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name e0a9069f-2a71-4d21-a269-dbf3c347fe9d -v /opt/radcis/ci.rad.levitte.org/cci/state/e0a9069f-2a71-4d21-a269-dbf3c347fe9d/s:/e0a9069f-2a71-4d21-a269-dbf3c347fe9d/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w:/e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w -w /e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:bookworm bash /e0a9069f-2a71-4d21-a269-dbf3c347fe9d/s/script.sh
+ export 'RUSTDOCFLAGS=-D warnings'
+ RUSTDOCFLAGS='-D warnings'
+ cargo --version
info: syncing channel updates for '1.88-x86_64-unknown-linux-gnu'
info: latest update on 2025-06-26, rust version 1.88.0 (6b00bc388 2025-06-23)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-src'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-src'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
cargo 1.88.0 (873a06493 2025-05-10)
+ rustc --version
rustc 1.88.0 (6b00bc388 2025-06-23)
+ cargo fmt --check
error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `CommandItem`
--> /e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w/crates/radicle-cli/src/commands/help.rs:65:5
|
64 | <<<<<<< HEAD
| - expected one of 7 possible tokens
65 | CommandItem::Lexopt(crate::commands::unfollow::HELP),
| ^^^^^^^^^^^ unexpected token
Error writing files: failed to resolve mod `help`: cannot parse /e0a9069f-2a71-4d21-a269-dbf3c347fe9d/w/crates/radicle-cli/src/commands/help.rs
Exit code: 1
{
"response": "finished",
"result": "failure"
}