cli: Remove `rad fetch` command
The command is replaced with `rad sync --fetch`. Soon, it will be called from the git-remote-helper, so users won't have to call it explicitly.
This commit is contained in:
parent
a285943166
commit
d6cebf613f
|
|
@ -18,7 +18,7 @@ Now that the project is tracked we can fetch it and we will have it in
|
|||
our local storage.
|
||||
|
||||
```
|
||||
$ rad fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Changes have been proposed by another person (or peer) via a radicle patch. To
|
|||
$ rad track did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob
|
||||
✓ Tracking policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
! Warning: fetch after track is not yet supported
|
||||
$ rad fetch
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ pub mod rad_comment;
|
|||
pub mod rad_delegate;
|
||||
#[path = "commands/edit.rs"]
|
||||
pub mod rad_edit;
|
||||
#[path = "commands/fetch.rs"]
|
||||
pub mod rad_fetch;
|
||||
#[path = "commands/fork.rs"]
|
||||
pub mod rad_fork;
|
||||
#[path = "commands/help.rs"]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use radicle::storage;
|
|||
use radicle::storage::git::Storage;
|
||||
|
||||
use crate::commands::rad_checkout as checkout;
|
||||
use crate::commands::rad_fetch as fetch;
|
||||
use crate::commands::rad_sync as sync;
|
||||
use crate::project;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
|
@ -176,7 +176,7 @@ pub fn clone<G: Signer>(
|
|||
);
|
||||
}
|
||||
|
||||
let results = fetch::fetch(id, node)?;
|
||||
let results = sync::fetch_all(id, node)?;
|
||||
let Ok(repository) = storage.repository(id) else {
|
||||
// If we don't have the project locally, even after attempting to fetch,
|
||||
// there's nothing we can do.
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
#![allow(clippy::or_fun_call)]
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use radicle::identity::doc::Id;
|
||||
use radicle::node;
|
||||
use radicle::node::{FetchResult, FetchResults, Handle as _, Node};
|
||||
use radicle::prelude::*;
|
||||
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "fetch",
|
||||
description: "Fetch repository refs from the network",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad fetch <rid> [<option>...]
|
||||
|
||||
By default, this command will fetch from all connected seeds.
|
||||
To instead specify a seed, use the `--seed <nid>` option.
|
||||
|
||||
Options
|
||||
|
||||
--seed <nid> Fetch seed a specific connected peer
|
||||
--force, -f Fetch even if the repository isn't tracked
|
||||
--help Print help
|
||||
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
rid: Option<Id>,
|
||||
seed: Option<NodeId>,
|
||||
#[allow(dead_code)]
|
||||
force: 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 seed: Option<NodeId> = None;
|
||||
let mut force = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("force") | Short('f') => {
|
||||
force = true;
|
||||
}
|
||||
Long("seed") => {
|
||||
let val = parser.value()?;
|
||||
let val = term::args::nid(&val)?;
|
||||
seed = Some(val);
|
||||
}
|
||||
Long("help") => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
Value(val) if rid.is_none() => {
|
||||
let val = term::args::rid(&val)?;
|
||||
rid = Some(val);
|
||||
}
|
||||
_ => return Err(anyhow!(arg.unexpected())),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((Options { rid, seed, force }, 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 = match options.rid {
|
||||
Some(rid) => rid,
|
||||
None => {
|
||||
let (_, rid) = radicle::rad::repo(Path::new("."))
|
||||
.context("Current directory is not a radicle project")?;
|
||||
|
||||
rid
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(cloudhead): Check that we're tracking the repo, and if not, and `--force` is not
|
||||
// used, abort with error.
|
||||
|
||||
let results = if let Some(seed) = options.seed {
|
||||
let result = fetch_from(rid, &seed, &mut node)?;
|
||||
FetchResults::from(vec![(seed, result)])
|
||||
} else {
|
||||
fetch(rid, &mut node)?
|
||||
};
|
||||
let success = results.success().count();
|
||||
let failed = results.failed().count();
|
||||
|
||||
if success == 0 {
|
||||
term::error(format!("Failed to fetch repository from {failed} seed(s)"));
|
||||
} else {
|
||||
term::success!("Fetched repository from {success} seed(s)");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch(rid: Id, node: &mut Node) -> Result<FetchResults, node::Error> {
|
||||
// Get seeds. This consults the local routing table only.
|
||||
let seeds = node.seeds(rid)?;
|
||||
let mut results = FetchResults::default();
|
||||
|
||||
if seeds.has_connections() {
|
||||
// Fetch from all seeds.
|
||||
for seed in seeds.connected() {
|
||||
let result = fetch_from(rid, seed, node)?;
|
||||
results.push(*seed, result);
|
||||
}
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn fetch_from(rid: Id, seed: &NodeId, node: &mut Node) -> Result<FetchResult, node::Error> {
|
||||
let spinner = term::spinner(format!(
|
||||
"Fetching {} from {}..",
|
||||
term::format::tertiary(rid),
|
||||
term::format::tertiary(term::format::node(seed))
|
||||
));
|
||||
let result = node.fetch(rid, *seed)?;
|
||||
|
||||
match &result {
|
||||
FetchResult::Success { .. } => {
|
||||
spinner.finish();
|
||||
}
|
||||
FetchResult::Failed { reason } => {
|
||||
spinner.error(reason);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ const COMMANDS: &[Help] = &[
|
|||
rad_checkout::HELP,
|
||||
rad_clone::HELP,
|
||||
rad_edit::HELP,
|
||||
rad_fetch::HELP,
|
||||
rad_fork::HELP,
|
||||
rad_help::HELP,
|
||||
rad_id::HELP,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use radicle::cob::patch;
|
|||
use radicle::cob::patch::PatchId;
|
||||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::commands::rad_fetch as fetch;
|
||||
use crate::commands::rad_sync as sync;
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
|
|
@ -259,7 +259,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let repository = profile.storage.repository(id)?;
|
||||
|
||||
if options.fetch {
|
||||
fetch::fetch(repository.id(), &mut Node::new(profile.socket()))?;
|
||||
sync::fetch_all(repository.id(), &mut Node::new(profile.socket()))?;
|
||||
}
|
||||
|
||||
match options.op {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use radicle::node::tracking::store::Config;
|
||||
use radicle::{git::Url, node::TRACKING_DB_FILE, prelude::Id, Profile};
|
||||
use radicle::{git::Url, prelude::Id, Profile};
|
||||
use radicle_crypto::PublicKey;
|
||||
|
||||
use crate::git::add_remote;
|
||||
|
|
@ -14,7 +13,10 @@ pub fn run(
|
|||
) -> anyhow::Result<()> {
|
||||
let name = match name {
|
||||
Some(name) => name,
|
||||
_ => get_remote_name(profile, pubkey)?
|
||||
_ => profile
|
||||
.tracking()?
|
||||
.node_policy(pubkey)?
|
||||
.and_then(|node| node.alias)
|
||||
.ok_or(anyhow::anyhow!("a `name` needs to be specified"))?,
|
||||
};
|
||||
if git::is_remote(repository, &name)? {
|
||||
|
|
@ -24,12 +26,6 @@ pub fn run(
|
|||
let url = Url::from(id).with_namespace(*pubkey);
|
||||
let remote = add_remote(repository, &name, &url)?;
|
||||
term::success!("Remote {} added with {url}", remote.name,);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the `git remote` name from the command `Options` and `pubkey`.
|
||||
fn get_remote_name(profile: &Profile, pubkey: &PublicKey) -> anyhow::Result<Option<String>> {
|
||||
let path = profile.home.node().join(TRACKING_DB_FILE);
|
||||
let storage = Config::reader(path)?;
|
||||
Ok(storage.node_policy(pubkey)?.and_then(|node| node.alias))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use std::{io, time};
|
|||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
|
||||
use radicle::node::Event;
|
||||
use radicle::node::Handle as _;
|
||||
use radicle::prelude::{Id, NodeId};
|
||||
use radicle::node;
|
||||
use radicle::node::{Event, FetchResult, FetchResults, Handle as _, Node};
|
||||
use radicle::prelude::{Id, NodeId, Profile};
|
||||
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
|
@ -20,11 +20,18 @@ pub const HELP: Help = Help {
|
|||
Usage
|
||||
|
||||
rad sync [<rid>] [<option>...]
|
||||
rad sync [<rid>] [--fetch] [--seed <nid>] [<option>...]
|
||||
|
||||
By default, the current repository is synced.
|
||||
|
||||
When `--fetch` is specified, this command will fetch from
|
||||
all connected seeds. To instead specify a seed, use the
|
||||
`--seed <nid>` option in combination with `--fetch`.
|
||||
|
||||
Options
|
||||
|
||||
--fetch, -f Fetch from seeds instead of having seeds fetch from us
|
||||
--seed <nid> Seed to fetch from (use with `--fetch`)
|
||||
--timeout <secs> How many seconds to wait while syncing
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
|
|
@ -32,11 +39,20 @@ Options
|
|||
"#,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub enum SyncMode {
|
||||
Fetch,
|
||||
#[default]
|
||||
Announce,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Options {
|
||||
pub rid: Option<Id>,
|
||||
pub seed: Option<NodeId>,
|
||||
pub verbose: bool,
|
||||
pub timeout: time::Duration,
|
||||
pub mode: SyncMode,
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
|
|
@ -47,12 +63,22 @@ impl Args for Options {
|
|||
let mut verbose = false;
|
||||
let mut timeout = time::Duration::from_secs(9);
|
||||
let mut rid = None;
|
||||
let mut seed = None;
|
||||
let mut mode = SyncMode::default();
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("verbose") | Short('v') => {
|
||||
verbose = true;
|
||||
}
|
||||
Long("seed") if matches!(mode, SyncMode::Fetch) => {
|
||||
let val = parser.value()?;
|
||||
let val = term::args::nid(&val)?;
|
||||
seed = Some(val);
|
||||
}
|
||||
Long("fetch") | Short('f') => {
|
||||
mode = SyncMode::Fetch;
|
||||
}
|
||||
Long("timeout") | Short('t') => {
|
||||
let value = parser.value()?;
|
||||
let secs = term::args::parse_value("timeout", value)?;
|
||||
|
|
@ -76,6 +102,8 @@ impl Args for Options {
|
|||
rid,
|
||||
verbose,
|
||||
timeout,
|
||||
seed,
|
||||
mode,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
|
|
@ -95,7 +123,15 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
};
|
||||
|
||||
let mut node = radicle::Node::new(profile.socket());
|
||||
let events = node.subscribe(options.timeout)?;
|
||||
|
||||
match options.mode {
|
||||
SyncMode::Announce => announce(rid, node, options.timeout),
|
||||
SyncMode::Fetch => fetch(rid, profile, &mut node, options.seed),
|
||||
}
|
||||
}
|
||||
|
||||
fn announce(rid: Id, mut node: Node, timeout: time::Duration) -> anyhow::Result<()> {
|
||||
let events = node.subscribe(timeout)?;
|
||||
let seeds = node.seeds(rid)?;
|
||||
let mut seeds = seeds.connected().collect::<BTreeSet<_>>();
|
||||
|
||||
|
|
@ -144,3 +180,64 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch(
|
||||
rid: Id,
|
||||
profile: Profile,
|
||||
node: &mut Node,
|
||||
seed: Option<NodeId>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !profile.tracking()?.is_repo_tracked(&rid)? {
|
||||
anyhow::bail!("repository {rid} is not tracked");
|
||||
}
|
||||
|
||||
let results = if let Some(seed) = seed {
|
||||
let result = fetch_from(rid, &seed, node)?;
|
||||
FetchResults::from(vec![(seed, result)])
|
||||
} else {
|
||||
fetch_all(rid, node)?
|
||||
};
|
||||
let success = results.success().count();
|
||||
let failed = results.failed().count();
|
||||
|
||||
if success == 0 {
|
||||
term::error(format!("Failed to fetch repository from {failed} seed(s)"));
|
||||
} else {
|
||||
term::success!("Fetched repository from {success} seed(s)");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch_all(rid: Id, node: &mut Node) -> Result<FetchResults, node::Error> {
|
||||
// Get seeds. This consults the local routing table only.
|
||||
let seeds = node.seeds(rid)?;
|
||||
let mut results = FetchResults::default();
|
||||
|
||||
if seeds.has_connections() {
|
||||
// Fetch from all seeds.
|
||||
for seed in seeds.connected() {
|
||||
let result = fetch_from(rid, seed, node)?;
|
||||
results.push(*seed, result);
|
||||
}
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn fetch_from(rid: Id, seed: &NodeId, node: &mut Node) -> Result<FetchResult, node::Error> {
|
||||
let spinner = term::spinner(format!(
|
||||
"Fetching {} from {}..",
|
||||
term::format::tertiary(rid),
|
||||
term::format::tertiary(term::format::node(seed))
|
||||
));
|
||||
let result = node.fetch(rid, *seed)?;
|
||||
|
||||
match &result {
|
||||
FetchResult::Success { .. } => {
|
||||
spinner.finish();
|
||||
}
|
||||
FetchResult::Failed { reason } => {
|
||||
spinner.error(reason);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,14 +163,6 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
|||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"fetch" => {
|
||||
term::run_command_args::<rad_fetch::Options, _>(
|
||||
rad_fetch::HELP,
|
||||
"Fetch",
|
||||
rad_fetch::run,
|
||||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"fork" => {
|
||||
term::run_command_args::<rad_fork::Options, _>(
|
||||
rad_fork::HELP,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use thiserror::Error;
|
|||
use crate::crypto::ssh::agent::Agent;
|
||||
use crate::crypto::ssh::{keystore, Keystore, Passphrase};
|
||||
use crate::crypto::{PublicKey, Signer};
|
||||
use crate::node;
|
||||
use crate::node::{self, tracking};
|
||||
use crate::prelude::Did;
|
||||
use crate::storage::git::transport;
|
||||
use crate::storage::git::Storage;
|
||||
|
|
@ -127,6 +127,14 @@ impl Profile {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return a read-only handle to the tracking configuration of the node.
|
||||
pub fn tracking(&self) -> Result<tracking::store::Config, tracking::store::Error> {
|
||||
let path = self.home.node().join(node::TRACKING_DB_FILE);
|
||||
let config = tracking::store::Config::reader(path)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Return the path to the keys folder.
|
||||
pub fn keys(&self) -> PathBuf {
|
||||
self.home.keys()
|
||||
|
|
|
|||
Loading…
Reference in New Issue