Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implemented config version migration #331

Merged
merged 3 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions src/commands/config/add_connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pub struct AddNetworkConnection {
#[interactive_clap(skip_default_input_arg)]
fastnear_url: Option<String>,
#[interactive_clap(long)]
staking_pools_factory_account_id: crate::types::account_id::AccountId,
#[interactive_clap(skip_default_input_arg)]
staking_pools_factory_account_id: Option<crate::types::account_id::AccountId>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -77,11 +78,13 @@ impl AddNetworkConnectionContext {
staking_pools_factory_account_id: scope
.staking_pools_factory_account_id
.clone()
.into(),
.map(|staking_pools_factory_account_id| {
staking_pools_factory_account_id.into()
}),
},
);
eprintln!();
crate::common::write_config_toml(config)?;
config.write_config_toml()?;
eprintln!(
"Network connection \"{}\" was successfully added to config.toml",
&scope.connection_name
Expand Down Expand Up @@ -244,4 +247,29 @@ impl AddNetworkConnection {
Ok(None)
}
}

fn input_staking_pools_factory_account_id(
_context: &crate::GlobalContext,
) -> color_eyre::eyre::Result<Option<crate::types::account_id::AccountId>> {
eprintln!();
#[derive(strum_macros::Display)]
enum ConfirmOptions {
#[strum(to_string = "Yes, I want to enter the staking pools factory account ID")]
Yes,
#[strum(to_string = "No, I don't want to enter the staking pools factory account ID")]
No,
}
let select_choose_input = Select::new(
"Do you want to enter the staking pools factory account ID?",
vec![ConfirmOptions::Yes, ConfirmOptions::No],
)
.prompt()?;
if let ConfirmOptions::Yes = select_choose_input {
let account_id: crate::types::account_id::AccountId =
CustomType::new("What is the staking pools factory account ID?").prompt()?;
Ok(Some(account_id))
} else {
Ok(None)
}
}
}
2 changes: 1 addition & 1 deletion src/commands/config/delete_connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl DeleteNetworkConnectionContext {
let mut config = previous_context.config;
config.network_connection.remove(&scope.connection_name);
eprintln!();
crate::common::write_config_toml(config)?;
config.write_config_toml()?;
eprintln!(
"Network connection \"{}\" was successfully removed from config.toml",
&scope.connection_name
Expand Down
41 changes: 7 additions & 34 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1253,39 +1253,6 @@ pub fn save_access_key_to_legacy_keychain(
}
}

pub fn get_config_toml() -> color_eyre::eyre::Result<crate::config::Config> {
if let Some(mut path_config_toml) = dirs::config_dir() {
path_config_toml.extend(&["near-cli", "config.toml"]);

if !path_config_toml.is_file() {
write_config_toml(crate::config::Config::default())?;
};
let config_toml = std::fs::read_to_string(&path_config_toml)?;
toml::from_str(&config_toml).or_else(|err| {
eprintln!("Warning: `near` CLI configuration file stored at {path_config_toml:?} could not be parsed due to: {err}");
eprintln!("Note: The default configuration printed below will be used instead:\n");
let default_config = crate::config::Config::default();
eprintln!("{}", toml::to_string(&default_config)?);
Ok(default_config)
})
} else {
Ok(crate::config::Config::default())
}
}
pub fn write_config_toml(config: crate::config::Config) -> CliResult {
let config_toml = toml::to_string(&config)?;
let mut path_config_toml = dirs::config_dir().wrap_err("Impossible to get your config dir!")?;
path_config_toml.push("near-cli");
std::fs::create_dir_all(&path_config_toml)?;
path_config_toml.push("config.toml");
std::fs::File::create(&path_config_toml)
.wrap_err_with(|| format!("Failed to create file: {path_config_toml:?}"))?
.write(config_toml.as_bytes())
.wrap_err_with(|| format!("Failed to write to file: {path_config_toml:?}"))?;
eprintln!("Note: `near` CLI configuration is stored in {path_config_toml:?}");
Ok(())
}

pub fn try_external_subcommand_execution(error: clap::Error) -> CliResult {
let (subcommand, args) = {
let mut args = std::env::args().skip(1);
Expand Down Expand Up @@ -1524,8 +1491,14 @@ pub fn fetch_validators_api(

pub fn fetch_validators_rpc(
json_rpc_client: &near_jsonrpc_client::JsonRpcClient,
staking_pools_factory_account_id: near_primitives::types::AccountId,
staking_pools_factory_account_id: Option<near_primitives::types::AccountId>,
) -> color_eyre::Result<std::collections::BTreeSet<near_primitives::types::AccountId>> {
let Some(staking_pools_factory_account_id) = staking_pools_factory_account_id else {
return Err(color_eyre::Report::msg(
"Staking pools factory account ID is not set for selected network",
));
};

let query_view_method_response = json_rpc_client
.blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest {
block_reference: near_primitives::types::Finality::Final.into(),
Expand Down
94 changes: 94 additions & 0 deletions src/config/migrations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::config::Config as ConfigV2;
use crate::config::NetworkConfig as NetworkConfigV2;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConfigV1 {
pub credentials_home_dir: std::path::PathBuf,
pub network_connection: linked_hash_map::LinkedHashMap<String, NetworkConfigV1>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NetworkConfigV1 {
pub network_name: String,
pub rpc_url: url::Url,
pub rpc_api_key: Option<crate::types::api_key::ApiKey>,
pub wallet_url: url::Url,
pub explorer_transaction_url: url::Url,
// https://github.com/near/near-cli-rs/issues/116
pub linkdrop_account_id: Option<near_primitives::types::AccountId>,
// https://docs.near.org/social/contract
pub near_social_db_contract_account_id: Option<near_primitives::types::AccountId>,
pub faucet_url: Option<url::Url>,
pub meta_transaction_relayer_url: Option<url::Url>,
}

impl From<ConfigV1> for ConfigV2 {
fn from(config: ConfigV1) -> Self {
ConfigV2 {
credentials_home_dir: config.credentials_home_dir,
network_connection: config
.network_connection
.into_iter()
.map(|(network_name, network_config)| (network_name, network_config.into()))
.collect(),
}
}
}

impl From<NetworkConfigV1> for NetworkConfigV2 {
fn from(network_config: NetworkConfigV1) -> Self {
match network_config.network_name.as_str() {
"mainnet" => NetworkConfigV2 {
network_name: network_config.network_name,
rpc_url: network_config.rpc_url,
wallet_url: network_config.wallet_url,
explorer_transaction_url: network_config.explorer_transaction_url,
rpc_api_key: network_config.rpc_api_key,
linkdrop_account_id: network_config.linkdrop_account_id,
near_social_db_contract_account_id: network_config
.near_social_db_contract_account_id,
faucet_url: network_config.faucet_url,
meta_transaction_relayer_url: network_config.meta_transaction_relayer_url,
fastnear_url: Some(String::from("https://api.fastnear.com")),
staking_pools_factory_account_id: Some("poolv1.near".parse().unwrap()),
},
"testnet" => NetworkConfigV2 {
network_name: network_config.network_name,
rpc_url: network_config.rpc_url,
wallet_url: network_config.wallet_url,
explorer_transaction_url: network_config.explorer_transaction_url,
rpc_api_key: network_config.rpc_api_key,
linkdrop_account_id: network_config.linkdrop_account_id,
near_social_db_contract_account_id: network_config
.near_social_db_contract_account_id,
faucet_url: network_config.faucet_url,
meta_transaction_relayer_url: network_config.meta_transaction_relayer_url,
fastnear_url: None,
staking_pools_factory_account_id: Some("pool.f863973.m0".parse().unwrap()),
},
_ => NetworkConfigV2 {
network_name: network_config.network_name,
rpc_url: network_config.rpc_url,
wallet_url: network_config.wallet_url,
explorer_transaction_url: network_config.explorer_transaction_url,
rpc_api_key: network_config.rpc_api_key,
linkdrop_account_id: network_config.linkdrop_account_id,
near_social_db_contract_account_id: network_config
.near_social_db_contract_account_id,
faucet_url: network_config.faucet_url,
meta_transaction_relayer_url: network_config.meta_transaction_relayer_url,
fastnear_url: None,
staking_pools_factory_account_id: None,
},
}
}
}

#[derive(serde::Serialize, serde::Deserialize)]
#[serde(tag = "version")]
pub enum ConfigVersion {
#[serde(rename = "1")]
V1(ConfigV1),
#[serde(rename = "2")]
V2(ConfigV2),
}
80 changes: 75 additions & 5 deletions src/config.rs → src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use color_eyre::eyre::WrapErr;
use std::str::FromStr;
mod migrations;

pub type CliResult = color_eyre::eyre::Result<()>;

use color_eyre::eyre::{ContextCompat, WrapErr};
use std::{io::Write, str::FromStr};
use tracing_indicatif::span_ext::IndicatifSpanExt;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
Expand Down Expand Up @@ -30,7 +34,7 @@ impl Default for Config {
faucet_url: None,
meta_transaction_relayer_url: None,
fastnear_url: Some(String::from("https://api.fastnear.com")),
staking_pools_factory_account_id: "poolv1.near".parse().unwrap(),
staking_pools_factory_account_id: Some("poolv1.near".parse().unwrap()),
},
);
network_connection.insert(
Expand All @@ -48,7 +52,7 @@ impl Default for Config {
faucet_url: Some("https://helper.nearprotocol.com/account".parse().unwrap()),
meta_transaction_relayer_url: None,
fastnear_url: None,
staking_pools_factory_account_id: "pool.f863973.m0".parse().unwrap(),
staking_pools_factory_account_id: Some("pool.f863973.m0".parse().unwrap()),
},
);

Expand All @@ -66,6 +70,57 @@ impl Config {
.map(|(_, network_config)| network_config.network_name.clone())
.collect()
}

pub fn into_latest_version(self) -> migrations::ConfigVersion {
migrations::ConfigVersion::V2(self)
}

pub fn get_config_toml() -> color_eyre::eyre::Result<Self> {
if let Some(mut path_config_toml) = dirs::config_dir() {
path_config_toml.extend(&["near-cli", "config.toml"]);

if !path_config_toml.is_file() {
Self::write_config_toml(crate::config::Config::default())?;
};

let config_toml = std::fs::read_to_string(&path_config_toml)?;

let config_version = toml::from_str::<migrations::ConfigVersion>(&config_toml).or_else::<color_eyre::eyre::Report, _>(|err| {
if let Ok(config_v1) = toml::from_str::<migrations::ConfigV1>(&config_toml) {
Ok(migrations::ConfigVersion::V1(config_v1))
} else {
eprintln!("Warning: `near` CLI configuration file stored at {path_config_toml:?} could not be parsed due to: {err}");
eprintln!("Note: The default configuration printed below will be used instead:\n");
let default_config = crate::config::Config::default();
eprintln!("{}", toml::to_string(&default_config)?);
Ok(default_config.into_latest_version())
}
})?;

Ok(config_version.into())
} else {
Ok(crate::config::Config::default())
}
}

pub fn write_config_toml(self) -> CliResult {
let config_toml = toml::to_string(&self.into_latest_version())?;
let mut path_config_toml =
dirs::config_dir().wrap_err("Impossible to get your config dir!")?;

path_config_toml.push("near-cli");
std::fs::create_dir_all(&path_config_toml)?;
path_config_toml.push("config.toml");

std::fs::File::create(&path_config_toml)
.wrap_err_with(|| format!("Failed to create file: {path_config_toml:?}"))?
.write(config_toml.as_bytes())
.wrap_err_with(|| format!("Failed to write to file: {path_config_toml:?}"))?;

eprintln!("Note: `near` CLI configuration is stored in {path_config_toml:?}");

Ok(())
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
Expand All @@ -82,7 +137,7 @@ pub struct NetworkConfig {
pub faucet_url: Option<url::Url>,
pub meta_transaction_relayer_url: Option<url::Url>,
pub fastnear_url: Option<String>,
pub staking_pools_factory_account_id: near_primitives::types::AccountId,
pub staking_pools_factory_account_id: Option<near_primitives::types::AccountId>,
}

impl NetworkConfig {
Expand Down Expand Up @@ -115,3 +170,18 @@ impl NetworkConfig {
}
}
}

impl From<migrations::ConfigVersion> for Config {
fn from(mut config_version: migrations::ConfigVersion) -> Self {
loop {
config_version = match config_version {
migrations::ConfigVersion::V1(config_v1) => {
migrations::ConfigVersion::V2(config_v1.into())
}
migrations::ConfigVersion::V2(config_v2) => {
break config_v2;
}
};
}
}
}
2 changes: 1 addition & 1 deletion src/js_command_match/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl DeployArgs {
if let Some(init_args) = &self.init_args {
let mut initial_function = "new".to_string();
if let Some(init_function) = &self.init_function {
initial_function = init_function.clone();
initial_function.clone_from(init_function);
}
vec![
"contract".to_owned(),
Expand Down
2 changes: 1 addition & 1 deletion src/js_command_match/dev_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl DevDeployArgs {
if let Some(init_args) = &self.init_args {
let mut initial_function = "new".to_string();
if let Some(init_function) = &self.init_function {
initial_function = init_function.clone();
initial_function.clone_from(init_function);
}
eprintln!(
" {}",
Expand Down
2 changes: 1 addition & 1 deletion src/js_command_match/generate_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct GenerateKeyArgs {

impl GenerateKeyArgs {
pub fn to_cli_args(&self, network_config: String) -> color_eyre::eyre::Result<Vec<String>> {
let config = crate::common::get_config_toml()?;
let config = crate::config::Config::get_config_toml()?;
let mut generation_method = "use-auto-generation".to_string();
if self.use_ledger_key.is_some() {
generation_method = "use-ledger".to_string();
Expand Down
2 changes: 1 addition & 1 deletion src/js_command_match/set_api_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct SetApiKeyArgs {

impl SetApiKeyArgs {
pub fn to_cli_args(&self, network_name: String) -> color_eyre::eyre::Result<Vec<String>> {
let config = crate::common::get_config_toml()?;
let config = crate::config::Config::get_config_toml()?;
let network_config = match config.network_connection.get(&network_name) {
Some(network_config) => network_config,
None => {
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl From<CmdContext> for crate::GlobalContext {
}

fn main() -> crate::common::CliResult {
let config = crate::common::get_config_toml()?;
let config = crate::config::Config::get_config_toml()?;

if !crate::common::is_used_account_list_exist(&config.credentials_home_dir) {
crate::common::create_used_account_list_from_keychain(&config.credentials_home_dir)?;
Expand Down
Loading