From 5ae619112eb5f3bdc9a123532ec48382491269c4 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 14 Jan 2026 18:14:28 -0800 Subject: [PATCH] Fix conda environment discovery for child environments under `envs` folder --- crates/pet-conda/src/environment_locations.rs | 17 ++++++ .../tests/environment_locations_test.rs | 56 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/crates/pet-conda/src/environment_locations.rs b/crates/pet-conda/src/environment_locations.rs index 744da92c..529a4427 100644 --- a/crates/pet-conda/src/environment_locations.rs +++ b/crates/pet-conda/src/environment_locations.rs @@ -197,6 +197,23 @@ pub fn get_environments(conda_dir: &Path) -> Vec { } } else if is_conda_env(conda_dir) { envs.push(conda_dir.to_path_buf()); + // If this is a conda environment under an `envs` folder, check if the grandparent + // is the conda install directory (base env) and include it as well. + // E.g. if conda_dir is `/opt/homebrew/Caskroom/miniforge/base/envs/test`, + // then the grandparent `/opt/homebrew/Caskroom/miniforge/base` is the base env. + // This ensures the base conda environment is discovered when only child envs are + // listed in environments.txt (see https://github.com/microsoft/python-environment-tools/issues/236) + if let Some(parent) = conda_dir.parent() { + if parent.file_name().map(|n| n == "envs").unwrap_or(false) { + if let Some(grandparent) = parent.parent() { + if is_conda_install(grandparent) && !envs.contains(&grandparent.to_path_buf()) { + // Recursively get environments from the conda install directory + // This will add the base env and any other sibling envs + envs.append(&mut get_environments(grandparent)); + } + } + } + } } else if conda_dir.join("envs").exists() { // This could be a directory where conda environments are stored. // I.e. its not necessarily the root conda install directory. diff --git a/crates/pet-conda/tests/environment_locations_test.rs b/crates/pet-conda/tests/environment_locations_test.rs index 0d3a61c0..5aa52065 100644 --- a/crates/pet-conda/tests/environment_locations_test.rs +++ b/crates/pet-conda/tests/environment_locations_test.rs @@ -39,3 +39,59 @@ fn list_conda_envs_in_install_location() { ] ); } + +/// Test that when get_environments is called with a child environment under the `envs` folder, +/// it also discovers the parent conda install (base environment) and all sibling environments. +/// This is the fix for https://github.com/microsoft/python-environment-tools/issues/236 +/// where the base conda environment wasn't discovered when only child envs were listed +/// in environments.txt (e.g., from Homebrew Cask installs like /opt/homebrew/Caskroom/miniforge/base). +#[cfg(unix)] +#[test] +fn list_conda_envs_discovers_base_from_child_env() { + use common::resolve_test_path; + use pet_conda::environment_locations::get_environments; + + // Call get_environments with a child environment path (not the install directory) + let child_env_path = resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "myenv"]); + + let mut locations = get_environments(&child_env_path); + locations.sort(); + + // Should discover not only the child env, but also the base env (conda install dir) + // and all sibling environments + assert_eq!( + locations, + vec![ + resolve_test_path(&["unix", "anaconda3-2023.03"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "env_python_3"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "myenv"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "without_python"]), + ] + ); +} + +/// Test that get_environments works correctly with an env_python_3 child environment +/// (another sibling to verify the fix works for any child env under envs folder). +#[cfg(unix)] +#[test] +fn list_conda_envs_discovers_base_from_another_child_env() { + use common::resolve_test_path; + use pet_conda::environment_locations::get_environments; + + // Call get_environments with a different child environment path + let child_env_path = resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "env_python_3"]); + + let mut locations = get_environments(&child_env_path); + locations.sort(); + + // Should discover the base env and all sibling environments + assert_eq!( + locations, + vec![ + resolve_test_path(&["unix", "anaconda3-2023.03"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "env_python_3"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "myenv"]), + resolve_test_path(&["unix", "anaconda3-2023.03", "envs", "without_python"]), + ] + ); +}