From 933b3fc438006646bf4b3dbd7a50c10010d4b12f Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Mon, 11 Nov 2024 13:05:42 +0100 Subject: [PATCH 1/4] EIM-68 enabled non-interactive mode --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/wizard/mod.rs | 46 +++++++++++++--------- src/wizard/prompts.rs | 89 +++++++++++++++++++++++++++---------------- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62a38ee..9fe98cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,7 +1677,7 @@ dependencies = [ [[package]] name = "idf-im-lib" version = "0.1.6" -source = "git+https://github.com/espressif/idf-im-lib.git?branch=master#1687f0174db8125d9c44544446ccb3cfc3094cd5" +source = "git+https://github.com/espressif/idf-im-lib.git?rev=830a35f3ce16c8acfcca92a9013274d62642a32c#830a35f3ce16c8acfcca92a9013274d62642a32c" dependencies = [ "colored", "config", diff --git a/Cargo.toml b/Cargo.toml index ea2724d..85d385e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] tokio = {version = "1.37.0", features=["full"]} -idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", branch="master" } +idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="830a35f3ce16c8acfcca92a9013274d62642a32c" } clap = {version = "4.5", features = ["cargo", "derive", "color"]} crossterm = "0.27.0" dialoguer = { git = "https://github.com/Hahihula/dialoguer.git", branch = "folder-select", features = ["folder-select"] } diff --git a/src/wizard/mod.rs b/src/wizard/mod.rs index f838923..565b69e 100644 --- a/src/wizard/mod.rs +++ b/src/wizard/mod.rs @@ -194,17 +194,26 @@ fn add_to_shell_rc(content: &str) -> Result<(), String> { } async fn select_targets_and_versions(mut config: Settings) -> Result { - if config.target.is_none() { + if (config.wizard_all_questions.unwrap_or_default() + || config.target.is_none() + || config.is_default("target")) + && config.non_interactive == Some(false) + { config.target = Some(select_target().await?); } - let target = config.target.clone().unwrap(); + let target = config.target.clone().unwrap_or_default(); debug!("Selected target: {:?}", target); - if config.idf_versions.is_none() { - config.idf_versions = Some(select_idf_version(&target[0]).await?); + // here the non-interactive flag is passed to the inner function + if config.wizard_all_questions.unwrap_or_default() + || config.idf_versions.is_none() + || config.is_default("idf_versions") + { + config.idf_versions = + Some(select_idf_version(&target[0], config.non_interactive.unwrap_or_default()).await?); // TODO: handle multiple targets } - let idf_versions = config.idf_versions.clone().unwrap(); + let idf_versions = config.idf_versions.clone().unwrap_or_default(); debug!("Selected idf version: {:?}", idf_versions); Ok(config) @@ -215,6 +224,7 @@ pub struct DownloadConfig { pub idf_version: String, pub idf_mirror: Option, pub recurse_submodules: Option, + pub non_interactive: Option, } pub enum DownloadError { @@ -261,8 +271,6 @@ pub fn download_idf(config: DownloadConfig) -> Result<(), DownloadError> { } }); - // let progress_bar = create_progress_bar(); - let tag = if config.idf_version == "master" { None } else { @@ -293,7 +301,13 @@ pub fn download_idf(config: DownloadConfig) -> Result<(), DownloadError> { handle.join().unwrap(); Ok(()) } - Err(err) => handle_download_error(err), + Err(err) => { + if config.non_interactive == Some(true) { + Ok(()) + } else { + handle_download_error(err) + } + } } } @@ -432,18 +446,11 @@ fn get_and_validate_idf_tools_path( pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> { debug!("Config entering wizard: {:?}", config); - if let Some(non_interactive) = config.non_interactive { - if non_interactive { - panic!("Non interactive instalation not yet supported."); - // panic!("Running Wizard in non-interactive mode is not supported."); - } - } - // Check prerequisites - check_and_install_prerequisites()?; + check_and_install_prerequisites(config.non_interactive.unwrap_or_default())?; // Python sanity check - check_and_install_python()?; + check_and_install_python(config.non_interactive.unwrap_or_default())?; // select target & idf version config = select_targets_and_versions(config).await?; @@ -470,6 +477,7 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> { idf_version: idf_version.to_string(), idf_mirror: config.idf_mirror.clone(), recurse_submodules: config.recurse_submodules, + non_interactive: config.non_interactive, }; match download_idf(download_config) { @@ -557,7 +565,9 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> { export_paths, ) } - save_config_if_desired(&config)?; + if !config.non_interactive.unwrap_or_default() { + save_config_if_desired(&config)?; + } let ide_conf_path_tmp = PathBuf::from(&config.esp_idf_json_path.clone().unwrap_or_default()); debug!("IDE configuration path: {}", ide_conf_path_tmp.display()); match ensure_path(ide_conf_path_tmp.to_str().unwrap()) { diff --git a/src/wizard/prompts.rs b/src/wizard/prompts.rs index 9f6a38f..d7c35ef 100644 --- a/src/wizard/prompts.rs +++ b/src/wizard/prompts.rs @@ -15,7 +15,10 @@ pub async fn select_target() -> Result, String> { first_defaulted_multiselect("wizard.select_target.prompt", &available_targets) } -pub async fn select_idf_version(target: &str) -> Result, String> { +pub async fn select_idf_version( + target: &str, + non_interactive: bool, +) -> Result, String> { let mut avalible_versions = if target == "all" { //todo process vector of targets idf_im_lib::idf_versions::get_idf_names().await @@ -23,7 +26,12 @@ pub async fn select_idf_version(target: &str) -> Result, String> { idf_im_lib::idf_versions::get_idf_name_by_target(&target.to_string().to_lowercase()).await }; avalible_versions.push("master".to_string()); - first_defaulted_multiselect("wizard.select_idf_version.prompt", &avalible_versions) + if non_interactive { + debug!("Non-interactive mode, selecting first available IDF version."); + return Ok(vec![avalible_versions.first().unwrap().clone()]); + } else { + first_defaulted_multiselect("wizard.select_idf_version.prompt", &avalible_versions) + } } fn check_prerequisites() -> Result, String> { @@ -40,8 +48,12 @@ fn check_prerequisites() -> Result, String> { Err(err) => Err(err), } } -pub fn check_and_install_prerequisites() -> Result<(), String> { - let unsatisfied_prerequisites = run_with_spinner(check_prerequisites)?; +pub fn check_and_install_prerequisites(non_interactive: bool) -> Result<(), String> { + let unsatisfied_prerequisites = if non_interactive { + check_prerequisites()? + } else { + run_with_spinner(check_prerequisites)? + }; if !unsatisfied_prerequisites.is_empty() { info!( "{}", @@ -50,8 +62,7 @@ pub fn check_and_install_prerequisites() -> Result<(), String> { l = unsatisfied_prerequisites.join(", ") ) ); - if std::env::consts::OS == "windows" { - //TODO: remove afte prerequisities install fix in linux + if std::env::consts::OS == "windows" && !non_interactive { if generic_confirm("prerequisites.install.prompt").map_err(|e| e.to_string())? { system_dependencies::install_prerequisites(unsatisfied_prerequisites) .map_err(|e| e.to_string())?; @@ -100,10 +111,15 @@ fn python_sanity_check(python: Option<&str>) -> Result<(), String> { Err(t!("python.sanitycheck.fail").to_string()) } } -pub fn check_and_install_python() -> Result<(), String> { +pub fn check_and_install_python(non_interactive: bool) -> Result<(), String> { info!("{}", t!("python.sanitycheck.info")); - if let Err(err) = run_with_spinner(|| python_sanity_check(None)) { - if std::env::consts::OS == "windows" { + let check_result = if non_interactive { + python_sanity_check(None) + } else { + run_with_spinner(|| python_sanity_check(None)) + }; + if let Err(err) = check_result { + if std::env::consts::OS == "windows" && !non_interactive { info!("{}", t!("python.sanitycheck.fail")); if generic_confirm("pythhon.install.prompt").map_err(|e| e.to_string())? { system_dependencies::install_prerequisites(vec!["python@3.11.5".to_string()]) @@ -138,42 +154,49 @@ pub fn check_and_install_python() -> Result<(), String> { } pub fn select_mirrors(mut config: Settings) -> Result { - config.idf_mirror = match config.idf_mirror { - Some(mirror) => Some(mirror), - None => Some(generic_select( + if (config.wizard_all_questions.unwrap_or_default() + || config.idf_mirror.is_none() + || config.is_default("idf_mirror")) + && config.non_interactive == Some(false) + { + config.idf_mirror = Some(generic_select( "wizard.idf.mirror", idf_im_lib::get_idf_mirrors_list(), - )?), - }; + )?) + } - config.mirror = match config.mirror { - Some(mirror) => Some(mirror), - None => Some(generic_select( + if (config.wizard_all_questions.unwrap_or_default() + || config.mirror.is_none() + || config.is_default("mirror")) + && config.non_interactive == Some(false) + { + config.mirror = Some(generic_select( "wizard.tools.mirror", idf_im_lib::get_idf_tools_mirrors_list(), - )?), - }; + )?) + } Ok(config) } pub fn select_installation_path(mut config: Settings) -> Result { - if config.path.is_none() { - let default_path = if std::env::consts::OS == "windows" { - "C:\\esp\\".to_string() - } else { - format!("{}/.espressif", dirs::home_dir().unwrap().display()) - }; - let mut installation_path = PathBuf::new(); - let path = generic_input( + if (config.wizard_all_questions.unwrap_or_default() + || config.path.is_none() + || config.is_default("path")) + && config.non_interactive == Some(false) + { + let path = match generic_input( "wizard.instalation_path.prompt", "wizard.instalation_path.unselected", - &default_path, - ) - .unwrap(); - - installation_path.push(path); - config.path = Some(installation_path.clone()); + &config.path.clone().unwrap_or_default().to_str().unwrap(), + ) { + Ok(path) => PathBuf::from(path), + Err(e) => { + log::error!("Error: {}", e); + config.path.clone().unwrap_or_default() + } + }; + config.path = Some(path); } Ok(config) From 9209935a5b4d9d243fdc7aab53736f86713fea6c Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Mon, 11 Nov 2024 14:31:59 +0100 Subject: [PATCH 2/4] enabled possibility to ask for prerequisities install using cli arg --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cli_args/mod.rs | 21 +++++++++++++++++++++ src/wizard/mod.rs | 10 ++++++++-- src/wizard/prompts.rs | 33 +++++++++++++++++++++++++++------ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fe98cf..8ce5722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,7 +1677,7 @@ dependencies = [ [[package]] name = "idf-im-lib" version = "0.1.6" -source = "git+https://github.com/espressif/idf-im-lib.git?rev=830a35f3ce16c8acfcca92a9013274d62642a32c#830a35f3ce16c8acfcca92a9013274d62642a32c" +source = "git+https://github.com/espressif/idf-im-lib.git?rev=e8cfbe96037bc1458c5885ca620d1c39d4d909c9#e8cfbe96037bc1458c5885ca620d1c39d4d909c9" dependencies = [ "colored", "config", diff --git a/Cargo.toml b/Cargo.toml index 85d385e..06aac89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] tokio = {version = "1.37.0", features=["full"]} -idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="830a35f3ce16c8acfcca92a9013274d62642a32c" } +idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="e8cfbe96037bc1458c5885ca620d1c39d4d909c9" } clap = {version = "4.5", features = ["cargo", "derive", "color"]} crossterm = "0.27.0" dialoguer = { git = "https://github.com/Hahihula/dialoguer.git", branch = "folder-select", features = ["folder-select"] } diff --git a/src/cli_args/mod.rs b/src/cli_args/mod.rs index 9532dc1..76295d0 100644 --- a/src/cli_args/mod.rs +++ b/src/cli_args/mod.rs @@ -115,6 +115,19 @@ pub struct Cli { help = "Should the installer recurse into submodules of the ESP-IDF repository (default true) " )] pub recurse_submodules: Option, + + #[arg( + short = 'a', + long, + help = "Should the installer attempt to install all missing prerequisites (default false). This flag only affects Windows platforms as we do not offer prerequisites for other platforms. " + )] + pub install_all_prerequisites: Option, + + #[arg( + long, + help = "if set, the installer will as it's very last move save the configuration to the specified file path. This file can than be used to repeat the instalation with the same settings." + )] + pub config_file_save_path: Option, } impl IntoIterator for Cli { @@ -168,6 +181,14 @@ impl IntoIterator for Cli { "recurse_submodules".to_string(), self.recurse_submodules.map(Into::into), ), + ( + "install_all_prerequisites".to_string(), + self.install_all_prerequisites.map(Into::into), + ), + ( + "config_file_save_path".to_string(), + self.config_file_save_path.map(Into::into), + ), ] .into_iter() } diff --git a/src/wizard/mod.rs b/src/wizard/mod.rs index 565b69e..a6432c1 100644 --- a/src/wizard/mod.rs +++ b/src/wizard/mod.rs @@ -447,10 +447,16 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> { debug!("Config entering wizard: {:?}", config); // Check prerequisites - check_and_install_prerequisites(config.non_interactive.unwrap_or_default())?; + check_and_install_prerequisites( + config.non_interactive.unwrap_or_default(), + config.install_all_prerequisites.unwrap_or_default(), + )?; // Python sanity check - check_and_install_python(config.non_interactive.unwrap_or_default())?; + check_and_install_python( + config.non_interactive.unwrap_or_default(), + config.install_all_prerequisites.unwrap_or_default(), + )?; // select target & idf version config = select_targets_and_versions(config).await?; diff --git a/src/wizard/prompts.rs b/src/wizard/prompts.rs index d7c35ef..eae8b4f 100644 --- a/src/wizard/prompts.rs +++ b/src/wizard/prompts.rs @@ -48,7 +48,10 @@ fn check_prerequisites() -> Result, String> { Err(err) => Err(err), } } -pub fn check_and_install_prerequisites(non_interactive: bool) -> Result<(), String> { +pub fn check_and_install_prerequisites( + non_interactive: bool, + install_all_prerequisites: bool, +) -> Result<(), String> { let unsatisfied_prerequisites = if non_interactive { check_prerequisites()? } else { @@ -62,8 +65,15 @@ pub fn check_and_install_prerequisites(non_interactive: bool) -> Result<(), Stri l = unsatisfied_prerequisites.join(", ") ) ); - if std::env::consts::OS == "windows" && !non_interactive { - if generic_confirm("prerequisites.install.prompt").map_err(|e| e.to_string())? { + if std::env::consts::OS == "windows" { + let res = if !install_all_prerequisites && !non_interactive { + generic_confirm("prerequisites.install.prompt") + } else if install_all_prerequisites { + Ok(true) + } else { + Ok(false) + }; + if res.map_err(|e| e.to_string())? { system_dependencies::install_prerequisites(unsatisfied_prerequisites) .map_err(|e| e.to_string())?; @@ -111,7 +121,10 @@ fn python_sanity_check(python: Option<&str>) -> Result<(), String> { Err(t!("python.sanitycheck.fail").to_string()) } } -pub fn check_and_install_python(non_interactive: bool) -> Result<(), String> { +pub fn check_and_install_python( + non_interactive: bool, + install_all_prerequisites: bool, +) -> Result<(), String> { info!("{}", t!("python.sanitycheck.info")); let check_result = if non_interactive { python_sanity_check(None) @@ -119,9 +132,17 @@ pub fn check_and_install_python(non_interactive: bool) -> Result<(), String> { run_with_spinner(|| python_sanity_check(None)) }; if let Err(err) = check_result { - if std::env::consts::OS == "windows" && !non_interactive { + if std::env::consts::OS == "windows" { info!("{}", t!("python.sanitycheck.fail")); - if generic_confirm("pythhon.install.prompt").map_err(|e| e.to_string())? { + let res = if !install_all_prerequisites && !non_interactive { + generic_confirm("pythhon.install.prompt") + } else if install_all_prerequisites { + Ok(true) + } else { + Ok(false) + }; + + if res.map_err(|e| e.to_string())? { system_dependencies::install_prerequisites(vec!["python@3.11.5".to_string()]) .map_err(|e| e.to_string())?; let scp = system_dependencies::get_scoop_path(); From ee4bbc939a5e876b431b5ac504c3334fac7004a3 Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Mon, 11 Nov 2024 15:01:00 +0100 Subject: [PATCH 3/4] EIM-70 parametrized eim config save location --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/wizard/mod.rs | 4 +--- src/wizard/prompts.rs | 14 ++++++++++++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ce5722..58a7e0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,7 +1677,7 @@ dependencies = [ [[package]] name = "idf-im-lib" version = "0.1.6" -source = "git+https://github.com/espressif/idf-im-lib.git?rev=e8cfbe96037bc1458c5885ca620d1c39d4d909c9#e8cfbe96037bc1458c5885ca620d1c39d4d909c9" +source = "git+https://github.com/espressif/idf-im-lib.git?rev=2ee80c1590b9ca47da7368182b1040a723fd4fbb#2ee80c1590b9ca47da7368182b1040a723fd4fbb" dependencies = [ "colored", "config", diff --git a/Cargo.toml b/Cargo.toml index 06aac89..a99d927 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] tokio = {version = "1.37.0", features=["full"]} -idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="e8cfbe96037bc1458c5885ca620d1c39d4d909c9" } +idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="2ee80c1590b9ca47da7368182b1040a723fd4fbb" } clap = {version = "4.5", features = ["cargo", "derive", "color"]} crossterm = "0.27.0" dialoguer = { git = "https://github.com/Hahihula/dialoguer.git", branch = "folder-select", features = ["folder-select"] } diff --git a/src/wizard/mod.rs b/src/wizard/mod.rs index a6432c1..c61a194 100644 --- a/src/wizard/mod.rs +++ b/src/wizard/mod.rs @@ -571,9 +571,7 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> { export_paths, ) } - if !config.non_interactive.unwrap_or_default() { - save_config_if_desired(&config)?; - } + save_config_if_desired(&config)?; let ide_conf_path_tmp = PathBuf::from(&config.esp_idf_json_path.clone().unwrap_or_default()); debug!("IDE configuration path: {}", ide_conf_path_tmp.display()); match ensure_path(ide_conf_path_tmp.to_str().unwrap()) { diff --git a/src/wizard/prompts.rs b/src/wizard/prompts.rs index eae8b4f..e2e031b 100644 --- a/src/wizard/prompts.rs +++ b/src/wizard/prompts.rs @@ -224,9 +224,19 @@ pub fn select_installation_path(mut config: Settings) -> Result Result<(), String> { - if let Ok(true) = generic_confirm("wizard.after_install.save_config.prompt") { + let res = + if config.non_interactive.unwrap_or_default() && config.config_file_save_path.is_some() { + debug!("Saving config in non-interactive mode."); + Ok(true) + } else if config.non_interactive.unwrap_or_default() { + debug!("Skipping config save in non-interactive mode."); + Ok(false) + } else { + generic_confirm("wizard.after_install.save_config.prompt") + }; + if let Ok(true) = res { config - .save("eim_config.toml") + .save() .map_err(|e| format!("{} {:?}", t!("wizard.after_install.config.save_failed"), e))?; println!("{}", t!("wizard.after_install.config.saved")); } From 1ca6e12c52c3eed6302d45b5e2abf3797978a1c2 Mon Sep 17 00:00:00 2001 From: Petr Gadorek Date: Thu, 14 Nov 2024 14:48:52 +0100 Subject: [PATCH 4/4] library switched to master --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58a7e0f..c57e2db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1676,8 +1676,8 @@ dependencies = [ [[package]] name = "idf-im-lib" -version = "0.1.6" -source = "git+https://github.com/espressif/idf-im-lib.git?rev=2ee80c1590b9ca47da7368182b1040a723fd4fbb#2ee80c1590b9ca47da7368182b1040a723fd4fbb" +version = "0.1.7" +source = "git+https://github.com/espressif/idf-im-lib.git?branch=master#98e7b899bc57b75c059d1b4611d37312a83f5b56" dependencies = [ "colored", "config", diff --git a/Cargo.toml b/Cargo.toml index a99d927..ea2724d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] tokio = {version = "1.37.0", features=["full"]} -idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", rev="2ee80c1590b9ca47da7368182b1040a723fd4fbb" } +idf-im-lib = { git = "https://github.com/espressif/idf-im-lib.git", branch="master" } clap = {version = "4.5", features = ["cargo", "derive", "color"]} crossterm = "0.27.0" dialoguer = { git = "https://github.com/Hahihula/dialoguer.git", branch = "folder-select", features = ["folder-select"] }