mamba/libmamba/src/core/context.cpp

355 lines
12 KiB
C++

// Copyright (c) 2019, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <iostream>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <spdlog/pattern_formatter.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include "mamba/core/context.hpp"
#include "mamba/core/environment.hpp"
#include "mamba/core/execution.hpp"
#include "mamba/core/output.hpp"
#include "mamba/core/thread_utils.hpp"
#include "mamba/core/util.hpp"
#include "mamba/core/util_os.hpp"
#include "mamba/util/string.hpp"
#include "mamba/util/url_manip.hpp"
namespace mamba
{
class Logger : public spdlog::logger
{
public:
Logger(const std::string& name, const std::string& pattern, const std::string& eol);
void dump_backtrace_no_guards();
};
Logger::Logger(const std::string& name, const std::string& pattern, const std::string& eol)
: spdlog::logger(name, std::make_shared<spdlog::sinks::stderr_color_sink_mt>())
{
auto f = std::make_unique<spdlog::pattern_formatter>(
pattern,
spdlog::pattern_time_type::local,
eol
);
set_formatter(std::move(f));
}
void Logger::dump_backtrace_no_guards()
{
using spdlog::details::log_msg;
if (tracer_.enabled())
{
tracer_.foreach_pop(
[this](const log_msg& msg)
{
if (this->should_log(msg.level))
{
this->sink_it_(msg);
}
}
);
}
}
spdlog::level::level_enum convert_log_level(log_level l)
{
return static_cast<spdlog::level::level_enum>(l);
}
Context::Context()
{
MainExecutor::instance().on_close(tasksync.synchronized([this] { logger->flush(); }));
on_ci = bool(env::get("CI"));
prefix_params.root_prefix = env::get("MAMBA_ROOT_PREFIX").value_or("");
prefix_params.conda_prefix = prefix_params.root_prefix;
envs_dirs = { prefix_params.root_prefix / "envs" };
pkgs_dirs = { prefix_params.root_prefix / "pkgs",
fs::u8path("~") / ".mamba" / "pkgs"
#ifdef _WIN32
,
fs::u8path(env::get("APPDATA").value_or("")) / ".mamba" / "pkgs"
#endif
};
keep_temp_files = env::get("MAMBA_KEEP_TEMP") ? true : false;
keep_temp_directories = env::get("MAMBA_KEEP_TEMP_DIRS") ? true : false;
{
const bool cout_is_atty = is_atty(std::cout);
graphics_params.no_progress_bars = (on_ci || !cout_is_atty);
graphics_params.palette = cout_is_atty ? Palette::terminal() : Palette::no_color();
}
#ifdef _WIN32
ascii_only = true;
#else
ascii_only = false;
#endif
set_default_signal_handler();
std::shared_ptr<spdlog::logger> l = std::make_shared<Logger>(
"libmamba",
output_params.log_pattern,
"\n"
);
std::shared_ptr<spdlog::logger> libcurl_logger = std::make_shared<Logger>(
"libcurl",
output_params.log_pattern,
""
);
std::shared_ptr<spdlog::logger> libsolv_logger = std::make_shared<Logger>(
"libsolv",
output_params.log_pattern,
""
);
spdlog::register_logger(libcurl_logger);
spdlog::register_logger(libsolv_logger);
spdlog::set_default_logger(l);
logger = std::dynamic_pointer_cast<Logger>(l);
spdlog::set_level(convert_log_level(output_params.logging_level));
}
Context::~Context() = default;
void Context::set_verbosity(int lvl)
{
this->output_params.verbosity = lvl;
switch (lvl)
{
case -3:
this->output_params.logging_level = log_level::off;
break;
case -2:
this->output_params.logging_level = log_level::critical;
break;
case -1:
this->output_params.logging_level = log_level::err;
break;
case 0:
this->output_params.logging_level = log_level::warn;
break;
case 1:
this->output_params.logging_level = log_level::info;
break;
case 2:
this->output_params.logging_level = log_level::debug;
break;
case 3:
this->output_params.logging_level = log_level::trace;
break;
default:
this->output_params.logging_level = log_level::info;
break;
}
spdlog::set_level(convert_log_level(output_params.logging_level));
}
void Context::set_log_level(log_level level)
{
output_params.logging_level = level;
spdlog::set_level(convert_log_level(level));
}
std::vector<std::string> Context::platforms()
{
return { platform, "noarch" };
}
std::map<std::string, AuthenticationInfo>& Context::authentication_info()
{
if (!m_authentication_infos_loaded)
{
load_authentication_info();
}
return m_authentication_info;
}
const std::map<std::string, AuthenticationInfo>& Context::authentication_info() const
{
return const_cast<Context*>(this)->authentication_info();
}
void Context::load_authentication_info()
{
auto& ctx = Context::instance();
std::vector<fs::u8path> found_tokens;
for (const auto& loc : ctx.token_locations)
{
auto px = env::expand_user(loc);
if (!fs::exists(px) || !fs::is_directory(px))
{
continue;
}
for (const auto& entry : fs::directory_iterator(px))
{
if (util::ends_with(entry.path().filename().string(), ".token"))
{
found_tokens.push_back(entry.path());
std::string token_url = util::url_decode(entry.path().filename().string());
// anaconda client writes out a token for https://api.anaconda.org...
// but we need the token for https://conda.anaconda.org
// conda does the same
std::size_t api_pos = token_url.find("://api.");
if (api_pos != std::string::npos)
{
token_url.replace(api_pos, 7, "://conda.");
}
// cut ".token" ending
token_url = token_url.substr(0, token_url.size() - 6);
std::string token_content = read_contents(entry.path());
AuthenticationInfo auth_info{ AuthenticationType::kCondaToken, token_content };
m_authentication_info[token_url] = auth_info;
LOG_INFO << "Found token for " << token_url << " at " << entry.path();
}
}
}
std::map<std::string, AuthenticationInfo> res;
fs::u8path auth_loc(mamba::env::home_directory() / ".mamba" / "auth" / "authentication.json");
try
{
if (fs::exists(auth_loc))
{
auto infile = open_ifstream(auth_loc);
nlohmann::json j;
infile >> j;
for (auto& [key, el] : j.items())
{
std::string host = key;
std::string type = el["type"];
AuthenticationInfo info;
if (type == "CondaToken")
{
info.type = AuthenticationType::kCondaToken;
info.value = el["token"].get<std::string>();
LOG_INFO << "Found token for host " << host
<< " in ~/.mamba/auth/authentication.json";
}
else if (type == "BasicHTTPAuthentication")
{
info.type = AuthenticationType::kBasicHTTPAuthentication;
const auto& user = el.value("user", "");
auto pass = decode_base64(el["password"].get<std::string>());
if (pass)
{
info.value = util::concat(user, ":", pass.value());
LOG_INFO << "Found credentials for user " << user << " for host "
<< host << " in ~/.mamba/auth/authentication.json";
}
else
{
LOG_ERROR
<< "Found credentials for user " << user << " for host " << host
<< " in ~/.mamba/auth/authentication.json but could not decode base64 password"
<< std::endl;
}
}
else if (type == "BearerToken")
{
info.type = AuthenticationType::kBearerToken;
info.value = el["token"].get<std::string>();
LOG_INFO << "Found bearer token for host " << host
<< " in ~/.mamba/auth/authentication.json";
}
m_authentication_info[host] = info;
}
}
}
catch (nlohmann::json::exception& e)
{
LOG_WARNING << "Could not parse authentication information from " << auth_loc << ": "
<< e.what();
}
m_authentication_infos_loaded = true;
}
std::string env_name(const fs::u8path& prefix)
{
if (prefix.empty())
{
throw std::runtime_error("Empty path");
}
if (paths_equal(prefix, Context::instance().prefix_params.root_prefix))
{
return ROOT_ENV_NAME;
}
fs::u8path maybe_env_dir = prefix.parent_path();
for (const auto& d : Context::instance().envs_dirs)
{
if (paths_equal(d, maybe_env_dir))
{
return prefix.filename().string();
}
}
return prefix.string();
}
void Context::debug_print() const
{
#define PRINT_CTX(xout, xname) fmt::print(xout, "{}: {}\n", #xname, xname)
#define PRINT_CTX_VEC(xout, xname) fmt::print(xout, "{}: [{}]\n", #xname, fmt::join(xname, ", "))
auto out = Console::stream();
out << std::boolalpha << ">>> MAMBA CONTEXT <<< \n";
PRINT_CTX(out, prefix_params.target_prefix);
PRINT_CTX(out, prefix_params.root_prefix);
PRINT_CTX(out, dry_run);
PRINT_CTX(out, always_yes);
PRINT_CTX(out, allow_softlinks);
PRINT_CTX(out, offline);
PRINT_CTX(out, output_params.quiet);
PRINT_CTX(out, src_params.no_rc);
PRINT_CTX(out, src_params.no_env);
PRINT_CTX(out, remote_fetch_params.ssl_no_revoke);
PRINT_CTX(out, remote_fetch_params.ssl_verify);
PRINT_CTX(out, remote_fetch_params.retry_timeout);
PRINT_CTX(out, remote_fetch_params.retry_backoff);
PRINT_CTX(out, remote_fetch_params.max_retries);
PRINT_CTX(out, remote_fetch_params.connect_timeout_secs);
PRINT_CTX(out, add_pip_as_python_dependency);
PRINT_CTX(out, override_channels_enabled);
PRINT_CTX(out, use_only_tar_bz2);
PRINT_CTX(out, auto_activate_base);
PRINT_CTX(out, extra_safety_checks);
PRINT_CTX(out, threads_params.download_threads);
PRINT_CTX(out, output_params.verbosity);
PRINT_CTX(out, channel_alias);
out << "channel_priority: " << static_cast<int>(channel_priority) << '\n';
PRINT_CTX_VEC(out, default_channels);
PRINT_CTX_VEC(out, channels);
PRINT_CTX_VEC(out, pinned_packages);
PRINT_CTX(out, platform);
out << ">>> END MAMBA CONTEXT <<< \n" << std::endl;
#undef PRINT_CTX
#undef PRINT_CTX_VEC
}
void Context::dump_backtrace_no_guards()
{
logger->dump_backtrace_no_guards();
}
} // namespace mamba