diff --git a/libmamba/src/api/list.cpp b/libmamba/src/api/list.cpp index e5cc740786..46a96e37e6 100644 --- a/libmamba/src/api/list.cpp +++ b/libmamba/src/api/list.cpp @@ -25,11 +25,12 @@ namespace mamba bool full_name; bool no_pip; bool reverse; + bool explicit_; }; struct formatted_pkg { - std::string name, version, build, channel; + std::string name, version, build, channel, url; }; bool compare_alphabetically(const formatted_pkg& a, const formatted_pkg& b) @@ -134,6 +135,7 @@ namespace mamba pkg_info.platform ); } + obj["url"] = pkg_info.package_url; obj["build_number"] = pkg_info.build_number; obj["build_string"] = pkg_info.build_string; obj["dist_name"] = pkg_info.str(); @@ -163,6 +165,7 @@ namespace mamba formatted_pkgs.name = package.second.name; formatted_pkgs.version = package.second.version; formatted_pkgs.build = package.second.build_string; + formatted_pkgs.url = package.second.package_url; if (package.second.channel.find("https://repo.anaconda.com/pkgs/") == 0) { formatted_pkgs.channel = ""; @@ -190,25 +193,34 @@ namespace mamba std::sort(packages.begin(), packages.end(), comparator); // format and print table - printers::Table t({ "Name", "Version", "Build", "Channel" }); - t.set_alignment({ printers::alignment::left, - printers::alignment::left, - printers::alignment::left, - printers::alignment::left }); - t.set_padding({ 2, 2, 2, 2 }); - - for (auto p : packages) + if (options.explicit_) { - printers::FormattedString formatted_name(p.name); - if (requested_specs.find(p.name) != requested_specs.end()) + for (auto p : packages) { - formatted_name = printers::FormattedString(p.name); - formatted_name.style = ctx.graphics_params.palette.user; + std::cout << p.url << std::endl; } - t.add_row({ formatted_name, p.version, p.build, p.channel }); } - - t.print(std::cout); + else + { + printers::Table t({ "Name", "Version", "Build", "Channel" }); + t.set_alignment({ printers::alignment::left, + printers::alignment::left, + printers::alignment::left, + printers::alignment::left }); + t.set_padding({ 2, 2, 2, 2 }); + + for (auto p : packages) + { + printers::FormattedString formatted_name(p.name); + if (requested_specs.find(p.name) != requested_specs.end()) + { + formatted_name = printers::FormattedString(p.name); + formatted_name.style = ctx.graphics_params.palette.user; + } + t.add_row({ formatted_name, p.version, p.build, p.channel }); + } + t.print(std::cout); + } } } @@ -228,6 +240,7 @@ namespace mamba options.full_name = config.at("full_name").value(); options.no_pip = config.at("no_pip").value(); options.reverse = config.at("reverse").value(); + options.explicit_ = config.at("explicit").value(); auto channel_context = ChannelContext::make_conda_compatible(config.context()); detail::list_packages(config.context(), regex, channel_context, std::move(options)); diff --git a/micromamba/src/list.cpp b/micromamba/src/list.cpp index 0baa505b28..354da91cdb 100644 --- a/micromamba/src/list.cpp +++ b/micromamba/src/list.cpp @@ -38,6 +38,14 @@ init_list_parser(CLI::App* subcom, Configuration& config) Configurable("reverse", false).group("cli").description("List installed packages in reverse order.") ); subcom->add_flag("--reverse", reverse.get_cli_config(), reverse.description()); + + auto& explicit_ = config.insert(Configurable("explicit", false) + .group("cli") + .description("List explicitly all installed packages with URL." + )); + subcom->add_flag("--explicit", explicit_.get_cli_config(), explicit_.description()); + + // TODO: implement this in libmamba/list.cpp /*auto& canonical = config.insert(Configurable("canonical", false) .group("cli") diff --git a/micromamba/tests/test_list.py b/micromamba/tests/test_list.py index 8f008ef2a4..51bc0c3745 100644 --- a/micromamba/tests/test_list.py +++ b/micromamba/tests/test_list.py @@ -27,7 +27,10 @@ def test_list( assert "xtensor" in names assert "xtl" in names assert all( - i["channel"] == "conda-forge" and i["base_url"] == "https://conda.anaconda.org/conda-forge" + i["channel"] == "conda-forge" + and i["base_url"] == "https://conda.anaconda.org/conda-forge" + and i["name"] in i["url"] + and "conda-forge" in i["url"] for i in res ) @@ -56,7 +59,7 @@ def test_list_no_json( assert "xtensor" in res assert "xtl" in res - # This is what res looks like in this case (with or without a header delimiter): + # This is what res looks like in this case: # List of packages in environment: "xxx" # Name Version Build Channel @@ -76,6 +79,25 @@ def test_list_no_json( assert res.find("xtensor") < res.find("xtl") +@pytest.mark.parametrize("explicit_flag", ["", "--explicit"]) +@pytest.mark.parametrize("env_selector", ["", "name", "prefix"]) +@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True) +def test_list_explicit_no_json( + tmp_home, tmp_root_prefix, tmp_env_name, tmp_xtensor_env, env_selector, explicit_flag +): + if env_selector == "prefix": + res = helpers.umamba_list("-p", tmp_xtensor_env, explicit_flag) + elif env_selector == "name": + res = helpers.umamba_list("-n", tmp_env_name, explicit_flag) + else: + res = helpers.umamba_list(explicit_flag) + + packages_url_list = res.strip().split("\n")[2:] + if explicit_flag == "--explicit": + for url in packages_url_list: + assert "conda-forge" in url + + @pytest.mark.parametrize("quiet_flag", ["", "-q", "--quiet"]) @pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True) def test_list_name(tmp_home, tmp_root_prefix, tmp_xtensor_env, quiet_flag):