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:
parent
7acd025d7f
commit
8e76ce03d5
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue