cli: Hints system

Enable hints by default, but allow users to turn them off via
configuration.

Add a hint when pushing while offline.
This commit is contained in:
cloudhead 2023-11-22 15:19:42 +01:00
parent 7acd025d7f
commit 8e76ce03d5
No known key found for this signature in database
7 changed files with 75 additions and 21 deletions

View File

@ -42,7 +42,7 @@ $ git commit -m "Third commit by Alice" --allow-empty -q
If we try to push now, we get an error with a hint, telling us that we need to
integrate Bob's changes before pushing ours:
``` ~alice (stderr) (fail)
``` ~alice (stderr) (fail) RAD_HINT=1
$ git push rad
hint: you are attempting to push a commit that would cause your upstream to diverge from the canonical head
hint: to integrate the remote changes, run `git pull --rebase` and try again

View File

@ -11,6 +11,7 @@ use serde_json::Value;
use time::OffsetDateTime;
use tower::ServiceExt;
use radicle::cli;
use radicle::cob::issue::Issues;
use radicle::cob::patch::{MergeTarget, Patches};
use radicle::crypto::ssh::keystore::MemorySigner;
@ -70,6 +71,7 @@ pub fn profile(home: &Path, seed: [u8; 32]) -> radicle::Profile {
public_key: keypair.pk.into(),
config: profile::Config {
node: node::Config::new(alias),
cli: cli::Config::default(),
},
}
}

View File

@ -18,7 +18,6 @@ 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;
@ -32,6 +31,7 @@ use radicle::rad;
use radicle::storage::{ReadStorage as _, RemoteRepository as _, SignRepository as _};
use radicle::test::fixtures;
use radicle::Storage;
use radicle::{cli, node};
use crate::node::NodeId;
use crate::service::Event;
@ -111,7 +111,12 @@ impl Environment {
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
let tracking_db = home.node().join(TRACKING_DB_FILE);
let alias = Alias::from_str(alias).unwrap();
let config = profile::Config::init(alias.clone(), &home.config()).unwrap();
let config = profile::Config {
node: node::Config::new(alias.clone()),
cli: cli::Config { hints: false },
};
config.write(&home.config()).unwrap();
let storage = Storage::open(
home.storage(),
git::UserInfo {

View File

@ -159,6 +159,7 @@ pub fn run(
let signer = profile.signer()?;
let mut line = String::new();
let mut ok = HashSet::new();
let hints = profile.hints();
assert_eq!(signer.public_key(), &nid);
@ -242,14 +243,16 @@ pub fn run(
// Not a rollback.
&& !rollback
{
eprintln!(
"hint: you are attempting to push a commit that would \
cause your upstream to diverge from the canonical head"
);
eprintln!(
"hint: to integrate the remote changes, run `git pull --rebase` \
and try again"
if hints {
eprintln!(
"hint: you are attempting to push a commit that would \
cause your upstream to diverge from the canonical head"
);
eprintln!(
"hint: to integrate the remote changes, run `git pull --rebase` \
and try again"
);
}
return Err(Error::HeadsDiverge(head.into(), *canonical_oid));
}
}
@ -283,6 +286,9 @@ pub fn run(
if node.is_running() {
// Nb. allow this to fail. The push to local storage was still successful.
sync(stored.id, node).ok();
} else if hints {
eprintln!("hint: offline push, your node is not running");
eprintln!("hint: to sync with the network, run `rad node start`");
}
}
}

14
radicle/src/cli.rs Normal file
View File

@ -0,0 +1,14 @@
/// CLI configuration.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
/// Whether to show hints or not in the CLI.
#[serde(default)]
pub hints: bool,
}
impl Default for Config {
fn default() -> Self {
Self { hints: true }
}
}

View File

@ -9,6 +9,7 @@ extern crate amplify;
extern crate radicle_git_ext as git_ext;
mod canonical;
pub mod cli;
pub mod cob;
pub mod collections;
pub mod git;

View File

@ -25,7 +25,7 @@ use crate::prelude::Did;
use crate::prelude::NodeId;
use crate::storage::git::transport;
use crate::storage::git::Storage;
use crate::{git, node};
use crate::{cli, git, node};
/// Environment variables used by radicle.
pub mod env {
@ -39,6 +39,13 @@ pub mod env {
pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE";
/// RNG seed. Must be convertible to a `u64`.
pub const RAD_RNG_SEED: &str = "RAD_RNG_SEED";
/// Show radicle hints.
pub const RAD_HINT: &str = "RAD_HINT";
/// Whether or not to show hints.
pub fn hints() -> bool {
var(RAD_HINT).is_ok()
}
/// Get the configured pager program from the environment.
pub fn pager() -> Option<String> {
@ -107,7 +114,10 @@ pub enum ConfigError {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
/// Node configuration.
pub node: node::Config,
/// CLI configuration.
pub cli: cli::Config,
}
impl Config {
@ -115,17 +125,9 @@ impl Config {
pub fn init(alias: Alias, path: &Path) -> io::Result<Self> {
let cfg = Self {
node: node::Config::new(alias),
cli: cli::Config::default(),
};
let mut file = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(path)?;
let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
let mut serializer = serde_json::Serializer::with_formatter(&file, formatter);
cfg.serialize(&mut serializer)?;
file.write_all(b"\n")?;
file.sync_all()?;
cfg.write(path)?;
Ok(cfg)
}
@ -145,11 +147,28 @@ impl Config {
};
Ok(Config {
node: node::Config::new(alias),
cli: cli::Config::default(),
})
}
}
}
/// Write configuration to disk.
pub fn write(&self, path: &Path) -> Result<(), io::Error> {
let mut file = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(path)?;
let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
let mut serializer = serde_json::Serializer::with_formatter(&file, formatter);
self.serialize(&mut serializer)?;
file.write_all(b"\n")?;
file.sync_all()?;
Ok(())
}
/// Get the user alias.
pub fn alias(&self) -> &Alias {
&self.node.alias
@ -226,6 +245,13 @@ impl Profile {
}
}
pub fn hints(&self) -> bool {
if env::hints() {
return true;
}
self.config.cli.hints
}
pub fn did(&self) -> Did {
Did::from(self.public_key)
}