cli: Choose repo visibility on init
This allows a user to choose private or public visibility on `rad init`. It also improves the output to tell the user what happened due to this choice.
This commit is contained in:
parent
d6d280d450
commit
f2e74fdad2
|
|
@ -1,16 +1,18 @@
|
||||||
Alice initializes a *private* repo.
|
Alice initializes a *private* repo.
|
||||||
|
|
||||||
``` ~alice
|
``` ~alice
|
||||||
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --announce --private
|
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --private
|
||||||
|
|
||||||
Initializing private radicle 👾 project in .
|
Initializing private radicle 👾 project in .
|
||||||
|
|
||||||
✓ Project heartwood created
|
✓ Project heartwood created.
|
||||||
✓ Syncing inventory..
|
|
||||||
✓ Announcing inventory..
|
|
||||||
|
|
||||||
Your project's Repository ID (RID) is rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu.
|
Your project's Repository ID (RID) is rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu.
|
||||||
You can show it any time by running `rad .`
|
You can show it any time by running `rad .` from this directory.
|
||||||
|
|
||||||
|
You have created a private repository.
|
||||||
|
This repository will only be visible to you, and to peers you explicitly allow.
|
||||||
|
To push changes, run `git push`.
|
||||||
```
|
```
|
||||||
|
|
||||||
Bob tries to clone it, and even though he's connected to Alice, it fails.
|
Bob tries to clone it, and even though he's connected to Alice, it fails.
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,17 @@ To create your first radicle project, navigate to a git repository, and run
|
||||||
the `init` command:
|
the `init` command:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --announce --scope trusted
|
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --public --scope trusted
|
||||||
|
|
||||||
Initializing radicle 👾 project in .
|
Initializing public radicle 👾 project in .
|
||||||
|
|
||||||
✓ Project heartwood created
|
✓ Project heartwood created.
|
||||||
✓ Syncing inventory..
|
|
||||||
✓ Announcing inventory..
|
|
||||||
|
|
||||||
Your project's Repository ID (RID) is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji.
|
Your project's Repository ID (RID) is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji.
|
||||||
You can show it any time by running `rad .`
|
You can show it any time by running `rad .` from this directory.
|
||||||
|
|
||||||
|
✓ Project successfully announced.
|
||||||
|
|
||||||
|
Your project has been announced to the network and is now discoverable by peers.
|
||||||
|
To push changes, run `git push`.
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ 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.
|
`init` command. Make sure you have [authenticated](../rad-auth.md) beforehand.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --no-track -v
|
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --no-track --public -v
|
||||||
|
|
||||||
Initializing radicle 👾 project in .
|
Initializing public radicle 👾 project in .
|
||||||
|
|
||||||
✓ Project heartwood created
|
✓ Project heartwood created.
|
||||||
{
|
{
|
||||||
"name": "heartwood",
|
"name": "heartwood",
|
||||||
"description": "Radicle Heartwood Protocol & Stack",
|
"description": "Radicle Heartwood Protocol & Stack",
|
||||||
|
|
@ -15,9 +15,11 @@ Initializing radicle 👾 project in .
|
||||||
}
|
}
|
||||||
|
|
||||||
Your project's Repository ID (RID) is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji.
|
Your project's Repository ID (RID) is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji.
|
||||||
You can show it any time by running `rad .`
|
You can show it any time by running `rad .` from this directory.
|
||||||
|
|
||||||
To publish your project to the network, run `git push`
|
Your project will be announced to the network when you start your node.
|
||||||
|
You can start your node with `rad node start`.
|
||||||
|
To push changes, run `git push`.
|
||||||
```
|
```
|
||||||
|
|
||||||
Projects can be listed with the `ls` command:
|
Projects can be listed with the `ls` command:
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@ Let's look at how patch updates work.
|
||||||
Alice creates a project and Bob clones it.
|
Alice creates a project and Bob clones it.
|
||||||
|
|
||||||
``` ~alice
|
``` ~alice
|
||||||
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --announce
|
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --public
|
||||||
|
|
||||||
Initializing radicle 👾 project in .
|
Initializing public radicle 👾 project in .
|
||||||
|
|
||||||
✓ Project heartwood created
|
✓ Project heartwood created.
|
||||||
✓ Syncing inventory..
|
|
||||||
✓ Announcing inventory..
|
|
||||||
|
|
||||||
Your project's Repository ID (RID) is rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK.
|
Your project's Repository ID (RID) is rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK.
|
||||||
You can show it any time by running `rad .`
|
You can show it any time by running `rad .` from this directory.
|
||||||
|
|
||||||
|
✓ Project successfully announced.
|
||||||
|
|
||||||
|
Your project has been announced to the network and is now discoverable by peers.
|
||||||
|
To push changes, run `git push`.
|
||||||
```
|
```
|
||||||
|
|
||||||
``` ~bob
|
``` ~bob
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ pub fn init(options: Options) -> anyhow::Result<()> {
|
||||||
|
|
||||||
if let Ok(version) = radicle::git::version() {
|
if let Ok(version) = radicle::git::version() {
|
||||||
if version < radicle::git::VERSION_REQUIRED {
|
if version < radicle::git::VERSION_REQUIRED {
|
||||||
term::warning(&format!(
|
term::warning(format!(
|
||||||
"Your git version is unsupported, please upgrade to {} or later",
|
"Your git version is unsupported, please upgrade to {} or later",
|
||||||
radicle::git::VERSION_REQUIRED,
|
radicle::git::VERSION_REQUIRED,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -522,7 +522,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
fn warn_out_of_date(revision: &Revision, previous: &Identity<Oid>) {
|
fn warn_out_of_date(revision: &Revision, previous: &Identity<Oid>) {
|
||||||
if revision.current != previous.current {
|
if revision.current != previous.current {
|
||||||
term::warning("Revision is out of date");
|
term::warning("Revision is out of date");
|
||||||
term::warning(&format!("{} =/= {}", revision.current, previous.current));
|
term::warning(format!("{} =/= {}", revision.current, previous.current));
|
||||||
term::tip!("Consider using 'rad id rebase' to update the proposal to the latest identity");
|
term::tip!("Consider using 'rad id rebase' to update the proposal to the latest identity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
#![allow(clippy::or_fun_call)]
|
#![allow(clippy::or_fun_call)]
|
||||||
|
#![allow(clippy::collapsible_else_if)]
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context as _};
|
use anyhow::{anyhow, bail, Context as _};
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
|
|
||||||
use radicle::crypto::ssh;
|
use radicle::crypto::{ssh, Verified};
|
||||||
use radicle::git::RefString;
|
use radicle::git::RefString;
|
||||||
use radicle::identity::Visibility;
|
use radicle::identity::Visibility;
|
||||||
use radicle::node::tracking::Scope;
|
use radicle::node::tracking::Scope;
|
||||||
use radicle::node::{Handle, NodeId};
|
use radicle::node::{Handle, NodeId};
|
||||||
use radicle::profile;
|
use radicle::prelude::Doc;
|
||||||
|
use radicle::{profile, Node};
|
||||||
|
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::terminal as term;
|
use crate::terminal as term;
|
||||||
|
|
@ -34,10 +37,10 @@ Options
|
||||||
--description <string> Description of the project
|
--description <string> Description of the project
|
||||||
--default-branch <name> The default branch of the project
|
--default-branch <name> The default branch of the project
|
||||||
--scope <scope> Tracking scope (default: all)
|
--scope <scope> Tracking scope (default: all)
|
||||||
--private Set repository visibility to *private* (default: public)
|
--private Set repository visibility to *private*
|
||||||
|
--public Set repository visibility to *public*
|
||||||
-u, --set-upstream Setup the upstream of the default branch
|
-u, --set-upstream Setup the upstream of the default branch
|
||||||
--setup-signing Setup the radicle key as a signing key for this repository
|
--setup-signing Setup the radicle key as a signing key for this repository
|
||||||
--announce Announce the new project to the network
|
|
||||||
--no-confirm Don't ask for confirmation during setup
|
--no-confirm Don't ask for confirmation during setup
|
||||||
-v, --verbose Verbose mode
|
-v, --verbose Verbose mode
|
||||||
--help Print help
|
--help Print help
|
||||||
|
|
@ -51,11 +54,10 @@ pub struct Options {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub branch: Option<String>,
|
pub branch: Option<String>,
|
||||||
pub interactive: Interactive,
|
pub interactive: Interactive,
|
||||||
pub visibility: Visibility,
|
pub visibility: Option<Visibility>,
|
||||||
pub setup_signing: bool,
|
pub setup_signing: bool,
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
pub set_upstream: bool,
|
pub set_upstream: bool,
|
||||||
pub announce: bool,
|
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
pub track: bool,
|
pub track: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -73,11 +75,10 @@ impl Args for Options {
|
||||||
let mut interactive = Interactive::Yes;
|
let mut interactive = Interactive::Yes;
|
||||||
let mut set_upstream = false;
|
let mut set_upstream = false;
|
||||||
let mut setup_signing = false;
|
let mut setup_signing = false;
|
||||||
let mut announce = false;
|
|
||||||
let mut scope = Scope::All;
|
let mut scope = Scope::All;
|
||||||
let mut track = true;
|
let mut track = true;
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
let mut visibility = Visibility::default();
|
let mut visibility = None;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -124,9 +125,6 @@ impl Args for Options {
|
||||||
Long("setup-signing") => {
|
Long("setup-signing") => {
|
||||||
setup_signing = true;
|
setup_signing = true;
|
||||||
}
|
}
|
||||||
Long("announce") => {
|
|
||||||
announce = true;
|
|
||||||
}
|
|
||||||
Long("no-confirm") => {
|
Long("no-confirm") => {
|
||||||
interactive = Interactive::No;
|
interactive = Interactive::No;
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +132,10 @@ impl Args for Options {
|
||||||
track = false;
|
track = false;
|
||||||
}
|
}
|
||||||
Long("private") => {
|
Long("private") => {
|
||||||
visibility = Visibility::private([]);
|
visibility = Some(Visibility::private([]));
|
||||||
|
}
|
||||||
|
Long("public") => {
|
||||||
|
visibility = Some(Visibility::Public);
|
||||||
}
|
}
|
||||||
Long("verbose") | Short('v') => {
|
Long("verbose") | Short('v') => {
|
||||||
verbose = true;
|
verbose = true;
|
||||||
|
|
@ -159,7 +160,6 @@ impl Args for Options {
|
||||||
interactive,
|
interactive,
|
||||||
set_upstream,
|
set_upstream,
|
||||||
setup_signing,
|
setup_signing,
|
||||||
announce,
|
|
||||||
track,
|
track,
|
||||||
visibility,
|
visibility,
|
||||||
verbose,
|
verbose,
|
||||||
|
|
@ -190,24 +190,30 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
|
|
||||||
term::headline(format!(
|
term::headline(format!(
|
||||||
"Initializing{}radicle 👾 project in {}",
|
"Initializing{}radicle 👾 project in {}",
|
||||||
if !options.visibility.is_public() {
|
if let Some(visibility) = &options.visibility {
|
||||||
|
if !visibility.is_public() {
|
||||||
term::format::yellow(" private ")
|
term::format::yellow(" private ")
|
||||||
|
} else {
|
||||||
|
term::format::positive(" public ")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
term::format::default(" ")
|
term::format::default(" ")
|
||||||
},
|
},
|
||||||
if path == cwd {
|
if path == cwd {
|
||||||
term::format::highlight(".").to_string()
|
term::format::tertiary(".").to_string()
|
||||||
} else {
|
} else {
|
||||||
term::format::highlight(path.display()).to_string()
|
term::format::tertiary(path.display()).to_string()
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// TODO: Move up.
|
||||||
if let Ok((remote, _)) = git::rad_remote(&repo) {
|
if let Ok((remote, _)) = git::rad_remote(&repo) {
|
||||||
if let Some(remote) = remote.url() {
|
if let Some(remote) = remote.url() {
|
||||||
bail!("repository is already initialized with remote {remote}");
|
bail!("repository is already initialized with remote {remote}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move up.
|
||||||
let signer = term::signer(profile)?;
|
let signer = term::signer(profile)?;
|
||||||
let head: String = repo
|
let head: String = repo
|
||||||
.head()
|
.head()
|
||||||
|
|
@ -241,6 +247,16 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
});
|
});
|
||||||
let branch = RefString::try_from(branch.clone())
|
let branch = RefString::try_from(branch.clone())
|
||||||
.map_err(|e| anyhow!("invalid branch name {:?}: {}", branch, e))?;
|
.map_err(|e| anyhow!("invalid branch name {:?}: {}", branch, e))?;
|
||||||
|
let visibility = if let Some(v) = options.visibility {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
let selected = term::select(
|
||||||
|
"Visibility",
|
||||||
|
&["public", "private"],
|
||||||
|
"Public repositories are accessible by anyone on the network after initialization",
|
||||||
|
)?;
|
||||||
|
Visibility::from_str(selected)?
|
||||||
|
};
|
||||||
|
|
||||||
let mut node = radicle::Node::new(profile.socket());
|
let mut node = radicle::Node::new(profile.socket());
|
||||||
let mut spinner = term::spinner("Initializing...");
|
let mut spinner = term::spinner("Initializing...");
|
||||||
|
|
@ -251,7 +267,7 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
&name,
|
&name,
|
||||||
&description,
|
&description,
|
||||||
branch,
|
branch,
|
||||||
options.visibility,
|
visibility,
|
||||||
&signer,
|
&signer,
|
||||||
&profile.storage,
|
&profile.storage,
|
||||||
) {
|
) {
|
||||||
|
|
@ -265,7 +281,7 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.message(format!(
|
spinner.message(format!(
|
||||||
"Project {} created",
|
"Project {} created.",
|
||||||
term::format::highlight(proj.name())
|
term::format::highlight(proj.name())
|
||||||
));
|
));
|
||||||
spinner.finish();
|
spinner.finish();
|
||||||
|
|
@ -291,24 +307,6 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
self::setup_signing(profile.id(), &repo, interactive)?;
|
self::setup_signing(profile.id(), &repo, interactive)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.is_running() {
|
|
||||||
let spinner = term::spinner("Syncing inventory..");
|
|
||||||
if let Err(e) = node.sync_inventory() {
|
|
||||||
spinner.error(e);
|
|
||||||
} else {
|
|
||||||
spinner.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.announce {
|
|
||||||
let spinner = term::spinner("Announcing inventory..");
|
|
||||||
if let Err(e) = node.announce_inventory() {
|
|
||||||
spinner.error(e);
|
|
||||||
} else {
|
|
||||||
spinner.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
term::blank();
|
term::blank();
|
||||||
term::info!(
|
term::info!(
|
||||||
"Your project's Repository ID {} is {}.",
|
"Your project's Repository ID {} is {}.",
|
||||||
|
|
@ -316,17 +314,21 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
term::format::highlight(id.urn())
|
term::format::highlight(id.urn())
|
||||||
);
|
);
|
||||||
term::info!(
|
term::info!(
|
||||||
"You can show it any time by running {}",
|
"You can show it any time by running {} from this directory.",
|
||||||
term::format::command("rad .")
|
term::format::command("rad .")
|
||||||
);
|
);
|
||||||
|
|
||||||
if !options.announce {
|
|
||||||
term::blank();
|
term::blank();
|
||||||
term::info!(
|
|
||||||
"To publish your project to the network, run {}",
|
// Announce inventory to network.
|
||||||
term::format::command(push_cmd)
|
if let Err(e) = announce(doc, &mut node) {
|
||||||
);
|
term::blank();
|
||||||
|
term::warning(format!(
|
||||||
|
"There was an error announcing your project to the network: {e}"
|
||||||
|
));
|
||||||
|
term::warning("Try again with `rad sync --announce`, or check your logs with `rad node logs`.");
|
||||||
|
term::blank();
|
||||||
}
|
}
|
||||||
|
term::info!("To push changes, run {}.", term::format::command(push_cmd));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
spinner.failed();
|
spinner.failed();
|
||||||
|
|
@ -340,6 +342,44 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn announce(doc: Doc<Verified>, node: &mut Node) -> anyhow::Result<()> {
|
||||||
|
if doc.visibility.is_public() {
|
||||||
|
if node.is_running() {
|
||||||
|
let mut spinner = term::spinner("Updating inventory..");
|
||||||
|
|
||||||
|
node.sync_inventory()?;
|
||||||
|
spinner.message("Announcing..");
|
||||||
|
node.announce_inventory()?;
|
||||||
|
spinner.message("Project successfully announced.");
|
||||||
|
spinner.finish();
|
||||||
|
|
||||||
|
term::blank();
|
||||||
|
term::info!(
|
||||||
|
"Your project has been announced to the network and is \
|
||||||
|
now discoverable by peers.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
term::info!("Your project will be announced to the network when you start your node.");
|
||||||
|
term::info!(
|
||||||
|
"You can start your node with {}.",
|
||||||
|
term::format::command("rad node start")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Tell users how to make the project public.
|
||||||
|
term::info!(
|
||||||
|
"You have created a {} repository.",
|
||||||
|
term::format::yellow("private")
|
||||||
|
);
|
||||||
|
term::info!(
|
||||||
|
"This repository will only be visible to you, \
|
||||||
|
and to peers you explicitly allow.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Setup radicle key as commit signing key in repository.
|
/// Setup radicle key as commit signing key in repository.
|
||||||
pub fn setup_signing(
|
pub fn setup_signing(
|
||||||
node_id: &NodeId,
|
node_id: &NodeId,
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl Args for Options {
|
||||||
let mut rid: Option<Id> = None;
|
let mut rid: Option<Id> = None;
|
||||||
let mut json: bool = false;
|
let mut json: bool = false;
|
||||||
let mut addr: Option<PeerAddr<NodeId, Address>> = None;
|
let mut addr: Option<PeerAddr<NodeId, Address>> = None;
|
||||||
let mut lines: usize = 10;
|
let mut lines: usize = 60;
|
||||||
let mut count: usize = usize::MAX;
|
let mut count: usize = usize::MAX;
|
||||||
let mut timeout = time::Duration::MAX;
|
let mut timeout = time::Duration::MAX;
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ fn untrack(rid: &Id, profile: &Profile) -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
term::warning(&format!("Failed to untrack repository: {e}"));
|
term::warning(format!("Failed to untrack repository: {e}"));
|
||||||
term::warning("Make sure to untrack this repository when your node is running");
|
term::warning("Make sure to untrack this repository when your node is running");
|
||||||
} else {
|
} else {
|
||||||
term::success!("Untracked {rid}")
|
term::success!("Untracked {rid}")
|
||||||
|
|
@ -116,7 +116,7 @@ fn remove_remote(rid: &Id) -> anyhow::Result<()> {
|
||||||
.map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
.and_then(|repo| git::remove_remote(&repo, rid))
|
.and_then(|repo| git::remove_remote(&repo, rid))
|
||||||
{
|
{
|
||||||
term::warning(&format!(
|
term::warning(format!(
|
||||||
"Attempted to remove 'rad' remote from working copy: {e}"
|
"Attempted to remove 'rad' remote from working copy: {e}"
|
||||||
));
|
));
|
||||||
term::warning("In case a working copy exists, make sure to `git remote remove rad`");
|
term::warning("In case a working copy exists, make sure to `git remote remove rad`");
|
||||||
|
|
|
||||||
|
|
@ -1004,7 +1004,7 @@ fn test_replication_via_seed() {
|
||||||
"Radicle Heartwood Protocol & Stack",
|
"Radicle Heartwood Protocol & Stack",
|
||||||
"--default-branch",
|
"--default-branch",
|
||||||
"master",
|
"master",
|
||||||
"--announce",
|
"--public",
|
||||||
],
|
],
|
||||||
working.join("alice"),
|
working.join("alice"),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ pub static CONFIG: Lazy<RenderConfig> = Lazy::new(|| RenderConfig {
|
||||||
prompt_prefix: Styled::new("?").with_fg(Color::LightBlue),
|
prompt_prefix: Styled::new("?").with_fg(Color::LightBlue),
|
||||||
answered_prompt_prefix: Styled::new("✓").with_fg(Color::LightGreen),
|
answered_prompt_prefix: Styled::new("✓").with_fg(Color::LightGreen),
|
||||||
answer: StyleSheet::new(),
|
answer: StyleSheet::new(),
|
||||||
highlighted_option_prefix: Styled::new("*").with_fg(Color::LightYellow),
|
highlighted_option_prefix: Styled::new("✓").with_fg(Color::LightYellow),
|
||||||
|
selected_option: Some(StyleSheet::new().with_fg(Color::LightYellow)),
|
||||||
|
option: StyleSheet::new(),
|
||||||
help_message: StyleSheet::new().with_fg(Color::DarkGrey),
|
help_message: StyleSheet::new().with_fg(Color::DarkGrey),
|
||||||
default_value: StyleSheet::new().with_fg(Color::LightBlue),
|
default_value: StyleSheet::new().with_fg(Color::LightBlue),
|
||||||
error_message: ErrorMessageRenderConfig::default_colored()
|
error_message: ErrorMessageRenderConfig::default_colored()
|
||||||
|
|
@ -152,7 +154,7 @@ pub fn subcommand(msg: impl fmt::Display) {
|
||||||
println!("{}", style(format!("Running `{msg}`...")).dim());
|
println!("{}", style(format!("Running `{msg}`...")).dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warning(warning: &str) {
|
pub fn warning(warning: impl fmt::Display) {
|
||||||
println!(
|
println!(
|
||||||
"{} {} {warning}",
|
"{} {} {warning}",
|
||||||
WARNING_PREFIX,
|
WARNING_PREFIX,
|
||||||
|
|
@ -237,23 +239,16 @@ pub fn passphrase_stdin() -> Result<Passphrase, anyhow::Error> {
|
||||||
Ok(Passphrase::from(input.trim_end().to_owned()))
|
Ok(Passphrase::from(input.trim_end().to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select<'a, T>(
|
pub fn select<'a, T>(prompt: &str, options: &'a [T], help: &str) -> Result<&'a T, InquireError>
|
||||||
prompt: &str,
|
|
||||||
options: &'a [T],
|
|
||||||
active: &'a T,
|
|
||||||
) -> Result<Option<&'a T>, InquireError>
|
|
||||||
where
|
where
|
||||||
T: fmt::Display + Eq + PartialEq,
|
T: fmt::Display + Eq + PartialEq,
|
||||||
{
|
{
|
||||||
let active = options.iter().position(|o| o == active);
|
let selection = Select::new(prompt, options.iter().collect::<Vec<_>>())
|
||||||
let selection =
|
.with_vim_mode(true)
|
||||||
Select::new(prompt, options.iter().collect::<Vec<_>>()).with_render_config(*CONFIG);
|
.with_help_message(help)
|
||||||
|
.with_render_config(*CONFIG);
|
||||||
|
|
||||||
if let Some(active) = active {
|
selection.with_starting_cursor(0).prompt()
|
||||||
selection.with_starting_cursor(active).prompt_skippable()
|
|
||||||
} else {
|
|
||||||
selection.prompt_skippable()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn markdown(content: &str) {
|
pub fn markdown(content: &str) {
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,16 @@ fn main() -> anyhow::Result<()> {
|
||||||
"editor",
|
"editor",
|
||||||
"prompt",
|
"prompt",
|
||||||
],
|
],
|
||||||
&"spinner",
|
"Choose wisely!",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match demo {
|
match *demo {
|
||||||
Some(&"confirm") => {
|
"confirm" => {
|
||||||
if terminal::confirm("Would you like to proceed?") {
|
if terminal::confirm("Would you like to proceed?") {
|
||||||
terminal::success!("You said 'yes'");
|
terminal::success!("You said 'yes'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(&"editor") => {
|
"editor" => {
|
||||||
let output = terminal::editor::Editor::new()
|
let output = terminal::editor::Editor::new()
|
||||||
.extension("rs")
|
.extension("rs")
|
||||||
.edit("// Enter code here.");
|
.edit("// Enter code here.");
|
||||||
|
|
@ -40,7 +40,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(&"spinner") => {
|
"spinner" => {
|
||||||
let mut spinner = terminal::spinner("Spinning turbines..");
|
let mut spinner = terminal::spinner("Spinning turbines..");
|
||||||
thread::sleep(time::Duration::from_secs(1));
|
thread::sleep(time::Duration::from_secs(1));
|
||||||
spinner.message("Still spinning..");
|
spinner.message("Still spinning..");
|
||||||
|
|
@ -51,27 +51,22 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
spinner.finish();
|
spinner.finish();
|
||||||
}
|
}
|
||||||
Some(&"spinner-drop") => {
|
"spinner-drop" => {
|
||||||
let _spinner = terminal::spinner("Spinning turbines..");
|
let _spinner = terminal::spinner("Spinning turbines..");
|
||||||
thread::sleep(time::Duration::from_secs(3));
|
thread::sleep(time::Duration::from_secs(3));
|
||||||
}
|
}
|
||||||
Some(&"spinner-error") => {
|
"spinner-error" => {
|
||||||
let spinner = terminal::spinner("Spinning turbines..");
|
let spinner = terminal::spinner("Spinning turbines..");
|
||||||
thread::sleep(time::Duration::from_secs(3));
|
thread::sleep(time::Duration::from_secs(3));
|
||||||
spinner.error("broken turbine");
|
spinner.error("broken turbine");
|
||||||
}
|
}
|
||||||
Some(&"prompt") => {
|
"prompt" => {
|
||||||
let fruit = terminal::io::select(
|
let fruit = terminal::io::select(
|
||||||
"Enter your favorite fruit:",
|
"Enter your favorite fruit:",
|
||||||
&["apple", "pear", "banana", "strawberry"],
|
&["apple", "pear", "banana", "strawberry"],
|
||||||
&"apple",
|
"Choose wisely!",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(fruit) = fruit {
|
|
||||||
terminal::success!("You have chosen '{fruit}'");
|
terminal::success!("You have chosen '{fruit}'");
|
||||||
} else {
|
|
||||||
terminal::info!("Ok, bye.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue