Skip to content

Commit ee5b093

Browse files
authored
Support for mambajs's environment lockfile format (#4085)
1 parent cd0e161 commit ee5b093

39 files changed

+7877
-361
lines changed

_typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pn = "pn"
55
Ome = "Ome"
66
haa = "haa"
77
"fo" = "fo"
8+
"ba" = "ba"

libmamba/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ set(
222222
${LIBMAMBA_SOURCE_DIR}/core/context.cpp
223223
${LIBMAMBA_SOURCE_DIR}/core/download_progress_bar.cpp
224224
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile.cpp
225+
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile_impl.hpp
226+
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile_conda.cpp
227+
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile_mambajs.cpp
225228
${LIBMAMBA_SOURCE_DIR}/core/environments_manager.cpp
226229
${LIBMAMBA_SOURCE_DIR}/core/error_handling.cpp
227230
${LIBMAMBA_SOURCE_DIR}/core/execution.cpp

libmamba/include/mamba/core/env_lockfile.hpp

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,38 @@ namespace mamba
2525
class Context;
2626
class ChannelContext;
2727

28-
enum class file_parsing_error_code
28+
enum class lockfile_parsing_error_code
2929
{
30-
unknown_failure, /// Something failed while parsing but we can't identify what.
31-
unsupported_version, /// The version of the file does not matched supported ver.
32-
parsing_failure, /// The content of the file doesnt match the expected format/language
33-
/// structure or constraints.
34-
invalid_data, /// The structure of the data in the file is fine but some fields have
35-
/// invalid values for our purpose.
30+
unknown_failure, ///< Something failed while parsing but we can't identify what.
31+
unsupported_version, ///< The version of the file does not matched supported ver.
32+
parsing_failure, ///< The content of the file doesn't match the expected format/language
33+
///< structure or constraints.
34+
invalid_data, ///< The structure of the data in the file is fine but some fields have
35+
///< invalid values for our purpose.
36+
not_env_lockfile ///< The file doesn't seem to be a valid or supported lockfile file
37+
///< format.
3638
};
3739

38-
struct EnvLockFileError
40+
struct EnvLockFileError // TODO: inherit from mamba error
3941
{
40-
file_parsing_error_code parsing_error_code = file_parsing_error_code::unknown_failure;
41-
std::optional<std::type_index> yaml_error_type{};
42+
lockfile_parsing_error_code parsing_error_code = lockfile_parsing_error_code::unknown_failure;
43+
std::optional<std::type_index> error_type{};
4244

43-
static const EnvLockFileError& get_details(const mamba_error& error)
45+
static auto get_details(const mamba_error& error) -> const EnvLockFileError&
4446
{
4547
return std::any_cast<const EnvLockFileError&>(error.data());
4648
}
4749

4850
template <typename StringT>
49-
static mamba_error make_error(
50-
file_parsing_error_code error_code,
51+
static auto make_error(
52+
lockfile_parsing_error_code error_code,
5153
StringT&& msg,
52-
std::optional<std::type_index> yaml_error_type = std::nullopt
53-
)
54+
std::optional<std::type_index> error_type = std::nullopt
55+
) -> mamba_error
5456
{
5557
return mamba_error{ std::forward<StringT>(msg),
5658
mamba_error_code::env_lockfile_parsing_failed,
57-
EnvLockFileError{ error_code, yaml_error_type } };
59+
EnvLockFileError{ error_code, error_type } };
5860
}
5961
};
6062

@@ -64,7 +66,8 @@ namespace mamba
6466

6567
struct Channel
6668
{
67-
std::string url;
69+
std::string name;
70+
std::vector<std::string> urls;
6871
std::vector<std::string> used_env_vars;
6972
};
7073

@@ -91,18 +94,62 @@ namespace mamba
9194
{
9295
}
9396

94-
std::vector<specs::PackageInfo> get_packages_for(
95-
std::string_view category,
96-
std::string_view platform,
97-
std::string_view manager
98-
) const;
97+
struct PackageFilter
98+
{
99+
std::optional<std::string> category = std::nullopt;
100+
std::optional<std::string> platform = std::nullopt;
101+
std::optional<std::string> manager = std::nullopt;
102+
bool allow_no_platform = false; // will match empty platform
103+
104+
auto matches(const Package& package) const -> bool
105+
{
106+
bool matches_platform = not platform.has_value() or (package.platform == *platform)
107+
or (package.platform == "noarch");
108+
if (platform.has_value() and not matches_platform and allow_no_platform)
109+
{
110+
matches_platform = package.platform.empty();
111+
}
112+
113+
return matches_platform
114+
and (not category.has_value() or (package.category == *category))
115+
and (not manager.has_value() or (package.manager == *manager));
116+
}
117+
118+
auto operator()(const Package& package) const -> bool
119+
{
120+
return matches(package);
121+
}
122+
};
123+
124+
template <typename F>
125+
requires std::is_invocable_r_v<bool, F, const Package&>
126+
auto get_packages_for(PackageFilter filter, F predicate) const
127+
-> std::vector<specs::PackageInfo>
128+
{
129+
std::vector<specs::PackageInfo> results;
130+
131+
for (const auto& package : m_packages)
132+
{
133+
if (filter.matches(package) and predicate(package))
134+
{
135+
results.push_back(package.info);
136+
}
137+
}
138+
139+
return results;
140+
}
141+
142+
auto get_packages_for(PackageFilter filter) const -> std::vector<specs::PackageInfo>
143+
{
144+
return get_packages_for(std::move(filter), [](const auto&) { return true; });
145+
}
99146

100-
const std::vector<Package>& get_all_packages() const
147+
auto get_all_packages() const -> const std::vector<Package>&
101148
{
102149
return m_packages;
103150
}
104151

105-
const Meta& get_metadata() const
152+
auto get_metadata() const -> const Meta&
106153
{
107154
return m_metadata;
108155
}
@@ -113,14 +160,37 @@ namespace mamba
113160
std::vector<Package> m_packages;
114161
};
115162

116-
/// Read an environment lock YAML file and returns it's structured content or an error if
163+
/// Describes a format of environment lockfile file supported by this library.
164+
enum class EnvLockfileFormat
165+
{
166+
undefined, ///< We don't know the format of the file.
167+
conda_yaml, ///< conda's yaml-based environment lockfile format.
168+
mambajs_json ///< mambajs's json-based environment lockfile format.
169+
};
170+
171+
/// Read an environment lock-file and returns it's structured content or an error if
117172
/// failed.
118-
tl::expected<EnvironmentLockFile, mamba_error>
119-
read_environment_lockfile(const mamba::fs::u8path& lockfile_location);
173+
///
174+
/// @param lockfile_location The filesystem path to the file to open and read.
175+
/// @param file_format The expected file format of the file. If `undefined`, which is the
176+
/// default value, we guess based on the file's extension and content.
177+
auto read_environment_lockfile(
178+
const fs::u8path& lockfile_location,
179+
EnvLockfileFormat file_format = EnvLockfileFormat::undefined
180+
) -> tl::expected<EnvironmentLockFile, mamba_error>;
181+
120182

183+
/// Returns `true` if the filename matches names of files which should be interpreted as conda
184+
/// or mambajs environment lockfile.
185+
/// NOTE: this does not check if the file exists.
186+
auto is_env_lockfile_name(std::string_view filename) -> bool;
121187

122188
/// Returns `true` if the filename matches names of files which should be interpreted as conda
123-
/// environment lockfile. NOTE: this does not check if the file exists.
124-
bool is_env_lockfile_name(std::string_view filename);
189+
/// NOTE: this does not check if the file exists.
190+
auto is_conda_env_lockfile_name(std::string_view filename) -> bool;
191+
192+
/// Deduce the environment lockfile format of a file path based on it's filename.
193+
/// TODO: more info?
194+
auto deduce_env_lockfile_format(const fs::u8path& lockfile_location) -> EnvLockfileFormat;
125195
}
126196
#endif

libmamba/include/mamba/specs/package_info.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ namespace mamba::specs
6666
PackageType package_type = PackageType::Unknown;
6767

6868
[[nodiscard]] static auto from_url(std::string_view url) -> expected_parse_t<PackageInfo>;
69+
[[nodiscard]] auto url_for_channel(std::string_view channel_mirror_url) const -> std::string;
70+
[[nodiscard]] auto
71+
url_for_channel_platform(std::string_view channel_mirror_platform_url) const -> std::string;
6972

7073
PackageInfo() = default;
7174
explicit PackageInfo(std::string name);

libmamba/src/api/configuration.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,18 @@ namespace mamba
16281628
[&](std::vector<std::string>& value)
16291629
{ return detail::file_specs_hook(*this, value); }
16301630
)
1631-
.description("File (yaml, explicit or plain)"));
1631+
.description("File providing package specifications (yaml, explicit or plain, or json)")
1632+
// clang-format off
1633+
.long_description(unindent(R"(
1634+
File providing package specifications, either an
1635+
environment file (yaml, explicit or plain) or a
1636+
an environment lockfile.
1637+
Valid environment lockfile formats: conda-lock file
1638+
(see https://github.com/conda/conda-lock , file name must end with '-lock.yaml'
1639+
or '-lock.yml') or mambajs's lockfile
1640+
(see https://github.com/emscripten-forge/mambajs/blob/main/packages/mambajs-core/schema/ )
1641+
)")));
1642+
// clang-format on
16321643

16331644
insert(Configurable("no_pin", false)
16341645
.group("Solver")

0 commit comments

Comments
 (0)