From 2c9f40b23c283618807cc07037dd8efcafd3e4d7 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 3 Aug 2026 17:00:42 +0200 Subject: [PATCH 01/20] Debug shell hook --- .github/workflows/windows_impl.yml | 2 +- libmamba/src/api/configuration.cpp | 3 ++- micromamba/tests/test_shell.py | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index fc73a8aebb..8e6884d7d0 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -129,7 +129,7 @@ jobs: - name: Run libmamba Python bindings tests run: | # Only rerun flaky tests on the `main` branch - python -m pytest libmambapy/tests/ ^ + python -m pytest libmambapy/tests/test_shell.py::test_hook ^ -vv --durations=50 ^ ${{ runner.debug == 'true' && '--capture=tee-sys' || '--exitfirst' }} ^ ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index f1e2ae3251..0005c7f87e 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -744,6 +744,7 @@ namespace mamba // We unconditionally name the subfolder "mamba" for compatibility between ``mamba`` // and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables. const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba"; + std::cout << "default_root_prefix_v2 is: " << default_root_prefix_v2.path() << std::endl; auto result = validate_existing_root_prefix(default_root_prefix_v1) .or_else([&default_root_prefix_v2](const auto& /* error */) @@ -754,7 +755,7 @@ namespace mamba } else { - throw std::move(result).value(); + throw std::move(result).value(); // TODO .error() instead of value() ? } LOG_TRACE << "Using default root prefix for micromamba: " << root_prefix; diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index 399ff95904..54ae6ba95d 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -25,7 +25,7 @@ def skip_if_shell_incompat(shell_type): "shell_type", ["bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh", "nu"], ) -def test_hook(tmp_home, tmp_root_prefix, shell_type): +def test_hook(tmp_home, tmp_root_prefix, shell_type, tmp_path): res = helpers.shell("hook", "-s", shell_type) mamba_exe = helpers.get_umamba() @@ -48,6 +48,20 @@ def test_hook(tmp_home, tmp_root_prefix, shell_type): assert res.count(mamba_exe_posix) == 5 elif shell_type == "cmd.exe": assert res == "" + # assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() + # assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() + # assert (tmp_root_prefix / "conda-meta").is_dir() + data = tmp_path / "data" + env = { + k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_")) + } + env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba + env["XDG_CONFIG_HOME"] = str(tmp_path / "config") + default_prefix = data / "mamba" + print("==========> default_prefix should be: ", default_prefix) + info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) + print("=================> INFO: ", info) + elif shell_type == "tcsh": assert res.count(mamba_exe_posix) == 5 elif shell_type == "nu": From 6541e71a1a9b153fbe6c120ee95ceed68084a07a Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 3 Aug 2026 17:12:45 +0200 Subject: [PATCH 02/20] Use string --- libmamba/src/api/configuration.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index 0005c7f87e..5a29e3325b 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -744,7 +744,8 @@ namespace mamba // We unconditionally name the subfolder "mamba" for compatibility between ``mamba`` // and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables. const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba"; - std::cout << "default_root_prefix_v2 is: " << default_root_prefix_v2.path() << std::endl; + std::cout << "default_root_prefix_v2 is: " << default_root_prefix_v2.string() + << std::endl; auto result = validate_existing_root_prefix(default_root_prefix_v1) .or_else([&default_root_prefix_v2](const auto& /* error */) From 6f5148fddc9f0297a4364473e0e2e87cae7e5ffb Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 3 Aug 2026 17:21:02 +0200 Subject: [PATCH 03/20] Run correct test in umamba not libmambapy --- .github/workflows/windows_impl.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index 8e6884d7d0..dd34739228 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -129,7 +129,7 @@ jobs: - name: Run libmamba Python bindings tests run: | # Only rerun flaky tests on the `main` branch - python -m pytest libmambapy/tests/test_shell.py::test_hook ^ + python -m pytest libmambapy/tests/ ^ -vv --durations=50 ^ ${{ runner.debug == 'true' && '--capture=tee-sys' || '--exitfirst' }} ^ ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} @@ -166,7 +166,7 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m pytest micromamba/tests/ ` + python -m pytest micromamba/tests/test_shell.py::test_hook ` -vv --durations=50 ` ${{ runner.debug == 'true' && '--capture=tee-sys' || '' }} ` ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} From 7695e4b25861a5c01f2032c81d941def7aa07027 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 3 Aug 2026 17:53:11 +0200 Subject: [PATCH 04/20] Use -s --- .github/workflows/windows_impl.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index dd34739228..bf450b0144 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -166,7 +166,8 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m pytest micromamba/tests/test_shell.py::test_hook ` - -vv --durations=50 ` - ${{ runner.debug == 'true' && '--capture=tee-sys' || '' }} ` - ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} + python -m -s pytest micromamba/tests/test_shell.py::test_hook ` + -vv + #--durations=50 ` + #${{ runner.debug == 'true' && '--capture=no' || '' }} ` + #${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} From 0b2eb8904f118fbccb8af755e1acee7388777351 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 3 Aug 2026 18:00:06 +0200 Subject: [PATCH 05/20] Fix typo --- .github/workflows/windows_impl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index bf450b0144..72243b1f89 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -166,7 +166,7 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m -s pytest micromamba/tests/test_shell.py::test_hook ` + python -m pytest -s micromamba/tests/test_shell.py::test_hook ` -vv #--durations=50 ` #${{ runner.debug == 'true' && '--capture=no' || '' }} ` From 7ced0e4b5ff649f41e0d9db2093cda98c0a7e787 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Tue, 4 Aug 2026 11:04:41 +0200 Subject: [PATCH 06/20] Add more checks --- micromamba/tests/test_shell.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index 54ae6ba95d..a9d7cdc7f5 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -62,6 +62,18 @@ def test_hook(tmp_home, tmp_root_prefix, shell_type, tmp_path): info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) print("=================> INFO: ", info) + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES1: ", res) + hook = subprocess.run( + [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True + ) + print("=================> HOOK: ", hook) + assert (default_prefix / "condabin").is_dir() + assert (default_prefix / "Scripts").is_dir() + + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES2: ", res) + elif shell_type == "tcsh": assert res.count(mamba_exe_posix) == 5 elif shell_type == "nu": From 66d303bd59d2fb579c23ea8bed649a9278e26c0b Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Tue, 4 Aug 2026 11:57:42 +0200 Subject: [PATCH 07/20] Add another step --- micromamba/tests/test_shell.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index a9d7cdc7f5..a2219539c2 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -64,15 +64,25 @@ def test_hook(tmp_home, tmp_root_prefix, shell_type, tmp_path): res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) print("=================> RES1: ", res) + assert res.returncode == 0 + hook = subprocess.run( [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True ) print("=================> HOOK: ", hook) assert (default_prefix / "condabin").is_dir() assert (default_prefix / "Scripts").is_dir() + assert hook.returncode == 0 res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) print("=================> RES2: ", res) + assert res.returncode != 0 + + # Deleting the default prefix dir makes mamba work again + shutil.rmtree(default_prefix) + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) + assert res.returncode == 0 elif shell_type == "tcsh": assert res.count(mamba_exe_posix) == 5 From 3a946b25b71d1691face1d70ef6586cff23a5033 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Tue, 4 Aug 2026 13:53:30 +0200 Subject: [PATCH 08/20] Use error instead of value --- libmamba/src/api/configuration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index 5a29e3325b..f2709bfb34 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -756,7 +756,7 @@ namespace mamba } else { - throw std::move(result).value(); // TODO .error() instead of value() ? + throw std::move(result).error(); // TODO .error() instead of value() ? } LOG_TRACE << "Using default root prefix for micromamba: " << root_prefix; From d3170b85ec1247318f30b31022e5e4080854b9d1 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Tue, 4 Aug 2026 14:50:29 +0200 Subject: [PATCH 09/20] Add try catch to main --- micromamba/src/main.cpp | 239 +++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 111 deletions(-) diff --git a/micromamba/src/main.cpp b/micromamba/src/main.cpp index a1559c9589..f67e84daf7 100644 --- a/micromamba/src/main.cpp +++ b/micromamba/src/main.cpp @@ -86,155 +86,172 @@ decide_log_handler(const ContextOptions& options) -> mamba::logging::AnyLogHandl int main(int argc, char** argv) { - mamba::MainExecutor scoped_threads; - const auto pre_config_options = decide_preconfig_context_options(argc, argv); - mamba::Context ctx{ pre_config_options, decide_log_handler(pre_config_options) }; - mamba::Console console{ ctx }; - mamba::Configuration config{ ctx }; + try + { + mamba::MainExecutor scoped_threads; + const auto pre_config_options = decide_preconfig_context_options(argc, argv); + mamba::Context ctx{ pre_config_options, decide_log_handler(pre_config_options) }; + mamba::Console console{ ctx }; + mamba::Configuration config{ ctx }; - init_console(); - mamba::on_scope_exit _console_reset{ [] { reset_console(); } }; + init_console(); + mamba::on_scope_exit _console_reset{ [] { reset_console(); } }; - ctx.command_params.is_mamba_exe = true; + ctx.command_params.is_mamba_exe = true; - CLI::App app{ "Version: " + version() + "\n" }; - set_umamba_command(&app, config); + CLI::App app{ "Version: " + version() + "\n" }; + set_umamba_command(&app, config); - char** utf8argv; + char** utf8argv; #ifdef _WIN32 - wchar_t** wargv; - wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + wchar_t** wargv; + wargv = CommandLineToArgvW(GetCommandLineW(), &argc); - std::vector utf8Args; - std::vector utf8CharArgs; - for (int i = 0; i < argc; i++) - { - utf8Args.push_back(util::windows_encoding_to_utf8(wargv[i])); - } - for (int i = 0; i < argc; ++i) - { - utf8CharArgs.push_back(utf8Args[i].data()); - } - utf8argv = utf8CharArgs.data(); + std::vector utf8Args; + std::vector utf8CharArgs; + for (int i = 0; i < argc; i++) + { + utf8Args.push_back(util::windows_encoding_to_utf8(wargv[i])); + } + for (int i = 0; i < argc; ++i) + { + utf8CharArgs.push_back(utf8Args[i].data()); + } + utf8argv = utf8CharArgs.data(); #else - utf8argv = argv; + utf8argv = argv; #endif - if (argc >= 2 && strcmp(argv[1], "completer") == 0) - { - get_completions(&app, config, argc, utf8argv); - return 0; - } + if (argc >= 2 && strcmp(argv[1], "completer") == 0) + { + get_completions(&app, config, argc, utf8argv); + return 0; + } - std::stringstream full_command; - for (int i = 0; i < argc; ++i) - { - full_command << utf8argv[i]; - if (i < argc - 1) + std::stringstream full_command; + for (int i = 0; i < argc; ++i) { - full_command << " "; + full_command << utf8argv[i]; + if (i < argc - 1) + { + full_command << " "; + } } - } - ctx.command_params.current_command = full_command.str(); + ctx.command_params.current_command = full_command.str(); - std::optional error_to_report; - auto handle_exception = [&](auto& e, const auto&... additional_messages) - { - using namespace std::literals; - error_to_report.emplace(e.what()); - (error_to_report->append(additional_messages), ...); - set_sig_interrupted(); - }; + std::optional error_to_report; + auto handle_exception = [&](auto& e, const auto&... additional_messages) + { + using namespace std::literals; + error_to_report.emplace(e.what()); + (error_to_report->append(additional_messages), ...); + set_sig_interrupted(); + }; - int return_value = EXIT_SUCCESS; + int return_value = EXIT_SUCCESS; - try - { - // Note: do not use CLI11_PARSE macro as its error handling - // would bypass ours. - app.parse(argc, utf8argv); - if (app.get_subcommands().size() == 0) - { - config.load(); - Console::instance().print(app.help()); - } - if (app.got_subcommand("config") - && app.get_subcommand("config")->get_subcommands().size() == 0) + try { - config.load(); - Console::instance().print(app.get_subcommand("config")->help()); + // Note: do not use CLI11_PARSE macro as its error handling + // would bypass ours. + app.parse(argc, utf8argv); + if (app.get_subcommands().size() == 0) + { + config.load(); + Console::instance().print(app.help()); + } + if (app.got_subcommand("config") + && app.get_subcommand("config")->get_subcommands().size() == 0) + { + config.load(); + Console::instance().print(app.get_subcommand("config")->help()); + } } - } - catch (const mamba::mamba_error& e) - { - // We treat interruptions (ctrl-c) specially by not logging a critical error. - const bool is_interruption = [&] + catch (const mamba::mamba_error& e) { - if (e.error_code() == mamba::mamba_error_code::aggregated) + // We treat interruptions (ctrl-c) specially by not logging a critical error. + const bool is_interruption = [&] { - // Prefer dynamic_cast: a plain mamba_error can incorrectly carry - // error_code::aggregated after object slicing (mamba-org/mamba#4352). - if (const auto* aggregated_error = dynamic_cast( - &e - )) + if (e.error_code() == mamba::mamba_error_code::aggregated) { - return aggregated_error->has_only_error(mamba::mamba_error_code::user_interrupted); + // Prefer dynamic_cast: a plain mamba_error can incorrectly carry + // error_code::aggregated after object slicing (mamba-org/mamba#4352). + if (const auto* aggregated_error = dynamic_cast( + &e + )) + { + return aggregated_error->has_only_error( + mamba::mamba_error_code::user_interrupted + ); + } + return false; } - return false; + else + { + return e.error_code() == mamba::mamba_error_code::user_interrupted; + } + }(); + + if (is_interruption) + { + LOG_WARNING << e.what(); + return 0; } else { - return e.error_code() == mamba::mamba_error_code::user_interrupted; + handle_exception(e); } - }(); - - if (is_interruption) - { - LOG_WARNING << e.what(); - return 0; } - else + catch (const CLI::Error& e) { - handle_exception(e); + using namespace std::literals; + // We only preserve CLI11 output behavior when errors from CLI11 + // occurs because of `--help` or `--version` is used. Otherwise we follow the + // logic that `--json` outputs everything as JSON. + static constexpr std::array non_error_request_names = { "CallForHelp"sv, + "CallForAllHelp"sv, + "CallForVersion"sv }; + const bool is_non_error_request = std::ranges::find(non_error_request_names, e.get_name()) + != non_error_request_names.end(); + + if (ctx.output_params.json and not is_non_error_request) + { + // we want the output to end up in the json log history + std::stringstream output; + return_value = app.exit(e, output, output); + LOG_WARNING << output.str(); + } + else + { + // we don't want any json output even if requested, CLI11 will handle this + console.cancel_json_print(); + return_value = app.exit(e); + } } - } - catch (const CLI::Error& e) - { - using namespace std::literals; - // We only preserve CLI11 output behavior when errors from CLI11 - // occurs because of `--help` or `--version` is used. Otherwise we follow the - // logic that `--json` outputs everything as JSON. - static constexpr std::array non_error_request_names = { "CallForHelp"sv, - "CallForAllHelp"sv, - "CallForVersion"sv }; - const bool is_non_error_request = std::ranges::find(non_error_request_names, e.get_name()) - != non_error_request_names.end(); - - if (ctx.output_params.json and not is_non_error_request) + catch (const std::exception& e) { - // we want the output to end up in the json log history - std::stringstream output; - return_value = app.exit(e, output, output); - LOG_WARNING << output.str(); + handle_exception(e); } - else + + if (error_to_report) { - // we don't want any json output even if requested, CLI11 will handle this - console.cancel_json_print(); - return_value = app.exit(e); + LOG_CRITICAL << error_to_report.value(); + return_value = EXIT_FAILURE; } + + return return_value; } + catch (const std::exception& e) { - handle_exception(e); + std::cerr << "micromamba: " << e.what() << std::endl; + return EXIT_FAILURE; } - if (error_to_report) + catch (...) { - LOG_CRITICAL << error_to_report.value(); - return_value = EXIT_FAILURE; + std::cerr << "micromamba: unknown error" << std::endl; + return EXIT_FAILURE; } - - return return_value; } From 83fc705db9a11ce2b6f61c2c5a35fd9df672ab90 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Tue, 4 Aug 2026 15:40:33 +0200 Subject: [PATCH 10/20] Check with value again --- libmamba/src/api/configuration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index f2709bfb34..5a29e3325b 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -756,7 +756,7 @@ namespace mamba } else { - throw std::move(result).error(); // TODO .error() instead of value() ? + throw std::move(result).value(); // TODO .error() instead of value() ? } LOG_TRACE << "Using default root prefix for micromamba: " << root_prefix; From 7050050120620f10408e9e84aa887dfecf9df8a6 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Wed, 5 Aug 2026 11:14:18 +0200 Subject: [PATCH 11/20] Use error and LOG_CRITICAL --- libmamba/src/api/configuration.cpp | 2 +- micromamba/src/main.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index 5a29e3325b..2b047b790e 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -756,7 +756,7 @@ namespace mamba } else { - throw std::move(result).value(); // TODO .error() instead of value() ? + throw std::move(result).error(); } LOG_TRACE << "Using default root prefix for micromamba: " << root_prefix; diff --git a/micromamba/src/main.cpp b/micromamba/src/main.cpp index f67e84daf7..1fc363792e 100644 --- a/micromamba/src/main.cpp +++ b/micromamba/src/main.cpp @@ -245,13 +245,13 @@ main(int argc, char** argv) catch (const std::exception& e) { - std::cerr << "micromamba: " << e.what() << std::endl; + LOG_CRITICAL << e.what(); return EXIT_FAILURE; } catch (...) { - std::cerr << "micromamba: unknown error" << std::endl; + LOG_CRITICAL << "Unhandled non-standard exception"; return EXIT_FAILURE; } } From 11e3a83c5f5b4b0b870f78d1cb8fd69f09ebd3b1 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Thu, 6 Aug 2026 12:19:51 +0200 Subject: [PATCH 12/20] Restore std::cerr for now --- micromamba/src/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/micromamba/src/main.cpp b/micromamba/src/main.cpp index 1fc363792e..986cd05a98 100644 --- a/micromamba/src/main.cpp +++ b/micromamba/src/main.cpp @@ -16,6 +16,7 @@ #endif #include +#include #include @@ -243,15 +244,19 @@ main(int argc, char** argv) return return_value; } + // TODO Find a better way to print the error + // (considering `--json` and `--quiet` cases, + // as `Console` destructor handling json case is not called + // if throw happens before its instantiation - i.e in `Context` instantiation for example) catch (const std::exception& e) { - LOG_CRITICAL << e.what(); + std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (...) { - LOG_CRITICAL << "Unhandled non-standard exception"; + std::cerr << "Unhandled non-standard exception" << std::endl; return EXIT_FAILURE; } } From 27d72eb3faa6087d943be924bd4bd0bc4af8746c Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Thu, 6 Aug 2026 15:59:11 +0200 Subject: [PATCH 13/20] Move test --- .github/workflows/windows_impl.yml | 2 +- micromamba/tests/test_shell.py | 80 ++++++++++++++++-------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index 72243b1f89..bd730cd14c 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -166,7 +166,7 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m pytest -s micromamba/tests/test_shell.py::test_hook ` + python -m pytest -s micromamba/tests/test_shell.py::test_hook_cmd_exe ` -vv #--durations=50 ` #${{ runner.debug == 'true' && '--capture=no' || '' }} ` diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index a2219539c2..d6a474305d 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -3,6 +3,7 @@ import platform import shutil import subprocess +import sys from pathlib import Path, PureWindowsPath import pytest @@ -21,11 +22,52 @@ def skip_if_shell_incompat(shell_type): pytest.skip("Incompatible shell/OS") +@pytest.mark.skipif(sys.platform != "win32", reason="Windows only") +def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): + res = helpers.shell("hook", "-s", "cmd.exe") + mamba_exe = helpers.get_umamba() + + assert res == "" + assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() + assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() + assert (tmp_root_prefix / "conda-meta").is_dir() + data = tmp_path / "data" + env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} + env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba + env["XDG_CONFIG_HOME"] = str(tmp_path / "config") + default_prefix = data / "mamba" + print("==========> default_prefix should be: ", default_prefix) + info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) + print("=================> INFO: ", info) + + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES1: ", res) + assert res.returncode == 0 + + hook = subprocess.run( + [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True + ) + print("=================> HOOK: ", hook) + assert (default_prefix / "condabin").is_dir() + assert (default_prefix / "Scripts").is_dir() + assert hook.returncode == 0 + + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES2: ", res) + assert res.returncode != 0 + + # Deleting the default prefix dir makes mamba work again + shutil.rmtree(default_prefix) + res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) + assert res.returncode == 0 + + @pytest.mark.parametrize( "shell_type", ["bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh", "nu"], ) -def test_hook(tmp_home, tmp_root_prefix, shell_type, tmp_path): +def test_hook(tmp_home, tmp_root_prefix, shell_type): res = helpers.shell("hook", "-s", shell_type) mamba_exe = helpers.get_umamba() @@ -48,42 +90,6 @@ def test_hook(tmp_home, tmp_root_prefix, shell_type, tmp_path): assert res.count(mamba_exe_posix) == 5 elif shell_type == "cmd.exe": assert res == "" - # assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() - # assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() - # assert (tmp_root_prefix / "conda-meta").is_dir() - data = tmp_path / "data" - env = { - k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_")) - } - env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba - env["XDG_CONFIG_HOME"] = str(tmp_path / "config") - default_prefix = data / "mamba" - print("==========> default_prefix should be: ", default_prefix) - info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) - print("=================> INFO: ", info) - - res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES1: ", res) - assert res.returncode == 0 - - hook = subprocess.run( - [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True - ) - print("=================> HOOK: ", hook) - assert (default_prefix / "condabin").is_dir() - assert (default_prefix / "Scripts").is_dir() - assert hook.returncode == 0 - - res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES2: ", res) - assert res.returncode != 0 - - # Deleting the default prefix dir makes mamba work again - shutil.rmtree(default_prefix) - res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) - assert res.returncode == 0 - elif shell_type == "tcsh": assert res.count(mamba_exe_posix) == 5 elif shell_type == "nu": From 9e3dc79689cff338b76e744b394efa644620fce8 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Thu, 6 Aug 2026 16:23:50 +0200 Subject: [PATCH 14/20] Fix assert --- micromamba/tests/test_shell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index d6a474305d..dcfa16919e 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -30,7 +30,8 @@ def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): assert res == "" assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() - assert (tmp_root_prefix / "conda-meta").is_dir() + assert not (tmp_root_prefix / "conda-meta").is_dir() + data = tmp_path / "data" env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba From 2388098f8c1fe2af3baf064f280d284a58a40c8a Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Thu, 6 Aug 2026 17:11:21 +0200 Subject: [PATCH 15/20] Add actual fix --- libmamba/src/api/configuration.cpp | 2 -- libmamba/src/core/shell_init.cpp | 10 ++++++++++ micromamba/tests/test_shell.py | 23 ++++++++++++----------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index 2b047b790e..ead2fdea14 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -744,8 +744,6 @@ namespace mamba // We unconditionally name the subfolder "mamba" for compatibility between ``mamba`` // and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables. const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba"; - std::cout << "default_root_prefix_v2 is: " << default_root_prefix_v2.string() - << std::endl; auto result = validate_existing_root_prefix(default_root_prefix_v1) .or_else([&default_root_prefix_v2](const auto& /* error */) diff --git a/libmamba/src/core/shell_init.cpp b/libmamba/src/core/shell_init.cpp index 4f3e4b0d6d..42b21ae465 100644 --- a/libmamba/src/core/shell_init.cpp +++ b/libmamba/src/core/shell_init.cpp @@ -800,6 +800,16 @@ namespace mamba { const ShellInitPathsWindowsCmd paths{ root_prefix }; + // The default root prefix is validated on the next invocation, so it must + // be a valid conda prefix (pkgs/conda-meta/envs) from the start. It used + // to contain only the hook scripts -> every following run aborted silently. + std::error_code ec [[maybe_unused]]; + fs::create_directories(root_prefix / "conda-meta", ec); + if (ec) + { + LOG_ERROR << "Failed to create directory '" << (root_prefix / "conda-meta").string() + << "' : " << ec.message(); + } for (const auto& directory : paths.every_generated_directories_paths()) { // Maybe the prefix isn't writable. No big deal, just keep going. diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index dcfa16919e..2dfdabf529 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -30,19 +30,19 @@ def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): assert res == "" assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() - assert not (tmp_root_prefix / "conda-meta").is_dir() + assert (tmp_root_prefix / "conda-meta").is_dir() data = tmp_path / "data" env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba env["XDG_CONFIG_HOME"] = str(tmp_path / "config") default_prefix = data / "mamba" - print("==========> default_prefix should be: ", default_prefix) - info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) - print("=================> INFO: ", info) + # print("==========> default_prefix should be: ", default_prefix) + # info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) + # print("=================> INFO: ", info) res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES1: ", res) + # print("=================> RES1: ", res) assert res.returncode == 0 hook = subprocess.run( @@ -51,18 +51,19 @@ def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): print("=================> HOOK: ", hook) assert (default_prefix / "condabin").is_dir() assert (default_prefix / "Scripts").is_dir() + assert (default_prefix / "conda-meta").is_dir() assert hook.returncode == 0 res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) print("=================> RES2: ", res) - assert res.returncode != 0 - - # Deleting the default prefix dir makes mamba work again - shutil.rmtree(default_prefix) - res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) assert res.returncode == 0 + # # Deleting the default prefix dir makes mamba work again + # shutil.rmtree(default_prefix) + # res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) + # print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) + # assert res.returncode == 0 + @pytest.mark.parametrize( "shell_type", From 2c0647794a568bf93d2ee6273814c3677e9ee55f Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Thu, 6 Aug 2026 17:52:28 +0200 Subject: [PATCH 16/20] Clean up and add asserts --- micromamba/tests/test_shell.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index 2dfdabf529..c8ab7a5e52 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -25,44 +25,37 @@ def skip_if_shell_incompat(shell_type): @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): res = helpers.shell("hook", "-s", "cmd.exe") - mamba_exe = helpers.get_umamba() assert res == "" assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() assert (tmp_root_prefix / "conda-meta").is_dir() + # Remove any preconfigured settings data = tmp_path / "data" env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} - env["XDG_DATA_HOME"] = str(data) # default prefix -> $XDG_DATA_HOME/mamba + env["XDG_DATA_HOME"] = str(data) env["XDG_CONFIG_HOME"] = str(tmp_path / "config") default_prefix = data / "mamba" - # print("==========> default_prefix should be: ", default_prefix) - # info = subprocess.run([mamba_exe, "info"], env=env, capture_output=True, text=True) - # print("=================> INFO: ", info) + + mamba_exe = helpers.get_umamba() res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - # print("=================> RES1: ", res) assert res.returncode == 0 hook = subprocess.run( [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True ) - print("=================> HOOK: ", hook) + assert hook.returncode == 0 + assert (default_prefix / "condabin").is_dir() assert (default_prefix / "Scripts").is_dir() assert (default_prefix / "conda-meta").is_dir() - assert hook.returncode == 0 + # Running `mamba_exe` again does not fail silently res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("=================> RES2: ", res) assert res.returncode == 0 - - # # Deleting the default prefix dir makes mamba work again - # shutil.rmtree(default_prefix) - # res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - # print("=================> RES3 AFTER DEL MAMBA PREFIX DIR: ", res) - # assert res.returncode == 0 + assert not res.stderr, f"stderr was not empty: {res.stderr}" @pytest.mark.parametrize( From ad946c88279144379bc698899255a937759b1928 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Fri, 7 Aug 2026 17:02:29 +0200 Subject: [PATCH 17/20] Check stderr --- libmamba/src/core/shell_init.cpp | 16 ++++++++-------- micromamba/src/main.cpp | 31 +++++++++++++++++++++++++------ micromamba/tests/test_shell.py | 26 ++++++++++++++------------ 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/libmamba/src/core/shell_init.cpp b/libmamba/src/core/shell_init.cpp index 42b21ae465..057dc10365 100644 --- a/libmamba/src/core/shell_init.cpp +++ b/libmamba/src/core/shell_init.cpp @@ -803,17 +803,17 @@ namespace mamba // The default root prefix is validated on the next invocation, so it must // be a valid conda prefix (pkgs/conda-meta/envs) from the start. It used // to contain only the hook scripts -> every following run aborted silently. - std::error_code ec [[maybe_unused]]; - fs::create_directories(root_prefix / "conda-meta", ec); - if (ec) - { - LOG_ERROR << "Failed to create directory '" << (root_prefix / "conda-meta").string() - << "' : " << ec.message(); - } + // std::error_code ec; + // fs::create_directories(root_prefix / "conda-meta", ec); + // if (ec) + // { + // LOG_ERROR << "Failed to create directory '" << (root_prefix / "conda-meta").string() + // << "' : " << ec.message(); + // } for (const auto& directory : paths.every_generated_directories_paths()) { // Maybe the prefix isn't writable. No big deal, just keep going. - std::error_code maybe_error [[maybe_unused]]; + std::error_code maybe_error; fs::create_directories(directory, maybe_error); if (maybe_error) { diff --git a/micromamba/src/main.cpp b/micromamba/src/main.cpp index 986cd05a98..4c6a171727 100644 --- a/micromamba/src/main.cpp +++ b/micromamba/src/main.cpp @@ -84,6 +84,27 @@ decide_log_handler(const ContextOptions& options) -> mamba::logging::AnyLogHandl return mamba::logging::spdlogimpl::LogHandler_spdlog{}; } +void +report_error(int argc, char** argv, const std::string& message) +{ + const auto options = decide_preconfig_context_options(argc, argv); + if (options.output_params and options.output_params->json) + { + nlohmann::json output{ + { "success", false }, + { "log_history", + nlohmann::json::array( + { { { "message", message }, { "level", "critical" }, { "source", "libmamba" } } } + ) } + }; + std::cout << output.dump(4) << std::endl; + } + else if (not options.output_params or not options.output_params->quiet) + { + std::cerr << message << std::endl; + } +} + int main(int argc, char** argv) { @@ -244,19 +265,17 @@ main(int argc, char** argv) return return_value; } - // TODO Find a better way to print the error - // (considering `--json` and `--quiet` cases, - // as `Console` destructor handling json case is not called - // if throw happens before its instantiation - i.e in `Context` instantiation for example) + // Handle errors printing - specifically for `--json` and `--quiet` options + // as `Console` is unreachable here catch (const std::exception& e) { - std::cerr << e.what() << std::endl; + report_error(argc, argv, e.what()); return EXIT_FAILURE; } catch (...) { - std::cerr << "Unhandled non-standard exception" << std::endl; + report_error(argc, argv, "Unhandled non-standard exception"); return EXIT_FAILURE; } } diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index c8ab7a5e52..9b75d764e5 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -26,36 +26,38 @@ def skip_if_shell_incompat(shell_type): def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): res = helpers.shell("hook", "-s", "cmd.exe") - assert res == "" - assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() - assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() - assert (tmp_root_prefix / "conda-meta").is_dir() + # assert res == "" + # assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() + # assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() + # assert (tmp_root_prefix / "conda-meta").is_dir() # Remove any preconfigured settings data = tmp_path / "data" env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} env["XDG_DATA_HOME"] = str(data) env["XDG_CONFIG_HOME"] = str(tmp_path / "config") - default_prefix = data / "mamba" + # default_prefix = data / "mamba" mamba_exe = helpers.get_umamba() res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - assert res.returncode == 0 + # assert res.returncode == 0 hook = subprocess.run( [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True ) - assert hook.returncode == 0 + print("===============> hook: ", hook) + # assert hook.returncode == 0 - assert (default_prefix / "condabin").is_dir() - assert (default_prefix / "Scripts").is_dir() - assert (default_prefix / "conda-meta").is_dir() + # assert (default_prefix / "condabin").is_dir() + # assert (default_prefix / "Scripts").is_dir() + # assert (default_prefix / "conda-meta").is_dir() # Running `mamba_exe` again does not fail silently res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - assert res.returncode == 0 - assert not res.stderr, f"stderr was not empty: {res.stderr}" + print("===============> RES2: ", res) + # assert res.returncode == 0 + # assert not res.stderr, f"stderr was not empty: {res.stderr}" @pytest.mark.parametrize( From 927d8a99f9180b4917b6ad0a52936378ee279e58 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 10 Aug 2026 11:33:41 +0200 Subject: [PATCH 18/20] Final clean up --- .github/workflows/windows_impl.yml | 9 ++++----- libmamba/src/core/shell_init.cpp | 14 +++++++------- micromamba/tests/test_shell.py | 26 ++++++++++++-------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index bd730cd14c..c5589c7123 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -166,8 +166,7 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m pytest -s micromamba/tests/test_shell.py::test_hook_cmd_exe ` - -vv - #--durations=50 ` - #${{ runner.debug == 'true' && '--capture=no' || '' }} ` - #${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} + python -m pytest -s micromamba/tests/ ` + -vv --durations=50 ` + ${{ runner.debug == 'true' && '--capture=no' || '' }} ` + ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} diff --git a/libmamba/src/core/shell_init.cpp b/libmamba/src/core/shell_init.cpp index 057dc10365..1d50aa2140 100644 --- a/libmamba/src/core/shell_init.cpp +++ b/libmamba/src/core/shell_init.cpp @@ -803,13 +803,13 @@ namespace mamba // The default root prefix is validated on the next invocation, so it must // be a valid conda prefix (pkgs/conda-meta/envs) from the start. It used // to contain only the hook scripts -> every following run aborted silently. - // std::error_code ec; - // fs::create_directories(root_prefix / "conda-meta", ec); - // if (ec) - // { - // LOG_ERROR << "Failed to create directory '" << (root_prefix / "conda-meta").string() - // << "' : " << ec.message(); - // } + std::error_code ec; + fs::create_directories(root_prefix / "conda-meta", ec); + if (ec) + { + LOG_ERROR << "Failed to create directory '" << (root_prefix / "conda-meta").string() + << "' : " << ec.message(); + } for (const auto& directory : paths.every_generated_directories_paths()) { // Maybe the prefix isn't writable. No big deal, just keep going. diff --git a/micromamba/tests/test_shell.py b/micromamba/tests/test_shell.py index 9b75d764e5..c8ab7a5e52 100644 --- a/micromamba/tests/test_shell.py +++ b/micromamba/tests/test_shell.py @@ -26,38 +26,36 @@ def skip_if_shell_incompat(shell_type): def test_hook_cmd_exe(tmp_home, tmp_root_prefix, tmp_path): res = helpers.shell("hook", "-s", "cmd.exe") - # assert res == "" - # assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() - # assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() - # assert (tmp_root_prefix / "conda-meta").is_dir() + assert res == "" + assert (tmp_root_prefix / "condabin" / "mamba_hook.bat").is_file() + assert (tmp_root_prefix / "Scripts" / "activate.bat").is_file() + assert (tmp_root_prefix / "conda-meta").is_dir() # Remove any preconfigured settings data = tmp_path / "data" env = {k: v for k, v in os.environ.items() if not k.startswith(("MAMBA_", "XDG_", "CONDA_"))} env["XDG_DATA_HOME"] = str(data) env["XDG_CONFIG_HOME"] = str(tmp_path / "config") - # default_prefix = data / "mamba" + default_prefix = data / "mamba" mamba_exe = helpers.get_umamba() res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - # assert res.returncode == 0 + assert res.returncode == 0 hook = subprocess.run( [mamba_exe, "shell", "hook", "-s", "cmd.exe"], env=env, capture_output=True, text=True ) - print("===============> hook: ", hook) - # assert hook.returncode == 0 + assert hook.returncode == 0 - # assert (default_prefix / "condabin").is_dir() - # assert (default_prefix / "Scripts").is_dir() - # assert (default_prefix / "conda-meta").is_dir() + assert (default_prefix / "condabin").is_dir() + assert (default_prefix / "Scripts").is_dir() + assert (default_prefix / "conda-meta").is_dir() # Running `mamba_exe` again does not fail silently res = subprocess.run([mamba_exe], env=env, capture_output=True, text=True) - print("===============> RES2: ", res) - # assert res.returncode == 0 - # assert not res.stderr, f"stderr was not empty: {res.stderr}" + assert res.returncode == 0 + assert not res.stderr, f"stderr was not empty: {res.stderr}" @pytest.mark.parametrize( From 465e0a9dc6d362ab4002b43f84525df55488d1e6 Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 10 Aug 2026 11:45:26 +0200 Subject: [PATCH 19/20] Restore workflow --- .github/workflows/windows_impl.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows_impl.yml b/.github/workflows/windows_impl.yml index c5589c7123..fc73a8aebb 100644 --- a/.github/workflows/windows_impl.yml +++ b/.github/workflows/windows_impl.yml @@ -166,7 +166,7 @@ jobs: $env:MAMBA_TEST_SHELL_TYPE='powershell' Remove-Item -Path "env:CONDARC" # Only rerun flaky tests on the `main` branch - python -m pytest -s micromamba/tests/ ` + python -m pytest micromamba/tests/ ` -vv --durations=50 ` - ${{ runner.debug == 'true' && '--capture=no' || '' }} ` + ${{ runner.debug == 'true' && '--capture=tee-sys' || '' }} ` ${{ github.ref == 'refs/heads/main' && '--reruns 3' || '' }} From a463a645b368ee2c7fc22a8da17169a934b6b7cd Mon Sep 17 00:00:00 2001 From: Hind Montassif Date: Mon, 10 Aug 2026 11:53:41 +0200 Subject: [PATCH 20/20] Make comment clearer --- libmamba/src/core/shell_init.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmamba/src/core/shell_init.cpp b/libmamba/src/core/shell_init.cpp index 1d50aa2140..ef4206b30e 100644 --- a/libmamba/src/core/shell_init.cpp +++ b/libmamba/src/core/shell_init.cpp @@ -801,8 +801,8 @@ namespace mamba const ShellInitPathsWindowsCmd paths{ root_prefix }; // The default root prefix is validated on the next invocation, so it must - // be a valid conda prefix (pkgs/conda-meta/envs) from the start. It used - // to contain only the hook scripts -> every following run aborted silently. + // be a valid conda prefix (with `pkgs`, `conda-meta` or `envs` folder) from the start. + // It used to contain only the hook scripts -> every following run aborted silently. std::error_code ec; fs::create_directories(root_prefix / "conda-meta", ec); if (ec)