mamba/micromamba/src/update.cpp

239 lines
7.1 KiB
C++

// Copyright (c) 2019, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <fmt/color.h>
#include <fmt/format.h>
#include <reproc++/run.hpp>
#include "mamba/api/channel_loader.hpp"
#include "mamba/api/configuration.hpp"
#include "mamba/api/update.hpp"
#include "mamba/core/channel_context.hpp"
#include "mamba/core/context.hpp"
#include "mamba/core/package_database_loader.hpp"
#include "mamba/core/transaction.hpp"
#include "mamba/core/util_os.hpp"
#include "mamba/util/build.hpp"
#ifdef __APPLE__
#include "mamba/core/util_os.hpp"
#endif
#include "common_options.hpp"
#include "version.hpp"
using namespace mamba; // NOLINT(build/namespaces)
void
set_update_command(CLI::App* subcom, Configuration& config)
{
init_install_options(subcom, config);
static bool prune_deps = true;
static bool update_all = false;
subcom->add_flag("--prune-deps,!--no-prune-deps", prune_deps, "Prune dependencies (default)");
subcom->get_option("specs")->description("Specs to update in the environment");
subcom->add_flag("-a,--all", update_all, "Update all packages in the environment");
subcom->callback(
[&]
{
auto update_params = UpdateParams{
update_all ? UpdateAll::Yes : UpdateAll::No,
prune_deps ? PruneDeps::Yes : PruneDeps::No,
EnvUpdate::No,
RemoveNotSpecified::No,
};
return update(config, update_params);
}
);
}
#ifdef BUILDING_MICROMAMBA
namespace
{
auto database_has_package(solver::libsolv::Database& database, specs::MatchSpec spec) -> bool
{
bool found = false;
database.for_each_package_matching(
spec,
[&](const auto&)
{
found = true;
return util::LoopControl::Break;
}
);
return found;
};
auto database_latest_package(solver::libsolv::Database& database, specs::MatchSpec spec)
-> std::optional<specs::PackageInfo>
{
auto out = std::optional<specs::PackageInfo>();
database.for_each_package_matching(
spec,
[&](auto pkg)
{
if (!out
|| (specs::Version::parse(pkg.version).value_or(specs::Version())
> specs::Version::parse(out->version).value_or(specs::Version())))
{
out = std::move(pkg);
}
}
);
return out;
};
}
int
update_self(Configuration& config, const std::optional<std::string>& version)
{
auto& ctx = config.context();
config.load();
// set target_prefix to root_prefix (irrelevant, but transaction tries to lock
// the conda-meta folder of the target_prefix)
ctx.prefix_params.target_prefix = ctx.prefix_params.root_prefix;
auto channel_context = ChannelContext::make_conda_compatible(ctx);
solver::libsolv::Database database{ channel_context.params() };
add_logger_to_database(database);
mamba::MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
auto exp_loaded = load_channels(ctx, channel_context, database, package_caches);
if (!exp_loaded)
{
throw exp_loaded.error();
}
auto matchspec = specs::MatchSpec::parse(
version ? fmt::format("micromamba={}", version.value())
: fmt::format("micromamba>{}", umamba::version())
)
.or_else([](specs::ParseError&& err) { throw std::move(err); })
.value();
auto latest_micromamba = database_latest_package(database, matchspec);
if (!latest_micromamba.has_value())
{
if (database_has_package(database, specs::MatchSpec::parse("micromamba").value()))
{
Console::instance().print(
fmt::format("\nYour micromamba version ({}) is already up to date.", umamba::version())
);
return 0;
}
else
{
throw mamba::mamba_error(
"No micromamba found in the loaded channels. Add 'conda-forge' to your config file.",
mamba_error_code::selfupdate_failure
);
}
}
Console::stream() << fmt::format(
fg(fmt::terminal_color::green),
"\n Installing micromamba version: {} (currently installed {})",
latest_micromamba.value().version,
umamba::version()
);
Console::instance().print(
fmt::format(" Fetching micromamba from {}\n", latest_micromamba.value().package_url)
);
ctx.download_only = true;
MTransaction t(ctx, database, { latest_micromamba.value() }, package_caches);
auto exp_prefix_data = PrefixData::create(ctx.prefix_params.root_prefix, channel_context);
if (!exp_prefix_data)
{
throw exp_prefix_data.error();
}
PrefixData& prefix_data = exp_prefix_data.value();
t.execute(ctx, channel_context, prefix_data);
fs::u8path mamba_exe = get_self_exe_path();
fs::u8path mamba_exe_bkup = mamba_exe;
mamba_exe_bkup.replace_extension(mamba_exe.extension().string() + ".bkup");
fs::u8path cache_path = package_caches.get_extracted_dir_path(latest_micromamba.value())
/ latest_micromamba.value().str();
fs::rename(mamba_exe, mamba_exe_bkup);
try
{
if (util::on_win)
{
fs::copy_file(
cache_path / "Library" / "bin" / "micromamba.exe",
mamba_exe,
fs::copy_options::overwrite_existing
);
}
else
{
fs::copy_file(
cache_path / "bin" / "micromamba",
mamba_exe,
fs::copy_options::overwrite_existing
);
#ifdef __APPLE__
codesign(mamba_exe, false);
#endif
fs::remove(mamba_exe_bkup);
}
}
catch (std::exception& e)
{
LOG_ERROR << "Error while updating micromamba: " << e.what();
LOG_ERROR << "Restoring backup";
fs::remove(mamba_exe);
fs::rename(mamba_exe_bkup, mamba_exe);
throw;
}
// Command to reinit shell from the new executable.
std::vector<std::string> command = { mamba_exe, "shell", "reinit" };
// The options for the process
reproc::options options;
options.redirect.parent = true; // Redirect output
options.deadline = reproc::milliseconds(5000);
// Run the command in a redirected process
int status = -1;
std::error_code ec;
std::tie(status, ec) = reproc::run(command, options);
if (ec)
{
std::cerr << "error: " << ec.message() << std::endl;
}
return ec ? ec.value() : status;
}
void
set_self_update_command(CLI::App* subcom, Configuration& config)
{
init_install_options(subcom, config);
static std::optional<std::string> version;
subcom->add_option("--version", version, "Install specific micromamba version")
->option_text("VERSION");
subcom->callback([&] { return update_self(config, version); });
}
#endif