mirror of https://github.com/mamba-org/mamba.git
Resolve ChannelSpec into a Channel (#2899)
* Change URL custom_channel matcher * Cannot change location at once * Simpler channel_alias_location * Change URL channel_alias matcher * Refactor ChannelContext::from_url * Add Windows URL path encoding test * Simplify URL::set_path * Fix URL encoding parsing * Refactor platforms urls * Fix missing forward declaration * Fix Win file URL encoding * Split Channel url functions * Remove no-URL ctor from ChannelContext::from_name * Remove no-URL ctor from ChannelContext::make_simple_channel * Remove old Channel ctor * Remove channel_alias_location * Refactor Channel::base_url * Try not to use location mamba.py * Remove Channel comparison * Try Channel::platform_url * Refactor Channel::platform_urls to use platform_url * Remove build_url * Remove concat_scheme_url * Use spec in from_name * Tuple equality for URL * Add tuple_hash * Add std::hash<mamba::util::URL> * Make CondaURL hashable * Refactor make_channel * New algorithm for authetification map * Add specs::AutheticationDataBase * Fix typo authentification > authentication * Use AuthenticationDataBase * Fix AuthenticationDataBase * use AuthenticationDataBase::find_compatible * Use canonical_name * Add Credential check to URL * Simplify crendential check in channel * Refactor AuthDB credential setters * No Channel::location in tests * Remove Channel user scheme password * Document AuthenticationInfo * Remove Channel token * Remove Channel::auth * Remove Channel::package_name * More replacement name > canonical_name * Remove unused function * No credential from URLs * Remove unneeded header * Disable Channel::repo_checker This is disbled as TUF implementation is not well defined and this function is not used except in experimental conditions. * Apply auth info as needed * Simplify custom_channels instanciation * Simplify custom_multi_channels instanciation * Review improvements
This commit is contained in:
parent
f23e078f2c
commit
c7aba975b5
|
@ -138,6 +138,7 @@ set(LIBMAMBA_SOURCES
|
||||||
${LIBMAMBA_SOURCE_DIR}/core/compression.cpp
|
${LIBMAMBA_SOURCE_DIR}/core/compression.cpp
|
||||||
# Implementation of version and matching specs
|
# Implementation of version and matching specs
|
||||||
${LIBMAMBA_SOURCE_DIR}/specs/archive.cpp
|
${LIBMAMBA_SOURCE_DIR}/specs/archive.cpp
|
||||||
|
${LIBMAMBA_SOURCE_DIR}/specs/authentication_info.cpp
|
||||||
${LIBMAMBA_SOURCE_DIR}/specs/platform.cpp
|
${LIBMAMBA_SOURCE_DIR}/specs/platform.cpp
|
||||||
${LIBMAMBA_SOURCE_DIR}/specs/conda_url.cpp
|
${LIBMAMBA_SOURCE_DIR}/specs/conda_url.cpp
|
||||||
${LIBMAMBA_SOURCE_DIR}/specs/version.cpp
|
${LIBMAMBA_SOURCE_DIR}/specs/version.cpp
|
||||||
|
@ -227,10 +228,11 @@ set(LIBMAMBA_PUBLIC_HEADERS
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/util/iterator.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/iterator.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/util/string.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/string.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/util/path_manip.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/path_manip.hpp
|
||||||
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/tuple_hash.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/util/url_manip.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/url_manip.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/util/url.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/util/url.hpp
|
||||||
# Implementation of version and matching specs
|
# Implementation of version and matching specs
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/authentification_info.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/authentication_info.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/archive.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/archive.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/platform.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/platform.hpp
|
||||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/conda_url.hpp
|
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/conda_url.hpp
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
#define MAMBA_CORE_CHANNEL_HPP
|
#define MAMBA_CORE_CHANNEL_HPP
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -44,21 +42,14 @@ namespace mamba
|
||||||
|
|
||||||
~Channel();
|
~Channel();
|
||||||
|
|
||||||
std::string_view scheme() const;
|
|
||||||
const std::string& location() const;
|
const std::string& location() const;
|
||||||
const std::string& name() const;
|
const std::string& name() const;
|
||||||
const std::string& canonical_name() const;
|
const std::string& canonical_name() const;
|
||||||
const util::flat_set<std::string>& platforms() const;
|
const util::flat_set<std::string>& platforms() const;
|
||||||
std::optional<std::string> auth() const;
|
const specs::CondaURL& url() const;
|
||||||
std::optional<std::string> user() const;
|
|
||||||
std::optional<std::string> password() const;
|
|
||||||
std::optional<std::string> token() const;
|
|
||||||
std::optional<std::string> package_filename() const;
|
|
||||||
const validation::RepoChecker&
|
|
||||||
repo_checker(Context& context, MultiPackageCache& caches) const;
|
|
||||||
|
|
||||||
std::string base_url() const;
|
std::string base_url() const;
|
||||||
std::string platform_url(std::string platform, bool with_credential = true) const;
|
std::string platform_url(std::string_view platform, bool with_credential = true) const;
|
||||||
// The pairs consist of (platform,url)
|
// The pairs consist of (platform,url)
|
||||||
util::flat_set<std::pair<std::string, std::string>>
|
util::flat_set<std::pair<std::string, std::string>>
|
||||||
platform_urls(bool with_credential = true) const;
|
platform_urls(bool with_credential = true) const;
|
||||||
|
@ -66,18 +57,6 @@ namespace mamba
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Channel(
|
|
||||||
std::string_view scheme,
|
|
||||||
std::string location,
|
|
||||||
std::string name,
|
|
||||||
std::string canonical_name,
|
|
||||||
std::string_view user = {},
|
|
||||||
std::string_view password = {},
|
|
||||||
std::string_view token = {},
|
|
||||||
std::string_view package_filename = {},
|
|
||||||
util::flat_set<std::string> platforms = {}
|
|
||||||
);
|
|
||||||
|
|
||||||
Channel(
|
Channel(
|
||||||
specs::CondaURL url,
|
specs::CondaURL url,
|
||||||
std::string location,
|
std::string location,
|
||||||
|
@ -86,17 +65,12 @@ namespace mamba
|
||||||
util::flat_set<std::string> platforms = {}
|
util::flat_set<std::string> platforms = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
const specs::CondaURL& url() const;
|
|
||||||
|
|
||||||
specs::CondaURL m_url;
|
specs::CondaURL m_url;
|
||||||
std::string m_location;
|
std::string m_location;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_canonical_name;
|
std::string m_canonical_name;
|
||||||
util::flat_set<std::string> m_platforms;
|
util::flat_set<std::string> m_platforms;
|
||||||
|
|
||||||
// This is used to make sure that there is a unique repo for every channel
|
|
||||||
mutable std::unique_ptr<validation::RepoChecker> p_repo_checker;
|
|
||||||
|
|
||||||
// Note: as long as Channel is not a regular value-type and we want each
|
// Note: as long as Channel is not a regular value-type and we want each
|
||||||
// instance only possible to create through ChannelContext, we need
|
// instance only possible to create through ChannelContext, we need
|
||||||
// to have Channel's constructor only available to ChannelContext,
|
// to have Channel's constructor only available to ChannelContext,
|
||||||
|
@ -106,10 +80,6 @@ namespace mamba
|
||||||
friend class ChannelContext;
|
friend class ChannelContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const Channel& lhs, const Channel& rhs);
|
|
||||||
bool operator!=(const Channel& lhs, const Channel& rhs);
|
|
||||||
|
|
||||||
|
|
||||||
using ChannelCache = std::map<std::string, Channel>;
|
using ChannelCache = std::map<std::string, Channel>;
|
||||||
|
|
||||||
class ChannelContext
|
class ChannelContext
|
||||||
|
@ -160,8 +130,10 @@ namespace mamba
|
||||||
Channel from_any_path(specs::ChannelSpec&& spec);
|
Channel from_any_path(specs::ChannelSpec&& spec);
|
||||||
Channel from_package_path(specs::ChannelSpec&& spec);
|
Channel from_package_path(specs::ChannelSpec&& spec);
|
||||||
Channel from_path(specs::ChannelSpec&& spec);
|
Channel from_path(specs::ChannelSpec&& spec);
|
||||||
|
Channel from_any_url(specs::ChannelSpec&& spec);
|
||||||
|
Channel from_package_url(specs::ChannelSpec&& spec);
|
||||||
Channel from_url(specs::ChannelSpec&& spec);
|
Channel from_url(specs::ChannelSpec&& spec);
|
||||||
Channel from_name(const std::string& name);
|
Channel from_name(specs::ChannelSpec&& spec);
|
||||||
Channel from_value(const std::string& value);
|
Channel from_value(const std::string& value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "mamba/core/palette.hpp"
|
#include "mamba/core/palette.hpp"
|
||||||
#include "mamba/core/tasksync.hpp"
|
#include "mamba/core/tasksync.hpp"
|
||||||
#include "mamba/fs/filesystem.hpp"
|
#include "mamba/fs/filesystem.hpp"
|
||||||
#include "mamba/specs/authentification_info.hpp"
|
#include "mamba/specs/authentication_info.hpp"
|
||||||
#include "mamba/specs/platform.hpp"
|
#include "mamba/specs/platform.hpp"
|
||||||
#include "mamba/version.hpp"
|
#include "mamba/version.hpp"
|
||||||
|
|
||||||
|
@ -214,9 +214,8 @@ namespace mamba
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string channel_alias = "https://conda.anaconda.org";
|
std::string channel_alias = "https://conda.anaconda.org";
|
||||||
using authentication_info_map_t = std::map<std::string, specs::AuthenticationInfo>;
|
specs::AuthenticationDataBase& authentication_info();
|
||||||
authentication_info_map_t& authentication_info();
|
const specs::AuthenticationDataBase& authentication_info() const;
|
||||||
const authentication_info_map_t& authentication_info() const;
|
|
||||||
std::vector<fs::u8path> token_locations{ "~/.continuum/anaconda-client/tokens" };
|
std::vector<fs::u8path> token_locations{ "~/.continuum/anaconda-client/tokens" };
|
||||||
|
|
||||||
bool override_channels_enabled = true;
|
bool override_channels_enabled = true;
|
||||||
|
@ -253,7 +252,7 @@ namespace mamba
|
||||||
bool on_ci = false;
|
bool on_ci = false;
|
||||||
|
|
||||||
void load_authentication_info();
|
void load_authentication_info();
|
||||||
std::map<std::string, specs::AuthenticationInfo> m_authentication_info;
|
specs::AuthenticationDataBase m_authentication_info;
|
||||||
bool m_authentication_infos_loaded = false;
|
bool m_authentication_infos_loaded = false;
|
||||||
|
|
||||||
std::shared_ptr<Logger> logger;
|
std::shared_ptr<Logger> logger;
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "mamba/core/pool.hpp"
|
#include "mamba/core/pool.hpp"
|
||||||
#include "mamba/util/graph.hpp"
|
#include "mamba/util/graph.hpp"
|
||||||
|
|
||||||
|
typedef struct s_Solvable Solvable;
|
||||||
|
|
||||||
namespace mamba
|
namespace mamba
|
||||||
{
|
{
|
||||||
using GraphicsParams = Context::GraphicsParams;
|
using GraphicsParams = Context::GraphicsParams;
|
||||||
|
|
|
@ -331,13 +331,6 @@ namespace mamba
|
||||||
void split_package_extension(const std::string& file, std::string& name, std::string& extension);
|
void split_package_extension(const std::string& file, std::string& name, std::string& extension);
|
||||||
fs::u8path strip_package_extension(const std::string& file);
|
fs::u8path strip_package_extension(const std::string& file);
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline bool vector_is_prefix(const std::vector<T>& prefix, const std::vector<T>& vec)
|
|
||||||
{
|
|
||||||
return vec.size() >= prefix.size()
|
|
||||||
&& prefix.end() == std::mismatch(prefix.begin(), prefix.end(), vec.begin()).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::expected<std::string, mamba_error> encode_base64(std::string_view input);
|
tl::expected<std::string, mamba_error> encode_base64(std::string_view input);
|
||||||
tl::expected<std::string, mamba_error> decode_base64(std::string_view input);
|
tl::expected<std::string, mamba_error> decode_base64(std::string_view input);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright (c) 2023, 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.
|
||||||
|
|
||||||
|
#ifndef MAMBA_SPECS_AUTHENTICATION_INFO_HPP
|
||||||
|
#define MAMBA_SPECS_AUTHENTICATION_INFO_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace mamba::specs
|
||||||
|
{
|
||||||
|
class CondaURL;
|
||||||
|
|
||||||
|
/** User and password authetification set in the URL. */
|
||||||
|
struct BasicHTTPAuthentication
|
||||||
|
{
|
||||||
|
std::string user;
|
||||||
|
std::string password;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** HTTP Bearer token set in the request headers. */
|
||||||
|
struct BearerToken
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A Conda token set in the URL path. */
|
||||||
|
struct CondaToken
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
using AuthenticationInfo = std::variant<BasicHTTPAuthentication, BearerToken, CondaToken>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that holds the authentication info stored by users.
|
||||||
|
*
|
||||||
|
* Essentially a map, except that some keys can match mutliple queries.
|
||||||
|
* For instance "mamba.org/private" should be matched by queries "mamba.org/private",
|
||||||
|
* "mamba.org/private/channel", but not "mamba.org/public".
|
||||||
|
*
|
||||||
|
* A best effort is made to satifiy this with `xxx_compatible`.
|
||||||
|
*
|
||||||
|
* Future development of this class should aim to replace the map and keys with a
|
||||||
|
* `AuthenticationSpec`, that can decide whether or not a URL should benefit from such
|
||||||
|
* its authentication.
|
||||||
|
* Possibly, a string reprensentation such as "*.mamba.org/private/channel*" could be added
|
||||||
|
* to parse users intentions, rather than relying on the assumptions made here.
|
||||||
|
*/
|
||||||
|
class AuthenticationDataBase : private std::unordered_map<std::string, AuthenticationInfo>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Base = std::unordered_map<std::string, AuthenticationInfo>;
|
||||||
|
|
||||||
|
using typename Base::key_type;
|
||||||
|
using typename Base::mapped_type;
|
||||||
|
using typename Base::value_type;
|
||||||
|
using typename Base::size_type;
|
||||||
|
using typename Base::iterator;
|
||||||
|
using typename Base::const_iterator;
|
||||||
|
|
||||||
|
using Base::Base;
|
||||||
|
|
||||||
|
using Base::begin;
|
||||||
|
using Base::end;
|
||||||
|
using Base::cbegin;
|
||||||
|
using Base::cend;
|
||||||
|
|
||||||
|
using Base::empty;
|
||||||
|
using Base::size;
|
||||||
|
using Base::max_size;
|
||||||
|
|
||||||
|
using Base::clear;
|
||||||
|
using Base::insert;
|
||||||
|
using Base::insert_or_assign;
|
||||||
|
using Base::emplace;
|
||||||
|
using Base::emplace_hint;
|
||||||
|
using Base::try_emplace;
|
||||||
|
using Base::erase;
|
||||||
|
using Base::swap;
|
||||||
|
using Base::extract;
|
||||||
|
using Base::merge;
|
||||||
|
|
||||||
|
using Base::reserve;
|
||||||
|
|
||||||
|
using Base::at;
|
||||||
|
|
||||||
|
[[nodiscard]] auto at_compatible(const key_type& key) -> mapped_type&;
|
||||||
|
[[nodiscard]] auto at_compatible(const key_type& key) const -> const mapped_type&;
|
||||||
|
|
||||||
|
using Base::find;
|
||||||
|
|
||||||
|
auto find_compatible(const key_type& key) -> iterator;
|
||||||
|
auto find_compatible(const key_type& key) const -> const_iterator;
|
||||||
|
|
||||||
|
[[nodiscard]] auto contains(const key_type& key) const -> bool;
|
||||||
|
|
||||||
|
[[nodiscard]] auto contains_compatible(const key_type& key) const -> bool;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (c) 2023, 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.
|
|
||||||
|
|
||||||
#ifndef MAMBA_SPECS_AUTHENTIFICATION_INFO_HPP
|
|
||||||
#define MAMBA_SPECS_AUTHENTIFICATION_INFO_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace mamba::specs
|
|
||||||
{
|
|
||||||
/** User and password authetification set in the URL. */
|
|
||||||
struct BasicHTTPAuthentication
|
|
||||||
{
|
|
||||||
std::string user;
|
|
||||||
std::string password;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** HTTP Bearer token set in the request headers. */
|
|
||||||
struct BearerToken
|
|
||||||
{
|
|
||||||
std::string token;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** A Conda token set in the URL path. */
|
|
||||||
struct CondaToken
|
|
||||||
{
|
|
||||||
std::string token;
|
|
||||||
};
|
|
||||||
|
|
||||||
using AuthenticationInfo = std::variant<BasicHTTPAuthentication, BearerToken, CondaToken>;
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -7,6 +7,7 @@
|
||||||
#ifndef MAMBA_SPECS_CONDA_URL_HPP
|
#ifndef MAMBA_SPECS_CONDA_URL_HPP
|
||||||
#define MAMBA_SPECS_CONDA_URL_HPP
|
#define MAMBA_SPECS_CONDA_URL_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "mamba/specs/platform.hpp"
|
#include "mamba/specs/platform.hpp"
|
||||||
|
@ -34,13 +35,17 @@ namespace mamba::specs
|
||||||
explicit CondaURL(util::URL&& url);
|
explicit CondaURL(util::URL&& url);
|
||||||
explicit CondaURL(const util::URL& url);
|
explicit CondaURL(const util::URL& url);
|
||||||
|
|
||||||
|
auto base() const -> const util::URL&;
|
||||||
|
|
||||||
using Base::scheme_is_defaulted;
|
using Base::scheme_is_defaulted;
|
||||||
using Base::scheme;
|
using Base::scheme;
|
||||||
using Base::set_scheme;
|
using Base::set_scheme;
|
||||||
using Base::clear_scheme;
|
using Base::clear_scheme;
|
||||||
|
using Base::has_user;
|
||||||
using Base::user;
|
using Base::user;
|
||||||
using Base::set_user;
|
using Base::set_user;
|
||||||
using Base::clear_user;
|
using Base::clear_user;
|
||||||
|
using Base::has_password;
|
||||||
using Base::password;
|
using Base::password;
|
||||||
using Base::set_password;
|
using Base::set_password;
|
||||||
using Base::clear_password;
|
using Base::clear_password;
|
||||||
|
@ -99,6 +104,9 @@ namespace mamba::specs
|
||||||
*/
|
*/
|
||||||
void append_path(std::string_view path, Encode::no_type);
|
void append_path(std::string_view path, Encode::no_type);
|
||||||
|
|
||||||
|
/** Return wether a token is set. */
|
||||||
|
[[nodiscard]] auto has_token() const -> bool;
|
||||||
|
|
||||||
/** Return the Conda token, as delimited with "/t/", or empty if there isn't any. */
|
/** Return the Conda token, as delimited with "/t/", or empty if there isn't any. */
|
||||||
[[nodiscard]] auto token() const -> std::string_view;
|
[[nodiscard]] auto token() const -> std::string_view;
|
||||||
|
|
||||||
|
@ -226,7 +234,7 @@ namespace mamba::specs
|
||||||
friend auto operator==(const CondaURL&, const CondaURL&) -> bool;
|
friend auto operator==(const CondaURL&, const CondaURL&) -> bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Compare two CondaURL. */
|
/** Tuple-like equality of all observable members */
|
||||||
auto operator==(const CondaURL& a, const CondaURL& b) -> bool;
|
auto operator==(const CondaURL& a, const CondaURL& b) -> bool;
|
||||||
auto operator!=(const CondaURL& a, const CondaURL& b) -> bool;
|
auto operator!=(const CondaURL& a, const CondaURL& b) -> bool;
|
||||||
|
|
||||||
|
@ -234,4 +242,11 @@ namespace mamba::specs
|
||||||
auto operator/(const CondaURL& url, std::string_view subpath) -> CondaURL;
|
auto operator/(const CondaURL& url, std::string_view subpath) -> CondaURL;
|
||||||
auto operator/(CondaURL&& url, std::string_view subpath) -> CondaURL;
|
auto operator/(CondaURL&& url, std::string_view subpath) -> CondaURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<mamba::specs::CondaURL>
|
||||||
|
{
|
||||||
|
auto operator()(const mamba::specs::CondaURL& p) const -> std::size_t;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef MAMBA_UTIL_TUPLE_HASH_HPP
|
||||||
|
#define MAMBA_UTIL_TUPLE_HASH_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace mamba::util
|
||||||
|
{
|
||||||
|
constexpr void hash_combine(std::size_t& seed, std::size_t other)
|
||||||
|
{
|
||||||
|
const auto boost_magic_num = 0x9e3779b9;
|
||||||
|
seed ^= other + boost_magic_num + (seed << 6) + (seed >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, typename Hasher = std::hash<T>>
|
||||||
|
constexpr void hash_combine_val(std::size_t& seed, const T& val, const Hasher& hasher = {})
|
||||||
|
{
|
||||||
|
hash_combine(seed, hasher(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
constexpr auto hash_vals(const T&... vals) -> std::size_t
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
(hash_combine_val(seed, vals), ...);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
constexpr auto hash_tuple(const std::tuple<T...>& t) -> std::size_t
|
||||||
|
{
|
||||||
|
return std::apply([](const auto&... vals) { return hash_vals(vals...); }, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
struct Tuplehasher
|
||||||
|
{
|
||||||
|
constexpr auto operator()(const std::tuple<T...>& t) const -> std::size_t
|
||||||
|
{
|
||||||
|
return hash_tuple(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -8,6 +8,7 @@
|
||||||
#define MAMBA_UTIL_URL_HPP
|
#define MAMBA_UTIL_URL_HPP
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
@ -98,6 +99,9 @@ namespace mamba::util
|
||||||
/** Clear the scheme back to a defaulted value and return the old value. */
|
/** Clear the scheme back to a defaulted value and return the old value. */
|
||||||
auto clear_scheme() -> std::string;
|
auto clear_scheme() -> std::string;
|
||||||
|
|
||||||
|
/** Return wether the user is empty. */
|
||||||
|
[[nodiscard]] auto has_user() const -> bool;
|
||||||
|
|
||||||
/** Return the encoded user, or empty if none. */
|
/** Return the encoded user, or empty if none. */
|
||||||
[[nodiscard]] auto user(Decode::no_type) const -> const std::string&;
|
[[nodiscard]] auto user(Decode::no_type) const -> const std::string&;
|
||||||
|
|
||||||
|
@ -113,6 +117,9 @@ namespace mamba::util
|
||||||
/** Clear and return the encoded user. */
|
/** Clear and return the encoded user. */
|
||||||
auto clear_user() -> std::string;
|
auto clear_user() -> std::string;
|
||||||
|
|
||||||
|
/** Return wether the password is empty. */
|
||||||
|
[[nodiscard]] auto has_password() const -> bool;
|
||||||
|
|
||||||
/** Return the encoded password, or empty if none. */
|
/** Return the encoded password, or empty if none. */
|
||||||
[[nodiscard]] auto password(Decode::no_type) const -> const std::string&;
|
[[nodiscard]] auto password(Decode::no_type) const -> const std::string&;
|
||||||
|
|
||||||
|
@ -270,6 +277,7 @@ namespace mamba::util
|
||||||
std::string m_fragment = {};
|
std::string m_fragment = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Tuple-like equality of all observable members */
|
||||||
auto operator==(URL const& a, URL const& b) -> bool;
|
auto operator==(URL const& a, URL const& b) -> bool;
|
||||||
auto operator!=(URL const& a, URL const& b) -> bool;
|
auto operator!=(URL const& a, URL const& b) -> bool;
|
||||||
|
|
||||||
|
@ -277,4 +285,10 @@ namespace mamba::util
|
||||||
auto operator/(URL const& url, std::string_view subpath) -> URL;
|
auto operator/(URL const& url, std::string_view subpath) -> URL;
|
||||||
auto operator/(URL&& url, std::string_view subpath) -> URL;
|
auto operator/(URL&& url, std::string_view subpath) -> URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::hash<mamba::util::URL>
|
||||||
|
{
|
||||||
|
auto operator()(const mamba::util::URL& p) const -> std::size_t;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#ifndef MAMBA_UTIL_URL_MANIP_HPP
|
#ifndef MAMBA_UTIL_URL_MANIP_HPP
|
||||||
#define MAMBA_UTIL_URL_MANIP_HPP
|
#define MAMBA_UTIL_URL_MANIP_HPP
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -34,15 +33,6 @@ namespace mamba::util
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] auto url_decode(std::string_view url) -> std::string;
|
[[nodiscard]] auto url_decode(std::string_view url) -> std::string;
|
||||||
|
|
||||||
std::string concat_scheme_url(const std::string& scheme, const std::string& location);
|
|
||||||
|
|
||||||
std::string build_url(
|
|
||||||
const std::optional<std::string>& auth,
|
|
||||||
const std::string& scheme,
|
|
||||||
const std::string& base,
|
|
||||||
bool with_credential
|
|
||||||
);
|
|
||||||
|
|
||||||
void split_platform(
|
void split_platform(
|
||||||
const std::vector<std::string>& known_platforms,
|
const std::vector<std::string>& known_platforms,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
|
|
@ -89,10 +89,10 @@ namespace mamba
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Consider 'flexible' and 'strict' the same way
|
// Consider 'flexible' and 'strict' the same way
|
||||||
if (channel->name() != prev_channel_name)
|
if (channel->canonical_name() != prev_channel_name)
|
||||||
{
|
{
|
||||||
max_prio--;
|
max_prio--;
|
||||||
prev_channel_name = channel->name();
|
prev_channel_name = channel->canonical_name();
|
||||||
}
|
}
|
||||||
priorities.push_back(std::make_pair(max_prio, 0));
|
priorities.push_back(std::make_pair(max_prio, 0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace mamba
|
||||||
obj["base_url"] = channel.base_url();
|
obj["base_url"] = channel.base_url();
|
||||||
obj["build_number"] = pkg_info.build_number;
|
obj["build_number"] = pkg_info.build_number;
|
||||||
obj["build_string"] = pkg_info.build_string;
|
obj["build_string"] = pkg_info.build_string;
|
||||||
obj["channel"] = channel.name();
|
obj["channel"] = channel.canonical_name();
|
||||||
obj["dist_name"] = pkg_info.str();
|
obj["dist_name"] = pkg_info.str();
|
||||||
obj["name"] = pkg_info.name;
|
obj["name"] = pkg_info.name;
|
||||||
obj["platform"] = pkg_info.subdir;
|
obj["platform"] = pkg_info.subdir;
|
||||||
|
@ -112,7 +112,7 @@ namespace mamba
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const Channel& channel = channel_context.make_channel(package.second.url);
|
const Channel& channel = channel_context.make_channel(package.second.url);
|
||||||
formatted_pkgs.channel = channel.name();
|
formatted_pkgs.channel = channel.canonical_name();
|
||||||
}
|
}
|
||||||
packages.push_back(formatted_pkgs);
|
packages.push_back(formatted_pkgs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,19 +38,6 @@ namespace mamba
|
||||||
|
|
||||||
const char LOCAL_CHANNELS_NAME[] = "local";
|
const char LOCAL_CHANNELS_NAME[] = "local";
|
||||||
const char DEFAULT_CHANNELS_NAME[] = "defaults";
|
const char DEFAULT_CHANNELS_NAME[] = "defaults";
|
||||||
|
|
||||||
std::optional<std::string> nonempty_str(std::string&& s)
|
|
||||||
{
|
|
||||||
return s.empty() ? std::optional<std::string>() : std::make_optional(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto channel_alias_location(specs::CondaURL url) -> std::string
|
|
||||||
{
|
|
||||||
url.clear_user();
|
|
||||||
url.clear_password();
|
|
||||||
url.clear_token();
|
|
||||||
return url.pretty_str(specs::CondaURL::StripScheme::yes, '/');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> get_known_platforms()
|
std::vector<std::string> get_known_platforms()
|
||||||
|
@ -63,48 +50,6 @@ namespace mamba
|
||||||
* Channel implementation *
|
* Channel implementation *
|
||||||
**************************/
|
**************************/
|
||||||
|
|
||||||
Channel::Channel(
|
|
||||||
std::string_view scheme,
|
|
||||||
std::string location,
|
|
||||||
std::string name,
|
|
||||||
std::string canonical_name,
|
|
||||||
std::string_view user,
|
|
||||||
std::string_view password,
|
|
||||||
std::string_view token,
|
|
||||||
std::string_view package_filename,
|
|
||||||
util::flat_set<std::string> platforms
|
|
||||||
)
|
|
||||||
: m_url()
|
|
||||||
, m_location(std::move(location))
|
|
||||||
, m_name(std::move(name))
|
|
||||||
, m_canonical_name(std::move(canonical_name))
|
|
||||||
, m_platforms(std::move(platforms))
|
|
||||||
{
|
|
||||||
if (m_name != UNKNOWN_CHANNEL)
|
|
||||||
{
|
|
||||||
if (scheme == "file")
|
|
||||||
{
|
|
||||||
m_url.set_path(m_location);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_url = specs::CondaURL::parse(m_location);
|
|
||||||
if (!token.empty())
|
|
||||||
{
|
|
||||||
m_url.set_token(token);
|
|
||||||
}
|
|
||||||
m_url.set_user(user);
|
|
||||||
m_url.set_password(password);
|
|
||||||
}
|
|
||||||
m_url.set_scheme(scheme);
|
|
||||||
m_url.append_path(m_name);
|
|
||||||
if (!package_filename.empty())
|
|
||||||
{
|
|
||||||
m_url.set_package(package_filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Channel::Channel(
|
Channel::Channel(
|
||||||
specs::CondaURL url,
|
specs::CondaURL url,
|
||||||
std::string location,
|
std::string location,
|
||||||
|
@ -127,11 +72,6 @@ namespace mamba
|
||||||
return m_url;
|
return m_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view Channel::scheme() const
|
|
||||||
{
|
|
||||||
return m_url.scheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& Channel::location() const
|
const std::string& Channel::location() const
|
||||||
{
|
{
|
||||||
return m_location;
|
return m_location;
|
||||||
|
@ -147,51 +87,6 @@ namespace mamba
|
||||||
return m_platforms;
|
return m_platforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Channel::auth() const
|
|
||||||
{
|
|
||||||
return nonempty_str(m_url.authentication());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> Channel::user() const
|
|
||||||
{
|
|
||||||
return nonempty_str(m_url.user());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> Channel::password() const
|
|
||||||
{
|
|
||||||
return nonempty_str(m_url.password());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> Channel::token() const
|
|
||||||
{
|
|
||||||
return nonempty_str(std::string(m_url.token()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> Channel::package_filename() const
|
|
||||||
{
|
|
||||||
return nonempty_str(m_url.package());
|
|
||||||
}
|
|
||||||
|
|
||||||
const validation::RepoChecker&
|
|
||||||
Channel::repo_checker(Context& context, MultiPackageCache& caches) const
|
|
||||||
{
|
|
||||||
if (p_repo_checker == nullptr)
|
|
||||||
{
|
|
||||||
p_repo_checker = std::make_unique<validation::RepoChecker>(
|
|
||||||
context,
|
|
||||||
util::rsplit(base_url(), "/", 1).front(),
|
|
||||||
context.prefix_params.root_prefix / "etc" / "trusted-repos"
|
|
||||||
/ util::cache_name_from_url(base_url()),
|
|
||||||
caches.first_writable_path() / "cache" / util::cache_name_from_url(base_url())
|
|
||||||
);
|
|
||||||
|
|
||||||
fs::create_directories(p_repo_checker->cache_path());
|
|
||||||
p_repo_checker->generate_index_checker();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *p_repo_checker;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& Channel::canonical_name() const
|
const std::string& Channel::canonical_name() const
|
||||||
{
|
{
|
||||||
return m_canonical_name;
|
return m_canonical_name;
|
||||||
|
@ -199,36 +94,17 @@ namespace mamba
|
||||||
|
|
||||||
std::string Channel::base_url() const
|
std::string Channel::base_url() const
|
||||||
{
|
{
|
||||||
if (name() == UNKNOWN_CHANNEL)
|
return url().str(specs::CondaURL::Credentials::Remove);
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return util::concat_scheme_url(std::string(scheme()), util::join_url(location(), name()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
util::flat_set<std::string> Channel::urls(bool with_credential) const
|
util::flat_set<std::string> Channel::urls(bool with_credential) const
|
||||||
{
|
{
|
||||||
if (auto fn = package_filename())
|
if (!url().package().empty())
|
||||||
{
|
{
|
||||||
std::string base = {};
|
return { url().str(
|
||||||
if (with_credential && token())
|
with_credential ? specs::CondaURL::Credentials::Show
|
||||||
{
|
: specs::CondaURL::Credentials::Remove
|
||||||
base = util::join_url(location(), "t", *token());
|
) };
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base = location();
|
|
||||||
}
|
|
||||||
|
|
||||||
return { { util::build_url(
|
|
||||||
auth(),
|
|
||||||
std::string(scheme()),
|
|
||||||
util::join_url(base, name(), std::move(fn).value()),
|
|
||||||
with_credential
|
|
||||||
) } };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = util::flat_set<std::string>{};
|
auto out = util::flat_set<std::string>{};
|
||||||
|
@ -242,51 +118,29 @@ namespace mamba
|
||||||
util::flat_set<std::pair<std::string, std::string>>
|
util::flat_set<std::pair<std::string, std::string>>
|
||||||
Channel::platform_urls(bool with_credential) const
|
Channel::platform_urls(bool with_credential) const
|
||||||
{
|
{
|
||||||
std::string base = location();
|
if (!url().package().empty())
|
||||||
if (with_credential && token())
|
|
||||||
{
|
{
|
||||||
base = util::join_url(base, "t", *token());
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = util::flat_set<std::pair<std::string, std::string>>{};
|
auto out = util::flat_set<std::pair<std::string, std::string>>{};
|
||||||
for (const auto& platform : platforms())
|
for (const auto& platform : platforms())
|
||||||
{
|
{
|
||||||
out.insert({
|
out.insert({ platform, platform_url(platform, with_credential) });
|
||||||
platform,
|
|
||||||
util::build_url(
|
|
||||||
auth(),
|
|
||||||
std::string(scheme()),
|
|
||||||
util::join_url(base, name(), platform),
|
|
||||||
with_credential
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Channel::platform_url(std::string platform, bool with_credential) const
|
std::string Channel::platform_url(std::string_view platform, bool with_credential) const
|
||||||
{
|
{
|
||||||
std::string base = location();
|
auto cred = with_credential ? specs::CondaURL::Credentials::Show
|
||||||
if (with_credential && token())
|
: specs::CondaURL::Credentials::Remove;
|
||||||
|
|
||||||
|
if (!url().package().empty())
|
||||||
{
|
{
|
||||||
base = util::join_url(base, "t", *token());
|
return url().str(cred);
|
||||||
}
|
}
|
||||||
return util::build_url(
|
return (url() / platform).str(cred);
|
||||||
auth(),
|
|
||||||
std::string(scheme()),
|
|
||||||
util::join_url(base, name(), platform),
|
|
||||||
with_credential
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Channel& lhs, const Channel& rhs)
|
|
||||||
{
|
|
||||||
return lhs.location() == rhs.location() && lhs.name() == rhs.name();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Channel& lhs, const Channel& rhs)
|
|
||||||
{
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************
|
/*********************************
|
||||||
|
@ -302,34 +156,36 @@ namespace mamba
|
||||||
{
|
{
|
||||||
if (!util::url_has_scheme(channel_url))
|
if (!util::url_has_scheme(channel_url))
|
||||||
{
|
{
|
||||||
auto ca_location = channel_alias_location(channel_alias);
|
const auto& alias = get_channel_alias();
|
||||||
|
auto url = alias;
|
||||||
|
auto name = std::string(util::strip(channel_name.empty() ? channel_url : channel_name, '/')
|
||||||
|
);
|
||||||
|
url.append_path(channel_url);
|
||||||
return Channel(
|
return Channel(
|
||||||
/* scheme= */ channel_alias.scheme(),
|
/* url= */ std::move(url),
|
||||||
/* location= */ std::move(ca_location),
|
/* location= */ alias.pretty_str(specs::CondaURL::StripScheme::yes, '/', specs::CondaURL::Credentials::Remove),
|
||||||
/* name= */ std::string(util::strip(channel_name.empty() ? channel_url : channel_name, '/')),
|
/* name= */ std::move(name),
|
||||||
/* canonical_name= */ channel_canonical_name,
|
/* canonical_name= */ channel_canonical_name
|
||||||
/* user= */ channel_alias.user(),
|
|
||||||
/* password= */ channel_alias.password(),
|
|
||||||
/* token= */ channel_alias.token(),
|
|
||||||
/* package_filename= */ {}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto url = specs::CondaURL::parse(channel_url);
|
auto url = specs::CondaURL::parse(channel_url);
|
||||||
std::string token = std::string(url.token());
|
std::string location = url.pretty_str(
|
||||||
url.clear_token();
|
specs::CondaURL::StripScheme::yes,
|
||||||
std::string user = url.user(); // % encoded
|
'/',
|
||||||
url.clear_user();
|
specs::CondaURL::Credentials::Remove
|
||||||
std::string password = url.password(); // % encoded
|
);
|
||||||
url.clear_password();
|
|
||||||
std::string location = url.pretty_str(specs::CondaURL::StripScheme::yes, '/');
|
|
||||||
|
|
||||||
std::string name(channel_name);
|
std::string name(channel_name);
|
||||||
if (name.empty())
|
|
||||||
|
if (channel_name.empty())
|
||||||
{
|
{
|
||||||
if (auto ca_location = channel_alias_location(channel_alias);
|
auto ca_location = channel_alias.pretty_str(
|
||||||
util::starts_with(location, ca_location))
|
specs::CondaURL::StripScheme::yes,
|
||||||
|
'/',
|
||||||
|
specs::CondaURL::Credentials::Remove
|
||||||
|
);
|
||||||
|
|
||||||
|
if (util::starts_with(location, ca_location))
|
||||||
{
|
{
|
||||||
name = std::string(util::strip(util::remove_prefix(location, ca_location), '/'));
|
name = std::string(util::strip(util::remove_prefix(location, ca_location), '/'));
|
||||||
location = std::move(ca_location);
|
location = std::move(ca_location);
|
||||||
|
@ -342,20 +198,21 @@ namespace mamba
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
location = util::concat(url.host(), url.port().empty() ? "" : ":", url.port());
|
location = url.authority(specs::CondaURL::Credentials::Remove);
|
||||||
name = url.path();
|
name = url.path_without_token();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
url.append_path(channel_name);
|
||||||
|
}
|
||||||
|
|
||||||
name = util::strip(name.empty() ? channel_url : name, '/');
|
name = util::strip(name.empty() ? channel_url : name, '/');
|
||||||
return Channel(
|
return Channel(
|
||||||
/* scheme= */ url.scheme(),
|
/* url = */ std::move(url),
|
||||||
/* location= */ location,
|
/* location= */ std::move(location),
|
||||||
/* name= */ name,
|
/* name= */ std::move(name),
|
||||||
/* canonical_name= */ channel_canonical_name,
|
/* canonical_name= */ channel_canonical_name
|
||||||
/* user= */ user,
|
|
||||||
/* password= */ password,
|
|
||||||
/* token= */ token,
|
|
||||||
/* package_filename= */ {}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +237,50 @@ namespace mamba
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_fallback_credential_from_auth(specs::CondaURL& url, const specs::AuthenticationInfo& auth)
|
||||||
|
{
|
||||||
|
std::visit(
|
||||||
|
[&](const auto& info)
|
||||||
|
{
|
||||||
|
using Info = std::decay_t<decltype(info)>;
|
||||||
|
if constexpr (std::is_same_v<Info, specs::BasicHTTPAuthentication>)
|
||||||
|
{
|
||||||
|
if (!url.has_user() && !url.has_password())
|
||||||
|
{
|
||||||
|
url.set_user(info.user, specs::CondaURL::Encode::yes);
|
||||||
|
url.set_password(info.password, specs::CondaURL::Encode::yes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<Info, specs::CondaToken>)
|
||||||
|
{
|
||||||
|
if (!url.has_token())
|
||||||
|
{
|
||||||
|
url.set_token(info.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auth
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_fallback_credential_from_db(specs::CondaURL& url, const specs::AuthenticationDataBase& db)
|
||||||
|
{
|
||||||
|
if (!url.has_token() || !url.has_user() || !url.has_password())
|
||||||
|
{
|
||||||
|
const auto key = url.pretty_str(
|
||||||
|
specs::CondaURL::StripScheme::yes,
|
||||||
|
'/',
|
||||||
|
specs::CondaURL::Credentials::Remove
|
||||||
|
);
|
||||||
|
if (auto it = db.find_compatible(key); it != db.end())
|
||||||
|
{
|
||||||
|
set_fallback_credential_from_auth(url, it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto rsplit_once(std::string_view str, char sep)
|
auto rsplit_once(std::string_view str, char sep)
|
||||||
{
|
{
|
||||||
auto [head, tail] = util::rstrip_if_parts(str, [sep](char c) { return c != sep; });
|
auto [head, tail] = util::rstrip_if_parts(str, [sep](char c) { return c != sep; });
|
||||||
|
@ -458,159 +359,102 @@ namespace mamba
|
||||||
return chan;
|
return chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
Channel ChannelContext::from_any_url(specs::ChannelSpec&& spec)
|
||||||
{
|
{
|
||||||
// Channel configuration
|
assert(util::url_has_scheme(spec.location()));
|
||||||
struct channel_configuration
|
auto url = specs::CondaURL::parse(spec.location());
|
||||||
|
assert(url.scheme() != "file");
|
||||||
|
|
||||||
|
using StripScheme = typename specs::CondaURL::StripScheme;
|
||||||
|
using Credentials = typename specs::CondaURL::Credentials;
|
||||||
|
|
||||||
|
std::string default_location = url.pretty_str(StripScheme::yes, '/', Credentials::Remove);
|
||||||
|
|
||||||
|
for (const auto& [canonical_name, chan] : get_custom_channels())
|
||||||
{
|
{
|
||||||
specs::CondaURL url;
|
if (url_match(chan.url(), url))
|
||||||
std::string location;
|
|
||||||
std::string name;
|
|
||||||
std::string canonical_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
channel_configuration
|
|
||||||
read_channel_configuration(ChannelContext& channel_context, specs::CondaURL url)
|
|
||||||
{
|
|
||||||
assert(url.scheme() != "file");
|
|
||||||
|
|
||||||
url.clear_package();
|
|
||||||
url.clear_token();
|
|
||||||
url.clear_password();
|
|
||||||
url.clear_user();
|
|
||||||
|
|
||||||
std::string default_location = url.pretty_str(
|
|
||||||
specs::CondaURL::StripScheme::yes,
|
|
||||||
'/',
|
|
||||||
specs::CondaURL::Credentials::Remove
|
|
||||||
);
|
|
||||||
|
|
||||||
// Case 4: custom_channels matches
|
|
||||||
for (const auto& [canonical_name, chan] : channel_context.get_custom_channels())
|
|
||||||
{
|
{
|
||||||
std::string test_url = util::join_url(chan.location(), chan.name());
|
std::string location = chan.location();
|
||||||
if (vector_is_prefix(util::split(test_url, "/"), util::split(default_location, "/")))
|
// TODO cannot change all the locations at once since they are used in from_name
|
||||||
{
|
// std::string location = chan.url().pretty_str(StripScheme::yes, '/',
|
||||||
auto subname = std::string(
|
// Credentials::Remove);
|
||||||
util::strip(default_location.replace(0u, test_url.size(), ""), '/')
|
std::string name = std::string(
|
||||||
);
|
util::strip(util::remove_prefix(default_location, location), '/')
|
||||||
|
);
|
||||||
auto location = chan.location();
|
return Channel(
|
||||||
auto name = util::join_url(chan.name(), subname);
|
/* url= */ std::move(url),
|
||||||
auto l_url = specs::CondaURL::parse(chan.base_url());
|
/* location= */ std::move(location),
|
||||||
l_url.append_path(subname);
|
/* name= */ std::move(name),
|
||||||
l_url.set_scheme(url.scheme());
|
/* canonical_name= */ std::string(canonical_name)
|
||||||
l_url.set_user(chan.user().value_or(""));
|
|
||||||
l_url.set_password(chan.password().value_or(""));
|
|
||||||
if (auto token = chan.token().value_or(""); !token.empty())
|
|
||||||
{
|
|
||||||
l_url.set_token(std::move(token));
|
|
||||||
}
|
|
||||||
return channel_configuration{
|
|
||||||
/* .url= */ std::move(l_url),
|
|
||||||
/* .location= */ std::move(location),
|
|
||||||
/* .name= */ std::move(name),
|
|
||||||
/* .canonical_name= */ std::move(canonical_name),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 5: channel_alias match
|
|
||||||
const auto& ca = channel_context.get_channel_alias();
|
|
||||||
if (auto ca_location = channel_alias_location(ca);
|
|
||||||
util::starts_with(default_location, ca_location))
|
|
||||||
{
|
|
||||||
auto name = std::string(
|
|
||||||
util::strip(util::remove_prefix(default_location, ca_location), '/')
|
|
||||||
);
|
);
|
||||||
auto l_url = specs::CondaURL::parse(util::join_url(ca_location, name));
|
|
||||||
l_url.set_scheme(url.scheme());
|
|
||||||
l_url.set_user(ca.user());
|
|
||||||
l_url.set_password(ca.password());
|
|
||||||
if (auto token = ca.token(); !token.empty())
|
|
||||||
{
|
|
||||||
l_url.set_token(std::move(token));
|
|
||||||
}
|
|
||||||
return channel_configuration{
|
|
||||||
/*. .url= */ std::move(l_url),
|
|
||||||
/* .location= */ std::move(ca_location),
|
|
||||||
/* .name= */ name,
|
|
||||||
/* .canonical_name= */ name,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: migrated_custom_channels not implemented yet
|
|
||||||
// Case 3: migrated_channel_aliases not implemented yet
|
|
||||||
|
|
||||||
// Case 1: No path given, channel name is ""
|
|
||||||
// Case 7: fallback, channel_location = host:port and channel_name = path
|
|
||||||
auto name = std::string(util::strip(url.path_without_token(), '/'));
|
|
||||||
auto location = url.authority(specs::CondaURL::Credentials::Remove);
|
|
||||||
auto canonical_name = url.pretty_str(
|
|
||||||
util::URL::StripScheme::no,
|
|
||||||
/* rstrip_path/ */ '/',
|
|
||||||
specs::CondaURL::Credentials::Remove
|
|
||||||
);
|
|
||||||
return channel_configuration{
|
|
||||||
/* .url= */ std::move(url),
|
|
||||||
/* .location= */ std::move(location),
|
|
||||||
/* .name= */ std::move(name),
|
|
||||||
/* .canonical_name= */ std::move(canonical_name),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto& ca = get_channel_alias(); url_match(ca, url))
|
||||||
|
{
|
||||||
|
auto location = ca.pretty_str(StripScheme::yes, '/', Credentials::Remove);
|
||||||
|
// Overridding url scheme since chan_url could have been defaulted
|
||||||
|
auto name = std::string(util::strip(util::remove_prefix(default_location, location), '/'));
|
||||||
|
return Channel(
|
||||||
|
/*..url= */ std::move(url),
|
||||||
|
/* location= */ std::move(location),
|
||||||
|
/* name= */ name,
|
||||||
|
/* canonical_name= */ name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = std::string(util::strip(url.path_without_token(), '/'));
|
||||||
|
auto location = url.authority(Credentials::Remove);
|
||||||
|
auto canonical_name = url.pretty_str(StripScheme::no, '/', Credentials::Remove);
|
||||||
|
return Channel(
|
||||||
|
/* url= */ std::move(url),
|
||||||
|
/* location= */ std::move(location),
|
||||||
|
/* name= */ std::move(name),
|
||||||
|
/* canonical_name= */ std::move(canonical_name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel ChannelContext::from_package_url(specs::ChannelSpec&& spec)
|
||||||
|
{
|
||||||
|
auto chan = from_any_url(std ::move(spec));
|
||||||
|
set_fallback_credential_from_db(chan.m_url, m_context.authentication_info());
|
||||||
|
return chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel ChannelContext::from_url(specs::ChannelSpec&& spec)
|
Channel ChannelContext::from_url(specs::ChannelSpec&& spec)
|
||||||
{
|
{
|
||||||
assert(util::url_has_scheme(spec.location()));
|
auto platforms = make_platforms(spec.clear_platform_filters(), m_context.platforms());
|
||||||
auto url = specs::CondaURL::parse(spec.location());
|
auto chan = from_any_url(std::move(spec));
|
||||||
|
chan.m_platforms = std::move(platforms);
|
||||||
auto config = read_channel_configuration(*this, url);
|
set_fallback_credential_from_db(chan.m_url, m_context.authentication_info());
|
||||||
|
return chan;
|
||||||
using Decode = typename specs::CondaURL::Decode;
|
|
||||||
using Encode = typename specs::CondaURL::Encode;
|
|
||||||
if (url.user(Decode::no).empty() && !config.url.user(Decode::no).empty())
|
|
||||||
{
|
|
||||||
url.set_user(config.url.clear_user(), Encode::no);
|
|
||||||
}
|
|
||||||
if (url.password(Decode::no).empty() && !config.url.password(Decode::no).empty())
|
|
||||||
{
|
|
||||||
url.set_password(config.url.clear_password(), Encode::no);
|
|
||||||
}
|
|
||||||
if (url.token().empty() && !config.url.token().empty())
|
|
||||||
{
|
|
||||||
url.set_token(config.url.token());
|
|
||||||
}
|
|
||||||
return Channel(
|
|
||||||
/* url= */ std::move(url),
|
|
||||||
/* location= */ std::move(config.location),
|
|
||||||
/* name= */ std::move(config.name),
|
|
||||||
/* canonical_name= */ std::move(config.canonical_name)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel ChannelContext::from_name(const std::string& name)
|
Channel ChannelContext::from_name(specs::ChannelSpec&& spec)
|
||||||
{
|
{
|
||||||
std::string tmp_stripped = name;
|
std::string name = spec.clear_location();
|
||||||
const auto& custom_channels = get_custom_channels();
|
const auto& custom_channels = get_custom_channels();
|
||||||
const auto it_end = custom_channels.end();
|
const auto it_end = custom_channels.end();
|
||||||
auto it = custom_channels.find(tmp_stripped);
|
auto it = custom_channels.find(name);
|
||||||
while (it == it_end)
|
|
||||||
{
|
{
|
||||||
const auto pos = tmp_stripped.rfind("/");
|
std::string considered_name = name;
|
||||||
if (pos == std::string::npos)
|
while (it == it_end)
|
||||||
{
|
{
|
||||||
break;
|
if (const auto pos = considered_name.rfind("/"); pos != std::string::npos)
|
||||||
}
|
{
|
||||||
else
|
considered_name = considered_name.substr(0, pos);
|
||||||
{
|
it = custom_channels.find(considered_name);
|
||||||
tmp_stripped = tmp_stripped.substr(0, pos);
|
}
|
||||||
it = custom_channels.find(tmp_stripped);
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it != it_end)
|
if (it != it_end)
|
||||||
{
|
{
|
||||||
|
auto url = it->second.url();
|
||||||
// we can have a channel like
|
// we can have a channel like
|
||||||
// testchannel: https://server.com/private/testchannel
|
// testchannel: https://server.com/private/testchannel
|
||||||
// where `name == private/testchannel` and we need to join the remaining label part
|
// where `name == private/testchannel` and we need to join the remaining label part
|
||||||
|
@ -624,6 +468,7 @@ namespace mamba
|
||||||
// Combine names properly
|
// Combine names properly
|
||||||
if (common_str.empty())
|
if (common_str.empty())
|
||||||
{
|
{
|
||||||
|
url.append_path(name);
|
||||||
combined_name += "/" + name;
|
combined_name += "/" + name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -632,33 +477,31 @@ namespace mamba
|
||||||
// beginning of `name` and at the end of `combined_name` (I don't know about
|
// beginning of `name` and at the end of `combined_name` (I don't know about
|
||||||
// other use cases for now)
|
// other use cases for now)
|
||||||
combined_name += name.substr(common_str.size());
|
combined_name += name.substr(common_str.size());
|
||||||
|
url.append_path(name.substr(common_str.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_fallback_credential_from_db(url, m_context.authentication_info());
|
||||||
return Channel(
|
return Channel(
|
||||||
/* scheme= */ it->second.scheme(),
|
/* url= */ std::move(url),
|
||||||
/* location= */ it->second.location(),
|
/* location= */ it->second.location(),
|
||||||
/* name= */ combined_name,
|
/* name= */ std::move(combined_name),
|
||||||
/* canonical_name= */ name,
|
/* canonical_name= */ std::move(name),
|
||||||
/* user= */ it->second.user().value_or(""),
|
/* platforms= */ make_platforms(spec.clear_platform_filters(), m_context.platforms())
|
||||||
/* password= */ it->second.password().value_or(""),
|
|
||||||
/* token= */ it->second.token().value_or(""),
|
|
||||||
/* package_filename= */ it->second.package_filename().value_or("")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& alias = get_channel_alias();
|
|
||||||
return Channel(
|
|
||||||
/* scheme= */ alias.scheme(),
|
|
||||||
/* location= */ channel_alias_location(alias),
|
|
||||||
/* name= */ name,
|
|
||||||
/* canonical_name= */ name,
|
|
||||||
/* user= */ alias.user(),
|
|
||||||
/* password= */ alias.password(),
|
|
||||||
/* token= */ alias.token()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& alias = get_channel_alias();
|
||||||
|
auto url = alias;
|
||||||
|
url.append_path(name);
|
||||||
|
set_fallback_credential_from_db(url, m_context.authentication_info());
|
||||||
|
return Channel(
|
||||||
|
/* url= */ std::move(url),
|
||||||
|
/* location= */ alias.pretty_str(specs::CondaURL::StripScheme::yes, '/', specs::CondaURL::Credentials::Remove),
|
||||||
|
/* name= */ name,
|
||||||
|
/* canonical_name= */ name,
|
||||||
|
/* platforms= */ make_platforms(spec.clear_platform_filters(), m_context.platforms())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel ChannelContext::from_value(const std::string& in_value)
|
Channel ChannelContext::from_value(const std::string& in_value)
|
||||||
|
@ -666,98 +509,51 @@ namespace mamba
|
||||||
if (INVALID_CHANNELS.count(in_value) > 0)
|
if (INVALID_CHANNELS.count(in_value) > 0)
|
||||||
{
|
{
|
||||||
return Channel(
|
return Channel(
|
||||||
/* scheme= */ "",
|
/* url= */ specs::CondaURL{},
|
||||||
/* location= */ "",
|
/* location= */ "",
|
||||||
/* name= */ UNKNOWN_CHANNEL,
|
/* name= */ UNKNOWN_CHANNEL,
|
||||||
/* canonical_name= */ ""
|
/* canonical_name= */ UNKNOWN_CHANNEL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto spec = specs::ChannelSpec::parse(in_value);
|
auto spec = specs::ChannelSpec::parse(in_value);
|
||||||
|
|
||||||
auto get_platforms = [&]()
|
switch (spec.type())
|
||||||
{
|
{
|
||||||
auto out = spec.platform_filters();
|
case specs::ChannelSpec::Type::PackagePath:
|
||||||
|
|
||||||
if (out.empty())
|
|
||||||
{
|
{
|
||||||
for (const auto& plat : m_context.platforms())
|
return from_package_path(std::move(spec));
|
||||||
{
|
|
||||||
out.insert(plat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return out;
|
case specs::ChannelSpec::Type::Path:
|
||||||
};
|
|
||||||
|
|
||||||
return [&](specs::ChannelSpec&& l_spec) -> Channel
|
|
||||||
{
|
|
||||||
switch (l_spec.type())
|
|
||||||
{
|
{
|
||||||
case specs::ChannelSpec::Type::PackagePath:
|
return from_path(std::move(spec));
|
||||||
{
|
|
||||||
return from_package_path(std::move(l_spec));
|
|
||||||
}
|
|
||||||
case specs::ChannelSpec::Type::Path:
|
|
||||||
{
|
|
||||||
return from_path(std::move(l_spec));
|
|
||||||
}
|
|
||||||
case specs::ChannelSpec::Type::PackageURL:
|
|
||||||
{
|
|
||||||
return from_url(std::move(l_spec));
|
|
||||||
}
|
|
||||||
case specs::ChannelSpec::Type::URL:
|
|
||||||
{
|
|
||||||
auto plats = get_platforms();
|
|
||||||
auto chan = from_url(std::move(l_spec));
|
|
||||||
chan.m_platforms = std::move(plats);
|
|
||||||
return chan;
|
|
||||||
}
|
|
||||||
case specs::ChannelSpec::Type::Name:
|
|
||||||
{
|
|
||||||
auto chan = from_name(l_spec.location());
|
|
||||||
chan.m_platforms = get_platforms();
|
|
||||||
return chan;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw std::invalid_argument("Invalid ChannelSpec::Type");
|
case specs::ChannelSpec::Type::PackageURL:
|
||||||
}(std::move(spec));
|
{
|
||||||
|
return from_package_url(std::move(spec));
|
||||||
|
}
|
||||||
|
case specs::ChannelSpec::Type::URL:
|
||||||
|
{
|
||||||
|
return from_url(std::move(spec));
|
||||||
|
}
|
||||||
|
case specs::ChannelSpec::Type::Name:
|
||||||
|
{
|
||||||
|
return from_name(std::move(spec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::invalid_argument("Invalid ChannelSpec::Type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const Channel& ChannelContext::make_channel(const std::string& value)
|
const Channel& ChannelContext::make_channel(const std::string& value)
|
||||||
{
|
{
|
||||||
auto res = m_channel_cache.find(value);
|
if (const auto it = m_channel_cache.find(value); it != m_channel_cache.end())
|
||||||
if (res == m_channel_cache.end())
|
|
||||||
{
|
{
|
||||||
auto chan = from_value(value);
|
return it->second;
|
||||||
if (!chan.token())
|
|
||||||
{
|
|
||||||
const auto& with_channel = util::join_url(
|
|
||||||
chan.location(),
|
|
||||||
chan.name() == UNKNOWN_CHANNEL ? "" : chan.name()
|
|
||||||
);
|
|
||||||
const auto& without_channel = chan.location();
|
|
||||||
for (const auto& auth : { with_channel, without_channel })
|
|
||||||
{
|
|
||||||
const auto& authentication_info = m_context.authentication_info();
|
|
||||||
auto it = authentication_info.find(auth);
|
|
||||||
if (it != authentication_info.end()
|
|
||||||
&& std::holds_alternative<specs::CondaToken>(it->second))
|
|
||||||
{
|
|
||||||
chan.m_url.set_token(std::get<specs::CondaToken>(it->second).token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (it != authentication_info.end() && std::holds_alternative<specs::BasicHTTPAuthentication>(it->second))
|
|
||||||
{
|
|
||||||
const auto& l_auth = std::get<specs::BasicHTTPAuthentication>(it->second);
|
|
||||||
chan.m_url.set_user(l_auth.user);
|
|
||||||
chan.m_url.set_password(l_auth.password);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = m_channel_cache.insert(std::make_pair(value, std::move(chan))).first;
|
|
||||||
}
|
}
|
||||||
return res->second;
|
|
||||||
|
auto [it, inserted] = m_channel_cache.emplace(value, from_value(value));
|
||||||
|
assert(inserted);
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const Channel*>
|
std::vector<const Channel*>
|
||||||
|
@ -877,31 +673,24 @@ namespace mamba
|
||||||
m_custom_multichannels.emplace(LOCAL_CHANNELS_NAME, std::move(local_names));
|
m_custom_multichannels.emplace(LOCAL_CHANNELS_NAME, std::move(local_names));
|
||||||
|
|
||||||
const auto& context_custom_channels = m_context.custom_channels;
|
const auto& context_custom_channels = m_context.custom_channels;
|
||||||
for (const auto& [n, p] : context_custom_channels)
|
for (const auto& [name, location] : context_custom_channels)
|
||||||
{
|
{
|
||||||
std::string url = p;
|
auto channel = from_value(location);
|
||||||
if (!util::starts_with(url, "http"))
|
channel.m_canonical_name = name;
|
||||||
{
|
m_custom_channels.emplace(name, std::move(channel));
|
||||||
url = util::path_or_url_to_url(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto channel = make_simple_channel(m_channel_alias, url, n, n);
|
|
||||||
m_custom_channels.emplace(n, std::move(channel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& multichannels = m_context.custom_multichannels;
|
for (const auto& [multi_name, location_list] : m_context.custom_multichannels)
|
||||||
for (auto& [multichannelname, urllist] : multichannels)
|
|
||||||
{
|
{
|
||||||
std::vector<std::string> names(urllist.size());
|
std::vector<std::string> names = {};
|
||||||
auto name_iter = names.begin();
|
names.reserve(location_list.size());
|
||||||
for (auto& url : urllist)
|
for (auto& location : location_list)
|
||||||
{
|
{
|
||||||
auto channel = make_simple_channel(m_channel_alias, url, "", multichannelname);
|
auto channel = from_value(location);
|
||||||
std::string name = channel.name();
|
// No cannonical name give to mulit_channels
|
||||||
m_custom_channels.emplace(std::move(name), std::move(channel));
|
names.push_back(location);
|
||||||
*name_iter++ = url;
|
|
||||||
}
|
}
|
||||||
m_custom_multichannels.emplace(multichannelname, std::move(names));
|
m_custom_multichannels.emplace(multi_name, std::move(names));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************
|
/*******************
|
||||||
|
|
|
@ -179,7 +179,7 @@ namespace mamba
|
||||||
return { platform, "noarch" };
|
return { platform, "noarch" };
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::authentication_info_map_t& Context::authentication_info()
|
specs::AuthenticationDataBase& Context::authentication_info()
|
||||||
{
|
{
|
||||||
if (!m_authentication_infos_loaded)
|
if (!m_authentication_infos_loaded)
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ namespace mamba
|
||||||
return m_authentication_info;
|
return m_authentication_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Context::authentication_info_map_t& Context::authentication_info() const
|
const specs::AuthenticationDataBase& Context::authentication_info() const
|
||||||
{
|
{
|
||||||
return const_cast<Context*>(this)->authentication_info();
|
return const_cast<Context*>(this)->authentication_info();
|
||||||
}
|
}
|
||||||
|
@ -223,9 +223,11 @@ namespace mamba
|
||||||
// cut ".token" ending
|
// cut ".token" ending
|
||||||
token_url = token_url.substr(0, token_url.size() - 6);
|
token_url = token_url.substr(0, token_url.size() - 6);
|
||||||
|
|
||||||
m_authentication_info[token_url] = specs::CondaToken{ read_contents(entry.path()
|
|
||||||
) };
|
|
||||||
LOG_INFO << "Found token for " << token_url << " at " << entry.path();
|
LOG_INFO << "Found token for " << token_url << " at " << entry.path();
|
||||||
|
m_authentication_info.emplace(
|
||||||
|
std::move(token_url),
|
||||||
|
specs::CondaToken{ read_contents(entry.path()) }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,7 +243,7 @@ namespace mamba
|
||||||
infile >> j;
|
infile >> j;
|
||||||
for (auto& [key, el] : j.items())
|
for (auto& [key, el] : j.items())
|
||||||
{
|
{
|
||||||
std::string const host = key;
|
const std::string host = key;
|
||||||
const auto type = el["type"].get<std::string_view>();
|
const auto type = el["type"].get<std::string_view>();
|
||||||
specs::AuthenticationInfo info;
|
specs::AuthenticationInfo info;
|
||||||
if (type == "CondaToken")
|
if (type == "CondaToken")
|
||||||
|
@ -277,7 +279,7 @@ namespace mamba
|
||||||
LOG_INFO << "Found bearer token for host " << host
|
LOG_INFO << "Found bearer token for host " << host
|
||||||
<< " in ~/.mamba/auth/authentication.json";
|
<< " in ~/.mamba/auth/authentication.json";
|
||||||
}
|
}
|
||||||
m_authentication_info[host] = info;
|
m_authentication_info.emplace(std::move(host), std::move(info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,10 +317,10 @@ namespace mamba
|
||||||
host += ":" + port;
|
host += ":" + port;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.authentication_info().count(host))
|
const auto& auth_info = context.authentication_info();
|
||||||
|
if (auto it = auth_info.find_compatible(host); it != auth_info.end())
|
||||||
{
|
{
|
||||||
const auto& auth = context.authentication_info().at(host);
|
if (const auto& auth = it->second; std::holds_alternative<specs::BearerToken>(auth))
|
||||||
if (std::holds_alternative<specs::BearerToken>(auth))
|
|
||||||
{
|
{
|
||||||
m_handle.add_header(
|
m_handle.add_header(
|
||||||
fmt::format("Authorization: Bearer {}", std::get<specs::BearerToken>(auth).token)
|
fmt::format("Authorization: Bearer {}", std::get<specs::BearerToken>(auth).token)
|
||||||
|
|
|
@ -90,11 +90,12 @@ namespace mamba
|
||||||
LOG_INFO << "need to expand path!";
|
LOG_INFO << "need to expand path!";
|
||||||
spec_str = util::path_or_url_to_url(fs::absolute(env::expand_user(spec_str)).string());
|
spec_str = util::path_or_url_to_url(fs::absolute(env::expand_user(spec_str)).string());
|
||||||
}
|
}
|
||||||
auto& parsed_channel = channel_context.make_channel(spec_str);
|
|
||||||
|
|
||||||
if (parsed_channel.package_filename())
|
const auto& parsed_channel = channel_context.make_channel(spec_str);
|
||||||
|
|
||||||
|
if (auto pkg = parsed_channel.url().package(); !pkg.empty())
|
||||||
{
|
{
|
||||||
auto dist = parse_legacy_dist(*parsed_channel.package_filename());
|
auto dist = parse_legacy_dist(pkg);
|
||||||
|
|
||||||
name = dist[0];
|
name = dist[0];
|
||||||
version = dist[1];
|
version = dist[1];
|
||||||
|
@ -106,7 +107,7 @@ namespace mamba
|
||||||
{
|
{
|
||||||
subdir = plats.front();
|
subdir = plats.front();
|
||||||
}
|
}
|
||||||
fn = *parsed_channel.package_filename();
|
fn = std::move(pkg);
|
||||||
url = spec_str;
|
url = spec_str;
|
||||||
is_file = true;
|
is_file = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace mamba
|
||||||
bool
|
bool
|
||||||
channel_match(ChannelContext& channel_context, const Channel& chan, const Channel& needle)
|
channel_match(ChannelContext& channel_context, const Channel& chan, const Channel& needle)
|
||||||
{
|
{
|
||||||
if ((chan) == needle)
|
if (chan.base_url() == needle.base_url())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ namespace mamba
|
||||||
for (auto el : (x->second))
|
for (auto el : (x->second))
|
||||||
{
|
{
|
||||||
const Channel& inner = channel_context.make_channel(el);
|
const Channel& inner = channel_context.make_channel(el);
|
||||||
if ((chan) == inner)
|
if (chan.base_url() == inner.base_url())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ namespace mamba
|
||||||
|
|
||||||
MatchSpec modified_spec(ms);
|
MatchSpec modified_spec(ms);
|
||||||
const Channel& chan = m_pool.channel_context().make_channel(std::string(solvable->channel()));
|
const Channel& chan = m_pool.channel_context().make_channel(std::string(solvable->channel()));
|
||||||
modified_spec.channel = chan.name();
|
modified_spec.channel = chan.canonical_name();
|
||||||
|
|
||||||
modified_spec.version = solvable->version();
|
modified_spec.version = solvable->version();
|
||||||
modified_spec.build_string = solvable->build_string();
|
modified_spec.build_string = solvable->build_string();
|
||||||
|
|
|
@ -380,7 +380,7 @@ namespace mamba
|
||||||
{
|
{
|
||||||
for (const auto& c : channel_context.context().repodata_has_zst)
|
for (const auto& c : channel_context.context().repodata_has_zst)
|
||||||
{
|
{
|
||||||
if (channel_context.make_channel(c) == channel)
|
if (channel_context.make_channel(c).base_url() == channel.base_url())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
#include <solv/selection.h>
|
#include <solv/selection.h>
|
||||||
|
|
||||||
#include "mamba/core/validate.hpp"
|
|
||||||
extern "C" // Incomplete header
|
extern "C" // Incomplete header
|
||||||
{
|
{
|
||||||
#include <solv/conda.h>
|
#include <solv/conda.h>
|
||||||
|
@ -34,7 +33,6 @@ extern "C" // Incomplete header
|
||||||
#include "mamba/core/pool.hpp"
|
#include "mamba/core/pool.hpp"
|
||||||
#include "mamba/core/thread_utils.hpp"
|
#include "mamba/core/thread_utils.hpp"
|
||||||
#include "mamba/core/transaction.hpp"
|
#include "mamba/core/transaction.hpp"
|
||||||
#include "mamba/core/validate.hpp"
|
|
||||||
#include "mamba/util/flat_set.hpp"
|
#include "mamba/util/flat_set.hpp"
|
||||||
#include "mamba/util/string.hpp"
|
#include "mamba/util/string.hpp"
|
||||||
#include "solv-cpp/pool.hpp"
|
#include "solv-cpp/pool.hpp"
|
||||||
|
@ -849,17 +847,46 @@ namespace mamba
|
||||||
solution.actions,
|
solution.actions,
|
||||||
[&](const auto& pkg)
|
[&](const auto& pkg)
|
||||||
{
|
{
|
||||||
if (ctx.experimental && ctx.validation_params.verify_artifacts)
|
// The following was used for the The Update Framework (TUF) / package signing
|
||||||
{
|
// proof of concept.
|
||||||
const auto& repo_checker = channel_context.make_channel(pkg.channel)
|
//
|
||||||
.repo_checker(ctx, multi_cache);
|
// Due to uncertainties on how TUF would be implemented, this was left commented
|
||||||
repo_checker.verify_package(
|
// out as in was getting in the way of the Channel refactoring.
|
||||||
pkg.json_signable(),
|
|
||||||
nlohmann::json::parse(pkg.signatures)
|
|
||||||
);
|
|
||||||
|
|
||||||
LOG_DEBUG << "'" << pkg.name << "' trusted from '" << pkg.channel << "'";
|
// In channel.cpp, repo-checkers were instanciated with the folowing:
|
||||||
}
|
// const validation::RepoChecker&
|
||||||
|
// Channel::repo_checker(Context& context, MultiPackageCache& caches) const
|
||||||
|
// {
|
||||||
|
// if (p_repo_checker == nullptr)
|
||||||
|
// {
|
||||||
|
// p_repo_checker = std::make_unique<validation::RepoChecker>(
|
||||||
|
// context,
|
||||||
|
// util::rsplit(base_url(), "/", 1).front(),
|
||||||
|
// context.prefix_params.root_prefix / "etc" / "trusted-repos"
|
||||||
|
// / util::cache_name_from_url(base_url()),
|
||||||
|
// caches.first_writable_path() / "cache" /
|
||||||
|
// util::cache_name_from_url(base_url())
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// fs::create_directories(p_repo_checker->cache_path());
|
||||||
|
// p_repo_checker->generate_index_checker();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return *p_repo_checker;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Here, the repo-checker would be fetched the following way:
|
||||||
|
// if (ctx.experimental && ctx.validation_params.verify_artifacts)
|
||||||
|
// {
|
||||||
|
// const auto& repo_checker = channel_context.make_channel(pkg.channel)
|
||||||
|
// .repo_checker(ctx, multi_cache);
|
||||||
|
// repo_checker.verify_package(
|
||||||
|
// pkg.json_signable(),
|
||||||
|
// nlohmann::json::parse(pkg.signatures)
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// LOG_DEBUG << "'" << pkg.name << "' trusted from '" << pkg.channel << "'";
|
||||||
|
// }
|
||||||
fetchers.emplace_back(pkg, channel_context, multi_cache);
|
fetchers.emplace_back(pkg, channel_context, multi_cache);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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 <stdexcept>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "mamba/specs/authentication_info.hpp"
|
||||||
|
#include "mamba/util/string.hpp"
|
||||||
|
|
||||||
|
namespace mamba::specs
|
||||||
|
{
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::at_compatible(const key_type& key) -> mapped_type&
|
||||||
|
{
|
||||||
|
if (auto it = find_compatible(key); it != end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
throw std::out_of_range(fmt::format(R"(No entry for key "{}")", key));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::at_compatible(const key_type& key) const -> const mapped_type&
|
||||||
|
{
|
||||||
|
return const_cast<AuthenticationDataBase*>(this)->at_compatible(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::find_compatible(const key_type& key) -> iterator
|
||||||
|
{
|
||||||
|
// First we start by adding a final '/' if absent, so that "mamba.org/" can
|
||||||
|
// be find by query "mamba.org".
|
||||||
|
std::string maybe_key = {};
|
||||||
|
if (!util::ends_with(key, '/'))
|
||||||
|
{
|
||||||
|
maybe_key = util::concat(key, '/');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maybe_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We look for successsive parent dirs as queries with and without trailing '/';
|
||||||
|
// "mamba.org/channel/" > "mamba.org/channel" > "mamba.org/" > "mamba.org"
|
||||||
|
auto it = Base::find(maybe_key);
|
||||||
|
while (it == Base::end())
|
||||||
|
{
|
||||||
|
const auto pos = maybe_key.rfind('/');
|
||||||
|
if (maybe_key.empty() || (pos == std::string::npos))
|
||||||
|
{
|
||||||
|
// Nothing else to try
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((pos + 1) == maybe_key.size())
|
||||||
|
{
|
||||||
|
// Try again without final '/'
|
||||||
|
maybe_key.erase(pos);
|
||||||
|
it = Base::find(maybe_key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try again without final element
|
||||||
|
maybe_key.erase(pos + 1);
|
||||||
|
it = Base::find(maybe_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::find_compatible(const key_type& key) const -> const_iterator
|
||||||
|
{
|
||||||
|
return static_cast<const_iterator>(
|
||||||
|
const_cast<AuthenticationDataBase*>(this)->find_compatible(key)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::contains(const key_type& key) const -> bool
|
||||||
|
{
|
||||||
|
return find(key) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AuthenticationDataBase::contains_compatible(const key_type& key) const -> bool
|
||||||
|
{
|
||||||
|
return find_compatible(key) != end();
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,6 +113,11 @@ namespace mamba::specs
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CondaURL::base() const -> const util::URL&
|
||||||
|
{
|
||||||
|
return static_cast<const util::URL&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
auto CondaURL::parse(std::string_view url) -> CondaURL
|
auto CondaURL::parse(std::string_view url) -> CondaURL
|
||||||
{
|
{
|
||||||
return CondaURL(URL::parse(url));
|
return CondaURL(URL::parse(url));
|
||||||
|
@ -142,6 +147,16 @@ namespace mamba::specs
|
||||||
ensure_path_without_token_leading_slash();
|
ensure_path_without_token_leading_slash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CondaURL::has_token() const -> bool
|
||||||
|
{
|
||||||
|
const auto& p = path(Decode::no);
|
||||||
|
return
|
||||||
|
// Fast return for easy cases
|
||||||
|
(p.size() > token_prefix.size())
|
||||||
|
// The actual check
|
||||||
|
&& util::starts_with(p, token_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
auto CondaURL::token() const -> std::string_view
|
auto CondaURL::token() const -> std::string_view
|
||||||
{
|
{
|
||||||
static constexpr auto npos = std::string_view::npos;
|
static constexpr auto npos = std::string_view::npos;
|
||||||
|
@ -491,7 +506,7 @@ namespace mamba::specs
|
||||||
|
|
||||||
auto operator==(const CondaURL& a, const CondaURL& b) -> bool
|
auto operator==(const CondaURL& a, const CondaURL& b) -> bool
|
||||||
{
|
{
|
||||||
return static_cast<const util::URL&>(a) == static_cast<const util::URL&>(b);
|
return a.base() == b.base();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator!=(const CondaURL& a, const CondaURL& b) -> bool
|
auto operator!=(const CondaURL& a, const CondaURL& b) -> bool
|
||||||
|
@ -510,3 +525,9 @@ namespace mamba::specs
|
||||||
return std::move(url);
|
return std::move(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
std::hash<mamba::specs::CondaURL>::operator()(const mamba::specs::CondaURL& u) const -> std::size_t
|
||||||
|
{
|
||||||
|
return std::hash<mamba::util::URL>()(u.base());
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "mamba/util/build.hpp"
|
#include "mamba/util/build.hpp"
|
||||||
#include "mamba/util/path_manip.hpp"
|
#include "mamba/util/path_manip.hpp"
|
||||||
#include "mamba/util/string.hpp"
|
#include "mamba/util/string.hpp"
|
||||||
|
#include "mamba/util/tuple_hash.hpp"
|
||||||
#include "mamba/util/url.hpp"
|
#include "mamba/util/url.hpp"
|
||||||
#include "mamba/util/url_manip.hpp"
|
#include "mamba/util/url_manip.hpp"
|
||||||
|
|
||||||
|
@ -185,8 +186,8 @@ namespace mamba::util
|
||||||
out.set_scheme(handle.get_part(CURLUPART_SCHEME).value_or(""));
|
out.set_scheme(handle.get_part(CURLUPART_SCHEME).value_or(""));
|
||||||
out.set_user(handle.get_part(CURLUPART_USER).value_or(""), Encode::no);
|
out.set_user(handle.get_part(CURLUPART_USER).value_or(""), Encode::no);
|
||||||
out.set_password(handle.get_part(CURLUPART_PASSWORD).value_or(""), Encode::no);
|
out.set_password(handle.get_part(CURLUPART_PASSWORD).value_or(""), Encode::no);
|
||||||
out.set_host(handle.get_part(CURLUPART_HOST).value_or(""));
|
out.set_host(handle.get_part(CURLUPART_HOST).value_or(""), Encode::no);
|
||||||
out.set_path(handle.get_part(CURLUPART_PATH).value_or("/"));
|
out.set_path(handle.get_part(CURLUPART_PATH).value_or("/"), Encode::no);
|
||||||
out.set_port(handle.get_part(CURLUPART_PORT).value_or(""));
|
out.set_port(handle.get_part(CURLUPART_PORT).value_or(""));
|
||||||
out.set_query(handle.get_part(CURLUPART_QUERY).value_or(""));
|
out.set_query(handle.get_part(CURLUPART_QUERY).value_or(""));
|
||||||
out.set_fragment(handle.get_part(CURLUPART_FRAGMENT).value_or(""));
|
out.set_fragment(handle.get_part(CURLUPART_FRAGMENT).value_or(""));
|
||||||
|
@ -222,6 +223,11 @@ namespace mamba::util
|
||||||
return std::exchange(m_scheme, "");
|
return std::exchange(m_scheme, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto URL::has_user() const -> bool
|
||||||
|
{
|
||||||
|
return !m_user.empty();
|
||||||
|
}
|
||||||
|
|
||||||
auto URL::user(Decode::no_type) const -> const std::string&
|
auto URL::user(Decode::no_type) const -> const std::string&
|
||||||
{
|
{
|
||||||
return m_user;
|
return m_user;
|
||||||
|
@ -247,6 +253,11 @@ namespace mamba::util
|
||||||
return std::exchange(m_user, "");
|
return std::exchange(m_user, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto URL::has_password() const -> bool
|
||||||
|
{
|
||||||
|
return !m_password.empty();
|
||||||
|
}
|
||||||
|
|
||||||
auto URL::password(Decode::no_type) const -> const std::string&
|
auto URL::password(Decode::no_type) const -> const std::string&
|
||||||
{
|
{
|
||||||
return m_password;
|
return m_password;
|
||||||
|
@ -458,27 +469,19 @@ namespace mamba::util
|
||||||
if (on_win && (scheme() == "file"))
|
if (on_win && (scheme() == "file"))
|
||||||
{
|
{
|
||||||
auto [slashes, no_slash_path] = lstrip_parts(path, '/');
|
auto [slashes, no_slash_path] = lstrip_parts(path, '/');
|
||||||
if (slashes.empty())
|
|
||||||
{
|
|
||||||
slashes = "/";
|
|
||||||
}
|
|
||||||
if ((no_slash_path.size() >= 2) && path_has_drive_letter(no_slash_path))
|
if ((no_slash_path.size() >= 2) && path_has_drive_letter(no_slash_path))
|
||||||
{
|
{
|
||||||
m_path = concat(
|
return set_path(
|
||||||
slashes,
|
concat(
|
||||||
no_slash_path.substr(0, 2),
|
slashes.empty() ? "/" : slashes,
|
||||||
url_encode(no_slash_path.substr(2), '/')
|
no_slash_path.substr(0, 2),
|
||||||
|
url_encode(no_slash_path.substr(2), '/')
|
||||||
|
),
|
||||||
|
Encode::no
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
m_path = concat(slashes, url_encode(no_slash_path, '/'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return set_path(url_encode(path, '/'), Encode::no);
|
|
||||||
}
|
}
|
||||||
|
return set_path(url_encode(path, '/'), Encode::no);
|
||||||
}
|
}
|
||||||
|
|
||||||
void URL::set_path(std::string path, Encode::no_type)
|
void URL::set_path(std::string path, Encode::no_type)
|
||||||
|
@ -512,9 +515,9 @@ namespace mamba::util
|
||||||
|
|
||||||
void URL::append_path(std::string_view subpath, Encode::yes_type)
|
void URL::append_path(std::string_view subpath, Encode::yes_type)
|
||||||
{
|
{
|
||||||
if (path(Decode::no) == "/")
|
if (util::lstrip(path(Decode::no), '/').empty())
|
||||||
{
|
{
|
||||||
// Allow hanldling of Windows drive letter encoding
|
// Allow handling of Windows drive letter encoding
|
||||||
return set_path(std::string(subpath), Encode::yes);
|
return set_path(std::string(subpath), Encode::yes);
|
||||||
}
|
}
|
||||||
return append_path(url_encode(subpath, '/'), Encode::no);
|
return append_path(url_encode(subpath, '/'), Encode::no);
|
||||||
|
@ -625,15 +628,34 @@ namespace mamba::util
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
auto attrs(URL const& url)
|
||||||
|
{
|
||||||
|
return std::tuple<
|
||||||
|
std::string_view,
|
||||||
|
const std::string&,
|
||||||
|
const std::string&,
|
||||||
|
std::string_view,
|
||||||
|
const std::string&,
|
||||||
|
const std::string&,
|
||||||
|
const std::string&,
|
||||||
|
const std::string&>{
|
||||||
|
url.scheme(),
|
||||||
|
url.user(URL::Decode::no),
|
||||||
|
url.password(URL::Decode::no),
|
||||||
|
url.host(URL::Decode::no),
|
||||||
|
url.port(),
|
||||||
|
url.path(URL::Decode::no),
|
||||||
|
url.query(),
|
||||||
|
url.fragment(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto operator==(URL const& a, URL const& b) -> bool
|
auto operator==(URL const& a, URL const& b) -> bool
|
||||||
{
|
{
|
||||||
return (a.scheme() == b.scheme())
|
return attrs(a) == attrs(b);
|
||||||
&& (a.user() == b.user())
|
|
||||||
// omitting password, is that desirable?
|
|
||||||
&& (a.host() == b.host())
|
|
||||||
// Would it be desirable to account for default ports?
|
|
||||||
&& (a.port() == b.port()) && (a.path() == b.path()) && (a.query() == b.query())
|
|
||||||
&& (a.fragment() == b.fragment());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator!=(URL const& a, URL const& b) -> bool
|
auto operator!=(URL const& a, URL const& b) -> bool
|
||||||
|
@ -653,3 +675,9 @@ namespace mamba::util
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mamba
|
} // namespace mamba
|
||||||
|
|
||||||
|
auto
|
||||||
|
std::hash<mamba::util::URL>::operator()(const mamba::util::URL& u) const -> std::size_t
|
||||||
|
{
|
||||||
|
return mamba::util::hash_tuple(mamba::util::attrs(u));
|
||||||
|
}
|
||||||
|
|
|
@ -126,37 +126,6 @@ namespace mamba::util
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// proper file scheme on Windows is `file:///C:/blabla`
|
|
||||||
// https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/
|
|
||||||
std::string concat_scheme_url(const std::string& scheme, const std::string& location)
|
|
||||||
{
|
|
||||||
if (scheme == "file" && location.size() > 1 && location[1] == ':')
|
|
||||||
{
|
|
||||||
return util::concat("file:///", location);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return util::concat(scheme, "://", location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string build_url(
|
|
||||||
const std::optional<std::string>& auth,
|
|
||||||
const std::string& scheme,
|
|
||||||
const std::string& base,
|
|
||||||
bool with_credential
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (with_credential && auth)
|
|
||||||
{
|
|
||||||
return concat_scheme_url(scheme, util::concat(*auth, "@", base));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return concat_scheme_url(scheme, base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void split_platform(
|
void split_platform(
|
||||||
const std::vector<std::string>& known_platforms,
|
const std::vector<std::string>& known_platforms,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
|
|
@ -37,9 +37,11 @@ set(LIBMAMBA_TEST_SRCS
|
||||||
src/util/test_graph.cpp
|
src/util/test_graph.cpp
|
||||||
src/util/test_iterator.cpp
|
src/util/test_iterator.cpp
|
||||||
src/util/test_path_manip.cpp
|
src/util/test_path_manip.cpp
|
||||||
|
src/util/test_tuple_hash.cpp
|
||||||
src/util/test_url_manip.cpp
|
src/util/test_url_manip.cpp
|
||||||
src/util/test_url.cpp
|
src/util/test_url.cpp
|
||||||
# Implementation of version and matching specs
|
# Implementation of version and matching specs
|
||||||
|
src/specs/test_authentication_info.cpp
|
||||||
src/specs/test_archive.cpp
|
src/specs/test_archive.cpp
|
||||||
src/specs/test_platform.cpp
|
src/specs/test_platform.cpp
|
||||||
src/specs/test_conda_url.cpp
|
src/specs/test_conda_url.cpp
|
||||||
|
|
|
@ -12,6 +12,11 @@
|
||||||
#include "mamba/core/context.hpp"
|
#include "mamba/core/context.hpp"
|
||||||
#include "mamba/core/environment.hpp"
|
#include "mamba/core/environment.hpp"
|
||||||
#include "mamba/util/flat_set.hpp"
|
#include "mamba/util/flat_set.hpp"
|
||||||
|
#include "mamba/util/string.hpp"
|
||||||
|
#include "mamba/util/url_manip.hpp"
|
||||||
|
|
||||||
|
#include "doctest-printer/conda_url.hpp"
|
||||||
|
#include "doctest-printer/flat_set.hpp"
|
||||||
|
|
||||||
#include "mambatests.hpp"
|
#include "mambatests.hpp"
|
||||||
|
|
||||||
|
@ -21,10 +26,12 @@ namespace mamba
|
||||||
static const std::string platform = std::string(specs::build_platform_name());
|
static const std::string platform = std::string(specs::build_platform_name());
|
||||||
using PlatformSet = typename util::flat_set<std::string>;
|
using PlatformSet = typename util::flat_set<std::string>;
|
||||||
using UrlSet = typename util::flat_set<std::string>;
|
using UrlSet = typename util::flat_set<std::string>;
|
||||||
|
using CondaURL = specs::CondaURL;
|
||||||
|
|
||||||
static_assert(std::is_move_constructible_v<mamba::Channel>);
|
static_assert(std::is_move_constructible_v<mamba::Channel>);
|
||||||
static_assert(std::is_move_assignable_v<mamba::Channel>);
|
static_assert(std::is_move_assignable_v<mamba::Channel>);
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE("ChannelContext")
|
TEST_SUITE("ChannelContext")
|
||||||
{
|
{
|
||||||
TEST_CASE("init")
|
TEST_CASE("init")
|
||||||
|
@ -38,21 +45,18 @@ namespace mamba
|
||||||
const auto& custom = channel_context.get_custom_channels();
|
const auto& custom = channel_context.get_custom_channels();
|
||||||
|
|
||||||
auto it = custom.find("pkgs/main");
|
auto it = custom.find("pkgs/main");
|
||||||
CHECK_NE(it, custom.end());
|
REQUIRE_NE(it, custom.end());
|
||||||
CHECK_EQ(it->second.name(), "pkgs/main");
|
CHECK_EQ(it->second.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
|
||||||
CHECK_EQ(it->second.location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(it->second.canonical_name(), "defaults");
|
CHECK_EQ(it->second.canonical_name(), "defaults");
|
||||||
|
|
||||||
it = custom.find("pkgs/pro");
|
it = custom.find("pkgs/pro");
|
||||||
CHECK_NE(it, custom.end());
|
REQUIRE_NE(it, custom.end());
|
||||||
CHECK_EQ(it->second.name(), "pkgs/pro");
|
CHECK_EQ(it->second.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/pro"));
|
||||||
CHECK_EQ(it->second.location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(it->second.canonical_name(), "pkgs/pro");
|
CHECK_EQ(it->second.canonical_name(), "pkgs/pro");
|
||||||
|
|
||||||
it = custom.find("pkgs/r");
|
it = custom.find("pkgs/r");
|
||||||
CHECK_NE(it, custom.end());
|
REQUIRE_NE(it, custom.end());
|
||||||
CHECK_EQ(it->second.name(), "pkgs/r");
|
CHECK_EQ(it->second.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/r"));
|
||||||
CHECK_EQ(it->second.location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(it->second.canonical_name(), "defaults");
|
CHECK_EQ(it->second.canonical_name(), "defaults");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,18 +75,14 @@ namespace mamba
|
||||||
const auto& custom = channel_context.get_custom_channels();
|
const auto& custom = channel_context.get_custom_channels();
|
||||||
|
|
||||||
auto it = custom.find("pkgs/main");
|
auto it = custom.find("pkgs/main");
|
||||||
CHECK_NE(it, custom.end());
|
REQUIRE_NE(it, custom.end());
|
||||||
CHECK_EQ(it->second.name(), "pkgs/main");
|
CHECK_EQ(it->second.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
|
||||||
CHECK_EQ(it->second.location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(it->second.canonical_name(), "defaults");
|
CHECK_EQ(it->second.canonical_name(), "defaults");
|
||||||
|
|
||||||
std::string value = "conda-forge";
|
std::string value = "conda-forge";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(c.url(), CondaURL::parse("https://mydomain.com/channels/conda-forge"));
|
||||||
CHECK_EQ(c.location(), "mydomain.com/channels");
|
|
||||||
CHECK_EQ(c.name(), "conda-forge");
|
|
||||||
CHECK_EQ(c.canonical_name(), "conda-forge");
|
CHECK_EQ(c.canonical_name(), "conda-forge");
|
||||||
// CHECK_EQ(c.url(), "conda-forge");
|
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
|
|
||||||
ctx.channel_alias = "https://conda.anaconda.org";
|
ctx.channel_alias = "https://conda.anaconda.org";
|
||||||
|
@ -125,9 +125,7 @@ namespace mamba
|
||||||
{
|
{
|
||||||
std::string value = "test_channel";
|
std::string value = "test_channel";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "file");
|
CHECK_EQ(c.url(), CondaURL::parse("file:///tmp/test_channel"));
|
||||||
CHECK_EQ(c.location(), "/tmp");
|
|
||||||
CHECK_EQ(c.name(), "test_channel");
|
|
||||||
CHECK_EQ(c.canonical_name(), "test_channel");
|
CHECK_EQ(c.canonical_name(), "test_channel");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
|
@ -140,9 +138,7 @@ namespace mamba
|
||||||
{
|
{
|
||||||
std::string value = "some_channel";
|
std::string value = "some_channel";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(c.url(), CondaURL::parse("https://conda.mydomain.xyz/some_channel"));
|
||||||
CHECK_EQ(c.location(), "conda.mydomain.xyz");
|
|
||||||
CHECK_EQ(c.name(), "some_channel");
|
|
||||||
CHECK_EQ(c.canonical_name(), "some_channel");
|
CHECK_EQ(c.canonical_name(), "some_channel");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
|
@ -261,23 +257,20 @@ namespace mamba
|
||||||
const Channel* c1 = x[0];
|
const Channel* c1 = x[0];
|
||||||
const Channel* c2 = x[1];
|
const Channel* c2 = x[1];
|
||||||
|
|
||||||
CHECK_EQ(c1->name(), "pkgs/main");
|
CHECK_EQ(c1->url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
std::string("https://repo.anaconda.com/pkgs/main/") + platform,
|
std::string("https://repo.anaconda.com/pkgs/main/") + platform,
|
||||||
"https://repo.anaconda.com/pkgs/main/noarch",
|
"https://repo.anaconda.com/pkgs/main/noarch",
|
||||||
});
|
});
|
||||||
CHECK_EQ(c1->urls(), exp_urls);
|
CHECK_EQ(c1->urls(), exp_urls);
|
||||||
|
|
||||||
CHECK_EQ(c2->name(), "pkgs/r");
|
CHECK_EQ(c2->url(), CondaURL::parse("https://repo.anaconda.com/pkgs/r"));
|
||||||
const UrlSet exp_urls2({
|
const UrlSet exp_urls2({
|
||||||
std::string("https://repo.anaconda.com/pkgs/r/") + platform,
|
std::string("https://repo.anaconda.com/pkgs/r/") + platform,
|
||||||
"https://repo.anaconda.com/pkgs/r/noarch",
|
"https://repo.anaconda.com/pkgs/r/noarch",
|
||||||
});
|
});
|
||||||
CHECK_EQ(c2->urls(), exp_urls2);
|
CHECK_EQ(c2->urls(), exp_urls2);
|
||||||
|
|
||||||
CHECK_EQ(c1->location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(c1->scheme(), "https");
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
ctx.custom_channels.clear();
|
ctx.custom_channels.clear();
|
||||||
}
|
}
|
||||||
|
@ -295,22 +288,20 @@ namespace mamba
|
||||||
const Channel* c1 = x[0];
|
const Channel* c1 = x[0];
|
||||||
const Channel* c2 = x[1];
|
const Channel* c2 = x[1];
|
||||||
|
|
||||||
CHECK_EQ(c1->name(), "test/channel");
|
CHECK_EQ(c1->url(), CondaURL::parse("https://mamba.com/test/channel"));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
std::string("https://mamba.com/test/channel/") + platform,
|
std::string("https://mamba.com/test/channel/") + platform,
|
||||||
"https://mamba.com/test/channel/noarch",
|
"https://mamba.com/test/channel/noarch",
|
||||||
});
|
});
|
||||||
CHECK_EQ(c1->urls(), exp_urls);
|
CHECK_EQ(c1->urls(), exp_urls);
|
||||||
|
|
||||||
|
CHECK_EQ(c2->url(), CondaURL::parse("https://mamba.com/stable/channel"));
|
||||||
const UrlSet exp_urls2({
|
const UrlSet exp_urls2({
|
||||||
std::string("https://mamba.com/stable/channel/") + platform,
|
std::string("https://mamba.com/stable/channel/") + platform,
|
||||||
"https://mamba.com/stable/channel/noarch",
|
"https://mamba.com/stable/channel/noarch",
|
||||||
});
|
});
|
||||||
CHECK_EQ(c2->urls(), exp_urls2);
|
CHECK_EQ(c2->urls(), exp_urls2);
|
||||||
|
|
||||||
CHECK_EQ(c2->name(), "stable/channel");
|
|
||||||
CHECK_EQ(c2->location(), "mamba.com");
|
|
||||||
CHECK_EQ(c2->scheme(), "https");
|
|
||||||
|
|
||||||
ctx.custom_channels.clear();
|
ctx.custom_channels.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,11 +320,9 @@ namespace mamba
|
||||||
CHECK_EQ(custom.size(), 4);
|
CHECK_EQ(custom.size(), 4);
|
||||||
|
|
||||||
auto it = custom.find("conda-bld");
|
auto it = custom.find("conda-bld");
|
||||||
CHECK_NE(it, custom.end());
|
REQUIRE_NE(it, custom.end());
|
||||||
CHECK_EQ(it->second.name(), "conda-bld");
|
CHECK_EQ(it->second.url(), CondaURL::parse(util::path_to_url(conda_bld_dir.string())));
|
||||||
CHECK_EQ(it->second.location(), env::home_directory());
|
|
||||||
CHECK_EQ(it->second.canonical_name(), "local");
|
CHECK_EQ(it->second.canonical_name(), "local");
|
||||||
CHECK_EQ(it->second.scheme(), "file");
|
|
||||||
|
|
||||||
auto local_channels = channel_context.get_channels({ "local" });
|
auto local_channels = channel_context.get_channels({ "local" });
|
||||||
CHECK_EQ(local_channels.size(), 1);
|
CHECK_EQ(local_channels.size(), 1);
|
||||||
|
@ -358,9 +347,7 @@ namespace mamba
|
||||||
{
|
{
|
||||||
std::string value = "test_channel";
|
std::string value = "test_channel";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(c.url(), CondaURL::parse("https://server.com/private/channels/test_channel"));
|
||||||
CHECK_EQ(c.location(), "server.com/private/channels");
|
|
||||||
CHECK_EQ(c.name(), "test_channel");
|
|
||||||
CHECK_EQ(c.canonical_name(), "test_channel");
|
CHECK_EQ(c.canonical_name(), "test_channel");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
|
@ -373,9 +360,10 @@ namespace mamba
|
||||||
{
|
{
|
||||||
std::string value = "test_channel/mylabel/xyz";
|
std::string value = "test_channel/mylabel/xyz";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(
|
||||||
CHECK_EQ(c.location(), "server.com/private/channels");
|
c.url(),
|
||||||
CHECK_EQ(c.name(), "test_channel/mylabel/xyz");
|
CondaURL::parse("https://server.com/private/channels/test_channel/mylabel/xyz")
|
||||||
|
);
|
||||||
CHECK_EQ(c.canonical_name(), "test_channel/mylabel/xyz");
|
CHECK_EQ(c.canonical_name(), "test_channel/mylabel/xyz");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
|
@ -387,11 +375,13 @@ namespace mamba
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// https://github.com/mamba-org/mamba/issues/2553
|
||||||
std::string value = "random/test_channel/pkg";
|
std::string value = "random/test_channel/pkg";
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(
|
||||||
CHECK_EQ(c.location(), "server.com/random/channels");
|
c.url(),
|
||||||
CHECK_EQ(c.name(), "random/test_channel/pkg");
|
CondaURL::parse("https://server.com/random/channels/random/test_channel/pkg")
|
||||||
|
);
|
||||||
CHECK_EQ(c.canonical_name(), "random/test_channel/pkg");
|
CHECK_EQ(c.canonical_name(), "random/test_channel/pkg");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
const UrlSet exp_urls({
|
const UrlSet exp_urls({
|
||||||
|
@ -414,9 +404,7 @@ namespace mamba
|
||||||
std::string value = "https://repo.mamba.pm/conda-forge";
|
std::string value = "https://repo.mamba.pm/conda-forge";
|
||||||
ChannelContext channel_context{ mambatests::context() };
|
ChannelContext channel_context{ mambatests::context() };
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(c.url(), CondaURL::parse("https://repo.mamba.pm/conda-forge"));
|
||||||
CHECK_EQ(c.location(), "repo.mamba.pm");
|
|
||||||
CHECK_EQ(c.name(), "conda-forge");
|
|
||||||
CHECK_EQ(c.canonical_name(), "https://repo.mamba.pm/conda-forge");
|
CHECK_EQ(c.canonical_name(), "https://repo.mamba.pm/conda-forge");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
}
|
}
|
||||||
|
@ -426,53 +414,33 @@ namespace mamba
|
||||||
std::string value = "conda-forge";
|
std::string value = "conda-forge";
|
||||||
ChannelContext channel_context{ mambatests::context() };
|
ChannelContext channel_context{ mambatests::context() };
|
||||||
const Channel& c = channel_context.make_channel(value);
|
const Channel& c = channel_context.make_channel(value);
|
||||||
CHECK_EQ(c.scheme(), "https");
|
CHECK_EQ(c.url(), CondaURL::parse("https://conda.anaconda.org/conda-forge"));
|
||||||
CHECK_EQ(c.location(), "conda.anaconda.org");
|
|
||||||
CHECK_EQ(c.name(), "conda-forge");
|
|
||||||
CHECK_EQ(c.canonical_name(), "conda-forge");
|
CHECK_EQ(c.canonical_name(), "conda-forge");
|
||||||
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
|
|
||||||
std::string value2 = "https://repo.anaconda.com/pkgs/main[" + platform + "]";
|
std::string value2 = "https://repo.anaconda.com/pkgs/main[" + platform + "]";
|
||||||
const Channel& c2 = channel_context.make_channel(value2);
|
const Channel& c2 = channel_context.make_channel(value2);
|
||||||
CHECK_EQ(c2.scheme(), "https");
|
CHECK_EQ(c2.url(), CondaURL::parse("https://repo.anaconda.com/pkgs/main"));
|
||||||
CHECK_EQ(c2.location(), "repo.anaconda.com");
|
|
||||||
CHECK_EQ(c2.name(), "pkgs/main");
|
|
||||||
CHECK_EQ(c2.canonical_name(), "https://repo.anaconda.com/pkgs/main");
|
CHECK_EQ(c2.canonical_name(), "https://repo.anaconda.com/pkgs/main");
|
||||||
CHECK_EQ(c2.platforms(), PlatformSet({ platform }));
|
CHECK_EQ(c2.platforms(), PlatformSet({ platform }));
|
||||||
|
|
||||||
std::string value3 = "https://conda.anaconda.org/conda-forge[" + platform + "]";
|
std::string value3 = "https://conda.anaconda.org/conda-forge[" + platform + "]";
|
||||||
const Channel& c3 = channel_context.make_channel(value3);
|
const Channel& c3 = channel_context.make_channel(value3);
|
||||||
CHECK_EQ(c3.scheme(), c.scheme());
|
CHECK_EQ(c3.url(), c.url());
|
||||||
CHECK_EQ(c3.location(), c.location());
|
|
||||||
CHECK_EQ(c3.name(), c.name());
|
|
||||||
CHECK_EQ(c3.canonical_name(), c.canonical_name());
|
CHECK_EQ(c3.canonical_name(), c.canonical_name());
|
||||||
CHECK_EQ(c3.platforms(), PlatformSet({ platform }));
|
CHECK_EQ(c3.platforms(), PlatformSet({ platform }));
|
||||||
|
|
||||||
std::string value4 = "/home/mamba/test/channel_b";
|
std::string value4 = "/home/mamba/test/channel_b";
|
||||||
const Channel& c4 = channel_context.make_channel(value4);
|
const Channel& c4 = channel_context.make_channel(value4);
|
||||||
CHECK_EQ(c4.scheme(), "file");
|
CHECK_EQ(c4.url(), CondaURL::parse(util::path_to_url(value4)));
|
||||||
#ifdef _WIN32
|
CHECK_EQ(c4.canonical_name(), c4.url().pretty_str());
|
||||||
std::string driveletter = fs::absolute(fs::u8path("/")).string().substr(0, 1);
|
|
||||||
CHECK_EQ(c4.location(), driveletter + ":/home/mamba/test");
|
|
||||||
CHECK_EQ(c4.canonical_name(), "file:///" + driveletter + ":/home/mamba/test/channel_b");
|
|
||||||
#else
|
|
||||||
CHECK_EQ(c4.location(), "/home/mamba/test");
|
|
||||||
CHECK_EQ(c4.canonical_name(), "file:///home/mamba/test/channel_b");
|
|
||||||
#endif
|
|
||||||
CHECK_EQ(c4.name(), "channel_b");
|
|
||||||
CHECK_EQ(c4.platforms(), PlatformSet({ platform, "noarch" }));
|
CHECK_EQ(c4.platforms(), PlatformSet({ platform, "noarch" }));
|
||||||
|
|
||||||
std::string value5 = "/home/mamba/test/channel_b[" + platform + "]";
|
std::string path5 = "/home/mamba/test/channel_b";
|
||||||
|
std::string value5 = util::concat(path5, '[', platform, ']');
|
||||||
const Channel& c5 = channel_context.make_channel(value5);
|
const Channel& c5 = channel_context.make_channel(value5);
|
||||||
CHECK_EQ(c5.scheme(), "file");
|
CHECK_EQ(c5.url(), CondaURL::parse(util::path_to_url(path5)));
|
||||||
#ifdef _WIN32
|
CHECK_EQ(c5.canonical_name(), c5.url().pretty_str());
|
||||||
CHECK_EQ(c5.location(), driveletter + ":/home/mamba/test");
|
|
||||||
CHECK_EQ(c5.canonical_name(), "file:///" + driveletter + ":/home/mamba/test/channel_b");
|
|
||||||
#else
|
|
||||||
CHECK_EQ(c5.location(), "/home/mamba/test");
|
|
||||||
CHECK_EQ(c5.canonical_name(), "file:///home/mamba/test/channel_b");
|
|
||||||
#endif
|
|
||||||
CHECK_EQ(c5.name(), "channel_b");
|
|
||||||
CHECK_EQ(c5.platforms(), PlatformSet({ platform }));
|
CHECK_EQ(c5.platforms(), PlatformSet({ platform }));
|
||||||
|
|
||||||
std::string value6a = "http://localhost:8000/conda-forge[noarch]";
|
std::string value6a = "http://localhost:8000/conda-forge[noarch]";
|
||||||
|
@ -518,12 +486,15 @@ namespace mamba
|
||||||
TEST_CASE("add_token")
|
TEST_CASE("add_token")
|
||||||
{
|
{
|
||||||
auto& ctx = mambatests::context();
|
auto& ctx = mambatests::context();
|
||||||
ctx.authentication_info()["conda.anaconda.org"] = specs::CondaToken{ "my-12345-token" };
|
ctx.authentication_info().emplace(
|
||||||
|
"conda.anaconda.org",
|
||||||
|
specs::CondaToken{ "my-12345-token" }
|
||||||
|
);
|
||||||
|
|
||||||
ChannelContext channel_context{ ctx };
|
ChannelContext channel_context{ ctx };
|
||||||
|
|
||||||
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
|
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
|
||||||
CHECK_EQ(chan.token(), "my-12345-token");
|
CHECK_EQ(chan.url().token(), "my-12345-token");
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
chan.urls(true),
|
chan.urls(true),
|
||||||
UrlSet({ "https://conda.anaconda.org/t/my-12345-token/conda-forge/noarch" })
|
UrlSet({ "https://conda.anaconda.org/t/my-12345-token/conda-forge/noarch" })
|
||||||
|
@ -534,15 +505,16 @@ namespace mamba
|
||||||
TEST_CASE("add_multiple_tokens")
|
TEST_CASE("add_multiple_tokens")
|
||||||
{
|
{
|
||||||
auto& ctx = mambatests::context();
|
auto& ctx = mambatests::context();
|
||||||
ctx.authentication_info()["conda.anaconda.org"] = specs::CondaToken{ "base-token" };
|
ctx.authentication_info().emplace("conda.anaconda.org", specs::CondaToken{ "base-token" });
|
||||||
ctx.authentication_info()["conda.anaconda.org/conda-forge"] = specs::CondaToken{
|
ctx.authentication_info().emplace(
|
||||||
"channel-token"
|
"conda.anaconda.org/conda-forge",
|
||||||
};
|
specs::CondaToken{ "channel-token" }
|
||||||
|
);
|
||||||
|
|
||||||
ChannelContext channel_context{ ctx };
|
ChannelContext channel_context{ ctx };
|
||||||
|
|
||||||
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
|
const auto& chan = channel_context.make_channel("conda-forge[noarch]");
|
||||||
CHECK_EQ(chan.token(), "channel-token");
|
CHECK_EQ(chan.url().token(), "channel-token");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("fix_win_file_path")
|
TEST_CASE("fix_win_file_path")
|
||||||
|
@ -572,7 +544,7 @@ namespace mamba
|
||||||
ChannelContext channel_context{ mambatests::context() };
|
ChannelContext channel_context{ mambatests::context() };
|
||||||
const Channel& c = channel_context.make_channel("http://localhost:8000/");
|
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.platform_url("win-64", false), "http://localhost:8000/win-64");
|
||||||
CHECK_EQ(c.base_url(), "http://localhost:8000");
|
CHECK_EQ(c.base_url(), "http://localhost:8000/");
|
||||||
const UrlSet expected_urls({ std::string("http://localhost:8000/") + platform,
|
const UrlSet expected_urls({ std::string("http://localhost:8000/") + platform,
|
||||||
"http://localhost:8000/noarch" });
|
"http://localhost:8000/noarch" });
|
||||||
CHECK_EQ(c.urls(true), expected_urls);
|
CHECK_EQ(c.urls(true), expected_urls);
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// 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 <doctest/doctest.h>
|
||||||
|
|
||||||
|
#include "mamba/specs/authentication_info.hpp"
|
||||||
|
|
||||||
|
using namespace mamba::specs;
|
||||||
|
|
||||||
|
TEST_SUITE("specs::AuthticationDataBase")
|
||||||
|
{
|
||||||
|
TEST_CASE("mamba.org")
|
||||||
|
{
|
||||||
|
auto db = AuthenticationDataBase{ { "mamba.org", BearerToken{ "mytoken" } } };
|
||||||
|
|
||||||
|
CHECK(db.contains("mamba.org"));
|
||||||
|
CHECK_FALSE(db.contains("mamba.org/"));
|
||||||
|
|
||||||
|
CHECK(db.contains_compatible("mamba.org"));
|
||||||
|
CHECK(db.contains_compatible("mamba.org/"));
|
||||||
|
CHECK(db.contains_compatible("mamba.org/channel"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("repo.mamba.org"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("/folder"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mamba.org/")
|
||||||
|
{
|
||||||
|
auto db = AuthenticationDataBase{ { "mamba.org/", BearerToken{ "mytoken" } } };
|
||||||
|
|
||||||
|
CHECK(db.contains("mamba.org/"));
|
||||||
|
CHECK_FALSE(db.contains("mamba.org"));
|
||||||
|
|
||||||
|
CHECK(db.contains_compatible("mamba.org"));
|
||||||
|
CHECK(db.contains_compatible("mamba.org/"));
|
||||||
|
CHECK(db.contains_compatible("mamba.org/channel"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("repo.mamba.org/"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("/folder"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mamba.org/channel")
|
||||||
|
{
|
||||||
|
auto db = AuthenticationDataBase{ { "mamba.org/channel", BearerToken{ "mytoken" } } };
|
||||||
|
|
||||||
|
CHECK(db.contains("mamba.org/channel"));
|
||||||
|
CHECK_FALSE(db.contains("mamba.org"));
|
||||||
|
|
||||||
|
CHECK_FALSE(db.contains_compatible("mamba.org"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("mamba.org/"));
|
||||||
|
CHECK(db.contains_compatible("mamba.org/channel"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("repo.mamba.org/"));
|
||||||
|
CHECK_FALSE(db.contains_compatible("/folder"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,15 +22,18 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("https://repo.mamba.pm/folder/file.txt")
|
SUBCASE("https://repo.mamba.pm/folder/file.txt")
|
||||||
{
|
{
|
||||||
url.set_path("/folder/file.txt");
|
url.set_path("/folder/file.txt");
|
||||||
|
CHECK_FALSE(url.has_token());
|
||||||
CHECK_EQ(url.token(), "");
|
CHECK_EQ(url.token(), "");
|
||||||
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
||||||
|
|
||||||
url.set_token("mytoken");
|
url.set_token("mytoken");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "mytoken");
|
CHECK_EQ(url.token(), "mytoken");
|
||||||
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
||||||
CHECK_EQ(url.path(), "/t/mytoken/folder/file.txt");
|
CHECK_EQ(url.path(), "/t/mytoken/folder/file.txt");
|
||||||
|
|
||||||
CHECK(url.clear_token());
|
CHECK(url.clear_token());
|
||||||
|
CHECK_FALSE(url.has_token());
|
||||||
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
|
||||||
CHECK_EQ(url.path(), "/folder/file.txt");
|
CHECK_EQ(url.path(), "/folder/file.txt");
|
||||||
}
|
}
|
||||||
|
@ -38,6 +41,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("https://repo.mamba.pm/t/xy-12345678-1234/conda-forge/linux-64")
|
SUBCASE("https://repo.mamba.pm/t/xy-12345678-1234/conda-forge/linux-64")
|
||||||
{
|
{
|
||||||
url.set_path("/t/xy-12345678-1234/conda-forge/linux-64");
|
url.set_path("/t/xy-12345678-1234/conda-forge/linux-64");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "xy-12345678-1234");
|
CHECK_EQ(url.token(), "xy-12345678-1234");
|
||||||
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
||||||
|
|
||||||
|
@ -45,6 +49,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
{
|
{
|
||||||
CHECK_THROWS_AS(url.set_token(""), std::invalid_argument);
|
CHECK_THROWS_AS(url.set_token(""), std::invalid_argument);
|
||||||
CHECK_THROWS_AS(url.set_token("?fds:g"), std::invalid_argument);
|
CHECK_THROWS_AS(url.set_token("?fds:g"), std::invalid_argument);
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "xy-12345678-1234");
|
CHECK_EQ(url.token(), "xy-12345678-1234");
|
||||||
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
||||||
CHECK_EQ(url.path(), "/t/xy-12345678-1234/conda-forge/linux-64");
|
CHECK_EQ(url.path(), "/t/xy-12345678-1234/conda-forge/linux-64");
|
||||||
|
@ -53,6 +58,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("Clear token")
|
SUBCASE("Clear token")
|
||||||
{
|
{
|
||||||
CHECK(url.clear_token());
|
CHECK(url.clear_token());
|
||||||
|
CHECK_FALSE(url.has_token());
|
||||||
CHECK_EQ(url.token(), "");
|
CHECK_EQ(url.token(), "");
|
||||||
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
||||||
CHECK_EQ(url.path(), "/conda-forge/linux-64");
|
CHECK_EQ(url.path(), "/conda-forge/linux-64");
|
||||||
|
@ -61,6 +67,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("Set token")
|
SUBCASE("Set token")
|
||||||
{
|
{
|
||||||
url.set_token("abcd");
|
url.set_token("abcd");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "abcd");
|
CHECK_EQ(url.token(), "abcd");
|
||||||
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
|
||||||
CHECK_EQ(url.path(), "/t/abcd/conda-forge/linux-64");
|
CHECK_EQ(url.path(), "/t/abcd/conda-forge/linux-64");
|
||||||
|
@ -70,14 +77,17 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("https://repo.mamba.pm/t/xy-12345678-1234-1234-1234-123456789012")
|
SUBCASE("https://repo.mamba.pm/t/xy-12345678-1234-1234-1234-123456789012")
|
||||||
{
|
{
|
||||||
url.set_path("/t/xy-12345678-1234-1234-1234-123456789012");
|
url.set_path("/t/xy-12345678-1234-1234-1234-123456789012");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "xy-12345678-1234-1234-1234-123456789012");
|
CHECK_EQ(url.token(), "xy-12345678-1234-1234-1234-123456789012");
|
||||||
|
|
||||||
url.set_token("abcd");
|
url.set_token("abcd");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "abcd");
|
CHECK_EQ(url.token(), "abcd");
|
||||||
CHECK_EQ(url.path_without_token(), "/");
|
CHECK_EQ(url.path_without_token(), "/");
|
||||||
CHECK_EQ(url.path(), "/t/abcd/");
|
CHECK_EQ(url.path(), "/t/abcd/");
|
||||||
|
|
||||||
CHECK(url.clear_token());
|
CHECK(url.clear_token());
|
||||||
|
CHECK_FALSE(url.has_token());
|
||||||
CHECK_EQ(url.token(), "");
|
CHECK_EQ(url.token(), "");
|
||||||
CHECK_EQ(url.path_without_token(), "/");
|
CHECK_EQ(url.path_without_token(), "/");
|
||||||
CHECK_EQ(url.path(), "/");
|
CHECK_EQ(url.path(), "/");
|
||||||
|
@ -86,9 +96,11 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("https://repo.mamba.pm/bar/t/xy-12345678-1234-1234-1234-123456789012/")
|
SUBCASE("https://repo.mamba.pm/bar/t/xy-12345678-1234-1234-1234-123456789012/")
|
||||||
{
|
{
|
||||||
url.set_path("/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
url.set_path("/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
||||||
|
CHECK_FALSE(url.has_token());
|
||||||
CHECK_EQ(url.token(), ""); // Not at begining of path
|
CHECK_EQ(url.token(), ""); // Not at begining of path
|
||||||
|
|
||||||
url.set_token("abcd");
|
url.set_token("abcd");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "abcd");
|
CHECK_EQ(url.token(), "abcd");
|
||||||
CHECK_EQ(url.path_without_token(), "/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
CHECK_EQ(url.path_without_token(), "/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
||||||
CHECK_EQ(url.path(), "/t/abcd/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
CHECK_EQ(url.path(), "/t/abcd/bar/t/xy-12345678-1234-1234-1234-123456789012/");
|
||||||
|
@ -118,6 +130,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
SUBCASE("Parse")
|
SUBCASE("Parse")
|
||||||
{
|
{
|
||||||
url = CondaURL::parse("mamba.org/t/xy-12345678-1234-1234-1234-123456789012");
|
url = CondaURL::parse("mamba.org/t/xy-12345678-1234-1234-1234-123456789012");
|
||||||
|
CHECK(url.has_token());
|
||||||
CHECK_EQ(url.token(), "xy-12345678-1234-1234-1234-123456789012");
|
CHECK_EQ(url.token(), "xy-12345678-1234-1234-1234-123456789012");
|
||||||
CHECK_EQ(url.path_without_token(), "/");
|
CHECK_EQ(url.path_without_token(), "/");
|
||||||
CHECK_EQ(url.path(), "/t/xy-12345678-1234-1234-1234-123456789012/");
|
CHECK_EQ(url.path(), "/t/xy-12345678-1234-1234-1234-123456789012/");
|
||||||
|
@ -365,7 +378,7 @@ TEST_SUITE("specs::CondaURL")
|
||||||
CHECK_EQ(url.pretty_str(CondaURL::StripScheme::no, '/'), "https://mamba.org/page");
|
CHECK_EQ(url.pretty_str(CondaURL::StripScheme::no, '/'), "https://mamba.org/page");
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Credentail option")
|
SUBCASE("Credential option")
|
||||||
{
|
{
|
||||||
CondaURL url = {};
|
CondaURL url = {};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2023, 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 <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
|
#include "mamba/util/tuple_hash.hpp"
|
||||||
|
|
||||||
|
using namespace mamba::util;
|
||||||
|
|
||||||
|
|
||||||
|
TEST_SUITE("util::tuple_hash")
|
||||||
|
{
|
||||||
|
TEST_CASE("hash_tuple")
|
||||||
|
{
|
||||||
|
const auto t1 = std::tuple{ 33, std::string("hello") };
|
||||||
|
CHECK_NE(hash_tuple(t1), 0);
|
||||||
|
|
||||||
|
// Hash colision are hard to predict, but this is so trivial it is likely a bug if it fails.
|
||||||
|
const auto t2 = std::tuple{ 0, std::string("hello") };
|
||||||
|
CHECK_NE(hash_tuple(t1), hash_tuple(t2));
|
||||||
|
|
||||||
|
const auto t3 = std::tuple{ std::string("hello"), 33 };
|
||||||
|
CHECK_NE(hash_tuple(t1), hash_tuple(t3));
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,9 @@ TEST_SUITE("util::URL")
|
||||||
{
|
{
|
||||||
URL url{};
|
URL url{};
|
||||||
CHECK_EQ(url.scheme(), URL::https);
|
CHECK_EQ(url.scheme(), URL::https);
|
||||||
|
CHECK_FALSE(url.has_user());
|
||||||
CHECK_EQ(url.user(), "");
|
CHECK_EQ(url.user(), "");
|
||||||
|
CHECK_FALSE(url.has_password());
|
||||||
CHECK_EQ(url.password(), "");
|
CHECK_EQ(url.password(), "");
|
||||||
CHECK_EQ(url.port(), "");
|
CHECK_EQ(url.port(), "");
|
||||||
CHECK_EQ(url.host(), URL::localhost);
|
CHECK_EQ(url.host(), URL::localhost);
|
||||||
|
@ -60,7 +62,9 @@ TEST_SUITE("util::URL")
|
||||||
|
|
||||||
CHECK_EQ(url.scheme(), "https");
|
CHECK_EQ(url.scheme(), "https");
|
||||||
CHECK_EQ(url.host(), "mamba.org");
|
CHECK_EQ(url.host(), "mamba.org");
|
||||||
|
CHECK(url.has_user());
|
||||||
CHECK_EQ(url.user(), "user");
|
CHECK_EQ(url.user(), "user");
|
||||||
|
CHECK(url.has_password());
|
||||||
CHECK_EQ(url.password(), "pass:word");
|
CHECK_EQ(url.password(), "pass:word");
|
||||||
CHECK_EQ(url.port(), "8080");
|
CHECK_EQ(url.port(), "8080");
|
||||||
CHECK_EQ(url.path(), "/folder/file.html");
|
CHECK_EQ(url.path(), "/folder/file.html");
|
||||||
|
@ -110,10 +114,12 @@ TEST_SUITE("util::URL")
|
||||||
CHECK_EQ(url.path(), "/C:/folder/file.txt");
|
CHECK_EQ(url.path(), "/C:/folder/file.txt");
|
||||||
if (on_win)
|
if (on_win)
|
||||||
{
|
{
|
||||||
|
CHECK_EQ(url.path(URL::Decode::no), "/C:/folder/file.txt");
|
||||||
CHECK_EQ(url.pretty_path(), "C:/folder/file.txt");
|
CHECK_EQ(url.pretty_path(), "C:/folder/file.txt");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
CHECK_EQ(url.path(URL::Decode::no), "/C%3A/folder/file.txt");
|
||||||
CHECK_EQ(url.pretty_path(), "/C:/folder/file.txt");
|
CHECK_EQ(url.pretty_path(), "/C:/folder/file.txt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,16 +298,10 @@ TEST_SUITE("util::URL")
|
||||||
|
|
||||||
SUBCASE("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333")
|
SUBCASE("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333")
|
||||||
{
|
{
|
||||||
|
// Not a valid IETF RFC 3986+ URL, but Curl parses it anyways.
|
||||||
|
// Undefined behavior, no assumptions are made
|
||||||
const URL url = URL::parse("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333");
|
const URL url = URL::parse("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333");
|
||||||
CHECK_EQ(url.scheme(), "https");
|
CHECK_NE(url.host(URL::Decode::no), "mamba%f0%9f%86%92%f0%9f%94%ac.org");
|
||||||
CHECK_EQ(url.host(), "mamba🆒🔬.org");
|
|
||||||
CHECK_EQ(url.path(), "/this/is/a/path/");
|
|
||||||
CHECK_EQ(url.pretty_path(), "/this/is/a/path/");
|
|
||||||
CHECK_EQ(url.user(), "");
|
|
||||||
CHECK_EQ(url.password(), "");
|
|
||||||
CHECK_EQ(url.port(), "");
|
|
||||||
CHECK_EQ(url.query(), "query=123&xyz=3333");
|
|
||||||
CHECK_EQ(url.fragment(), "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("file://C:/Users/wolfv/test/document.json")
|
SUBCASE("file://C:/Users/wolfv/test/document.json")
|
||||||
|
@ -312,14 +312,8 @@ TEST_SUITE("util::URL")
|
||||||
CHECK_EQ(url.scheme(), "file");
|
CHECK_EQ(url.scheme(), "file");
|
||||||
CHECK_EQ(url.host(), "");
|
CHECK_EQ(url.host(), "");
|
||||||
CHECK_EQ(url.path(), "/C:/Users/wolfv/test/document.json");
|
CHECK_EQ(url.path(), "/C:/Users/wolfv/test/document.json");
|
||||||
if (on_win)
|
CHECK_EQ(url.path(URL::Decode::no), "/C:/Users/wolfv/test/document.json");
|
||||||
{
|
CHECK_EQ(url.pretty_path(), "C:/Users/wolfv/test/document.json");
|
||||||
CHECK_EQ(url.pretty_path(), "C:/Users/wolfv/test/document.json");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHECK_EQ(url.pretty_path(), "/C:/Users/wolfv/test/document.json");
|
|
||||||
}
|
|
||||||
CHECK_EQ(url.user(), "");
|
CHECK_EQ(url.user(), "");
|
||||||
CHECK_EQ(url.password(), "");
|
CHECK_EQ(url.password(), "");
|
||||||
CHECK_EQ(url.port(), "");
|
CHECK_EQ(url.port(), "");
|
||||||
|
@ -330,19 +324,39 @@ TEST_SUITE("util::URL")
|
||||||
|
|
||||||
SUBCASE("file:///home/wolfv/test/document.json")
|
SUBCASE("file:///home/wolfv/test/document.json")
|
||||||
{
|
{
|
||||||
if (!on_win)
|
const URL url = URL::parse("file:///home/wolfv/test/document.json");
|
||||||
{
|
CHECK_EQ(url.scheme(), "file");
|
||||||
const URL url = URL::parse("file:///home/wolfv/test/document.json");
|
CHECK_EQ(url.host(), "");
|
||||||
CHECK_EQ(url.scheme(), "file");
|
CHECK_EQ(url.path(), "/home/wolfv/test/document.json");
|
||||||
CHECK_EQ(url.host(), "");
|
CHECK_EQ(url.pretty_path(), "/home/wolfv/test/document.json");
|
||||||
CHECK_EQ(url.path(), "/home/wolfv/test/document.json");
|
CHECK_EQ(url.user(), "");
|
||||||
CHECK_EQ(url.pretty_path(), "/home/wolfv/test/document.json");
|
CHECK_EQ(url.password(), "");
|
||||||
CHECK_EQ(url.user(), "");
|
CHECK_EQ(url.port(), "");
|
||||||
CHECK_EQ(url.password(), "");
|
CHECK_EQ(url.query(), "");
|
||||||
CHECK_EQ(url.port(), "");
|
CHECK_EQ(url.fragment(), "");
|
||||||
CHECK_EQ(url.query(), "");
|
}
|
||||||
CHECK_EQ(url.fragment(), "");
|
|
||||||
}
|
SUBCASE("file:///home/great:doc.json")
|
||||||
|
{
|
||||||
|
// Not a valid IETF RFC 3986+ URL, but Curl parses it anyways.
|
||||||
|
// Undefined behavior, no assumptions are made
|
||||||
|
const URL url = URL::parse("file:///home/great:doc.json");
|
||||||
|
CHECK_NE(url.path(URL::Decode::no), "/home/great%3Adoc.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("file:///home/great%3Adoc.json")
|
||||||
|
{
|
||||||
|
const URL url = URL::parse("file:///home/great%3Adoc.json");
|
||||||
|
CHECK_EQ(url.scheme(), "file");
|
||||||
|
CHECK_EQ(url.host(), "");
|
||||||
|
CHECK_EQ(url.path(), "/home/great:doc.json");
|
||||||
|
CHECK_EQ(url.path(URL::Decode::no), "/home/great%3Adoc.json");
|
||||||
|
CHECK_EQ(url.pretty_path(), "/home/great:doc.json");
|
||||||
|
CHECK_EQ(url.user(), "");
|
||||||
|
CHECK_EQ(url.password(), "");
|
||||||
|
CHECK_EQ(url.port(), "");
|
||||||
|
CHECK_EQ(url.query(), "");
|
||||||
|
CHECK_EQ(url.fragment(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("https://169.254.0.0/page")
|
SUBCASE("https://169.254.0.0/page")
|
||||||
|
@ -679,4 +693,54 @@ TEST_SUITE("util::URL")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Comparison")
|
||||||
|
{
|
||||||
|
URL url{};
|
||||||
|
url.set_scheme("https");
|
||||||
|
url.set_user("user");
|
||||||
|
url.set_password("password");
|
||||||
|
url.set_host("mamba.org");
|
||||||
|
url.set_port("33");
|
||||||
|
url.set_path("/folder/file.html");
|
||||||
|
auto other = url;
|
||||||
|
|
||||||
|
CHECK_EQ(url, other);
|
||||||
|
|
||||||
|
SUBCASE("Different scheme")
|
||||||
|
{
|
||||||
|
other.set_scheme("ftp");
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Different hosts")
|
||||||
|
{
|
||||||
|
other.clear_host();
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Different users")
|
||||||
|
{
|
||||||
|
other.clear_user();
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Different passwords")
|
||||||
|
{
|
||||||
|
other.clear_password();
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Different ports")
|
||||||
|
{
|
||||||
|
other.clear_port();
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Different path")
|
||||||
|
{
|
||||||
|
other.clear_path();
|
||||||
|
CHECK_NE(url, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,36 +48,6 @@ TEST_SUITE("util::url_manip")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("concat_scheme_url")
|
|
||||||
{
|
|
||||||
auto url = concat_scheme_url("https", "mamba.com");
|
|
||||||
CHECK_EQ(url, "https://mamba.com");
|
|
||||||
|
|
||||||
url = concat_scheme_url("file", "C:/some_folder");
|
|
||||||
CHECK_EQ(url, "file:///C:/some_folder");
|
|
||||||
|
|
||||||
url = concat_scheme_url("file", "some_folder");
|
|
||||||
CHECK_EQ(url, "file://some_folder");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("build_url")
|
|
||||||
{
|
|
||||||
auto url = build_url(std::nullopt, "https", "mamba.com", true);
|
|
||||||
CHECK_EQ(url, "https://mamba.com");
|
|
||||||
|
|
||||||
url = build_url(std::nullopt, "https", "mamba.com", false);
|
|
||||||
CHECK_EQ(url, "https://mamba.com");
|
|
||||||
|
|
||||||
url = build_url(std::optional<std::string>("auth"), "https", "mamba.com", false);
|
|
||||||
CHECK_EQ(url, "https://mamba.com");
|
|
||||||
|
|
||||||
url = build_url(std::optional<std::string>("auth"), "https", "mamba.com", true);
|
|
||||||
CHECK_EQ(url, "https://auth@mamba.com");
|
|
||||||
|
|
||||||
url = build_url(std::optional<std::string>(""), "https", "mamba.com", true);
|
|
||||||
CHECK_EQ(url, "https://@mamba.com");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("split_platform")
|
TEST_CASE("split_platform")
|
||||||
{
|
{
|
||||||
std::string platform_found, cleaned_url;
|
std::string platform_found, cleaned_url;
|
||||||
|
|
|
@ -136,11 +136,6 @@ class Channel:
|
||||||
) -> typing.Set[typing.Tuple[str, str]]: ...
|
) -> typing.Set[typing.Tuple[str, str]]: ...
|
||||||
def urls(self, with_credentials: bool = True) -> typing.Set[str]: ...
|
def urls(self, with_credentials: bool = True) -> typing.Set[str]: ...
|
||||||
@property
|
@property
|
||||||
def auth(self) -> typing.Optional[str]:
|
|
||||||
"""
|
|
||||||
:type: typing.Optional[str]
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def canonical_name(self) -> str:
|
def canonical_name(self) -> str:
|
||||||
"""
|
"""
|
||||||
:type: str
|
:type: str
|
||||||
|
@ -156,25 +151,10 @@ class Channel:
|
||||||
:type: str
|
:type: str
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def package_filename(self) -> typing.Optional[str]:
|
|
||||||
"""
|
|
||||||
:type: typing.Optional[str]
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def platforms(self) -> typing.Set[str]:
|
def platforms(self) -> typing.Set[str]:
|
||||||
"""
|
"""
|
||||||
:type: typing.Set[str]
|
:type: typing.Set[str]
|
||||||
"""
|
"""
|
||||||
@property
|
|
||||||
def scheme(self) -> str:
|
|
||||||
"""
|
|
||||||
:type: str
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def token(self) -> typing.Optional[str]:
|
|
||||||
"""
|
|
||||||
:type: typing.Optional[str]
|
|
||||||
"""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ChannelPriority:
|
class ChannelPriority:
|
||||||
|
|
|
@ -1147,12 +1147,8 @@ PYBIND11_MODULE(bindings, m)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
.def_property_readonly("scheme", &Channel::scheme)
|
|
||||||
.def_property_readonly("location", &Channel::location)
|
.def_property_readonly("location", &Channel::location)
|
||||||
.def_property_readonly("name", &Channel::name)
|
.def_property_readonly("name", &Channel::name)
|
||||||
.def_property_readonly("auth", &Channel::auth)
|
|
||||||
.def_property_readonly("token", &Channel::token)
|
|
||||||
.def_property_readonly("package_filename", &Channel::package_filename)
|
|
||||||
.def_property_readonly("platforms", &Channel::platforms)
|
.def_property_readonly("platforms", &Channel::platforms)
|
||||||
.def_property_readonly("canonical_name", &Channel::canonical_name)
|
.def_property_readonly("canonical_name", &Channel::canonical_name)
|
||||||
.def("urls", &Channel::urls, py::arg("with_credentials") = true)
|
.def("urls", &Channel::urls, py::arg("with_credentials") = true)
|
||||||
|
|
|
@ -106,7 +106,7 @@ class MambaSolver:
|
||||||
pkg_cache_path = self.context.pkgs_dirs
|
pkg_cache_path = self.context.pkgs_dirs
|
||||||
|
|
||||||
package_cache = libmambapy.MultiPackageCache(pkg_cache_path)
|
package_cache = libmambapy.MultiPackageCache(pkg_cache_path)
|
||||||
return libmambapy.Transaction(api_solver, package_cache)
|
return libmambapy.Transaction(self.pool, api_solver, package_cache)
|
||||||
|
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
|
|
|
@ -263,19 +263,8 @@ def init_api_context(use_mamba_experimental=False):
|
||||||
|
|
||||||
|
|
||||||
def to_conda_channel(channel, platform):
|
def to_conda_channel(channel, platform):
|
||||||
if channel.scheme == "file":
|
return CondaChannel.from_value(
|
||||||
return CondaChannel.from_value(
|
channel.platform_url(platform, with_credentials=False)
|
||||||
channel.platform_url(platform, with_credentials=False)
|
|
||||||
)
|
|
||||||
|
|
||||||
return CondaChannel(
|
|
||||||
channel.scheme,
|
|
||||||
channel.auth,
|
|
||||||
channel.location,
|
|
||||||
channel.token,
|
|
||||||
channel.name,
|
|
||||||
platform,
|
|
||||||
channel.package_filename,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ set_env_command(CLI::App* com, Configuration& config)
|
||||||
dependencies << "- ";
|
dependencies << "- ";
|
||||||
if (channel_subdir)
|
if (channel_subdir)
|
||||||
{
|
{
|
||||||
dependencies << channel.name() << "/" << v.subdir << "::";
|
dependencies << channel.canonical_name() << "/" << v.subdir << "::";
|
||||||
}
|
}
|
||||||
dependencies << v.name << "=" << v.version;
|
dependencies << v.name << "=" << v.version;
|
||||||
if (!no_build)
|
if (!no_build)
|
||||||
|
@ -200,7 +200,7 @@ set_env_command(CLI::App* com, Configuration& config)
|
||||||
dependencies << "\n";
|
dependencies << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
channels.insert(channel.name());
|
channels.insert(channel.canonical_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& c : channels)
|
for (const auto& c : channels)
|
||||||
|
|
Loading…
Reference in New Issue