diff --git a/config.example.toml b/config.example.toml index 479a0067..bca777fc 100644 --- a/config.example.toml +++ b/config.example.toml @@ -111,6 +111,19 @@ # poetry_force_self_update = true +[conda] +# Additional named conda environments to update (`conda env update -n env_name`) +# env_names = [ +# "Toolbox", +# "PyTorch" +# ] +# Additional conda environment paths to update (`conda env update -p env_path`) +# env_paths = [ +# "~/webserver/.conda/", +# "~/experiments/.conda/" +# ] + + [composer] # self_update = true diff --git a/src/config.rs b/src/config.rs index 1a6f52f6..9e9a0ff1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -268,6 +268,16 @@ pub struct Python { poetry_force_self_update: Option, } +#[derive(Deserialize, Default, Debug, Merge)] +#[serde(deny_unknown_fields)] +pub struct Conda { + #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] + env_names: Option>, + + #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] + env_paths: Option>, +} + #[derive(Deserialize, Default, Debug, Merge)] #[serde(deny_unknown_fields)] #[allow(clippy::upper_case_acronyms)] @@ -537,6 +547,9 @@ pub struct ConfigFile { #[merge(strategy = crate::utils::merge_strategies::commands_merge_opt)] commands: Option, + #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)] + conda: Option, + #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)] python: Option, @@ -1002,6 +1015,22 @@ impl Config { &self.config_file.commands } + /// The list of additional named conda environments. + pub fn conda_env_names(&self) -> Option<&Vec> { + self.config_file + .conda + .as_ref() + .and_then(|conda| conda.env_names.as_ref()) + } + + /// The list of additional conda environment paths. + pub fn conda_env_paths(&self) -> Option<&Vec> { + self.config_file + .conda + .as_ref() + .and_then(|conda| conda.env_paths.as_ref()) + } + /// The list of additional git repositories to pull. pub fn git_repos(&self) -> Option<&Vec> { self.config_file.git.as_ref().and_then(|git| git.repos.as_ref()) diff --git a/src/steps/generic.rs b/src/steps/generic.rs index 5d712a5f..e4dccecb 100644 --- a/src/steps/generic.rs +++ b/src/steps/generic.rs @@ -6,6 +6,7 @@ use regex::bytes::Regex; use rust_i18n::t; use semver::Version; use std::ffi::OsString; +use std::iter::once; use std::path::PathBuf; use std::process::Command; use std::sync::LazyLock; @@ -565,13 +566,33 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> { print_separator("Conda"); - let mut command = ctx.run_type().execute(&conda); - command.args(["update", "--all", "-n", "base"]); - if ctx.config().yes(Step::Conda) { - command.arg("--yes"); - } - command.status_checked()?; + // Update named environments, starting with the always-present "base" + let base_env_name = "base".to_string(); + let addl_env_names = ctx.config().conda_env_names().into_iter().flatten(); + let env_names = once(&base_env_name).chain(addl_env_names); + for env_name in env_names { + let mut command = ctx.run_type().execute(&conda); + command.args(["update", "--all", "-n", env_name]); + if ctx.config().yes(Step::Conda) { + command.arg("--yes"); + } + command.status_checked()?; + } + + // Update any environments given by path + if let Some(env_paths) = ctx.config().conda_env_paths() { + for env_path in env_paths.iter() { + let mut command = ctx.run_type().execute(&conda); + command.args(["update", "--all", "-p", env_path]); + if ctx.config().yes(Step::Conda) { + command.arg("--yes"); + } + command.status_checked()?; + } + } + + // Cleanup (conda clean) is global (not tied to a particular environment) if ctx.config().cleanup() { let mut command = ctx.run_type().execute(conda); command.args(["clean", "--all"]);