diff --git a/crates/radicle-remote-helper/src/main.rs b/crates/radicle-remote-helper/src/main.rs index 06f52d9a..c45f4393 100644 --- a/crates/radicle-remote-helper/src/main.rs +++ b/crates/radicle-remote-helper/src/main.rs @@ -243,6 +243,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { let mut opts = Options::default(); let mut expected_refs = Vec::new(); let git = service::RealGitService; + let mut node = service::RealNodeSession::new(&profile); if let Err(e) = radicle::io::set_file_limit(4096) { if debug { @@ -310,6 +311,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { opts, &expected_refs, &git, + &mut node, )?; for line in output { diff --git a/crates/radicle-remote-helper/src/push.rs b/crates/radicle-remote-helper/src/push.rs index a75b4e4b..e35daf6b 100644 --- a/crates/radicle-remote-helper/src/push.rs +++ b/crates/radicle-remote-helper/src/push.rs @@ -4,7 +4,6 @@ mod canonical; mod error; use std::collections::HashMap; -use std::io::IsTerminal; use std::process::ExitStatus; use std::str::FromStr; use std::{assert_eq, io}; @@ -22,16 +21,16 @@ use radicle::crypto; use radicle::explorer::ExplorerResource; use radicle::identity::{CanonicalRefs, Did}; use radicle::node; -use radicle::node::{Handle, NodeId}; +use radicle::node::NodeId; use radicle::storage; use radicle::storage::git::transport::local::Url; use radicle::storage::{ReadRepository, SignRepository as _, WriteRepository}; use radicle::Profile; use radicle::{git, rad}; -use radicle_cli as cli; use radicle_cli::terminal as term; use crate::service::GitService; +use crate::service::NodeSession; use crate::{hint, read_line, warn, Options, Verbosity}; #[derive(Debug, Error)] @@ -254,6 +253,7 @@ pub fn run( opts: Options, expected_refs: &[String], git: &impl GitService, + node: &mut impl NodeSession, ) -> Result, Error> { // Don't allow push if either of these conditions is true: // @@ -462,10 +462,10 @@ pub fn run( // Connect to local node and announce refs to the network. // If our node is not running, we simply skip this step, as the // refs will be announced eventually, when the node restarts. - let node = radicle::Node::new(profile.socket()); if node.is_running() { // Nb. allow this to fail. The push to local storage was still successful. - sync(stored, ok.into_values().flatten(), opts, node, profile).ok(); + node.sync(stored, ok.into_values().flatten().collect(), opts, profile) + .ok(); } else if hints { hint("offline push, your node is not running"); hint("to sync with the network, run `rad node start`"); @@ -1038,59 +1038,3 @@ fn push_ref( Ok(()) } - -/// Sync with the network. -fn sync( - repo: &storage::git::Repository, - updated: impl Iterator, - opts: Options, - mut node: radicle::Node, - profile: &Profile, -) -> Result<(), cli::node::SyncError> { - let progress = if io::stderr().is_terminal() { - term::PaintTarget::Stderr - } else { - term::PaintTarget::Hidden - }; - let result = cli::node::announce( - repo, - cli::node::SyncSettings::default().with_profile(profile), - cli::node::SyncReporting { - progress, - completion: term::PaintTarget::Stderr, - debug: opts.sync_debug, - }, - &mut node, - profile, - )?; - - let mut urls = Vec::new(); - - if let Some(result) = result { - for seed in profile.config.preferred_seeds.iter() { - if result.is_synced(&seed.id) { - for resource in updated { - let url = profile - .config - .public_explorer - .url(seed.addr.host.clone(), repo.id) - .resource(resource); - - urls.push(url); - } - break; - } - } - } - - // Print URLs to the updated resources. - if !urls.is_empty() { - eprintln!(); - for url in urls { - eprintln!(" {}", term::format::dim(url)); - } - eprintln!(); - } - - Ok(()) -} diff --git a/crates/radicle-remote-helper/src/service.rs b/crates/radicle-remote-helper/src/service.rs index 38c26154..c2c1cd15 100644 --- a/crates/radicle-remote-helper/src/service.rs +++ b/crates/radicle-remote-helper/src/service.rs @@ -1,9 +1,15 @@ use std::io; +use std::io::IsTerminal; use std::path::Path; use std::process; +use radicle::explorer::ExplorerResource; use radicle::git; +use radicle::node::Handle; use radicle::storage; +use radicle::Profile; +use radicle_cli::node::{SyncError, SyncReporting, SyncSettings}; +use radicle_cli::terminal as term; /// Abstraction for Git subprocess calls. pub(super) trait GitService { @@ -38,3 +44,90 @@ impl GitService for RealGitService { git::run(working, args) } } + +/// Abstraction for Node interaction. +pub(super) trait NodeSession { + fn is_running(&self) -> bool; + + fn sync( + &mut self, + repo: &storage::git::Repository, + updated: Vec, + opts: crate::Options, + profile: &Profile, + ) -> Result<(), SyncError>; +} + +pub(super) struct RealNodeSession { + node: radicle::Node, +} + +impl RealNodeSession { + pub(super) fn new(profile: &Profile) -> Self { + Self { + node: radicle::Node::new(profile.socket()), + } + } +} + +impl NodeSession for RealNodeSession { + fn is_running(&self) -> bool { + self.node.is_running() + } + + fn sync( + &mut self, + repo: &storage::git::Repository, + updated: Vec, + opts: crate::Options, + profile: &Profile, + ) -> Result<(), SyncError> { + let progress = if io::stderr().is_terminal() { + term::PaintTarget::Stderr + } else { + term::PaintTarget::Hidden + }; + + let result = radicle_cli::node::announce( + repo, + SyncSettings::default().with_profile(profile), + SyncReporting { + progress, + completion: term::PaintTarget::Stderr, + debug: opts.sync_debug, + }, + &mut self.node, + profile, + )?; + + let mut urls = Vec::new(); + + if let Some(result) = result { + for seed in profile.config.preferred_seeds.iter() { + if result.is_synced(&seed.id) { + for resource in updated { + let url = profile + .config + .public_explorer + .url(seed.addr.host.clone(), repo.id) + .resource(resource); + + urls.push(url); + } + break; + } + } + } + + // Print URLs to the updated resources. + if !urls.is_empty() { + eprintln!(); + for url in urls { + eprintln!(" {}", term::format::dim(url)); + } + eprintln!(); + } + + Ok(()) + } +}