Skip to content

Commit 635aca1

Browse files
committed
fix: Clone pip packages with environment
Signed-off-by: Julien Jerphanion <git@jjerphan.xyz>
1 parent b98c281 commit 635aca1

File tree

3 files changed

+305
-89
lines changed

3 files changed

+305
-89
lines changed

libmamba/src/api/create.cpp

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
//
55
// The full license is in the file LICENSE, distributed with this software.
66

7+
#include <fstream>
78
#include <iostream>
89

10+
#include <yaml-cpp/yaml.h>
11+
912
#include "mamba/api/configuration.hpp"
1013
#include "mamba/api/create.hpp"
1114
#include "mamba/api/install.hpp"
@@ -57,6 +60,7 @@ namespace mamba
5760
void clone_environment(
5861
Context& ctx,
5962
ChannelContext& channel_context,
63+
Configuration& config,
6064
const fs::u8path& source_prefix,
6165
bool create_env,
6266
bool remove_prefix_on_failure
@@ -77,40 +81,85 @@ namespace mamba
7781
}
7882
const PrefixData& source_prefix_data = maybe_prefix_data.value();
7983

80-
std::vector<std::string> explicit_urls;
81-
const auto records = source_prefix_data.sorted_records();
82-
explicit_urls.reserve(records.size());
83-
84-
for (const auto& pkg : records)
84+
// Export source environment to YAML format
85+
TemporaryFile yaml_file("mamba_env_", ".yml");
8586
{
86-
if (pkg.package_url.empty())
87+
YAML::Emitter out;
88+
out << YAML::BeginMap;
89+
90+
// Add dependencies section
91+
out << YAML::Key << "dependencies" << YAML::Value << YAML::BeginSeq;
92+
93+
// Add conda packages
94+
const auto records = source_prefix_data.sorted_records();
95+
for (const auto& pkg : records)
8796
{
88-
// Fallback to channel/platform/filename if possible.
89-
if (pkg.channel.empty() || pkg.platform.empty() || pkg.filename.empty())
90-
{
91-
LOG_WARNING << "Skipping package without URL information while cloning: "
92-
<< pkg.name;
93-
continue;
94-
}
95-
const auto url = pkg.url_for_channel(pkg.channel);
96-
explicit_urls.push_back(url);
97+
out << fmt::format("{}={}={}", pkg.name, pkg.version, pkg.build_string);
9798
}
98-
else
99+
100+
// Add pip packages as a sub-map
101+
const auto& pip_records = source_prefix_data.pip_records();
102+
if (!pip_records.empty())
99103
{
100-
std::string url = pkg.package_url;
101-
if (!pkg.sha256.empty())
104+
out << YAML::BeginMap;
105+
out << YAML::Key << "pip" << YAML::Value << YAML::BeginSeq;
106+
for (const auto& [name, pkg] : pip_records)
102107
{
103-
url += "#sha256:" + pkg.sha256;
108+
out << (pkg.name + "==" + pkg.version);
104109
}
105-
else if (!pkg.md5.empty())
106-
{
107-
url += "#" + pkg.md5;
108-
}
109-
explicit_urls.push_back(std::move(url));
110+
out << YAML::EndSeq;
111+
out << YAML::EndMap;
110112
}
113+
114+
out << YAML::EndSeq;
115+
out << YAML::EndMap;
116+
117+
// Write YAML to temporary file
118+
std::ofstream yaml_out = open_ofstream(yaml_file.path());
119+
yaml_out << out.c_str();
120+
yaml_out.close();
111121
}
112122

113-
install_explicit_specs(ctx, channel_context, explicit_urls, create_env, remove_prefix_on_failure);
123+
// Read YAML file and populate config
124+
const auto parse_result = detail::read_yaml_file(
125+
ctx,
126+
yaml_file.path().string(),
127+
ctx.platform,
128+
ctx.use_uv
129+
);
130+
131+
// Populate config with parsed YAML contents
132+
if (!parse_result.dependencies.empty())
133+
{
134+
auto& specs = config.at("specs").value<std::vector<std::string>>();
135+
specs.insert(
136+
specs.end(),
137+
parse_result.dependencies.begin(),
138+
parse_result.dependencies.end()
139+
);
140+
}
141+
142+
if (!parse_result.others_pkg_mgrs_specs.empty())
143+
{
144+
auto& others_pkg_mgrs_specs = config.at("others_pkg_mgrs_specs")
145+
.value<std::vector<detail::other_pkg_mgr_spec>>();
146+
others_pkg_mgrs_specs.insert(
147+
others_pkg_mgrs_specs.end(),
148+
parse_result.others_pkg_mgrs_specs.begin(),
149+
parse_result.others_pkg_mgrs_specs.end()
150+
);
151+
}
152+
153+
// Install packages from config
154+
const auto& install_specs_vec = config.at("specs").value<std::vector<std::string>>();
155+
install_specs(
156+
ctx,
157+
channel_context,
158+
config,
159+
install_specs_vec,
160+
create_env,
161+
remove_prefix_on_failure
162+
);
114163
}
115164
} // namespace
116165

@@ -239,7 +288,14 @@ namespace mamba
239288
{
240289
const auto clone_value = clone_cfg.value<std::string>();
241290
const auto source_prefix = compute_clone_source_prefix(ctx, clone_value);
242-
clone_environment(ctx, channel_context, source_prefix, create_env, remove_prefix_on_failure);
291+
clone_environment(
292+
ctx,
293+
channel_context,
294+
config,
295+
source_prefix,
296+
create_env,
297+
remove_prefix_on_failure
298+
);
243299
return;
244300
}
245301

libmamba/src/api/utils.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ namespace mamba
2727
{
2828
tl::expected<command_args, std::runtime_error> get_pkg_mgr_install_command(
2929
const std::string& name,
30-
const std::string& target_prefix,
30+
const fs::u8path& prefix,
3131
const fs::u8path& spec_file,
3232
pip::Update update
3333
)
3434
{
3535
const auto get_python_path = [&]
36-
{ return util::which_in("python", util::get_path_dirs(target_prefix)).string(); };
36+
{ return util::which_in("python", util::get_path_dirs(prefix)).string(); };
3737

3838
const auto get_uv_path = [&]
39-
{ return util::which_in("uv", util::get_path_dirs(target_prefix)).string(); };
39+
{ return util::which_in("uv", util::get_path_dirs(prefix)).string(); };
4040

4141
command_args cmd = [&]
4242
{
@@ -157,14 +157,21 @@ namespace mamba
157157
{
158158
specs_f << d.c_str() << '\n';
159159
}
160+
specs_f.flush();
161+
specs_f.close();
160162
}
161163

164+
// Use target_prefix to find pip/python executable
165+
const fs::u8path& target_prefix = ctx.prefix_params.target_prefix;
166+
// Ensure we use an absolute path for the requirements file
167+
const fs::u8path specs_path = fs::absolute(specs.path());
168+
162169
command_args command = [&]
163170
{
164171
const auto maybe_command = get_pkg_mgr_install_command(
165172
pkg_mgr,
166-
ctx.prefix_params.target_prefix.string(),
167-
specs.path(),
173+
target_prefix,
174+
specs_path,
168175
update
169176
);
170177
if (maybe_command)
@@ -198,6 +205,7 @@ namespace mamba
198205
fmt::print(LOG_INFO, "Calling: {}", fmt::join(command, " "));
199206

200207
auto [status, ec] = reproc::run(wrapped_command, options);
208+
201209
assert_reproc_success(options, status, ec);
202210
if (status != 0)
203211
{

0 commit comments

Comments
 (0)