cli: From "tracking" to "seeding" and "following"
Split the commands and change the naming. * Tracking a repo is now called "seeding" * Tracking a peer is now called "following" In this commit, we don't change most of the internal code, only the user-facing part.
This commit is contained in:
parent
ee9ee69130
commit
4605348d68
|
|
@ -6,15 +6,15 @@ necessary.
|
|||
Instead, we want to fetch the project from the network into our local
|
||||
storage. In this scenario, we know that the project is
|
||||
`rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji`. In order to fetch it, we first
|
||||
have to track the project.
|
||||
have to update our seeding policy for the project.
|
||||
|
||||
```
|
||||
$ rad track rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-fetch
|
||||
✓ Tracking policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted'
|
||||
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-fetch
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
```
|
||||
|
||||
Now that the project is tracked we can fetch it and we will have it in
|
||||
our local storage. Note that the `track` command can also be told to fetch
|
||||
Now that the project is seeding we can fetch it and we will have it in
|
||||
our local storage. Note that the `seed` command can also be told to fetch
|
||||
by passing the `--fetch` option.
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice,
|
|||
Bob tries to fetch it, and even though he's connected to Alice, it fails.
|
||||
|
||||
``` ~bob
|
||||
$ rad track rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
|
||||
✓ Tracking policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'trusted'
|
||||
$ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --scope trusted
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'trusted'
|
||||
$ rad ls
|
||||
```
|
||||
``` ~bob (fail)
|
||||
|
|
|
|||
|
|
@ -18,22 +18,22 @@ $ rad node status
|
|||
✓ Node is running.
|
||||
```
|
||||
|
||||
The node also allows us to query data that it has access too such as
|
||||
the tracking relationships and the routing table. Before we explore
|
||||
those commands we'll first track a peer so that we have something to
|
||||
The node also allows us to query data that it has access to such as
|
||||
the follow policies and the routing table. Before we explore
|
||||
those commands we'll first follow a peer so that we have something to
|
||||
see.
|
||||
|
||||
```
|
||||
$ rad track did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias Bob
|
||||
✓ Tracking policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (Bob)
|
||||
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias Bob
|
||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (Bob)
|
||||
```
|
||||
|
||||
Now, when we use the `rad node tracking` command we will see
|
||||
information for repositories that we track -- in this case a
|
||||
Now, when we use the `rad node seeding` command we will see
|
||||
information for repositories that we seed -- in this case a
|
||||
repository that was already created:
|
||||
|
||||
```
|
||||
$ rad node tracking
|
||||
$ rad node seeding
|
||||
╭──────────────────────────────────────────────────────╮
|
||||
│ RID Scope Policy │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
|
|
@ -41,12 +41,10 @@ $ rad node tracking
|
|||
╰──────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
This is the same as using the `--repos` flag, but if we wish to see
|
||||
which nodes we are specifically tracking, then we use the `--nodes`
|
||||
flag:
|
||||
If we wish to see which nodes we are following:
|
||||
|
||||
```
|
||||
$ rad node tracking --nodes
|
||||
$ rad node following
|
||||
╭───────────────────────────────────────────────────────────────────────────╮
|
||||
│ DID Alias Policy │
|
||||
├───────────────────────────────────────────────────────────────────────────┤
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ $ rad sync --fetch rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 --seed z6MksmpU5b1dS7oaqF2b
|
|||
✗ Error: to sync a repository, your node must be running. To start it, run `rad node start`
|
||||
```
|
||||
|
||||
Note that tracking works fine without a running node:
|
||||
Note that seeding works fine without a running node:
|
||||
|
||||
``` ~alice
|
||||
$ rad track rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
||||
✓ Tracking policy updated for rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 with scope 'trusted'
|
||||
$ rad seed rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
||||
✓ Seeding policy updated for rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 with scope 'all'
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ To configure our node's tracking policy, we can use the `rad track` command.
|
|||
For example, let's track a remote node we know about, and alias it to "eve":
|
||||
|
||||
```
|
||||
$ rad track did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
|
||||
✓ Tracking policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
|
||||
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
|
||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
|
||||
```
|
||||
|
||||
Now let's track one of Eve's repositories:
|
||||
|
||||
```
|
||||
$ rad track rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted --no-fetch
|
||||
✓ Tracking policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted'
|
||||
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted --no-fetch
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted'
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Back to being the project maintainer.
|
||||
|
||||
Changes have been proposed by another person (or peer) via a radicle patch. To
|
||||
follow changes by another, we must 'track' them.
|
||||
follow changes by another, we must 'follow' them.
|
||||
|
||||
```
|
||||
$ rad track did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob
|
||||
✓ Tracking policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob
|
||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
```
|
||||
|
||||
Additionally, we need to add a new 'git remote' to our working copy for the
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ pub mod rad_cob;
|
|||
pub mod rad_config;
|
||||
#[path = "commands/diff.rs"]
|
||||
pub mod rad_diff;
|
||||
#[path = "commands/follow.rs"]
|
||||
pub mod rad_follow;
|
||||
#[path = "commands/fork.rs"]
|
||||
pub mod rad_fork;
|
||||
#[path = "commands/help.rs"]
|
||||
|
|
@ -36,11 +38,11 @@ pub mod rad_path;
|
|||
pub mod rad_publish;
|
||||
#[path = "commands/remote.rs"]
|
||||
pub mod rad_remote;
|
||||
#[path = "commands/seed.rs"]
|
||||
pub mod rad_seed;
|
||||
#[path = "commands/self.rs"]
|
||||
pub mod rad_self;
|
||||
#[path = "commands/sync.rs"]
|
||||
pub mod rad_sync;
|
||||
#[path = "commands/track.rs"]
|
||||
pub mod rad_track;
|
||||
#[path = "commands/untrack.rs"]
|
||||
pub mod rad_untrack;
|
||||
#[path = "commands/unfollow.rs"]
|
||||
pub mod rad_unfollow;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node::tracking::Alias;
|
||||
use radicle::node::{Handle, NodeId};
|
||||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "follow",
|
||||
description: "Manage node follow policies",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad follow <nid> [--alias <name>] [<option>...]
|
||||
|
||||
The `follow` command takes a Node ID, optionally in DID format, and updates the follow
|
||||
policy for that peer.
|
||||
|
||||
Options
|
||||
|
||||
--alias <name> Associate an alias to a followed peer
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub nid: NodeId,
|
||||
pub alias: Option<Alias>,
|
||||
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 verbose = false;
|
||||
let mut nid: Option<NodeId> = None;
|
||||
let mut alias: Option<Alias> = None;
|
||||
|
||||
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("alias") if alias.is_none() => {
|
||||
let name = parser.value()?;
|
||||
let name = term::args::alias(&name)?;
|
||||
|
||||
alias = Some(name.to_owned());
|
||||
}
|
||||
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"))?,
|
||||
alias,
|
||||
verbose,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
|
||||
follow(options.nid, options.alias, &mut node, &profile)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn follow(
|
||||
nid: NodeId,
|
||||
alias: Option<Alias>,
|
||||
node: &mut Node,
|
||||
profile: &Profile,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let followed = match node.track_node(nid, alias.clone()) {
|
||||
Ok(updated) => updated,
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.track_node(&nid, alias.as_deref())?
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let outcome = if followed { "updated" } else { "exists" };
|
||||
|
||||
if let Some(alias) = alias {
|
||||
term::success!(
|
||||
"Follow policy {outcome} for {} ({alias})",
|
||||
term::format::tertiary(nid),
|
||||
);
|
||||
} else {
|
||||
term::success!(
|
||||
"Follow policy {outcome} for {}",
|
||||
term::format::tertiary(nid),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -28,8 +28,9 @@ const COMMANDS: &[Help] = &[
|
|||
rad_path::HELP,
|
||||
rad_clean::HELP,
|
||||
rad_self::HELP,
|
||||
rad_track::HELP,
|
||||
rad_untrack::HELP,
|
||||
rad_seed::HELP,
|
||||
rad_follow::HELP,
|
||||
rad_unfollow::HELP,
|
||||
rad_remote::HELP,
|
||||
rad_sync::HELP,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ use crate::terminal::Element as _;
|
|||
mod control;
|
||||
#[path = "node/events.rs"]
|
||||
mod events;
|
||||
#[path = "node/policies.rs"]
|
||||
mod policies;
|
||||
#[path = "node/routing.rs"]
|
||||
mod routing;
|
||||
#[path = "node/tracking.rs"]
|
||||
mod tracking;
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "node",
|
||||
|
|
@ -32,7 +32,8 @@ Usage
|
|||
rad node logs [-n <lines>]
|
||||
rad node connect <nid>@<addr> [<option>...]
|
||||
rad node routing [--rid <rid>] [--nid <nid>] [--json] [<option>...]
|
||||
rad node tracking [--repos | --nodes] [<option>...]
|
||||
rad node following [<option>...]
|
||||
rad node seeding [<option>...]
|
||||
rad node events [--timeout <secs>] [-n <count>] [<option>...]
|
||||
rad node config
|
||||
|
||||
|
|
@ -49,11 +50,6 @@ Routing options
|
|||
--nid <nid> Show the routing table entries for the given NID
|
||||
--json Output the routing table as json
|
||||
|
||||
Tracking options
|
||||
|
||||
--repos Show the tracked repositories table
|
||||
--nodes Show the tracked nodes table
|
||||
|
||||
Events options
|
||||
|
||||
--timeout <secs> How long to wait to receive an event before giving up
|
||||
|
|
@ -95,16 +91,8 @@ pub enum Operation {
|
|||
Status,
|
||||
Sessions,
|
||||
Stop,
|
||||
Tracking {
|
||||
mode: TrackingMode,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum TrackingMode {
|
||||
#[default]
|
||||
Repos,
|
||||
Nodes,
|
||||
Following,
|
||||
Seeding,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
|
|
@ -119,7 +107,8 @@ pub enum OperationName {
|
|||
Status,
|
||||
Sessions,
|
||||
Stop,
|
||||
Tracking,
|
||||
Following,
|
||||
Seeding,
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
|
|
@ -130,7 +119,6 @@ impl Args for Options {
|
|||
let mut options = vec![];
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut op: Option<OperationName> = None;
|
||||
let mut tracking_mode = TrackingMode::default();
|
||||
let mut nid: Option<NodeId> = None;
|
||||
let mut rid: Option<Id> = None;
|
||||
let mut json: bool = false;
|
||||
|
|
@ -154,7 +142,8 @@ impl Args for Options {
|
|||
"start" => op = Some(OperationName::Start),
|
||||
"status" => op = Some(OperationName::Status),
|
||||
"stop" => op = Some(OperationName::Stop),
|
||||
"tracking" => op = Some(OperationName::Tracking),
|
||||
"seeding" => op = Some(OperationName::Seeding),
|
||||
"following" => op = Some(OperationName::Following),
|
||||
"sessions" => op = Some(OperationName::Sessions),
|
||||
|
||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||
|
|
@ -181,12 +170,6 @@ impl Args for Options {
|
|||
let val = parser.value()?;
|
||||
count = term::args::number(&val)?;
|
||||
}
|
||||
Long("repos") if matches!(op, Some(OperationName::Tracking)) => {
|
||||
tracking_mode = TrackingMode::Repos
|
||||
}
|
||||
Long("nodes") if matches!(op, Some(OperationName::Tracking)) => {
|
||||
tracking_mode = TrackingMode::Nodes;
|
||||
}
|
||||
Long("foreground") if matches!(op, Some(OperationName::Start)) => {
|
||||
foreground = true;
|
||||
}
|
||||
|
|
@ -222,9 +205,8 @@ impl Args for Options {
|
|||
OperationName::Status => Operation::Status,
|
||||
OperationName::Sessions => Operation::Sessions,
|
||||
OperationName::Stop => Operation::Stop,
|
||||
OperationName::Tracking => Operation::Tracking {
|
||||
mode: tracking_mode,
|
||||
},
|
||||
OperationName::Seeding => Operation::Seeding,
|
||||
OperationName::Following => Operation::Following,
|
||||
};
|
||||
Ok((Options { op }, vec![]))
|
||||
}
|
||||
|
|
@ -266,7 +248,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
Operation::Stop => {
|
||||
control::stop(node)?;
|
||||
}
|
||||
Operation::Tracking { mode } => tracking::run(&profile, mode)?,
|
||||
Operation::Seeding => policies::seeding(&profile)?,
|
||||
Operation::Following => policies::following(&profile)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,24 +1,13 @@
|
|||
use radicle::crypto::PublicKey;
|
||||
use radicle::node::{tracking, AliasStore, TRACKING_DB_FILE};
|
||||
use radicle::node::{tracking, AliasStore};
|
||||
use radicle::prelude::Did;
|
||||
use radicle::Profile;
|
||||
|
||||
use crate::terminal as term;
|
||||
use term::Element;
|
||||
|
||||
use super::TrackingMode;
|
||||
|
||||
pub fn run(profile: &Profile, mode: TrackingMode) -> anyhow::Result<()> {
|
||||
let store =
|
||||
radicle::node::tracking::store::Config::reader(profile.home.node().join(TRACKING_DB_FILE))?;
|
||||
match mode {
|
||||
TrackingMode::Repos => print_repos(&store)?,
|
||||
TrackingMode::Nodes => print_nodes(&store, &profile.aliases())?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_repos(store: &tracking::store::ConfigReader) -> anyhow::Result<()> {
|
||||
pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
|
||||
let store = profile.tracking()?;
|
||||
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
||||
t.push([
|
||||
term::format::default(String::from("RID")),
|
||||
|
|
@ -43,10 +32,9 @@ fn print_repos(store: &tracking::store::ConfigReader) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn print_nodes(
|
||||
store: &tracking::store::ConfigReader,
|
||||
aliases: &impl AliasStore,
|
||||
) -> anyhow::Result<()> {
|
||||
pub fn following(profile: &Profile) -> anyhow::Result<()> {
|
||||
let store = profile.tracking()?;
|
||||
let aliases = profile.aliases();
|
||||
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
||||
t.push([
|
||||
term::format::default(String::from("DID")),
|
||||
|
|
@ -59,7 +47,7 @@ fn print_nodes(
|
|||
t.push([
|
||||
term::format::highlight(Did::from(id).to_string()),
|
||||
match alias {
|
||||
None => term::format::secondary(fallback_alias(&id, aliases)),
|
||||
None => term::format::secondary(fallback_alias(&id, &aliases)),
|
||||
Some(alias) => term::format::secondary(alias.to_string()),
|
||||
},
|
||||
term::format::secondary(policy.to_string()),
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
use std::ffi::OsString;
|
||||
use std::time;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node::tracking::Scope;
|
||||
use radicle::node::Handle;
|
||||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::commands::rad_sync as sync;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::{project, terminal as term};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "seed",
|
||||
description: "Manage repository seeding policies",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad seed <rid> [-d | --delete] [--[no-]fetch] [--scope <scope>] [<option>...]
|
||||
|
||||
The `seed` command takes a Repository ID (<rid>) and updates the seeding policy
|
||||
for that repository. By default, a seeding policy will be created or updated.
|
||||
To delete a policy, use the `--delete` flag.
|
||||
|
||||
When seeding a repository, a scope can be specified: this can be either `all` or
|
||||
`trusted`. When using `all`, all remote nodes will be followed for that repository.
|
||||
On the other hand, with `trusted`, only the repository delegates will be followed,
|
||||
plus any remote that is explicitly followed via `rad follow <nid>`.
|
||||
|
||||
Options
|
||||
|
||||
--delete, -d Delete the seeding policy
|
||||
--[no-]fetch Fetch repository after updating seeding policy
|
||||
--scope <scope> Peer follow scope for a repository
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub rid: Id,
|
||||
pub scope: Scope,
|
||||
pub delete: bool,
|
||||
pub fetch: bool,
|
||||
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 rid: Option<Id> = None;
|
||||
let mut scope: Option<Scope> = None;
|
||||
let mut fetch = true;
|
||||
let mut delete = false;
|
||||
let mut verbose = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match &arg {
|
||||
Value(val) => {
|
||||
rid = Some(term::args::rid(val)?);
|
||||
}
|
||||
Long("scope") if scope.is_none() => {
|
||||
let val = parser.value()?;
|
||||
scope = Some(term::args::parse_value("scope", val)?);
|
||||
}
|
||||
Long("delete") | Short('d') => delete = true,
|
||||
Long("fetch") => fetch = true,
|
||||
Long("no-fetch") => fetch = false,
|
||||
Long("verbose") | Short('v') => verbose = true,
|
||||
Long("help") | Short('h') => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scope.is_some() && delete {
|
||||
anyhow::bail!("`--scope` may not be used with `--delete` or `-d`");
|
||||
}
|
||||
if fetch && delete {
|
||||
anyhow::bail!("`--fetch` may not be used with `--delete` or `-d`");
|
||||
}
|
||||
|
||||
Ok((
|
||||
Options {
|
||||
rid: rid.ok_or_else(|| anyhow!("a Repository ID must be specified"))?,
|
||||
scope: scope.unwrap_or(Scope::All),
|
||||
delete,
|
||||
fetch,
|
||||
verbose,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
let rid = options.rid;
|
||||
let scope = options.scope;
|
||||
|
||||
if options.delete {
|
||||
delete(rid, &mut node, &profile)?;
|
||||
} else {
|
||||
update(rid, scope, &mut node, &profile)?;
|
||||
|
||||
if options.fetch && node.is_running() {
|
||||
sync::fetch(
|
||||
rid,
|
||||
sync::RepoSync::default(),
|
||||
time::Duration::from_secs(6),
|
||||
&mut node,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
rid: Id,
|
||||
scope: Scope,
|
||||
node: &mut Node,
|
||||
profile: &Profile,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let updated = project::track(rid, scope, node, profile)?;
|
||||
let outcome = if updated { "updated" } else { "exists" };
|
||||
|
||||
term::success!(
|
||||
"Seeding policy {outcome} for {} with scope '{scope}'",
|
||||
term::format::tertiary(rid),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete(rid: Id, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
|
||||
if project::untrack(rid, node, profile)? {
|
||||
term::success!("Seeding policy for {} removed", term::format::tertiary(rid));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
use std::ffi::OsString;
|
||||
use std::time;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node::tracking::{Alias, Scope};
|
||||
use radicle::node::{Handle, NodeId};
|
||||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::commands::rad_sync as sync;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::{project, terminal as term};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "track",
|
||||
description: "Manage repository and node tracking policy",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad track <nid> [--alias <name>] [<option>...]
|
||||
rad track <rid> [--[no-]fetch] [--scope <scope>] [<option>...]
|
||||
|
||||
The `track` command takes either an NID or an RID. Based on the argument, it will
|
||||
either update the tracking policy of a node (NID), or a repository (RID).
|
||||
|
||||
When tracking a repository, a scope can be specified: this can be either `all` or
|
||||
`trusted`. When using `all`, all remote nodes will be tracked for that repository.
|
||||
On the other hand, with `trusted`, only the repository delegates will be tracked,
|
||||
plus any remote that is explicitly tracked via `rad track <nid>`.
|
||||
|
||||
Options
|
||||
|
||||
--alias <name> Associate an alias to a tracked node
|
||||
--[no-]fetch Fetch refs after tracking
|
||||
--scope <scope> Node (remote) tracking scope for a repository
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Operation {
|
||||
TrackNode { nid: NodeId, alias: Option<Alias> },
|
||||
TrackRepo { rid: Id, scope: Scope },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub op: Operation,
|
||||
pub fetch: bool,
|
||||
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 op: Option<Operation> = None;
|
||||
let mut fetch = true;
|
||||
let mut verbose = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match (&arg, &mut op) {
|
||||
(Value(val), None) => {
|
||||
if let Ok(rid) = term::args::rid(val) {
|
||||
op = Some(Operation::TrackRepo {
|
||||
rid,
|
||||
scope: Scope::default(),
|
||||
});
|
||||
} else if let Ok(did) = term::args::did(val) {
|
||||
op = Some(Operation::TrackNode {
|
||||
nid: did.into(),
|
||||
alias: None,
|
||||
});
|
||||
} else if let Ok(nid) = term::args::nid(val) {
|
||||
op = Some(Operation::TrackNode { nid, alias: None });
|
||||
}
|
||||
}
|
||||
(Long("alias"), Some(Operation::TrackNode { alias, .. })) => {
|
||||
let name = parser.value()?;
|
||||
let name = term::args::alias(&name)?;
|
||||
|
||||
*alias = Some(name.to_owned());
|
||||
}
|
||||
(Long("scope"), Some(Operation::TrackRepo { scope, .. })) => {
|
||||
let val = parser.value()?;
|
||||
|
||||
*scope = term::args::parse_value("scope", val)?;
|
||||
}
|
||||
(Long("fetch"), Some(Operation::TrackRepo { .. })) => fetch = true,
|
||||
(Long("no-fetch"), Some(Operation::TrackRepo { .. })) => fetch = false,
|
||||
(Long("verbose") | Short('v'), _) => verbose = true,
|
||||
(Long("help") | Short('h'), _) => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
Options {
|
||||
op: op.ok_or_else(|| anyhow!("either a NID or an RID must be specified"))?,
|
||||
fetch,
|
||||
verbose,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
|
||||
match options.op {
|
||||
Operation::TrackNode { nid, alias } => {
|
||||
track_node(nid, alias, &mut node, &profile)?;
|
||||
}
|
||||
Operation::TrackRepo { rid, scope } => {
|
||||
track_repo(rid, scope, &mut node, &profile)?;
|
||||
|
||||
if options.fetch && node.is_running() {
|
||||
sync::fetch(
|
||||
rid,
|
||||
sync::RepoSync::default(),
|
||||
time::Duration::from_secs(6),
|
||||
&mut node,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn track_repo(
|
||||
rid: Id,
|
||||
scope: Scope,
|
||||
node: &mut Node,
|
||||
profile: &Profile,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let tracked = project::track(rid, scope, node, profile)?;
|
||||
let outcome = if tracked { "updated" } else { "exists" };
|
||||
|
||||
term::success!(
|
||||
"Tracking policy {outcome} for {} with scope '{scope}'",
|
||||
term::format::tertiary(rid),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn track_node(
|
||||
nid: NodeId,
|
||||
alias: Option<Alias>,
|
||||
node: &mut Node,
|
||||
profile: &Profile,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let tracked = match node.track_node(nid, alias.clone()) {
|
||||
Ok(updated) => updated,
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.track_node(&nid, alias.as_deref())?
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let outcome = if tracked { "updated" } else { "exists" };
|
||||
|
||||
if let Some(alias) = alias {
|
||||
term::success!(
|
||||
"Tracking policy {outcome} for {} ({alias})",
|
||||
term::format::tertiary(nid),
|
||||
);
|
||||
} else {
|
||||
term::success!(
|
||||
"Tracking policy {outcome} for {}",
|
||||
term::format::tertiary(nid),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node::{Handle, NodeId};
|
||||
|
||||
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!("CARGO_PKG_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 fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
let nid = options.nid;
|
||||
|
||||
let unfollowed = match node.untrack_node(nid) {
|
||||
Ok(updated) => updated,
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.untrack_node(&nid)?
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
if unfollowed {
|
||||
term::success!("Follow policy for {} removed", term::format::tertiary(nid),);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node::{Handle, NodeId};
|
||||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::project;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "untrack",
|
||||
description: "Untrack a repository or node",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad untrack <nid> [<option>...]
|
||||
rad untrack <rid> [<option>...]
|
||||
|
||||
The `untrack` command takes either an NID or an RID. Based on the argument, it will
|
||||
either update the tracking policy of a node (NID), or a repository (RID).
|
||||
|
||||
Options
|
||||
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Operation {
|
||||
UntrackNode { nid: NodeId },
|
||||
UntrackRepo { rid: Id },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub op: Operation,
|
||||
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 op: Option<Operation> = None;
|
||||
let mut verbose = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match (&arg, &mut op) {
|
||||
(Value(val), None) => {
|
||||
if let Ok(rid) = term::args::rid(val) {
|
||||
op = Some(Operation::UntrackRepo { rid });
|
||||
} else if let Ok(did) = term::args::did(val) {
|
||||
op = Some(Operation::UntrackNode { nid: did.into() });
|
||||
} else if let Ok(nid) = term::args::nid(val) {
|
||||
op = Some(Operation::UntrackNode { nid });
|
||||
}
|
||||
}
|
||||
(Long("verbose") | Short('v'), _) => verbose = true,
|
||||
(Long("help") | Short('h'), _) => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
Options {
|
||||
op: op.ok_or_else(|| anyhow!("either an NID or an RID must be specified"))?,
|
||||
verbose,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
|
||||
match options.op {
|
||||
Operation::UntrackNode { nid } => untrack_node(nid, &mut node, &profile),
|
||||
Operation::UntrackRepo { rid } => untrack_repo(rid, &mut node, &profile),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn untrack_repo(rid: Id, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
|
||||
if project::untrack(rid, node, profile)? {
|
||||
term::success!(
|
||||
"Tracking policy for {} removed",
|
||||
term::format::tertiary(rid),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn untrack_node(nid: NodeId, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
|
||||
let untracked = match node.untrack_node(nid) {
|
||||
Ok(updated) => updated,
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.untrack_node(&nid)?
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
if untracked {
|
||||
term::success!(
|
||||
"Tracking policy for {} removed",
|
||||
term::format::tertiary(nid),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -139,6 +139,13 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
|||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"follow" => {
|
||||
term::run_command_args::<rad_follow::Options, _>(
|
||||
rad_follow::HELP,
|
||||
rad_follow::run,
|
||||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"fork" => {
|
||||
term::run_command_args::<rad_fork::Options, _>(
|
||||
rad_fork::HELP,
|
||||
|
|
@ -229,17 +236,17 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
|||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"track" => {
|
||||
term::run_command_args::<rad_track::Options, _>(
|
||||
rad_track::HELP,
|
||||
rad_track::run,
|
||||
"seed" => {
|
||||
term::run_command_args::<rad_seed::Options, _>(
|
||||
rad_seed::HELP,
|
||||
rad_seed::run,
|
||||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"untrack" => {
|
||||
term::run_command_args::<rad_untrack::Options, _>(
|
||||
rad_untrack::HELP,
|
||||
rad_untrack::run,
|
||||
"unfollow" => {
|
||||
term::run_command_args::<rad_unfollow::Options, _>(
|
||||
rad_unfollow::HELP,
|
||||
rad_unfollow::run,
|
||||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,7 @@ fn test_replication_via_seed() {
|
|||
.unwrap();
|
||||
|
||||
alice
|
||||
.rad("track", &[&bob.id.to_human()], working.join("alice"))
|
||||
.rad("follow", &[&bob.id.to_human()], working.join("alice"))
|
||||
.unwrap();
|
||||
|
||||
alice.routes_to(&[(rid, alice.id), (rid, seed.id)]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue