Skip to content

Commit

Permalink
EIM-68 enabled non-interactive mode (#69)
Browse files Browse the repository at this point in the history
* EIM-68 enabled non-interactive mode

* enabled possibility to ask for prerequisities install using cli arg

* EIM-70 parametrized eim config save location

---------

Co-authored-by: Petr Gadorek <[email protected]>
  • Loading branch information
Hahihula and Petr Gadorek authored Nov 14, 2024
1 parent e966f99 commit 7f4b508
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 54 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions src/cli_args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>,

#[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<bool>,

#[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<String>,
}

impl IntoIterator for Cli {
Expand Down Expand Up @@ -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()
}
Expand Down
48 changes: 31 additions & 17 deletions src/wizard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,26 @@ fn add_to_shell_rc(content: &str) -> Result<(), String> {
}

async fn select_targets_and_versions(mut config: Settings) -> Result<Settings, String> {
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)
Expand All @@ -215,6 +224,7 @@ pub struct DownloadConfig {
pub idf_version: String,
pub idf_mirror: Option<String>,
pub recurse_submodules: Option<bool>,
pub non_interactive: Option<bool>,
}

pub enum DownloadError {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}
}
}

Expand Down Expand Up @@ -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?;
Expand All @@ -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) {
Expand Down
124 changes: 89 additions & 35 deletions src/wizard/prompts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,23 @@ pub async fn select_target() -> Result<Vec<String>, String> {
first_defaulted_multiselect("wizard.select_target.prompt", &available_targets)
}

pub async fn select_idf_version(target: &str) -> Result<Vec<String>, String> {
pub async fn select_idf_version(
target: &str,
non_interactive: bool,
) -> Result<Vec<String>, String> {
let mut avalible_versions = if target == "all" {
//todo process vector of targets
idf_im_lib::idf_versions::get_idf_names().await
} else {
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<Vec<String>, String> {
Expand All @@ -40,8 +48,15 @@ fn check_prerequisites() -> Result<Vec<String>, 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!(
"{}",
Expand All @@ -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())?;

Expand Down Expand Up @@ -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!["[email protected]".to_string()])
.map_err(|e| e.to_string())?;
let scp = system_dependencies::get_scoop_path();
Expand Down Expand Up @@ -138,51 +175,68 @@ pub fn check_and_install_python() -> Result<(), String> {
}

pub fn select_mirrors(mut config: Settings) -> Result<Settings, String> {
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<Settings, String> {
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"));
}
Expand Down

0 comments on commit 7f4b508

Please sign in to comment.