diff --git a/Cargo.lock b/Cargo.lock index 62a38ee..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?branch=master#1687f0174db8125d9c44544446ccb3cfc3094cd5" +version = "0.1.7" +source = "git+https://github.com/espressif/idf-im-lib.git?branch=master#98e7b899bc57b75c059d1b4611d37312a83f5b56" dependencies = [ "colored", "config", 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 f838923..c61a194 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,17 @@ 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(), + config.install_all_prerequisites.unwrap_or_default(), + )?; // Python sanity check - check_and_install_python()?; + 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?; @@ -470,6 +483,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) { diff --git a/src/wizard/prompts.rs b/src/wizard/prompts.rs index 9f6a38f..e2e031b 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,15 @@ 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, + install_all_prerequisites: bool, +) -> Result<(), String> { + let unsatisfied_prerequisites = if non_interactive { + check_prerequisites()? + } else { + run_with_spinner(check_prerequisites)? + }; if !unsatisfied_prerequisites.is_empty() { info!( "{}", @@ -51,8 +66,14 @@ pub fn check_and_install_prerequisites() -> Result<(), String> { ) ); if std::env::consts::OS == "windows" { - //TODO: remove afte prerequisities install fix in linux - if generic_confirm("prerequisites.install.prompt").map_err(|e| e.to_string())? { + 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())?; @@ -100,12 +121,28 @@ 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, + install_all_prerequisites: bool, +) -> Result<(), String> { info!("{}", t!("python.sanitycheck.info")); - if let Err(err) = run_with_spinner(|| python_sanity_check(None)) { + 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" { 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(); @@ -138,51 +175,68 @@ 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) } pub fn save_config_if_desired(config: &Settings) -> 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")); }