Migrate Channel::make_channel to resolve multi channels (#2986)

* Add one to many get_channels

* Refactor ChannelContext params

* ChannelContext::make_channel migration 1

* ChannelContext::make_channel migration 2

* ChannelContext::make_channel migration 3

* ChannelContext::make_channel migration 4

* ChannelContext::make_channel migration 5

* ChannelContext::make_channel migration 6

* ChannelContext::make_channel migration 7

* Remove Channel bindings

* Remove ChannelContext::make_channel

* ChannelContext::get_channels migration 1

* ChannelContext::get_channels migration 2

* ChannelContext::get_channels migration 3

* Remove get_channels bindings

* Remove ChannelContext::get_channels

* Channel::resolve return a list

* Resolve multi_channel in Channel::resolve

* Use weakening map for multi_channels

* Split Channel resolve_name

* Cleanup signatures

* Add Channel::clear_xxx

* Force removal of trailing '/' in Channel

* Add weak channel comparison

* Use better channel comparisons

* Remove Channel::base_url

* Rename Channel::make_chan > make_channel

* Add doc

* Resolve Channel paths from params

* Review from JohanMabille

* Fix absolute path
This commit is contained in:
Antoine Prouvost 2023-11-21 12:07:19 +01:00 committed by GitHub
parent 0177c44383
commit e874e7ea71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 545 additions and 402 deletions

View File

@ -55,40 +55,55 @@ namespace mamba
-> std::optional<std::string_view>;
};
template <typename Key, typename Value>
using name_map = util::weakening_map<std::unordered_map<Key, Value>, NameWeakener>;
using platform_list = util::flat_set<std::string>;
using channel_list = std::vector<Channel>;
using channel_map = util::weakening_map<std::unordered_map<std::string, Channel>, NameWeakener>;
using multichannel_map = std::unordered_map<std::string, channel_list>;
using channel_map = name_map<std::string, Channel>;
using multichannel_map = name_map<std::string, channel_list>;
const platform_list& platforms;
const specs::CondaURL& channel_alias;
const channel_map& custom_channels;
const specs::AuthenticationDataBase& auth_db;
// TODO add CWD and home
const multichannel_map& custom_multichannels;
const specs::AuthenticationDataBase& authentication_db;
std::string_view home_dir;
std::string_view current_working_dir;
};
using platform_list = util::flat_set<std::string>;
using platform_list = ResolveParams::platform_list;
using channel_list = ResolveParams::channel_list;
[[nodiscard]] static auto resolve(specs::ChannelSpec spec, ResolveParams params) -> Channel;
[[nodiscard]] static auto resolve(specs::ChannelSpec spec, ResolveParams params)
-> channel_list;
Channel(specs::CondaURL url, std::string display_name, util::flat_set<std::string> platforms = {});
[[nodiscard]] auto url() const -> const specs::CondaURL&;
auto clear_url() -> const specs::CondaURL;
void set_url(specs::CondaURL url);
[[nodiscard]] auto platforms() const -> const platform_list&;
auto clear_platforms() -> platform_list;
void set_platforms(platform_list platforms);
[[nodiscard]] auto display_name() const -> const std::string&;
auto clear_display_name() -> std::string;
void set_display_name(std::string display_name);
std::string base_url() const;
std::string platform_url(std::string_view platform, bool with_credential = true) const;
[[nodiscard]] auto url_equivalent_with(const Channel& other) const -> bool;
[[nodiscard]] auto is_equivalent_to(const Channel& other) const -> bool;
[[nodiscard]] auto contains_equivalent(const Channel& other) const -> bool;
[[nodiscard]] auto
platform_url(std::string_view platform, bool with_credential = true) const -> std::string;
// The pairs consist of (platform,url)
util::flat_set<std::pair<std::string, std::string>>
platform_urls(bool with_credential = true) const;
util::flat_set<std::string> urls(bool with_credential = true) const;
[[nodiscard]] auto platform_urls(bool with_credential = true) const
-> util::flat_set<std::pair<std::string, std::string>>;
[[nodiscard]] auto urls(bool with_credential = true) const -> util::flat_set<std::string>;
private:
@ -118,40 +133,42 @@ namespace mamba
using channel_map = Channel::ResolveParams::channel_map;
using channel_list = Channel::ResolveParams::channel_list;
using multichannel_map = Channel::ResolveParams::multichannel_map;
using platform_list = Channel::ResolveParams::platform_list;
using multichannel_map = std::unordered_map<std::string, channel_list>;
ChannelContext(Context& context);
~ChannelContext();
ChannelContext(const ChannelContext&) = delete;
ChannelContext& operator=(const ChannelContext&) = delete;
auto operator=(const ChannelContext&) -> ChannelContext& = delete;
ChannelContext(ChannelContext&&) = delete;
ChannelContext& operator=(ChannelContext&&) = delete;
auto operator=(ChannelContext&&) -> ChannelContext& = delete;
const Channel& make_channel(const std::string& value);
auto get_channels(const std::vector<std::string>& channel_names) -> channel_list;
auto make_channel(std::string_view name) -> channel_list;
const specs::CondaURL& get_channel_alias() const;
const channel_map& get_custom_channels() const;
const multichannel_map& get_custom_multichannels() const;
auto get_channel_alias() const -> const specs::CondaURL&;
auto get_custom_channels() const -> const channel_map&;
auto get_custom_multichannels() const -> const multichannel_map&;
Context& context() const
auto context() const -> Context&
{
return m_context;
}
private:
using ChannelCache = std::map<std::string, Channel>;
using ChannelCache = std::unordered_map<std::string, channel_list>;
Context& m_context;
ChannelCache m_channel_cache;
specs::CondaURL m_channel_alias;
platform_list m_platforms;
channel_map m_custom_channels;
multichannel_map m_custom_multichannels;
platform_list m_platforms;
std::string m_home_dir;
std::string m_current_working_dir;
auto params() -> Channel::ResolveParams;
void init_custom_channels();
};

View File

@ -13,6 +13,16 @@
namespace mamba::util
{
/**
* Lightweight file path manipulation.
*
* The purpose of this file is to provide a lightweight functions for manipulating paths
* for things that manipulate "path-like" objects, such as parsers and URLs.
* In general, users should prefer using the correct abstraction, such as @ref URL
* and @ref u8path.
* However some features provided here, such as @ref expand_home, are not available elsewhere.
*/
inline static constexpr char preferred_path_separator_posix = '/';
inline static constexpr char preferred_path_separator_win = '\\';

View File

@ -51,50 +51,51 @@ namespace mamba
auto& ctx = pool.context();
std::vector<std::string> channel_urls = ctx.channels;
std::vector<MSubdirData> subdirs;
std::vector<std::pair<int, int>> priorities;
int max_prio = static_cast<int>(channel_urls.size());
int max_prio = static_cast<int>(ctx.channels.size());
auto prev_channel_url = specs::CondaURL();
Console::instance().init_progress_bar_manager(ProgressBarMode::multi);
std::vector<mamba_error> error_list;
for (auto channel : pool.channel_context().get_channels(channel_urls))
for (const auto& location : ctx.channels)
{
for (auto& [platform, url] : channel.platform_urls(true))
for (auto channel : pool.channel_context().make_channel(location))
{
auto sdires = MSubdirData::create(
pool.channel_context(),
channel,
platform,
url,
package_caches,
"repodata.json"
);
if (!sdires.has_value())
for (auto& [platform, url] : channel.platform_urls(true))
{
error_list.push_back(std::move(sdires).error());
continue;
}
auto sdir = std::move(sdires).value();
subdirs.push_back(std::move(sdir));
if (ctx.channel_priority == ChannelPriority::Disabled)
{
priorities.push_back(std::make_pair(0, 0));
}
else
{
// Consider 'flexible' and 'strict' the same way
if (channel.url() != prev_channel_url)
auto sdires = MSubdirData::create(
pool.channel_context(),
channel,
platform,
url,
package_caches,
"repodata.json"
);
if (!sdires.has_value())
{
max_prio--;
prev_channel_url = channel.url();
error_list.push_back(std::move(sdires).error());
continue;
}
auto sdir = std::move(sdires).value();
subdirs.push_back(std::move(sdir));
if (ctx.channel_priority == ChannelPriority::Disabled)
{
priorities.push_back(std::make_pair(0, 0));
}
else
{
// Consider 'flexible' and 'strict' the same way
if (channel.url() != prev_channel_url)
{
max_prio--;
prev_channel_url = channel.url();
}
priorities.push_back(std::make_pair(max_prio, 0));
}
priorities.push_back(std::make_pair(max_prio, 0));
}
}
}

View File

@ -160,16 +160,17 @@ namespace mamba
}
items.push_back({ "virtual packages", virtual_pkgs });
std::vector<std::string> channels = ctx.channels;
// Always append context channels
auto& ctx_channels = ctx.channels;
std::copy(ctx_channels.begin(), ctx_channels.end(), std::back_inserter(channels));
std::vector<std::string> channel_urls;
for (auto channel : channel_context.get_channels(channels))
channel_urls.reserve(ctx.channels.size() * 2); // Lower bound * (platform + noarch)
for (const auto& loc : ctx.channels)
{
for (auto url : channel.urls(true))
for (auto channel : channel_context.make_channel(loc))
{
channel_urls.push_back(url);
for (auto url : channel.urls(true))
{
channel_urls.push_back(url);
}
}
}
items.push_back({ "channels", channel_urls });

View File

@ -73,11 +73,13 @@ namespace mamba
if (regex.empty() || std::regex_search(pkg_info.name, spec_pat))
{
auto& channel = channel_context.make_channel(pkg_info.url);
obj["base_url"] = channel.base_url();
auto channels = channel_context.make_channel(pkg_info.url);
assert(channels.size() == 1); // A URL can only resolve to one channel
obj["base_url"] = channels.front().url().str(specs::CondaURL::Credentials::Remove
);
obj["build_number"] = pkg_info.build_number;
obj["build_string"] = pkg_info.build_string;
obj["channel"] = channel.display_name();
obj["channel"] = channels.front().display_name();
obj["dist_name"] = pkg_info.str();
obj["name"] = pkg_info.name;
obj["platform"] = pkg_info.subdir;
@ -111,8 +113,9 @@ namespace mamba
}
else
{
const Channel& channel = channel_context.make_channel(package.second.url);
formatted_pkgs.channel = channel.display_name();
auto channels = channel_context.make_channel(package.second.url);
assert(channels.size() == 1); // A URL can only resolve to one channel
formatted_pkgs.channel = channels.front().display_name();
}
packages.push_back(formatted_pkgs);
}

View File

@ -6,13 +6,13 @@
#include <cassert>
#include <tuple>
#include <unordered_set>
#include <utility>
#include "mamba/core/channel.hpp"
#include "mamba/core/context.hpp"
#include "mamba/specs/channel_spec.hpp"
#include "mamba/specs/conda_url.hpp"
#include "mamba/util/environment.hpp"
#include "mamba/util/path_manip.hpp"
#include "mamba/util/string.hpp"
#include "mamba/util/tuple_hash.hpp"
@ -22,9 +22,9 @@
namespace mamba
{
/********************************
* NameWeakener Implmentation *
********************************/
/*********************************
* NameWeakener Implementation *
*********************************/
auto Channel::ResolveParams::NameWeakener::make_first_key(std::string_view key) const
-> std::string_view
@ -47,6 +47,9 @@ namespace mamba
, m_display_name(std::move(display_name))
, m_platforms(std::move(platforms))
{
auto p = m_url.clear_path();
p = util::rstrip(p, '/');
m_url.set_path(std::move(p));
}
auto Channel::url() const -> const specs::CondaURL&
@ -54,6 +57,11 @@ namespace mamba
return m_url;
}
auto Channel::clear_url() -> const specs::CondaURL
{
return std::exchange(m_url, {});
}
void Channel::set_url(specs::CondaURL url)
{
m_url = std::move(url);
@ -64,6 +72,11 @@ namespace mamba
return m_platforms;
}
auto Channel::clear_platforms() -> platform_list
{
return std::exchange(m_platforms, {});
}
void Channel::set_platforms(platform_list platforms)
{
m_platforms = std::move(platforms);
@ -74,17 +87,41 @@ namespace mamba
return m_display_name;
}
auto Channel::clear_display_name() -> std::string
{
return std::exchange(m_display_name, {});
}
void Channel::set_display_name(std::string display_name)
{
m_display_name = std::move(display_name);
}
std::string Channel::base_url() const
auto Channel::url_equivalent_with(const Channel& other) const -> bool
{
return url().str(specs::CondaURL::Credentials::Remove);
using Decode = typename specs::CondaURL::Decode;
const auto& this_url = url();
const auto& other_url = other.url();
// Not checking users, passwords, and tokens
return
// Schemes
(this_url.scheme() == other_url.scheme())
// Hosts
&& (this_url.host(Decode::no) == other_url.host(Decode::no))
// Different ports are considered different channels
&& (this_url.port() == other_url.port())
// Removing potential trailing '/'
&& (util::rstrip(this_url.path_without_token(Decode::no), '/')
== util::rstrip(other_url.path_without_token(Decode::no), '/'));
}
util::flat_set<std::string> Channel::urls(bool with_credential) const
auto Channel::contains_equivalent(const Channel& other) const -> bool
{
return url_equivalent_with(other) && util::set_is_superset_of(platforms(), other.platforms());
}
auto Channel::urls(bool with_credential) const -> util::flat_set<std::string>
{
if (!url().package().empty())
{
@ -102,8 +139,8 @@ namespace mamba
return out;
}
util::flat_set<std::pair<std::string, std::string>>
Channel::platform_urls(bool with_credential) const
auto Channel::platform_urls(bool with_credential) const
-> util::flat_set<std::pair<std::string, std::string>>
{
if (!url().package().empty())
{
@ -118,7 +155,7 @@ namespace mamba
return out;
}
std::string Channel::platform_url(std::string_view platform, bool with_credential) const
auto Channel::platform_url(std::string_view platform, bool with_credential) const -> std::string
{
auto cred = with_credential ? specs::CondaURL::Credentials::Show
: specs::CondaURL::Credentials::Remove;
@ -256,9 +293,25 @@ namespace mamba
return uri.pretty_str();
}
auto resolve_path_location(std::string location, Channel::ResolveParams params) -> std::string
{
if (util::url_has_scheme(location))
{
return location;
}
const auto home = util::path_to_posix(std::string(params.home_dir));
auto path = fs::u8path(util::expand_home(location, home));
if (path.is_relative())
{
path = (fs::u8path(params.current_working_dir) / std::move(path)).lexically_normal();
}
return util::abs_path_to_url(std::move(path).string());
}
auto resolve_path(specs::ChannelSpec&& spec, Channel::ResolveParams params) -> Channel
{
auto uri = specs::CondaURL::parse(util::path_or_url_to_url(spec.location()));
auto uri = specs::CondaURL::parse(resolve_path_location(spec.clear_location(), params));
auto display_name = resolve_path_name(uri, params);
auto platforms = Channel::ResolveParams::platform_list{};
if (spec.type() == specs::ChannelSpec::Type::Path)
@ -266,7 +319,7 @@ namespace mamba
platforms = make_platforms(spec.clear_platform_filters(), params.platforms);
}
return Channel(std::move(uri), std::move(display_name), std::move(platforms));
return { std::move(uri), std::move(display_name), std::move(platforms) };
}
auto resolve_url_name(const specs::CondaURL& url, Channel::ResolveParams params) -> std::string
@ -300,68 +353,110 @@ namespace mamba
auto url = specs::CondaURL::parse(spec.location());
auto display_name = resolve_url_name(url, params);
set_fallback_credential_from_db(url, params.auth_db);
set_fallback_credential_from_db(url, params.authentication_db);
auto platforms = Channel::ResolveParams::platform_list{};
if (spec.type() == specs::ChannelSpec::Type::URL)
{
platforms = make_platforms(spec.clear_platform_filters(), params.platforms);
}
return Channel(std::move(url), std::move(display_name), std::move(platforms));
return { std::move(url), std::move(display_name), std::move(platforms) };
}
auto resolve_name(specs::ChannelSpec&& spec, Channel::ResolveParams params) -> Channel
auto resolve_name_in_custom_channel(
specs::ChannelSpec&& spec,
Channel::ResolveParams params,
const Channel& match
) -> Channel
{
std::string name = spec.clear_location();
auto url = match.url();
// we can have a channel like
// testchannel: https://server.com/private/testchannel
// where `name == private/testchannel` and we need to join the remaining label part
// of the channel (e.g. -c testchannel/mylabel/xyz)
// needs to result in `name = private/testchannel/mylabel/xyz`
std::string combined_name = util::concat_dedup_splits(
util::rstrip(url.path(), '/'),
util::lstrip(spec.location(), '/'),
'/'
);
url.set_path(combined_name);
const auto& custom_chans = params.custom_channels;
if (auto it = custom_chans.find_weaken(name); it != custom_chans.cend())
set_fallback_credential_from_db(url, params.authentication_db);
return {
/* url= */ std::move(url),
/* display_name= */ spec.clear_location(),
/* platforms= */ make_platforms(spec.clear_platform_filters(), params.platforms),
};
}
auto resolve_name_in_custom_mulitchannels(
const specs::ChannelSpec& spec,
Channel::ResolveParams params,
const Channel::channel_list& matches
) -> Channel::channel_list
{
auto out = Channel::channel_list();
out.reserve(matches.size());
for (const auto& chan : matches)
{
auto url = it->second.url();
// we can have a channel like
// testchannel: https://server.com/private/testchannel
// where `name == private/testchannel` and we need to join the remaining label part
// of the channel (e.g. -c testchannel/mylabel/xyz)
// needs to result in `name = private/testchannel/mylabel/xyz`
std::string combined_name = util::concat_dedup_splits(
util::rstrip(url.path(), '/'),
util::lstrip(name, '/'),
'/'
);
url.set_path(combined_name);
set_fallback_credential_from_db(url, params.auth_db);
return Channel(
auto url = chan.url();
set_fallback_credential_from_db(url, params.authentication_db);
out.emplace_back(
/* url= */ std::move(url),
/* display_name= */ std::move(name),
/* platforms= */ make_platforms(spec.clear_platform_filters(), params.platforms)
/* display_name= */ chan.display_name(), // Not using multi_channel name
/* platforms= */ make_platforms(spec.platform_filters(), params.platforms)
);
}
return out;
}
auto resolve_name_from_alias(specs::ChannelSpec&& spec, Channel::ResolveParams params)
-> Channel
{
auto url = params.channel_alias;
url.append_path(name);
set_fallback_credential_from_db(url, params.auth_db);
return Channel(
url.append_path(spec.location());
set_fallback_credential_from_db(url, params.authentication_db);
return {
/* url= */ std::move(url),
/* display_name= */ name,
/* platforms= */ make_platforms(spec.clear_platform_filters(), params.platforms)
);
/* display_name= */ spec.clear_location(),
/* platforms= */ make_platforms(spec.clear_platform_filters(), params.platforms),
};
}
auto resolve_name(specs::ChannelSpec&& spec, Channel::ResolveParams params)
-> Channel::channel_list
{
if (auto it = params.custom_channels.find_weaken(spec.location());
it != params.custom_channels.cend())
{
return { resolve_name_in_custom_channel(std::move(spec), params, it->second) };
}
if (auto it = params.custom_multichannels.find(spec.location());
it != params.custom_multichannels.end())
{
return resolve_name_in_custom_mulitchannels(spec, params, it->second);
}
return { resolve_name_from_alias(std::move(spec), params) };
}
}
auto Channel::resolve(specs::ChannelSpec spec, ResolveParams params) -> Channel
auto Channel::resolve(specs::ChannelSpec spec, ResolveParams params) -> channel_list
{
switch (spec.type())
{
case specs::ChannelSpec::Type::PackagePath:
case specs::ChannelSpec::Type::Path:
{
return resolve_path(std::move(spec), params);
return { resolve_path(std::move(spec), params) };
}
case specs::ChannelSpec::Type::PackageURL:
case specs::ChannelSpec::Type::URL:
{
return resolve_url(std::move(spec), params);
return { resolve_url(std::move(spec), params) };
}
case specs::ChannelSpec::Type::Name:
{
@ -369,69 +464,41 @@ namespace mamba
}
case specs::ChannelSpec::Type::Unknown:
{
return Channel(specs::CondaURL{}, spec.clear_location());
return { { specs::CondaURL{}, spec.clear_location() } };
}
}
throw std::invalid_argument("Invalid ChannelSpec::Type");
}
const Channel& ChannelContext::make_channel(const std::string& value)
auto ChannelContext::params() -> Channel::ResolveParams
{
if (const auto it = m_channel_cache.find(value); it != m_channel_cache.end())
return {
/* .platforms= */ m_platforms,
/* .channel_alias= */ m_channel_alias,
/* .custom_channels= */ m_custom_channels,
/* .custom_multichannels= */ m_custom_multichannels,
/* .authentication_db= */ m_context.authentication_info(),
/* .home_dir= */ m_home_dir,
/* .current_working_dir= */ m_current_working_dir,
};
}
auto ChannelContext::make_channel(std::string_view name) -> channel_list
{
if (const auto it = m_channel_cache.find(std::string(name)); it != m_channel_cache.end())
{
return it->second;
}
auto spec = specs::ChannelSpec::parse(value);
auto params = Channel::ResolveParams{
/* .platforms */ m_platforms,
/* .channel_alias */ m_channel_alias,
/* .custom_channels */ m_custom_channels,
/* .auth_db */ m_context.authentication_info(),
};
auto [it, inserted] = m_channel_cache.emplace(value, Channel::resolve(std::move(spec), params));
auto [it, inserted] = m_channel_cache.emplace(
name,
Channel::resolve(specs::ChannelSpec::parse(name), params())
);
assert(inserted);
return it->second;
return { it->second };
}
auto ChannelContext::get_channels(const std::vector<std::string>& channel_names) -> channel_list
{
auto added = std::unordered_set<Channel>();
auto result = channel_list();
for (auto name : channel_names)
{
auto spec = specs::ChannelSpec::parse(name);
const auto& multi_chan = get_custom_multichannels();
if (auto iter = multi_chan.find(spec.location()); iter != multi_chan.end())
{
for (const auto& chan : iter->second)
{
auto channel = chan;
if (!spec.platform_filters().empty())
{
channel.set_platforms(spec.platform_filters());
}
if (added.insert(channel).second)
{
result.push_back(std::move(channel));
}
}
}
else
{
auto channel = make_channel(name);
if (added.insert(channel).second)
{
result.push_back(std::move(channel));
}
}
}
return result;
}
const specs::CondaURL& ChannelContext::get_channel_alias() const
auto ChannelContext::get_channel_alias() const -> const specs::CondaURL&
{
return m_channel_alias;
}
@ -449,9 +516,13 @@ namespace mamba
ChannelContext::ChannelContext(Context& context)
: m_context(context)
, m_channel_alias(specs::CondaURL::parse(util::path_or_url_to_url(m_context.channel_alias)))
, m_home_dir(util::user_home_dir())
, m_current_working_dir(fs::current_path())
{
m_platforms = [](const auto& plats)
{ return platform_list(plats.cbegin(), plats.cend()); }(m_context.platforms());
{
const auto& plats = m_context.platforms();
m_platforms = Channel::ResolveParams::platform_list(plats.cbegin(), plats.cend());
}
init_custom_channels();
}
@ -461,9 +532,10 @@ namespace mamba
{
for (const auto& [name, location] : m_context.custom_channels)
{
auto channel = make_channel(location);
channel.set_display_name(name);
m_custom_channels.emplace(name, std::move(channel));
auto channels = make_channel(location);
assert(channels.size() == 1);
channels.front().set_display_name(name);
m_custom_channels.emplace(name, std::move(channels.front()));
}
for (const auto& [multi_name, location_list] : m_context.custom_multichannels)
@ -472,7 +544,10 @@ namespace mamba
channels.reserve(location_list.size());
for (auto& location : location_list)
{
channels.push_back(make_channel(location));
for (auto& chan : make_channel(location))
{
channels.push_back(std::move(chan));
}
}
m_custom_multichannels.emplace(multi_name, std::move(channels));
}

View File

@ -93,9 +93,11 @@ namespace mamba
);
}
const auto& parsed_channel = channel_context.make_channel(spec_str);
auto channels = channel_context.make_channel(spec_str);
if (auto pkg = parsed_channel.url().package(); !pkg.empty())
// TODO we are not handling the custom_multichannel case where `channels` can have
// more than one element.
if (auto pkg = channels.front().url().package(); !pkg.empty())
{
auto dist = parse_legacy_dist(pkg);
@ -103,9 +105,9 @@ namespace mamba
version = dist[1];
build_string = dist[2];
channel = parsed_channel.display_name();
channel = channels.front().display_name();
// TODO how to handle this with multiple platforms?
if (const auto& plats = parsed_channel.platforms(); !plats.empty())
if (const auto& plats = channels.front().platforms(); !plats.empty())
{
subdir = plats.front();
}

View File

@ -78,7 +78,9 @@ namespace mamba
// FIXME: only do this for micromamba for now
if (channel_context.context().command_params.is_micromamba)
{
m_url = channel_context.make_channel(pkg_info.url).urls(true)[0];
auto channels = channel_context.make_channel(pkg_info.url);
assert(channels.size() == 1); // A URL can only resolve to one channel
m_url = channels.front().urls(true)[0];
}
else
{

View File

@ -140,63 +140,37 @@ namespace mamba
namespace
{
bool
channel_match(ChannelContext& channel_context, const Channel& chan, const Channel& needle)
enum struct ChannelMatch
{
if (chan.base_url() == needle.base_url())
{
return true;
}
None,
ChannelOnly,
ChannelAndSubdir,
};
auto& custom_multichannels = channel_context.context().custom_multichannels;
auto x = custom_multichannels.find(needle.display_name());
if (x != custom_multichannels.end())
auto channel_match(
const std::vector<Channel>& repo_channels,
const std::vector<Channel>& candidate_channels
) -> ChannelMatch
{
// More than one element means the channel spec was a custom_multi_channel,
// which should never happen for the repo
for (const auto& repo_chan : repo_channels)
{
for (auto el : (x->second))
// More than one element means the channel spec was a custom_multi_channel.
// We need to add any repo that matches.
for (const auto& cand_chan : candidate_channels)
{
const Channel& inner = channel_context.make_channel(el);
if (chan.base_url() == inner.base_url())
if (repo_chan.url_equivalent_with(cand_chan))
{
return true;
if (util::set_is_subset_of(repo_chan.platforms(), cand_chan.platforms()))
{
return ChannelMatch::ChannelAndSubdir;
}
return ChannelMatch::ChannelOnly;
}
}
}
return false;
}
bool subdir_match(std::string candidate_repo_url, std::string needle_spec)
{
// Example candidate_repo_url: https://.../conda-forge/linux-64
// example needle_spec: conda-forge/osx-64::xtensor
std::string needle_channel = util::split(needle_spec, ":", 1)[0];
if (!util::contains(needle_channel, "/"))
{
// Subdir not specified, so any subdir is fine
return true;
}
std::string needle_subdir = util::rsplit(needle_channel, "/", 1)[1];
auto known_platforms = specs::known_platform_names();
if (std::find(known_platforms.begin(), known_platforms.end(), needle_subdir)
== known_platforms.end())
{
// Not a known subdir. This can happen for specs like pkgs/main.
// so any subdir is fine
return true;
}
std::string candidate_repo_subdir = util::rsplit(candidate_repo_url, "/", 1)[1];
if (candidate_repo_subdir == needle_subdir)
{
return true;
}
throw std::runtime_error(fmt::format(
"The package \"{}\" is not available for the specified platform",
needle_spec
));
return ChannelMatch::None;
}
/**
@ -223,18 +197,8 @@ namespace mamba
ms.conda_build_form().c_str()
);
const auto& multi_chan = channel_context.get_custom_multichannels();
auto channels = std::vector<Channel>();
if (auto iter = multi_chan.find(ms.channel); iter != multi_chan.end())
{
channels.insert(channels.end(), iter->second.cbegin(), iter->second.cend());
}
else
{
channels.push_back(channel_context.make_channel(ms.channel));
}
solv::ObjQueue selected_pkgs = {};
bool found_partial_matches = false;
pool.for_each_whatprovides(
match,
[&](solv::ObjSolvableViewConst s)
@ -242,21 +206,46 @@ namespace mamba
// TODO this does not work with s.url(), we need to proper channel class
// to properly manage this.
auto repo = solv::ObjRepoView(*s.raw()->repo);
// TODO make_channel should disapear avoiding conflict here
auto const url = std::string(repo.url());
for (auto const& c : channels)
// TODO Replace MatchSpec channel and subdir with a ChannelSpec
const auto chan_spec = [&]() -> std::string
{
if (channel_match(channel_context, channel_context.make_channel(url), c))
if (ms.subdir.empty())
{
return ms.channel;
}
return util::concat(ms.channel, '[', ms.subdir, ']');
}();
const auto match = channel_match(
channel_context.make_channel(repo.url()),
channel_context.make_channel(chan_spec)
);
switch (match)
{
case (ChannelMatch::ChannelAndSubdir):
{
selected_pkgs.push_back(s.id());
break;
}
case (ChannelMatch::ChannelOnly):
{
found_partial_matches = true;
break;
}
case (ChannelMatch::None):
{
if (subdir_match(url, ms.spec))
{
selected_pkgs.push_back(s.id());
}
}
}
}
);
if (selected_pkgs.empty() && found_partial_matches)
{
throw std::runtime_error(fmt::format(
R"(The package "{}" is not available for the specified platform)",
ms.spec
));
}
const solv::StringId repr_id = pool.add_string(repr);
// FRAGILE This get deleted when calling ``pool_createwhatprovides`` so care
// must be taken to do it before

View File

@ -164,8 +164,12 @@ namespace mamba
// and conda-meta json files with channel names while mamba expects
// PackageInfo channels to be platform urls. This fixes the issue described
// in https://github.com/mamba-org/mamba/issues/2665
const Channel& channel = m_channel_context.make_channel(prec.channel);
prec.channel = channel.platform_url(prec.subdir);
auto channels = m_channel_context.make_channel(prec.channel);
// If someone wrote multichannel names in repodata_record, we don't know which one is the
// correct URL. This is must never happen!
assert(channels.size() == 1);
prec.channel = channels.front().platform_url(prec.subdir);
m_package_records.insert({ prec.name, std::move(prec) });
}
} // namespace mamba

View File

@ -107,8 +107,19 @@ namespace mamba
}
MatchSpec modified_spec(ms);
const Channel& chan = m_pool.channel_context().make_channel(std::string(solvable->channel()));
modified_spec.channel = chan.display_name();
{
auto channels = m_pool.channel_context().make_channel(std::string(solvable->channel()));
if (channels.size() == 1)
{
modified_spec.channel = channels.front().display_name();
}
else
{
// If there is more than one, it's a custom_multi_channel name.
// This should never happen.
modified_spec.channel = solvable->channel();
}
}
modified_spec.version = solvable->version();
modified_spec.build_string = solvable->build_string();
@ -146,7 +157,7 @@ namespace mamba
// moment
}
::Id const job_id = m_pool.matchspec2id(ms);
const ::Id job_id = m_pool.matchspec2id(ms);
// This is checking if SOLVER_ERASE and SOLVER_INSTALL are set
// which are the flags for SOLVER_UPDATE
@ -224,7 +235,7 @@ namespace mamba
// Add dummy solvable with a constraint on the pin (not installed if not present)
auto [cons_solv_id, cons_solv] = installed->add_solvable();
// TODO set some "pin" key on the solvable so that we can retrieve it during error messages
std::string const cons_solv_name = fmt::format("pin-{}", m_pinned_specs.size());
const std::string cons_solv_name = fmt::format("pin-{}", m_pinned_specs.size());
cons_solv.set_name(cons_solv_name);
cons_solv.set_version("1");
cons_solv.add_constraints(solv::ObjQueue{ m_pool.matchspec2id(pin_ms) });

View File

@ -378,11 +378,16 @@ namespace mamba
bool check_zst(ChannelContext& channel_context, const Channel& channel)
{
// TODO the list of channels with zst should really be computed only once in
// the ChannelContext
for (const auto& c : channel_context.context().repodata_has_zst)
{
if (channel_context.make_channel(c).base_url() == channel.base_url())
for (const auto& chan : channel_context.make_channel(c))
{
return true;
if (chan.contains_equivalent(channel))
{
return true;
}
}
}
return false;

View File

@ -1231,8 +1231,17 @@ namespace mamba
}
else
{
const Channel& chan = m_pool.channel_context().make_channel(str);
chan_name = chan.display_name();
auto channels = m_pool.channel_context().make_channel(str);
if (channels.size() == 1)
{
chan_name = channels.front().display_name();
}
else
{
// If there is more than on, it's a custom_multi_channel name
// This should never happen
chan_name = str;
}
}
}
else

View File

@ -89,10 +89,14 @@ namespace mamba
CHECK_EQ(it->second.display_name(), "pkgs/main");
std::string value = "conda-forge";
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("https://mydomain.com/channels/conda-forge"));
CHECK_EQ(c.display_name(), "conda-forge");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
channels.front().url(),
CondaURL::parse("https://mydomain.com/channels/conda-forge")
);
CHECK_EQ(channels.front().display_name(), "conda-forge");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
ctx.channel_alias = "https://conda.anaconda.org";
}
@ -108,8 +112,9 @@ namespace mamba
ChannelContext channel_context{ ctx };
auto base = std::string("https://ali.as/prefix-and-more/");
auto& chan = channel_context.make_channel(base);
CHECK_EQ(chan.urls(), UrlSet{ base + platform, base + "noarch" });
const auto channels = channel_context.make_channel(base);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(channels.front().urls(), UrlSet{ base + platform, base + "noarch" });
ctx.channel_alias = "https://conda.anaconda.org";
ctx.custom_channels.clear();
@ -133,28 +138,33 @@ namespace mamba
{
std::string value = "test_channel";
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("file:///tmp/test_channel"));
CHECK_EQ(c.display_name(), "test_channel");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(channels.front().url(), CondaURL::parse("file:///tmp/test_channel"));
CHECK_EQ(channels.front().display_name(), "test_channel");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
const UrlSet exp_urls({
std::string("file:///tmp/test_channel/") + platform,
"file:///tmp/test_channel/noarch",
});
CHECK_EQ(c.urls(), exp_urls);
CHECK_EQ(channels.front().urls(), exp_urls);
}
{
std::string value = "some_channel";
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("https://conda.mydomain.xyz/some_channel"));
CHECK_EQ(c.display_name(), "some_channel");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
channels.front().url(),
CondaURL::parse("https://conda.mydomain.xyz/some_channel")
);
CHECK_EQ(channels.front().display_name(), "some_channel");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
const UrlSet exp_urls({
std::string("https://conda.mydomain.xyz/some_channel/") + platform,
"https://conda.mydomain.xyz/some_channel/noarch",
});
CHECK_EQ(c.urls(), exp_urls);
CHECK_EQ(channels.front().urls(), exp_urls);
}
ctx.channel_alias = "https://conda.anaconda.org";
@ -179,7 +189,7 @@ namespace mamba
ChannelContext channel_context{ ctx };
auto x = channel_context.get_channels({ "xtest" });
auto x = channel_context.make_channel("xtest");
CHECK_EQ(x.size(), 3);
auto c1 = x[0];
@ -196,7 +206,7 @@ namespace mamba
"https://otherdomain.com/snakepit/noarch",
});
auto y = channel_context.get_channels({ "ytest" });
auto y = channel_context.make_channel("ytest");
auto y3 = y[2];
CHECK_EQ(y3.urls(), exp_urlsy3);
@ -223,7 +233,7 @@ namespace mamba
ChannelContext channel_context{ ctx };
auto x = channel_context.get_channels({ "everything" });
auto x = channel_context.make_channel("everything");
CHECK_EQ(x.size(), 3);
auto c1 = x[0];
@ -266,7 +276,7 @@ namespace mamba
ChannelContext channel_context{ ctx };
auto x = channel_context.get_channels({ "defaults" });
auto x = channel_context.make_channel("defaults");
#if !defined(_WIN32)
const Channel c1 = x[0];
const Channel c2 = x[1];
@ -301,7 +311,7 @@ namespace mamba
ctx.custom_multichannels["defaults"] = ctx.default_channels;
ChannelContext channel_context{ ctx };
auto x = channel_context.get_channels({ "defaults" });
auto x = channel_context.make_channel("defaults");
const Channel c1 = x[0];
const Channel c2 = x[1];
@ -338,7 +348,7 @@ namespace mamba
CHECK_EQ(channel_context.get_custom_multichannels().at("local").size(), 3);
auto local_channels = channel_context.get_channels({ "local" });
auto local_channels = channel_context.make_channel("local");
CHECK_EQ(local_channels.size(), 3);
}
@ -353,50 +363,56 @@ namespace mamba
{
std::string value = "test_channel";
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("https://server.com/private/channels/test_channel"));
CHECK_EQ(c.display_name(), "test_channel");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
channels.front().url(),
CondaURL::parse("https://server.com/private/channels/test_channel")
);
CHECK_EQ(channels.front().display_name(), "test_channel");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
const UrlSet exp_urls({
std::string("https://server.com/private/channels/test_channel/") + platform,
"https://server.com/private/channels/test_channel/noarch",
});
CHECK_EQ(c.urls(), exp_urls);
CHECK_EQ(channels.front().urls(), exp_urls);
}
{
std::string value = "test_channel/mylabel/xyz";
const Channel& c = channel_context.make_channel(value);
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
c.url(),
channels.front().url(),
CondaURL::parse("https://server.com/private/channels/test_channel/mylabel/xyz")
);
CHECK_EQ(c.display_name(), "test_channel/mylabel/xyz");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
CHECK_EQ(channels.front().display_name(), "test_channel/mylabel/xyz");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
const UrlSet exp_urls({
std::string("https://server.com/private/channels/test_channel/mylabel/xyz/")
+ platform,
"https://server.com/private/channels/test_channel/mylabel/xyz/noarch",
});
CHECK_EQ(c.urls(), exp_urls);
CHECK_EQ(channels.front().urls(), exp_urls);
}
{
// https://github.com/mamba-org/mamba/issues/2553
std::string value = "random/test_channel/pkg";
const Channel& c = channel_context.make_channel(value);
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
c.url(),
channels.front().url(),
CondaURL::parse("https://server.com/random/channels/random/test_channel/pkg")
);
CHECK_EQ(c.display_name(), "random/test_channel/pkg");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
CHECK_EQ(channels.front().display_name(), "random/test_channel/pkg");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
const UrlSet exp_urls({
std::string("https://server.com/random/channels/random/test_channel/pkg/")
+ platform,
"https://server.com/random/channels/random/test_channel/pkg/noarch",
});
CHECK_EQ(c.urls(), exp_urls);
CHECK_EQ(channels.front().urls(), exp_urls);
}
ctx.channel_alias = "https://conda.anaconda.org";
@ -410,69 +426,82 @@ namespace mamba
{
std::string value = "https://repo.mamba.pm/conda-forge";
ChannelContext channel_context{ mambatests::context() };
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("https://repo.mamba.pm/conda-forge"));
CHECK_EQ(c.display_name(), "https://repo.mamba.pm/conda-forge");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels = channel_context.make_channel(value);
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(channels.front().url(), CondaURL::parse("https://repo.mamba.pm/conda-forge"));
CHECK_EQ(channels.front().display_name(), "https://repo.mamba.pm/conda-forge");
CHECK_EQ(channels.front().platforms(), PlatformSet({ platform, "noarch" }));
}
TEST_CASE("make_channel")
TEST_CASE("make_chan")
{
std::string value = "conda-forge";
std::string value1 = "conda-forge";
ChannelContext channel_context{ mambatests::context() };
const Channel& c = channel_context.make_channel(value);
CHECK_EQ(c.url(), CondaURL::parse("https://conda.anaconda.org/conda-forge"));
CHECK_EQ(c.display_name(), "conda-forge");
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels1 = channel_context.make_channel(value1);
REQUIRE_EQ(channels1.size(), 1);
CHECK_EQ(channels1.front().url(), CondaURL::parse("https://conda.anaconda.org/conda-forge"));
CHECK_EQ(channels1.front().display_name(), "conda-forge");
CHECK_EQ(channels1.front().platforms(), PlatformSet({ platform, "noarch" }));
std::string value2 = "https://repo.anaconda.com/pkgs/main[" + platform + "]";
const Channel& c2 = channel_context.make_channel(value2);
CHECK_EQ(c2.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
CHECK_EQ(c2.display_name(), "https://repo.anaconda.com/pkgs/main");
CHECK_EQ(c2.platforms(), PlatformSet({ platform }));
const auto channels2 = channel_context.make_channel(value2);
REQUIRE_EQ(channels2.size(), 1);
CHECK_EQ(channels2.front().url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
CHECK_EQ(channels2.front().display_name(), "https://repo.anaconda.com/pkgs/main");
CHECK_EQ(channels2.front().platforms(), PlatformSet({ platform }));
std::string value3 = "https://conda.anaconda.org/conda-forge[" + platform + "]";
const Channel& c3 = channel_context.make_channel(value3);
CHECK_EQ(c3.url(), c.url());
CHECK_EQ(c3.display_name(), c.display_name());
CHECK_EQ(c3.platforms(), PlatformSet({ platform }));
const auto channels3 = channel_context.make_channel(value3);
REQUIRE_EQ(channels3.size(), 1);
CHECK_EQ(channels3.front().url(), channels1.front().url());
CHECK_EQ(channels3.front().display_name(), channels1.front().display_name());
CHECK_EQ(channels3.front().platforms(), PlatformSet({ platform }));
std::string value4 = "/home/mamba/test/channel_b";
const Channel& c4 = channel_context.make_channel(value4);
CHECK_EQ(c4.url(), CondaURL::parse(util::path_to_url(value4)));
CHECK_EQ(c4.display_name(), c4.url().pretty_str());
CHECK_EQ(c4.platforms(), PlatformSet({ platform, "noarch" }));
const auto channels4 = channel_context.make_channel(value4);
REQUIRE_EQ(channels4.size(), 1);
CHECK_EQ(channels4.front().url(), CondaURL::parse(util::path_to_url(value4)));
CHECK_EQ(channels4.front().display_name(), channels4.front().url().pretty_str());
CHECK_EQ(channels4.front().platforms(), PlatformSet({ platform, "noarch" }));
std::string path5 = "/home/mamba/test/channel_b";
std::string value5 = util::concat(path5, '[', platform, ']');
const Channel& c5 = channel_context.make_channel(value5);
CHECK_EQ(c5.url(), CondaURL::parse(util::path_to_url(path5)));
CHECK_EQ(c5.display_name(), c5.url().pretty_str());
CHECK_EQ(c5.platforms(), PlatformSet({ platform }));
const auto channels5 = channel_context.make_channel(value5);
REQUIRE_EQ(channels5.size(), 1);
CHECK_EQ(channels5.front().url(), CondaURL::parse(util::path_to_url(path5)));
CHECK_EQ(channels5.front().display_name(), channels5.front().url().pretty_str());
CHECK_EQ(channels5.front().platforms(), PlatformSet({ platform }));
std::string value6a = "http://localhost:8000/conda-forge[noarch]";
const Channel& c6a = channel_context.make_channel(value6a);
CHECK_EQ(c6a.urls(false), UrlSet({ "http://localhost:8000/conda-forge/noarch" }));
const auto channels6a = channel_context.make_channel(value6a);
REQUIRE_EQ(channels6a.size(), 1);
CHECK_EQ(
channels6a.front().urls(false),
UrlSet({ "http://localhost:8000/conda-forge/noarch" })
);
std::string value6b = "http://localhost:8000/conda_mirror/conda-forge[noarch]";
const Channel& c6b = channel_context.make_channel(value6b);
const auto channels6b = channel_context.make_channel(value6b);
REQUIRE_EQ(channels6b.size(), 1);
CHECK_EQ(
c6b.urls(false),
channels6b.front().urls(false),
UrlSet({ "http://localhost:8000/conda_mirror/conda-forge/noarch" })
);
std::string value7 = "conda-forge[noarch,arbitrary]";
const Channel& c7 = channel_context.make_channel(value7);
CHECK_EQ(c7.platforms(), PlatformSet({ "noarch", "arbitrary" }));
const auto channels7 = channel_context.make_channel(value7);
REQUIRE_EQ(channels7.size(), 1);
CHECK_EQ(channels7.front().platforms(), PlatformSet({ "noarch", "arbitrary" }));
}
TEST_CASE("urls")
{
std::string value = "https://conda.anaconda.org/conda-forge[noarch,win-64,arbitrary]";
std::string value1 = "https://conda.anaconda.org/conda-forge[noarch,win-64,arbitrary]";
ChannelContext channel_context{ mambatests::context() };
const Channel& c = channel_context.make_channel(value);
const auto channels1 = channel_context.make_channel(value1);
REQUIRE_EQ(channels1.size(), 1);
CHECK_EQ(
c.urls(),
channels1.front().urls(),
UrlSet({
"https://conda.anaconda.org/conda-forge/arbitrary",
"https://conda.anaconda.org/conda-forge/noarch",
@ -480,9 +509,11 @@ namespace mamba
})
);
const Channel& c1 = channel_context.make_channel("https://conda.anaconda.org/conda-forge");
const auto channels2 = channel_context.make_channel("https://conda.anaconda.org/conda-forge"
);
REQUIRE_EQ(channels2.size(), 1);
CHECK_EQ(
c1.urls(),
channels2.front().urls(),
UrlSet({
"https://conda.anaconda.org/conda-forge/" + platform,
"https://conda.anaconda.org/conda-forge/noarch",
@ -500,13 +531,17 @@ namespace mamba
ChannelContext channel_context{ ctx };
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
CHECK_EQ(chan.url().token(), "my-12345-token");
const auto channels = channel_context.make_channel("conda-forge[noarch]");
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(channels.front().url().token(), "my-12345-token");
CHECK_EQ(
chan.urls(true),
channels.front().urls(true),
UrlSet({ "https://conda.anaconda.org/t/my-12345-token/conda-forge/noarch" })
);
CHECK_EQ(chan.urls(false), UrlSet({ "https://conda.anaconda.org/conda-forge/noarch" }));
CHECK_EQ(
channels.front().urls(false),
UrlSet({ "https://conda.anaconda.org/conda-forge/noarch" })
);
}
TEST_CASE("add_multiple_tokens")
@ -520,8 +555,9 @@ namespace mamba
ChannelContext channel_context{ ctx };
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
CHECK_EQ(chan.url().token(), "channel-token");
const auto channels = channel_context.make_channel("conda-forge[noarch]");
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(channels.front().url().token(), "channel-token");
}
TEST_CASE("fix_win_file_path")
@ -529,17 +565,19 @@ namespace mamba
ChannelContext channel_context{ mambatests::context() };
if (platform == "win-64")
{
const Channel& c = channel_context.make_channel("C:\\test\\channel");
const auto channels = channel_context.make_channel(R"(C:\test\channel)");
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
c.urls(false),
channels.front().urls(false),
UrlSet({ "file:///C:/test/channel/win-64", "file:///C:/test/channel/noarch" })
);
}
else
{
const Channel& c = channel_context.make_channel("/test/channel");
const auto channels = channel_context.make_channel("/test/channel");
REQUIRE_EQ(channels.size(), 1);
CHECK_EQ(
c.urls(false),
channels.front().urls(false),
UrlSet({ std::string("file:///test/channel/") + platform,
"file:///test/channel/noarch" })
);
@ -549,33 +587,42 @@ namespace mamba
TEST_CASE("trailing_slash")
{
ChannelContext channel_context{ mambatests::context() };
const Channel& c = channel_context.make_channel("http://localhost:8000/");
CHECK_EQ(c.platform_url("win-64", false), "http://localhost:8000/win-64");
CHECK_EQ(c.base_url(), "http://localhost:8000/");
const auto channels1 = channel_context.make_channel("http://localhost:8000/");
REQUIRE_EQ(channels1.size(), 1);
CHECK_EQ(channels1.front().platform_url("win-64", false), "http://localhost:8000/win-64");
CHECK_EQ(channels1.front().url().str(), "http://localhost:8000/");
const UrlSet expected_urls({ std::string("http://localhost:8000/") + platform,
"http://localhost:8000/noarch" });
CHECK_EQ(c.urls(true), expected_urls);
const Channel& c4 = channel_context.make_channel("http://localhost:8000");
CHECK_EQ(c4.platform_url("linux-64", false), "http://localhost:8000/linux-64");
const Channel& c2 = channel_context.make_channel("http://user:test@localhost:8000/");
CHECK_EQ(c2.platform_url("win-64", false), "http://localhost:8000/win-64");
CHECK_EQ(c2.platform_url("win-64", true), "http://user:test@localhost:8000/win-64");
const Channel& c3 = channel_context.make_channel(
"https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012"
);
CHECK_EQ(c3.platform_url("win-64", false), "https://localhost:8000/win-64");
CHECK_EQ(channels1.front().urls(true), expected_urls);
const auto channels4 = channel_context.make_channel("http://localhost:8000");
REQUIRE_EQ(channels4.size(), 1);
CHECK_EQ(channels4.front().platform_url("linux-64", false), "http://localhost:8000/linux-64");
const auto channels2 = channel_context.make_channel("http://user:test@localhost:8000/");
REQUIRE_EQ(channels2.size(), 1);
CHECK_EQ(channels2.front().platform_url("win-64", false), "http://localhost:8000/win-64");
CHECK_EQ(
c3.platform_url("win-64", true),
"https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012/win-64"
channels2.front().platform_url("win-64", true),
"http://user:test@localhost:8000/win-64"
);
const UrlSet expected_urls2({
const auto channels3 = channel_context.make_channel(
"https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012"
);
REQUIRE_EQ(channels3.size(), 1);
CHECK_EQ(channels3.front().platform_url("win-64", false), "https://localhost:8000/win-64");
CHECK_EQ(
channels3.front().platform_url("win-64", true),
"https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012/win-64"
);
const UrlSet expected_urls3({
std::string("https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012/")
+ platform,
"https://localhost:8000/t/xy-12345678-1234-1234-1234-123456789012/noarch",
});
CHECK_EQ(c3.urls(true), expected_urls2);
CHECK_EQ(channels3.front().urls(true), expected_urls3);
}
TEST_CASE("load_tokens")

View File

@ -330,14 +330,17 @@ namespace
auto load_channels(MPool& pool, MultiPackageCache& cache, std::vector<std::string>&& channels)
{
auto sub_dirs = std::vector<MSubdirData>();
for (const auto& chan : pool.channel_context().get_channels(channels))
for (const auto& location : channels)
{
for (auto& [platform, url] : chan.platform_urls(true))
for (const auto& chan : pool.channel_context().make_channel(location))
{
auto sub_dir = expected_value_or_throw(
MSubdirData::create(pool.channel_context(), chan, platform, url, cache)
);
sub_dirs.push_back(std::move(sub_dir));
for (auto& [platform, url] : chan.platform_urls(true))
{
auto sub_dir = expected_value_or_throw(
MSubdirData::create(pool.channel_context(), chan, platform, url, cache)
);
sub_dirs.push_back(std::move(sub_dir));
}
}
}

View File

@ -1147,47 +1147,8 @@ bind_submodule_impl(pybind11::module_ m)
py::arg("json_str")
);
pyChannel
.def(py::init(
[](const std::string& value) {
return const_cast<Channel*>(&mambapy::singletons.channel_context().make_channel(value)
);
}
))
.def_property_readonly("platforms", &Channel::platforms)
.def_property_readonly("canonical_name", &Channel::display_name)
.def("urls", &Channel::urls, py::arg("with_credentials") = true)
.def("platform_urls", &Channel::platform_urls, py::arg("with_credentials") = true)
.def("platform_url", &Channel::platform_url, py::arg("platform"), py::arg("with_credentials") = true)
.def(
"__repr__",
[](const Channel& c)
{
auto s = c.display_name();
s += "[";
bool first = true;
for (const auto& platform : c.platforms())
{
if (!first)
{
s += ",";
}
s += platform;
first = false;
}
s += "]";
return s;
}
);
m.def("clean", [](int flags) { return clean(mambapy::singletons.config(), flags); });
m.def(
"get_channels",
[](const std::vector<std::string>& channel_names)
{ return mambapy::singletons.channel_context().get_channels(channel_names); }
);
m.def(
"transmute",
+[](const fs::u8path& pkg_file, const fs::u8path& target, int compression_level, int compression_threads

View File

@ -144,11 +144,8 @@ set_env_command(CLI::App* com, Configuration& config)
for (const auto& record : records)
{
auto url = specs::CondaURL::parse(record.url);
url.clear_token();
url.clear_password();
url.clear_user();
std::cout << url.str();
using Credentials = typename specs::CondaURL::Credentials;
std::cout << specs::CondaURL::parse(record.url).str(Credentials::Hide);
if (no_md5 != 1)
{
std::cout << "#" << record.md5;
@ -176,7 +173,7 @@ set_env_command(CLI::App* com, Configuration& config)
continue;
}
const Channel& channel = channel_context.make_channel(v.channel);
auto chans = channel_context.make_channel(v.channel);
if (from_history)
{
@ -187,7 +184,10 @@ set_env_command(CLI::App* com, Configuration& config)
dependencies << "- ";
if (channel_subdir)
{
dependencies << channel.display_name() << "/" << v.subdir << "::";
dependencies
// If the size is not one, it's a custom mutli channel
<< ((chans.size() == 1) ? chans.front().display_name() : v.channel)
<< "/" << v.subdir << "::";
}
dependencies << v.name << "=" << v.version;
if (!no_build)
@ -201,7 +201,10 @@ set_env_command(CLI::App* com, Configuration& config)
dependencies << "\n";
}
channels.insert(channel.display_name());
for (auto const& chan : chans)
{
channels.insert(chan.display_name());
}
}
for (const auto& c : channels)