Merge pull request #344 from AleoHQ/feature/config-automatic

Automatic update configurations
This commit is contained in:
Howard Wu 2020-09-03 02:46:04 -07:00 committed by GitHub
commit 02f9aa8dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 265 additions and 58 deletions

View File

@ -33,11 +33,17 @@ pub trait CLI {
fn new<'a, 'b>() -> App<'a, 'b> {
let arguments = &Self::ARGUMENTS
.iter()
.map(|a| Arg::with_name(a.0).help(a.1).required(a.2).index(a.3))
.map(|a| {
let mut args = Arg::with_name(a.0).help(a.1).required(a.3).index(a.4);
if a.2.len() > 0 {
args = args.possible_values(a.2);
}
args
})
.collect::<Vec<Arg<'static, 'static>>>();
let flags = &Self::FLAGS
.iter()
.map(|a| Arg::from_usage(a).global(true))
.map(|a| Arg::from_usage(a))
.collect::<Vec<Arg<'static, 'static>>>();
let options = &Self::OPTIONS
.iter()
@ -56,6 +62,22 @@ pub trait CLI {
.about(s.1)
.args(
&s.2.iter()
.map(|a| {
let mut args = Arg::with_name(a.0).help(a.1).required(a.3).index(a.4);
if a.2.len() > 0 {
args = args.possible_values(a.2);
}
args
})
.collect::<Vec<Arg<'static, 'static>>>(),
)
.args(
&s.3.iter()
.map(|a| Arg::from_usage(a))
.collect::<Vec<Arg<'static, 'static>>>(),
)
.args(
&s.4.iter()
.map(|a| match a.2.len() > 0 {
true => Arg::from_usage(a.0)
.conflicts_with_all(a.1)
@ -65,7 +87,7 @@ pub trait CLI {
})
.collect::<Vec<Arg<'static, 'static>>>(),
)
.settings(s.3)
.settings(s.5)
})
.collect::<Vec<App<'static, 'static>>>();

View File

@ -24,10 +24,14 @@ pub type DescriptionType = &'static str;
pub type RequiredType = bool;
pub type PossibleValuesType = &'static [&'static str];
pub type IndexType = u64;
pub type ArgumentType = (NameType, DescriptionType, RequiredType, IndexType);
pub type ArgumentType = (NameType, DescriptionType, PossibleValuesType, RequiredType, IndexType);
// Format
// "[flag] -f --flag 'Add flag description here'"
pub type FlagType = &'static str;
// Format
@ -39,4 +43,11 @@ pub type OptionType = (
&'static [&'static str],
);
pub type SubCommandType = (NameType, AboutType, &'static [OptionType], &'static [AppSettings]);
pub type SubCommandType = (
NameType,
AboutType,
&'static [ArgumentType],
&'static [FlagType],
&'static [OptionType],
&'static [AppSettings],
);

View File

@ -35,7 +35,7 @@ use std::{
io::{Read, Write},
};
pub const ADD_URL: &str = "api/package/fetch";
pub const ADD_URL: &str = "v1/package/fetch";
#[derive(Debug)]
pub struct AddCommand;
@ -47,10 +47,11 @@ impl CLI for AddCommand {
const ABOUT: AboutType = "Install a package from the Aleo Package Manager";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, required, index)
// (name, description, possible_values, required, index)
(
"REMOTE",
"Install a package from the Aleo Package Manager with the given remote",
&[],
false,
1u64,
),

View File

@ -186,9 +186,9 @@ impl CLI for BuildCommand {
// Drop "Compiling" context for console logging
drop(enter);
// Begin "Finished" context for console logging todo: @collin figure a way to get this output with tracing without dropping span
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
tracing::info!("Completed in {} milliseconds\n", start.elapsed().as_millis());
// Begin "Done" context for console logging todo: @collin figure a way to get this output with tracing without dropping span
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {} milliseconds\n", start.elapsed().as_millis());
});
return Ok(Some((program, checksum_differs)));

View File

@ -75,8 +75,8 @@ impl CLI for CleanCommand {
// Drop "Compiling" context for console logging
drop(enter);
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
// Begin "Done" context for console logging
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Program workspace cleaned\n");
});

View File

@ -30,7 +30,7 @@ use crate::{
use std::collections::HashMap;
pub const LOGIN_URL: &str = "api/account/authenticate";
pub const LOGIN_URL: &str = "v1/account/authenticate";
#[derive(Debug)]
pub struct LoginCommand;
@ -42,10 +42,11 @@ impl CLI for LoginCommand {
const ABOUT: AboutType = "Login to the Aleo Package Manager";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, required, index)
// (name, description, possible_values, required, index)
(
"NAME",
"Sets the authentication token for login to the package manager",
&[],
false,
1u64,
),

View File

@ -33,10 +33,11 @@ impl CLI for NewCommand {
const ABOUT: AboutType = "Create a new Leo package in a new directory";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, required, index)
// (name, description, possible_values, required, index)
(
"NAME",
"Sets the resulting package name, defaults to the directory name",
&[],
true,
1u64,
),

View File

@ -75,9 +75,9 @@ impl CLI for ProveCommand {
// Drop "Prover" context for console logging
drop(enter);
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
tracing::info!("Completed in {:?} milliseconds\n", end);
// Begin "Done" context for console logging
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {:?} milliseconds\n", end);
});
Ok((program_proof, prepared_verifying_key))

View File

@ -38,7 +38,7 @@ use reqwest::{
use serde::Deserialize;
use std::{convert::TryFrom, env::current_dir};
const PUBLISH_URL: &str = "api/package/publish";
pub const PUBLISH_URL: &str = "v1/package/publish";
#[derive(Deserialize)]
struct ResponseJson {

View File

@ -29,8 +29,14 @@ impl CLI for RemoveCommand {
const ABOUT: AboutType = "Uninstall a package from the current package";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, required, index)
("NAME", "Removes the package from the current directory", true, 1u64),
// (name, description, possible_values, required, index)
(
"NAME",
"Removes the package from the current directory",
&[],
true,
1u64,
),
];
const FLAGS: &'static [FlagType] = &[];
const NAME: NameType = "remove";

View File

@ -48,7 +48,7 @@ impl CLI for RunCommand {
let (proof, prepared_verifying_key) = ProveCommand::output(options)?;
// Begin "Verifying" context for console logging
let span = tracing::span!(tracing::Level::INFO, "Verifier");
let span = tracing::span!(tracing::Level::INFO, "Verifying");
let enter = span.enter();
tracing::info!("Starting...");
@ -76,9 +76,9 @@ impl CLI for RunCommand {
// Drop "Verifying" context for console logging
drop(enter);
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
tracing::info!("Completed in {:?} milliseconds\n", end);
// Begin "Done" context for console logging
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {:?} milliseconds\n", end);
});
Ok(())

View File

@ -138,9 +138,9 @@ impl CLI for SetupCommand {
// Drop "Setup" context for console logging
drop(enter);
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
tracing::info!("Completed in {:?} milliseconds\n", end);
// Begin "Done" context for console logging
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {:?} milliseconds\n", end);
});
Ok((program, proving_key, prepared_verifying_key))

View File

@ -110,8 +110,8 @@ impl CLI for TestCommand {
// Set the result of the test command to passed if no tests failed.
if failed == 0 {
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::INFO, "Finished").in_scope(|| {
// Begin "Done" context for console logging
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!(
"Tests passed in {} milliseconds. {} passed; {} failed;\n",
start.elapsed().as_millis(),
@ -120,8 +120,8 @@ impl CLI for TestCommand {
);
});
} else {
// Begin "Finished" context for console logging
tracing::span!(tracing::Level::ERROR, "Finished").in_scope(|| {
// Begin "Done" context for console logging
tracing::span!(tracing::Level::ERROR, "Done").in_scope(|| {
tracing::error!(
"Tests failed in {} milliseconds. {} passed; {} failed;\n",
start.elapsed().as_millis(),

View File

@ -14,25 +14,59 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{cli::CLI, cli_types::*, updater::Updater};
use crate::{cli::CLI, cli_types::*, config::Config, updater::Updater};
use clap::AppSettings;
#[derive(Debug)]
pub struct UpdateCommand;
impl CLI for UpdateCommand {
type Options = (bool,);
// (show_all_versions, quiet)
type Options = Option<(bool, bool, bool)>;
type Output = ();
const ABOUT: AboutType = "Update Leo to the latest version";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[("--list")];
const FLAGS: &'static [FlagType] = &[
"[list] -l --list 'List all available versions of Leo'",
"[quiet] -q --quiet 'Suppress outputs to terminal'",
"[studio] -s --studio 'For Aleo Studio only'",
];
const NAME: NameType = "update";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[
// (name, description, options, settings)
(
UpdateAutomatic::NAME,
UpdateAutomatic::ABOUT,
UpdateAutomatic::ARGUMENTS,
UpdateAutomatic::FLAGS,
&UpdateAutomatic::OPTIONS,
&[
AppSettings::ColoredHelp,
AppSettings::DisableHelpSubcommand,
AppSettings::DisableVersion,
],
),
];
fn parse(arguments: &clap::ArgMatches) -> Result<Self::Options, crate::errors::CLIError> {
match arguments.subcommand() {
("automatic", Some(arguments)) => {
// Run the `automatic` subcommand
let options = UpdateAutomatic::parse(arguments)?;
let _output = UpdateAutomatic::output(options)?;
return Ok(None);
}
_ => {}
};
let show_all_versions = arguments.is_present("list");
Ok((show_all_versions,))
let quiet = arguments.is_present("quiet");
let studio = arguments.is_present("studio");
Ok(Some((show_all_versions, quiet, studio)))
}
fn output(options: Self::Options) -> Result<Self::Output, crate::errors::CLIError> {
@ -40,29 +74,123 @@ impl CLI for UpdateCommand {
let span = tracing::span!(tracing::Level::INFO, "Updating");
let _enter = span.enter();
match options {
(true,) => match Updater::show_available_releases() {
let (show_all_versions, quiet, studio) = match options {
Some(options) => options,
None => return Ok(()),
};
match show_all_versions {
true => match Updater::show_available_releases() {
Ok(_) => return Ok(()),
Err(e) => {
tracing::error!("Could not fetch that latest version of Leo");
tracing::error!("{}", e);
}
},
(false,) => match Updater::update_to_latest_release(true) {
Ok(status) => {
if status.uptodate() {
tracing::info!("Leo is already on the latest version: {}", status.version());
} else if status.updated() {
tracing::info!("Leo has successfully updated to version: {}", status.version());
}
false => {
let config = Config::read_config()?;
// If update is run with studio and the automatic update is off, finish quietly
if studio && !config.update.automatic {
return Ok(());
}
Err(e) => {
tracing::error!("Could not update Leo to the latest version");
tracing::error!("{}", e);
match Updater::update_to_latest_release(!quiet) {
Ok(status) => {
if !quiet {
if status.uptodate() {
tracing::info!("Leo is already on the latest version {}", status.version());
} else if status.updated() {
tracing::info!("Leo has successfully updated to version {}", status.version());
}
}
return Ok(());
}
Err(e) => {
if !quiet {
tracing::error!("Could not update Leo to the latest version");
tracing::error!("{}", e);
}
}
}
},
}
}
Ok(())
}
}
//TODO (raychu86) Move this to dedicated file/module
#[derive(Debug)]
pub struct UpdateAutomatic;
impl CLI for UpdateAutomatic {
// (is_automatic, quiet)
type Options = (Option<bool>, bool);
type Output = ();
const ABOUT: AboutType = "Setting for automatic updates of Leo";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, possible_values, required, index)
(
"automatic",
"Enable or disable automatic updates",
&["true", "false"],
false,
1u64,
),
];
const FLAGS: &'static [FlagType] = &["[quiet] -q --quiet 'Suppress outputs to terminal'"];
const NAME: NameType = "automatic";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
fn parse(arguments: &clap::ArgMatches) -> Result<Self::Options, crate::errors::CLIError> {
let quiet = arguments.is_present("quiet");
match arguments.value_of("automatic") {
Some(automatic) => {
// TODO enforce that the possible values is true or false
let automatic = match automatic {
"true" => Some(true),
"false" => Some(false),
_ => unreachable!(),
};
Ok((automatic, quiet))
}
None => Ok((None, quiet)),
}
}
fn output(options: Self::Options) -> Result<Self::Output, crate::errors::CLIError> {
// Begin "Settings" context for console logging
let span = tracing::span!(tracing::Level::INFO, "Settings");
let enter = span.enter();
// If a boolean value is provided, update the saved
// `automatic` configuration value to this boolean value.
if let Some(automatic) = options.0 {
Config::set_update_automatic(automatic)?;
}
// If --quiet is not enabled, log the output.
if !options.1 {
// Read the `automatic` value now.
let automatic = Config::read_config()?.update.automatic;
// Log the output.
tracing::debug!("automatic = {}", automatic);
match automatic {
true => tracing::info!("Automatic updates are enabled. Leo will update as new versions are released."),
false => {
tracing::info!("Automatic updates are disabled. Leo will not update as new versions are released.")
}
};
}
// Drop "Settings" context for console logging.
drop(enter);
Ok(())
}
}

View File

@ -26,7 +26,7 @@ use std::{
path::{Path, PathBuf},
};
pub const PACKAGE_MANAGER_URL: &str = "https://apm-backend-prod.herokuapp.com/";
pub const PACKAGE_MANAGER_URL: &str = "https://api.aleo.pm/";
pub const LEO_CREDENTIALS_FILE: &str = "credentials";
pub const LEO_CONFIG_FILE: &str = "config.toml";
@ -49,14 +49,27 @@ lazy_static! {
};
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Update {
pub automatic: bool,
}
impl Default for Update {
fn default() -> Self {
Self { automatic: true }
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
pub auto_update: bool,
pub update: Update,
}
impl Default for Config {
fn default() -> Self {
Self { auto_update: false }
Self {
update: Update::default(),
}
}
}
@ -76,10 +89,19 @@ impl Config {
}
let toml_string = match fs::read_to_string(&config_path) {
Ok(toml) => toml,
Ok(mut toml) => {
// If the config is using an incorrect format, rewrite it.
if let Err(_) = toml::from_str::<Config>(&toml) {
let default_config_string = toml::to_string(&Config::default())?;
fs::write(&config_path, default_config_string.clone())?;
toml = default_config_string;
}
toml
}
Err(_) => {
create_dir_all(&config_dir)?;
String::new()
toml::to_string(&Config::default())?
}
};
@ -88,6 +110,21 @@ impl Config {
Ok(config)
}
/// Update the `automatic` configuration in the `config.toml` file.
pub fn set_update_automatic(automatic: bool) -> Result<(), CLIError> {
let mut config = Self::read_config()?;
if config.update.automatic != automatic {
config.update.automatic = automatic;
// Update the config file
let config_path = LEO_CONFIG_PATH.clone();
fs::write(&config_path, toml::to_string(&config)?)?;
}
Ok(())
}
}
pub fn write_token(token: &str) -> Result<(), io::Error> {

View File

@ -186,7 +186,7 @@ where
}
}
write!(writer, "{:>12} ", colored_string(meta.level(), &message)).expect("Error writing event");
write!(writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event");
}
ctx.format_fields(writer, event)?;

View File

@ -33,7 +33,7 @@ impl Updater {
.repo_name(Self::LEO_REPO_NAME)
.bin_name(Self::LEO_BIN_NAME)
.current_version(&include_str!("./leo-version").replace('v', ""))
.show_download_progress(true)
.show_download_progress(show_output)
.no_confirm(true)
.show_output(show_output)
.build()?
@ -65,7 +65,7 @@ impl Updater {
pub fn print_cli() {
let config = Config::read_config().unwrap();
if config.auto_update {
if config.update.automatic {
// If the auto update configuration is on, attempt to update the version.
if let Ok(status) = Self::update_to_latest_release(false) {
if status.updated() {