cli: Update tracking without a running node
This change allows users to track/untrack peers and repos without a running node. This fixes a bug when running `rad init` without the node running, where the repo would be tracked with a different scope than what was specified on the CLI.
This commit is contained in:
parent
a70664368e
commit
d150938b38
|
|
@ -3,7 +3,7 @@ To create your first radicle project, navigate to a git repository, and run the
|
|||
`init` command. Make sure you have [authenticated](../rad-auth.md) beforehand.
|
||||
|
||||
```
|
||||
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --no-track --public -v
|
||||
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --public -v
|
||||
|
||||
Initializing public radicle 👾 project in .
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
When you try to track, clone, or sync without your node running, it gives you an error:
|
||||
|
||||
``` ~alice (fail)
|
||||
$ rad track rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 --no-fetch
|
||||
✗ Error: to track a repository, your node must be running. To start it, run `rad node start`
|
||||
```
|
||||
When you try to clone or sync without your node running, it gives you an error:
|
||||
|
||||
``` ~bob (fail)
|
||||
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
||||
|
|
@ -14,3 +9,10 @@ $ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
|||
$ rad sync --fetch rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 --seed z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz
|
||||
✗ 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:
|
||||
|
||||
``` ~alice
|
||||
$ rad track rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
||||
✓ Tracking policy updated for rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 with scope 'trusted'
|
||||
```
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use radicle::node::{Handle, NodeId};
|
|||
use radicle::prelude::Doc;
|
||||
use radicle::{profile, Node};
|
||||
|
||||
use crate as cli;
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
|
@ -268,12 +269,6 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
|||
Ok((id, doc, _)) => {
|
||||
let proj = doc.project()?;
|
||||
|
||||
if options.track && node.is_running() {
|
||||
// It's important to track our own repositories to make sure that our node signals
|
||||
// interest for them. This ensures that messages relating to them are relayed to us.
|
||||
node.track_repo(id, options.scope)?;
|
||||
}
|
||||
|
||||
spinner.message(format!(
|
||||
"Project {} created.",
|
||||
term::format::highlight(proj.name())
|
||||
|
|
@ -284,6 +279,12 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
|||
term::blob(json::to_string_pretty(&proj)?);
|
||||
}
|
||||
|
||||
// It's important to track our own repositories to make sure that our node signals
|
||||
// interest for them. This ensures that messages relating to them are relayed to us.
|
||||
if options.track {
|
||||
cli::project::track(id, options.scope, &mut node, profile)?;
|
||||
}
|
||||
|
||||
if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
|
||||
// Setup eg. `master` -> `rad/master`
|
||||
radicle::git::set_upstream(
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ use std::time;
|
|||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::node;
|
||||
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 as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::{project, terminal as term};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "track",
|
||||
|
|
@ -119,16 +118,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
|
||||
match options.op {
|
||||
Operation::TrackNode { nid, alias } => {
|
||||
if let Err(node::Error::Connect(_)) = track_node(nid, alias, &mut node) {
|
||||
anyhow::bail!("to track another node, your node must be running. To start it, run `rad node start`");
|
||||
}
|
||||
track_node(nid, alias, &mut node, &profile)?;
|
||||
}
|
||||
Operation::TrackRepo { rid, scope } => {
|
||||
if let Err(node::Error::Connect(_)) = track_repo(rid, scope, &mut node) {
|
||||
anyhow::bail!("to track a repository, your node must be running. To start it, run `rad node start`");
|
||||
}
|
||||
track_repo(rid, scope, &mut node, &profile)?;
|
||||
|
||||
if options.fetch {
|
||||
if options.fetch && node.is_running() {
|
||||
sync::fetch(
|
||||
rid,
|
||||
sync::RepoSync::default(),
|
||||
|
|
@ -141,8 +136,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn track_repo(rid: Id, scope: Scope, node: &mut Node) -> Result<(), node::Error> {
|
||||
let tracked = node.track_repo(rid, scope)?;
|
||||
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!(
|
||||
|
|
@ -153,8 +153,20 @@ pub fn track_repo(rid: Id, scope: Scope, node: &mut Node) -> Result<(), node::Er
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn track_node(nid: NodeId, alias: Option<Alias>, node: &mut Node) -> Result<(), node::Error> {
|
||||
let tracked = node.track_node(nid, alias.clone())?;
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ 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};
|
||||
|
||||
|
|
@ -84,16 +85,15 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let mut node = radicle::Node::new(profile.socket());
|
||||
|
||||
match options.op {
|
||||
Operation::UntrackNode { nid } => untrack_node(nid, &mut node),
|
||||
Operation::UntrackRepo { rid } => untrack_repo(rid, &mut node),
|
||||
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) -> anyhow::Result<()> {
|
||||
let untracked = node.untrack_repo(rid)?;
|
||||
if untracked {
|
||||
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),
|
||||
|
|
@ -102,8 +102,15 @@ pub fn untrack_repo(rid: Id, node: &mut Node) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn untrack_node(nid: NodeId, node: &mut Node) -> anyhow::Result<()> {
|
||||
let untracked = node.untrack_node(nid)?;
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ use radicle::prelude::*;
|
|||
|
||||
use crate::git;
|
||||
use radicle::git::RefStr;
|
||||
use radicle::node::tracking::Scope;
|
||||
use radicle::node::{Handle, NodeId};
|
||||
use radicle::Node;
|
||||
|
||||
/// Setup a project remote and tracking branch.
|
||||
pub struct SetupRemote<'a> {
|
||||
|
|
@ -48,3 +51,34 @@ impl<'a> SetupRemote<'a> {
|
|||
Ok((remote, None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Track a repository by first trying to track through the node, and if the node isn't running,
|
||||
/// by updating the tracking database directly.
|
||||
pub fn track(
|
||||
rid: Id,
|
||||
scope: Scope,
|
||||
node: &mut Node,
|
||||
profile: &Profile,
|
||||
) -> Result<bool, anyhow::Error> {
|
||||
match node.track_repo(rid, scope) {
|
||||
Ok(updated) => Ok(updated),
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.track_repo(&rid, scope).map_err(|e| e.into())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Untrack a repository by first trying to untrack through the node, and if the node isn't running,
|
||||
/// by updating the tracking database directly.
|
||||
pub fn untrack(rid: Id, node: &mut Node, profile: &Profile) -> Result<bool, anyhow::Error> {
|
||||
match node.untrack_repo(rid) {
|
||||
Ok(updated) => Ok(updated),
|
||||
Err(e) if e.is_connection_err() => {
|
||||
let mut config = profile.tracking_mut()?;
|
||||
config.untrack_repo(&rid).map_err(|e| e.into())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -414,12 +414,7 @@ where
|
|||
|
||||
for rid in rids {
|
||||
if !self.is_tracking(&rid)? {
|
||||
if self
|
||||
.track_repo(&rid, tracking::Scope::Trusted)
|
||||
.expect("Service::initialize: error tracking repository")
|
||||
{
|
||||
info!(target: "service", "Tracking local repository {rid}");
|
||||
}
|
||||
warn!(target: "service", "Local repository {rid} is not tracked");
|
||||
}
|
||||
}
|
||||
// Ensure that our local node is in our address database.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use radicle::crypto::{KeyPair, Seed, Signer};
|
|||
use radicle::git;
|
||||
use radicle::git::refname;
|
||||
use radicle::identity::{Id, Visibility};
|
||||
use radicle::node;
|
||||
use radicle::node::address::Book;
|
||||
use radicle::node::routing;
|
||||
use radicle::node::routing::Store;
|
||||
|
|
@ -406,6 +407,11 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
|
|||
.map(|(id, _, _)| id)
|
||||
.unwrap();
|
||||
|
||||
assert!(self
|
||||
.tracking
|
||||
.track_repo(&id, node::tracking::Scope::Trusted)
|
||||
.unwrap());
|
||||
|
||||
log::debug!(
|
||||
target: "test",
|
||||
"Initialized project {id} for node {}", self.signer.public_key()
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ where
|
|||
) -> Self {
|
||||
let routing = routing::Table::memory().unwrap();
|
||||
let tracking = tracking::Store::<tracking::store::Write>::memory().unwrap();
|
||||
let tracking = tracking::Config::new(config.policy, config.scope, tracking);
|
||||
let mut tracking = tracking::Config::new(config.policy, config.scope, tracking);
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let id = *config.signer.public_key();
|
||||
let ip = ip.into();
|
||||
|
|
@ -165,6 +165,9 @@ where
|
|||
// Make sure the peer address is advertized.
|
||||
config.config.external_addresses.push(local_addr.into());
|
||||
|
||||
for rid in storage.inventory().unwrap() {
|
||||
tracking.track_repo(&rid, Scope::Trusted).unwrap();
|
||||
}
|
||||
let announcement = service::gossip::node(&config.config, config.local_time.as_secs());
|
||||
let emitter: Emitter<Event> = Default::default();
|
||||
let service = Service::new(
|
||||
|
|
|
|||
|
|
@ -243,6 +243,14 @@ impl Profile {
|
|||
Ok(config)
|
||||
}
|
||||
|
||||
/// Return a read-write handle to the tracking configuration of the node.
|
||||
pub fn tracking_mut(&self) -> Result<tracking::store::ConfigWriter, tracking::store::Error> {
|
||||
let path = self.home.node().join(node::TRACKING_DB_FILE);
|
||||
let config = tracking::store::Config::open(path)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Return a read-only handle to the routing database of the node.
|
||||
pub fn routing(&self) -> Result<routing::Table, routing::Error> {
|
||||
let path = self.home.node().join(node::ROUTING_DB_FILE);
|
||||
|
|
|
|||
Loading…
Reference in New Issue