Skip to content

Commit

Permalink
Merge branch 'main' into list_refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
SandrineP authored Jan 24, 2025
2 parents 353835e + 7fa748f commit abb5f04
Show file tree
Hide file tree
Showing 10 changed files with 806 additions and 45 deletions.
2 changes: 2 additions & 0 deletions libmamba/include/mamba/api/configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ namespace mamba

namespace detail
{
auto get_root_prefix() -> fs::u8path;

template <class T>
bool ConfigurableImpl<T>::cli_configured() const
{
Expand Down
79 changes: 43 additions & 36 deletions libmamba/src/api/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,22 +713,52 @@ namespace mamba
return { fs::weakly_canonical(std::move(prefix)) };
}

/**
* In mamba 1.0, only micromamba was using this location.
*/
auto default_root_prefix_v1() -> fs::u8path
auto get_default_root_prefix(fs::u8path& prefix) -> void
{
return fs::u8path(util::user_home_dir()) / "micromamba";
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
{
prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value();
LOG_WARNING << unindent(R"(
'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose.
Consider using 'MAMBA_ROOT_PREFIX' instead)");
}
else
{
#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE
// mamba case
// set the root prefix as the mamba installation path
get_root_prefix_from_mamba_bin(util::which("mamba"))
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
.or_else([](mamba_error&& error) { throw std::move(error); });
#else
// micromamba case

// In 1.0, only micromamba was using this location.
const fs::u8path default_root_prefix_v1 = fs::u8path(util::user_home_dir())
/ "micromamba";

// In 2.0, we change the default location.
// 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";

validate_existing_root_prefix(default_root_prefix_v1)
.or_else([&default_root_prefix_v2](const auto& /* error */)
{ return validate_root_prefix(default_root_prefix_v2); })
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
.or_else([](mamba_error&& error) { throw std::move(error); });
#endif
}
}

/**
* In mamba 2.0, we change the default location.
* We unconditionally name the subfolder "mamba" for compatibility between ``mamba``
* and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables.
*/
auto default_root_prefix_v2() -> fs::u8path
auto get_root_prefix() -> fs::u8path
{
return fs::u8path(util::user_data_dir()) / "mamba";
fs::u8path root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or("");
if (root_prefix.empty())
{
get_default_root_prefix(root_prefix);
}
return root_prefix;
}

void root_prefix_hook(Configuration& config, fs::u8path& prefix)
Expand All @@ -737,30 +767,7 @@ namespace mamba

if (prefix.empty())
{
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
{
prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value();
LOG_WARNING << unindent(R"(
'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose.
Consider using 'MAMBA_ROOT_PREFIX' instead)");
}
else
{
#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE
// mamba case
// set the root prefix as the mamba installation path
get_root_prefix_from_mamba_bin(util::which("mamba"))
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
.or_else([](mamba_error&& error) { throw std::move(error); });
#else
// micromamba case
validate_existing_root_prefix(default_root_prefix_v1())
.or_else([](const auto& /* error */)
{ return validate_root_prefix(default_root_prefix_v2()); })
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
.or_else([](mamba_error&& error) { throw std::move(error); });
#endif
}
prefix = get_root_prefix();

if (env_name.configured())
{
Expand Down
3 changes: 2 additions & 1 deletion libmamba/src/core/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>

#include "mamba/api/configuration.hpp"
#include "mamba/core/context.hpp"
#include "mamba/core/execution.hpp"
#include "mamba/core/output.hpp"
Expand Down Expand Up @@ -177,7 +178,7 @@ namespace mamba
Context::Context(const ContextOptions& options)
{
on_ci = static_cast<bool>(util::get_env("CI"));
prefix_params.root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or("");
prefix_params.root_prefix = detail::get_root_prefix();
prefix_params.conda_prefix = prefix_params.root_prefix;

envs_dirs = { prefix_params.root_prefix / "envs" };
Expand Down
46 changes: 44 additions & 2 deletions libmamba/src/download/downloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// The full license is in the file LICENSE, distributed with this software.

#include "mamba/api/configuration.hpp"
#include "mamba/core/invoke.hpp"
#include "mamba/core/thread_utils.hpp"
#include "mamba/core/util.hpp"
Expand All @@ -24,13 +25,18 @@ namespace mamba::download
namespace
{

constexpr std::array<const char*, 6> cert_locations{
constexpr std::array<const char*, 10> cert_locations{
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/pki/tls/cacert.pem", // OpenELEC
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
"/etc/ssl/cert.pem", // Alpine Linux
// MacOS
"/System/Library/OpenSSL/certs/cert.pem",
"/usr/local/etc/openssl/cert.pem",
"/usr/local/share/certs/ca-root-nss.crt",
"/usr/local/share/certs/ca-root.crt",
};

void init_remote_fetch_params(Context::RemoteFetchParams& remote_fetch_params)
Expand Down Expand Up @@ -74,16 +80,52 @@ namespace mamba::download
LOG_INFO << "Using REQUESTS_CA_BUNDLE " << remote_fetch_params.ssl_verify;
}
}
else if (remote_fetch_params.ssl_verify == "<system>" && util::on_linux)
// TODO: Adapt the semantic of `<system>` to decouple the use of CA certificates
// from `conda-forge::ca-certificates` and the system CA certificates.
else if (remote_fetch_params.ssl_verify == "<system>")
{
// Use the CA certificates from `conda-forge::ca-certificates` installed in the
// root prefix or the system CA certificates if the certificate is not present.
fs::u8path libmamba_library_path;

fs::u8path root_prefix = detail::get_root_prefix();
fs::u8path env_prefix_conda_cert = root_prefix / "ssl" / "cacert.pem";

LOG_INFO << "Checking for CA certificates at the root prefix: "
<< env_prefix_conda_cert;

if (fs::exists(env_prefix_conda_cert))
{
LOG_INFO << "Using CA certificates from `conda-forge::ca-certificates` installed in the root prefix "
<< "(i.e " << env_prefix_conda_cert << ")";
remote_fetch_params.ssl_verify = env_prefix_conda_cert;
remote_fetch_params.curl_initialized = true;
return;
}

// Fallback on system CA certificates.
bool found = false;

// TODO: find if one needs to specify a CA certificate on Windows or not
// given that the location of system's CA certificates is not clear on Windows.
// For now, just use `libcurl` and the SSL libraries' default.
if (util::on_win)
{
LOG_INFO << "Using libcurl/the SSL library's default CA certification";
remote_fetch_params.ssl_verify = "";
found = true;
remote_fetch_params.curl_initialized = true;
return;
}

for (const auto& loc : cert_locations)
{
if (fs::exists(loc))
{
LOG_INFO << "Using system CA certificates at: " << loc;
remote_fetch_params.ssl_verify = loc;
found = true;
break;
}
}

Expand Down
14 changes: 14 additions & 0 deletions libmamba/src/specs/package_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,20 @@ namespace mamba::specs
}
}

// A git repository URL over https and used by `pip`
// git+https://<repository-url>@<commit|branch|tag>#egg=<package-name>
if (util::starts_with(str, "git+https"))
{
auto pkg = PackageInfo();
pkg.package_url = str;
const std::string pkg_name_marker = "#egg=";
if (const auto idx = str.rfind(pkg_name_marker); idx != std::string_view::npos)
{
pkg.name = str.substr(idx + pkg_name_marker.length());
}
return pkg;
}

return make_unexpected_parse(fmt::format(R"(Fail to parse PackageInfo URL "{}")", str));
}

Expand Down
42 changes: 42 additions & 0 deletions libmamba/tests/src/download/test_downloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <catch2/catch_all.hpp>

#include "mamba/api/configuration.hpp"
#include "mamba/download/downloader.hpp"

#include "mambatests.hpp"
Expand Down Expand Up @@ -54,5 +55,46 @@ namespace mamba
std::runtime_error
);
}

TEST_CASE("Use CA certificate from the root prefix")
{
// Create a context, make a request and check that ssl_verify is set to the correct path
auto& context = mambatests::singletons().context;

// Set the context values to the default ones
context.remote_fetch_params.curl_initialized = false;
context.remote_fetch_params.ssl_verify = "<system>";

download::Request request(
"test",
download::MirrorName(""),
"https://conda.anaconda.org/conda-forge/linux-64/repodata.json",
"test_download_repodata.json"
);
download::MultiRequest dl_request{ std::vector{ std::move(request) } };

// Downloading must initialize curl and set `ssl_verify` to the path of the CA
// certificate
REQUIRE(!context.remote_fetch_params.curl_initialized);
download::MultiResult res = download::download(dl_request, context.mirrors, context);
REQUIRE(context.remote_fetch_params.curl_initialized);

auto certificates = context.remote_fetch_params.ssl_verify;
const fs::u8path root_prefix = detail::get_root_prefix();
const fs::u8path expected_certificates = root_prefix / "ssl" / "cert.pem";

// TODO: is libmamba tested without a root prefix or a base installation?
bool reach_fallback_certificates;
if (util::on_win)
{
// Default certificates from libcurl/libssl are used on Windows
reach_fallback_certificates = certificates == "";
}
else
{
reach_fallback_certificates = (mamba::util::ends_with(certificates, "cert.pem") || mamba::util::ends_with(certificates, "ca-certificates.crt"));
}
REQUIRE((certificates == expected_certificates || reach_fallback_certificates));
}
}
}
9 changes: 9 additions & 0 deletions libmamba/tests/src/specs/test_package_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ namespace
static constexpr std::string_view url = "https://conda.anaconda.org/conda-forge/linux-64/pkg.conda";
REQUIRE_FALSE(PackageInfo::from_url(url).has_value());
}

SECTION("git+https://github.com/urllib3/[email protected]#egg=urllib3")
{
static constexpr std::string_view url = "git+https://github.com/urllib3/[email protected]#egg=urllib3";
auto pkg = PackageInfo::from_url(url).value();

REQUIRE(pkg.name == "urllib3");
REQUIRE(pkg.package_url == url);
}
}

TEST_CASE("PackageInfo serialization")
Expand Down
Loading

0 comments on commit abb5f04

Please sign in to comment.