mirror of https://github.com/mamba-org/mamba.git
add micromamba self-update functionality (#2023)
This commit is contained in:
parent
405cb607fa
commit
c889fa6855
|
@ -97,12 +97,26 @@ function Invoke-Mamba() {
|
|||
"deactivate" {
|
||||
Exit-MambaEnvironment;
|
||||
}
|
||||
|
||||
"self-update" {
|
||||
& $Env:MAMBA_EXE $Command @OtherArgs;
|
||||
$MAMBA_EXE_BKUP = $Env:MAMBA_EXE + ".bkup";
|
||||
if (Test-Path $MAMBA_EXE_BKUP) {
|
||||
Remove-Item $MAMBA_EXE_BKUP
|
||||
}
|
||||
}
|
||||
default {
|
||||
# There may be a command we don't know want to handle
|
||||
# differently in the shell wrapper, pass it through
|
||||
# verbatim.
|
||||
& $Env:MAMBA_EXE $Command @OtherArgs;
|
||||
|
||||
# reactivate environment
|
||||
if (@("install", "update", "remove").contains($Command))
|
||||
{
|
||||
$activateCommand = (& $Env:MAMBA_EXE shell reactivate -s powershell $Args | Out-String);
|
||||
Write-Verbose "[micromamba shell reactivate --shell powershell $Args]`n$activateCommand";
|
||||
Invoke-Expression -Command $activateCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@ __MAMBA_INSERT_ROOT_PREFIX__
|
|||
@IF [%1]==[upgrade] "%~dp0_mamba_activate" reactivate
|
||||
@IF [%1]==[remove] "%~dp0_mamba_activate" reactivate
|
||||
@IF [%1]==[uninstall] "%~dp0_mamba_activate" reactivate
|
||||
@IF [%1]==[self-update] @CALL DEL /f %MAMBA_EXE%.bkup
|
||||
|
||||
@EXIT /B %errorlevel%
|
||||
|
|
|
@ -39,6 +39,14 @@ micromamba() {
|
|||
__mamba_exe "$@" || \return
|
||||
__mamba_reactivate
|
||||
;;
|
||||
self-update)
|
||||
__mamba_exe "$@" || \return
|
||||
|
||||
# remove leftover backup file on Windows
|
||||
if [ -f "$MAMBA_EXE.bkup" ]; then
|
||||
rm -f $MAMBA_EXE.bkup
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
__mamba_exe "$@"
|
||||
;;
|
||||
|
|
|
@ -152,6 +152,7 @@ namespace mamba
|
|||
bool on_ci = false;
|
||||
bool no_progress_bars = false;
|
||||
bool dry_run = false;
|
||||
bool download_only = false;
|
||||
bool always_yes = false;
|
||||
|
||||
bool allow_softlinks = false;
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace mamba
|
|||
openssl_failed,
|
||||
internal_failure,
|
||||
lockfile_failure,
|
||||
selfupdate_failure,
|
||||
};
|
||||
|
||||
class mamba_error : public std::runtime_error
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace mamba
|
|||
void set_debuglevel();
|
||||
void create_whatprovides();
|
||||
|
||||
std::vector<Id> select_solvables(Id id) const;
|
||||
std::vector<Id> select_solvables(Id id, bool sorted = false) const;
|
||||
Id matchspec2id(const std::string& ms);
|
||||
|
||||
std::optional<PackageInfo> id2pkginfo(Id id);
|
||||
|
|
|
@ -66,6 +66,8 @@ namespace mamba
|
|||
|
||||
void init_shell(const std::string& shell, const fs::u8path& conda_prefix);
|
||||
void deinit_shell(const std::string& shell, const fs::u8path& conda_prefix);
|
||||
std::vector<std::string> find_initialized_shells();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,8 @@ namespace mamba
|
|||
ConsoleFeatures get_console_features();
|
||||
int get_console_width();
|
||||
int get_console_height();
|
||||
|
||||
void codesign(const fs::u8path& path, bool verbose = false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1425,6 +1425,12 @@ namespace mamba
|
|||
.set_env_var_names()
|
||||
.description("Only display what would have been done"));
|
||||
|
||||
insert(Configurable("download_only", &ctx.download_only)
|
||||
.group("Output, Prompt and Flow Control")
|
||||
.set_env_var_names()
|
||||
.description(
|
||||
"Only download and extract packages, do not link them into environment."));
|
||||
|
||||
insert(Configurable("log_level", &ctx.logging_level)
|
||||
.group("Output, Prompt and Flow Control")
|
||||
.set_rc_configurable()
|
||||
|
|
|
@ -110,6 +110,14 @@ namespace mamba
|
|||
|
||||
deinit_shell(shell_type, shell_prefix);
|
||||
}
|
||||
else if (action == "reinit")
|
||||
{
|
||||
// re-initialize all the shell scripts after update
|
||||
for (auto& shell_type : find_initialized_shells())
|
||||
{
|
||||
shell("init", shell_type, prefix, false);
|
||||
}
|
||||
}
|
||||
else if (action == "hook")
|
||||
{
|
||||
// TODO do we need to do something wtih `shell_prefix -> root_prefix?`?
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "mamba/core/transaction_context.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
#include "mamba/core/validate.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
|
||||
#if _WIN32
|
||||
#include "../data/conda_exe.hpp"
|
||||
|
@ -706,24 +707,7 @@ namespace mamba
|
|||
#if defined(__APPLE__)
|
||||
if (binary_changed && m_pkg_info.subdir == "osx-arm64")
|
||||
{
|
||||
reproc::options options;
|
||||
options.env.behavior = reproc::env::empty;
|
||||
if (Context::instance().verbosity <= 1)
|
||||
{
|
||||
reproc::redirect silence;
|
||||
silence.type = reproc::redirect::discard;
|
||||
options.redirect.out = silence;
|
||||
options.redirect.err = silence;
|
||||
}
|
||||
|
||||
std::vector<std::string> cmd
|
||||
= { "/usr/bin/codesign", "-s", "-", "-f", dst.string() };
|
||||
auto [status, ec] = reproc::run(cmd, options);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not codesign executable")
|
||||
+ ec.message());
|
||||
}
|
||||
codesign(dst, Context::instance().verbosity > 1);
|
||||
}
|
||||
#endif
|
||||
return std::make_tuple(validate::sha256sum(dst), rel_dst.string());
|
||||
|
|
|
@ -9,6 +9,7 @@ extern "C"
|
|||
#include <solv/pool.h>
|
||||
#include <solv/solver.h>
|
||||
#include <solv/selection.h>
|
||||
#include <solv/evr.h>
|
||||
}
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
@ -87,11 +88,23 @@ namespace mamba
|
|||
return m_pool;
|
||||
}
|
||||
|
||||
std::vector<Id> MPool::select_solvables(Id matchspec) const
|
||||
std::vector<Id> MPool::select_solvables(Id matchspec, bool sorted) const
|
||||
{
|
||||
MQueue job, solvables;
|
||||
job.push(SOLVER_SOLVABLE_PROVIDES, matchspec);
|
||||
selection_solvables(m_pool, job, solvables);
|
||||
|
||||
if (sorted)
|
||||
{
|
||||
std::sort(solvables.begin(),
|
||||
solvables.end(),
|
||||
[this](Id a, Id b)
|
||||
{
|
||||
Solvable* sa = pool_id2solvable(this->m_pool, a);
|
||||
Solvable* sb = pool_id2solvable(this->m_pool, b);
|
||||
return (pool_evrcmp(this->m_pool, sa->evr, sb->evr, EVRCMP_COMPARE) > 0);
|
||||
});
|
||||
}
|
||||
return solvables.as<std::vector>();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,16 @@ namespace mamba
|
|||
{
|
||||
namespace
|
||||
{
|
||||
std::regex CONDA_INITIALIZE_RE_BLOCK("\n# >>> mamba initialize >>>(?:\n|\r\n)?"
|
||||
std::regex MAMBA_INITIALIZE_RE_BLOCK("\n?# >>> mamba initialize >>>(?:\n|\r\n)?"
|
||||
"([\\s\\S]*?)"
|
||||
"# <<< mamba initialize <<<(?:\n|\r\n)?");
|
||||
|
||||
std::regex CONDA_INITIALIZE_PS_RE_BLOCK("\n#region mamba initialize(?:\n|\r\n)?"
|
||||
std::regex MAMBA_INITIALIZE_PS_RE_BLOCK("\n?#region mamba initialize(?:\n|\r\n)?"
|
||||
"([\\s\\S]*?)"
|
||||
"#endregion(?:\n|\r\n)?");
|
||||
std::wregex MAMBA_CMDEXE_HOOK_REGEX(L"(\"[^\"]*?mamba[-_]hook\\.bat\")",
|
||||
std::regex_constants::icase);
|
||||
|
||||
}
|
||||
|
||||
std::string guess_shell()
|
||||
|
@ -129,9 +132,10 @@ namespace mamba
|
|||
|
||||
// modify registry key
|
||||
std::wstring replace_str(L"__CONDA_REPLACE_ME_123__");
|
||||
std::wregex hook_regex(L"(\"[^\"]*?mamba[-_]hook\\.bat\")", std::regex_constants::icase);
|
||||
std::wstring replaced_value = std::regex_replace(
|
||||
prev_value, hook_regex, replace_str, std::regex_constants::format_first_only);
|
||||
std::wstring replaced_value = std::regex_replace(prev_value,
|
||||
MAMBA_CMDEXE_HOOK_REGEX,
|
||||
replace_str,
|
||||
std::regex_constants::format_first_only);
|
||||
|
||||
std::wstring new_value = replaced_value;
|
||||
|
||||
|
@ -429,7 +433,7 @@ namespace mamba
|
|||
}
|
||||
|
||||
std::string result
|
||||
= std::regex_replace(rc_content, CONDA_INITIALIZE_RE_BLOCK, conda_init_content);
|
||||
= std::regex_replace(rc_content, MAMBA_INITIALIZE_RE_BLOCK, conda_init_content);
|
||||
|
||||
if (result.find("# >>> mamba initialize >>>") == std::string::npos)
|
||||
{
|
||||
|
@ -474,7 +478,7 @@ namespace mamba
|
|||
return;
|
||||
}
|
||||
|
||||
std::string result = std::regex_replace(rc_content, CONDA_INITIALIZE_RE_BLOCK, "");
|
||||
std::string result = std::regex_replace(rc_content, MAMBA_INITIALIZE_RE_BLOCK, "");
|
||||
|
||||
if (Context::instance().dry_run)
|
||||
{
|
||||
|
@ -822,7 +826,7 @@ namespace mamba
|
|||
{
|
||||
LOG_DEBUG << "Found mamba initialize. Replacing mamba initialize block.";
|
||||
profile_content = std::regex_replace(
|
||||
profile_content, CONDA_INITIALIZE_PS_RE_BLOCK, conda_init_content);
|
||||
profile_content, MAMBA_INITIALIZE_PS_RE_BLOCK, conda_init_content);
|
||||
}
|
||||
|
||||
LOG_DEBUG << "Original profile content:\n" << profile_original_content;
|
||||
|
@ -873,7 +877,7 @@ namespace mamba
|
|||
<< "#region mamba initialize\n...\n#endregion\n"
|
||||
<< termcolor::reset;
|
||||
|
||||
profile_content = std::regex_replace(profile_content, CONDA_INITIALIZE_PS_RE_BLOCK, "");
|
||||
profile_content = std::regex_replace(profile_content, MAMBA_INITIALIZE_PS_RE_BLOCK, "");
|
||||
LOG_DEBUG << "Profile content:\n" << profile_content;
|
||||
|
||||
if (Context::instance().dry_run)
|
||||
|
@ -1066,4 +1070,74 @@ namespace mamba
|
|||
|
||||
deinit_root_prefix(shell, conda_prefix);
|
||||
}
|
||||
|
||||
fs::u8path config_path_for_shell(const std::string& shell)
|
||||
{
|
||||
fs::u8path home = env::home_directory();
|
||||
fs::u8path config_path;
|
||||
if (shell == "bash")
|
||||
{
|
||||
config_path = (on_mac || on_win) ? home / ".bash_profile" : home / ".bashrc";
|
||||
}
|
||||
else if (shell == "zsh")
|
||||
{
|
||||
config_path = home / ".zshrc";
|
||||
}
|
||||
else if (shell == "xonsh")
|
||||
{
|
||||
config_path = home / ".xonshrc";
|
||||
}
|
||||
else if (shell == "csh")
|
||||
{
|
||||
config_path = home / ".tcshrc";
|
||||
}
|
||||
else if (shell == "fish")
|
||||
{
|
||||
config_path = home / ".config" / "fish" / "config.fish";
|
||||
}
|
||||
return config_path;
|
||||
}
|
||||
|
||||
std::vector<std::string> find_initialized_shells()
|
||||
{
|
||||
fs::u8path home = env::home_directory();
|
||||
|
||||
std::vector<std::string> result;
|
||||
std::vector<std::string> supported_shells = { "bash", "zsh", "xonsh", "csh", "fish" };
|
||||
for (const std::string& shell : supported_shells)
|
||||
{
|
||||
fs::u8path config_path = config_path_for_shell(shell);
|
||||
|
||||
if (fs::exists(config_path))
|
||||
{
|
||||
auto contents = read_contents(config_path);
|
||||
if (contents.find("# >>> mamba initialize >>>") != std::string::npos)
|
||||
{
|
||||
result.push_back(shell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// cmd.exe
|
||||
std::wstring reg = get_autorun_registry_key(L"Software\\Microsoft\\Command Processor");
|
||||
if (std::regex_match(reg, MAMBA_CMDEXE_HOOK_REGEX) != std::wstring::npos)
|
||||
{
|
||||
result.push_back("cmd.exe");
|
||||
}
|
||||
#endif
|
||||
// powershell
|
||||
{
|
||||
std::set<std::string> pwsh_profiles;
|
||||
for (auto& exe : std::vector<std::string>{ "powershell", "pwsh", "pwsh-preview" })
|
||||
{
|
||||
auto profile_path = find_powershell_paths(exe);
|
||||
if (!profile_path.empty() && fs::exists(profile_path))
|
||||
{
|
||||
result.push_back("powershell");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -927,6 +927,13 @@ namespace mamba
|
|||
Console::stream() << "\nTransaction starting";
|
||||
fetch_extract_packages();
|
||||
|
||||
if (ctx.download_only)
|
||||
{
|
||||
Console::stream()
|
||||
<< "Download only - packages are downloaded and extracted. Skipping the linking phase.";
|
||||
return true;
|
||||
}
|
||||
|
||||
History::UserRequest ur = History::UserRequest::prefilled();
|
||||
|
||||
TransactionRollback rollback;
|
||||
|
|
|
@ -542,4 +542,25 @@ namespace mamba
|
|||
return coninfo.srWindow.Bottom - coninfo.srWindow.Top + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void codesign(const fs::u8path& path, bool verbose)
|
||||
{
|
||||
reproc::options options;
|
||||
options.env.behavior = reproc::env::empty;
|
||||
if (!verbose)
|
||||
{
|
||||
reproc::redirect silence;
|
||||
silence.type = reproc::redirect::discard;
|
||||
options.redirect.out = silence;
|
||||
options.redirect.err = silence;
|
||||
}
|
||||
|
||||
const std::vector<std::string> cmd
|
||||
= { "/usr/bin/codesign", "-s", "-", "-f", path.string() };
|
||||
auto [status, ec] = reproc::run(cmd, options);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error(std::string("Could not codesign executable: ") + ec.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,6 +307,14 @@ class Context:
|
|||
def default_channels(self, arg0: typing.List[str]) -> None:
|
||||
pass
|
||||
@property
|
||||
def download_only(self) -> bool:
|
||||
"""
|
||||
:type: bool
|
||||
"""
|
||||
@download_only.setter
|
||||
def download_only(self, arg0: bool) -> None:
|
||||
pass
|
||||
@property
|
||||
def download_threads(self) -> int:
|
||||
"""
|
||||
:type: int
|
||||
|
@ -830,7 +838,7 @@ class Pool:
|
|||
def create_whatprovides(self) -> None: ...
|
||||
def id2pkginfo(self, id: int) -> typing.Optional[PackageInfo]: ...
|
||||
def matchspec2id(self, ms: str) -> int: ...
|
||||
def select_solvables(self, id: int) -> typing.List[int]: ...
|
||||
def select_solvables(self, id: int, sorted: bool = False) -> typing.List[int]: ...
|
||||
def set_debuglevel(self) -> None: ...
|
||||
pass
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ PYBIND11_MODULE(bindings, m)
|
|||
.def(py::init<>())
|
||||
.def("set_debuglevel", &MPool::set_debuglevel)
|
||||
.def("create_whatprovides", &MPool::create_whatprovides)
|
||||
.def("select_solvables", &MPool::select_solvables, py::arg("id"))
|
||||
.def("select_solvables", &MPool::select_solvables, py::arg("id"), py::arg("sorted") = false)
|
||||
.def("matchspec2id", &MPool::matchspec2id, py::arg("ms"))
|
||||
.def("id2pkginfo", &MPool::id2pkginfo, py::arg("id"));
|
||||
|
||||
|
@ -322,6 +322,7 @@ PYBIND11_MODULE(bindings, m)
|
|||
.def_readwrite("extract_threads", &Context::extract_threads)
|
||||
.def_readwrite("always_yes", &Context::always_yes)
|
||||
.def_readwrite("dry_run", &Context::dry_run)
|
||||
.def_readwrite("download_only", &Context::download_only)
|
||||
.def_readwrite("ssl_verify", &Context::ssl_verify)
|
||||
.def_readwrite("proxy_servers", &Context::proxy_servers)
|
||||
.def_readwrite("max_retries", &Context::max_retries)
|
||||
|
|
|
@ -82,6 +82,12 @@ init_general_options(CLI::App* subcom)
|
|||
subcom->add_flag("--dry-run", dry_run.get_cli_config<bool>(), dry_run.description())
|
||||
->group(cli_group);
|
||||
|
||||
auto& download_only = config.at("download_only");
|
||||
subcom
|
||||
->add_flag(
|
||||
"--download-only", download_only.get_cli_config<bool>(), download_only.description())
|
||||
->group(cli_group);
|
||||
|
||||
auto& experimental = config.at("experimental");
|
||||
subcom
|
||||
->add_flag(
|
||||
|
|
|
@ -48,6 +48,7 @@ init_shell_parser(CLI::App* subcom)
|
|||
subcom->add_option("action", action.get_cli_config<std::string>(), action.description())
|
||||
->check(CLI::IsMember(std::vector<std::string>({ "init",
|
||||
"deinit",
|
||||
"reinit",
|
||||
"hook",
|
||||
"activate",
|
||||
"deactivate",
|
||||
|
|
|
@ -52,6 +52,9 @@ set_umamba_command(CLI::App* com)
|
|||
= com->add_subcommand("update", "Update packages in active environment");
|
||||
set_update_command(update_subcom);
|
||||
|
||||
CLI::App* self_update_subcom = com->add_subcommand("self-update", "Update micromamba");
|
||||
set_self_update_command(self_update_subcom);
|
||||
|
||||
CLI::App* repoquery_subcom = com->add_subcommand(
|
||||
"repoquery", "Find and analyze packages in active environment or channels");
|
||||
set_repoquery_command(repoquery_subcom);
|
||||
|
|
|
@ -55,6 +55,9 @@ set_umamba_command(CLI::App* com);
|
|||
void
|
||||
set_update_command(CLI::App* subcom);
|
||||
|
||||
void
|
||||
set_self_update_command(CLI::App* subcom);
|
||||
|
||||
void
|
||||
set_repoquery_command(CLI::App* subcom);
|
||||
|
||||
|
|
|
@ -4,14 +4,135 @@
|
|||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <termcolor/termcolor.hpp>
|
||||
|
||||
#include "common_options.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/api/channel_loader.hpp"
|
||||
#include "mamba/api/shell.hpp"
|
||||
#include "mamba/api/update.hpp"
|
||||
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/transaction.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
|
||||
|
||||
using namespace mamba; // NOLINT(build/namespaces)
|
||||
|
||||
int
|
||||
update_self(const std::optional<std::string>& version)
|
||||
{
|
||||
auto& config = mamba::Configuration::instance();
|
||||
auto& ctx = mamba::Context::instance();
|
||||
config.load();
|
||||
|
||||
// set target_prefix to root_prefix (irrelevant, but transaction tries to lock
|
||||
// the conda-meta folder of the target_prefix)
|
||||
ctx.target_prefix = ctx.root_prefix;
|
||||
|
||||
mamba::MPool pool;
|
||||
mamba::MultiPackageCache package_caches(ctx.pkgs_dirs);
|
||||
|
||||
auto exp_loaded = load_channels(pool, package_caches, 0);
|
||||
if (!exp_loaded)
|
||||
{
|
||||
throw exp_loaded.error();
|
||||
}
|
||||
|
||||
pool.create_whatprovides();
|
||||
std::string matchspec = version ? fmt::format("micromamba={}", version.value())
|
||||
: fmt::format("micromamba>{}", umamba::version());
|
||||
|
||||
auto solvable_ids = pool.select_solvables(pool.matchspec2id(matchspec), true);
|
||||
|
||||
if (solvable_ids.empty())
|
||||
{
|
||||
if (pool.select_solvables(pool.matchspec2id("micromamba")).empty())
|
||||
{
|
||||
throw mamba::mamba_error(
|
||||
"No micromamba found in the loaded channels. Add 'conda-forge' to your config file.",
|
||||
mamba_error_code::selfupdate_failure);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::instance().print(fmt::format(
|
||||
"\nYour micromamba version ({}) is already up to date.", umamba::version()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<PackageInfo> latest_micromamba = pool.id2pkginfo(solvable_ids[0]);
|
||||
if (!latest_micromamba)
|
||||
{
|
||||
throw mamba::mamba_error("Could not convert solvable to PackageInfo",
|
||||
mamba_error_code::internal_failure);
|
||||
}
|
||||
Console::instance().stream()
|
||||
<< termcolor::green
|
||||
<< fmt::format("\n Installing micromamba version: {} (currently installed {})",
|
||||
latest_micromamba.value().version,
|
||||
umamba::version())
|
||||
<< termcolor::reset;
|
||||
|
||||
Console::instance().print(
|
||||
fmt::format(" Fetching micromamba from {}\n", latest_micromamba.value().url));
|
||||
|
||||
ctx.download_only = true;
|
||||
MTransaction t(pool, { latest_micromamba.value() }, package_caches);
|
||||
auto exp_prefix_data = PrefixData::create(ctx.root_prefix);
|
||||
if (!exp_prefix_data)
|
||||
{
|
||||
throw exp_prefix_data.error();
|
||||
}
|
||||
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
t.execute(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 (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;
|
||||
}
|
||||
|
||||
Console::instance().print("\nReinitializing all previously initialized shells\n");
|
||||
std::string shell_type = "";
|
||||
mamba::shell("reinit", shell_type, ctx.root_prefix, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
set_update_command(CLI::App* subcom)
|
||||
|
@ -29,3 +150,16 @@ set_update_command(CLI::App* subcom)
|
|||
|
||||
subcom->callback([&]() { update(update_all, prune); });
|
||||
}
|
||||
|
||||
void
|
||||
set_self_update_command(CLI::App* subcom)
|
||||
{
|
||||
Configuration::instance();
|
||||
|
||||
init_install_options(subcom);
|
||||
|
||||
static std::optional<std::string> version;
|
||||
subcom->add_option("--version", version, "Install specific micromamba version");
|
||||
|
||||
subcom->callback([&]() { return update_self(version); });
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ from . import helpers
|
|||
def tmp_home(tmp_path: pathlib.Path) -> Generator[pathlib.Path, None, None]:
|
||||
"""Change the home directory to a tmp folder for the duration of a test."""
|
||||
# Try multiple combination for Unix/Windows
|
||||
home_envs = [k for k in ("HOME", "USERPROFILE") if k in os.environ]
|
||||
old_homes = {name: os.environ[name] for name in home_envs}
|
||||
home_envs = ["HOME", "USERPROFILE"]
|
||||
old_homes = {name: os.environ.get(name) for name in home_envs}
|
||||
|
||||
if len(home_envs) > 0:
|
||||
new_home = tmp_path / "home"
|
||||
|
@ -22,7 +22,10 @@ def tmp_home(tmp_path: pathlib.Path) -> Generator[pathlib.Path, None, None]:
|
|||
os.environ[env] = str(new_home)
|
||||
yield new_home
|
||||
for env, home in old_homes.items():
|
||||
os.environ[env] = home
|
||||
if old_homes[env] is None:
|
||||
del os.environ[env]
|
||||
else:
|
||||
os.environ[env] = home
|
||||
else:
|
||||
yield pathlib.Path.home()
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@ import os
|
|||
import pathlib
|
||||
import platform
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import PurePosixPath, PureWindowsPath
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -101,7 +100,7 @@ def write_script(interpreter, lines, path):
|
|||
|
||||
|
||||
possible_interpreters = {
|
||||
"win": {"powershell", "cmd.exe"},
|
||||
"win": {"powershell", "cmd.exe", "bash"},
|
||||
"unix": {"bash", "zsh", "fish", "xonsh", "tcsh"},
|
||||
}
|
||||
|
||||
|
@ -140,6 +139,13 @@ def find_path_in_str(p, s):
|
|||
return False
|
||||
|
||||
|
||||
def format_path(p, interpreter):
|
||||
if plat == "win" and interpreter == "bash":
|
||||
return str(PurePosixPath(PureWindowsPath(p)))
|
||||
else:
|
||||
return str(p)
|
||||
|
||||
|
||||
def call_interpreter(s, tmp_path, interpreter, interactive=False, env=None):
|
||||
if interactive and interpreter == "powershell":
|
||||
# "Get-Content -Path $PROFILE.CurrentUserAllHosts | Invoke-Expression"
|
||||
|
@ -166,6 +172,8 @@ def call_interpreter(s, tmp_path, interpreter, interactive=False, env=None):
|
|||
args = ["cmd.exe", "/Q", "/C", f]
|
||||
elif interpreter == "powershell":
|
||||
args = ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", f]
|
||||
elif interpreter == "bash" and plat == "win":
|
||||
args = [os.path.join(os.environ["PROGRAMFILES"], "Git", "bin", "bash.exe"), f]
|
||||
else:
|
||||
args = [interpreter, f]
|
||||
if interactive:
|
||||
|
@ -243,6 +251,26 @@ def get_valid_interpreters():
|
|||
valid_interpreters = get_valid_interpreters()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backup_umamba():
|
||||
mamba_exe = get_umamba()
|
||||
shutil.copyfile(mamba_exe, mamba_exe + ".orig")
|
||||
|
||||
yield mamba_exe
|
||||
|
||||
shutil.move(mamba_exe + ".orig", mamba_exe)
|
||||
os.chmod(mamba_exe, 0o755)
|
||||
|
||||
|
||||
def get_self_update_interpreters():
|
||||
if plat == "win":
|
||||
return ["cmd.exe", "powershell", "bash"]
|
||||
if plat == "osx":
|
||||
return ["zsh", "bash"]
|
||||
else:
|
||||
return ["bash"]
|
||||
|
||||
|
||||
def shvar(v, interpreter):
|
||||
if interpreter in ["bash", "zsh", "xonsh", "fish", "tcsh", "dash"]:
|
||||
return f"${v}"
|
||||
|
@ -286,7 +314,10 @@ class TestActivation:
|
|||
tmp_path,
|
||||
interpreter,
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
# TODO enable these tests also on win + bash!
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -387,7 +418,9 @@ class TestActivation:
|
|||
tmp_path,
|
||||
interpreter,
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -479,7 +512,9 @@ class TestActivation:
|
|||
tmp_path,
|
||||
interpreter,
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -529,7 +564,9 @@ class TestActivation:
|
|||
def test_env_activation(
|
||||
self, tmp_home, winreg_value, tmp_root_prefix, tmp_path, interpreter
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -626,7 +663,9 @@ class TestActivation:
|
|||
tmp_path,
|
||||
interpreter,
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -747,7 +786,9 @@ class TestActivation:
|
|||
tmp_path,
|
||||
interpreter,
|
||||
):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
umamba = get_umamba()
|
||||
|
@ -849,7 +890,9 @@ class TestActivation:
|
|||
|
||||
@pytest.mark.parametrize("interpreter", get_interpreters())
|
||||
def test_activate_path(self, tmp_empty_env, tmp_env_name, interpreter, tmp_path):
|
||||
if interpreter not in valid_interpreters:
|
||||
if interpreter not in valid_interpreters or (
|
||||
plat == "win" and interpreter == "bash"
|
||||
):
|
||||
pytest.skip(f"{interpreter} not available")
|
||||
|
||||
# Activate env name
|
||||
|
@ -868,3 +911,67 @@ class TestActivation:
|
|||
res = shell("activate", prefix_short, "-s", interpreter)
|
||||
dict_res = self.to_dict(res, interpreter)
|
||||
assert any([str(tmp_empty_env) in p for p in dict_res.values()])
|
||||
|
||||
@pytest.mark.parametrize("interpreter", get_self_update_interpreters())
|
||||
def test_self_update(
|
||||
self,
|
||||
backup_umamba,
|
||||
tmp_home,
|
||||
tmp_path,
|
||||
tmp_root_prefix,
|
||||
winreg_value,
|
||||
interpreter,
|
||||
):
|
||||
mamba_exe = backup_umamba
|
||||
|
||||
shell_init = [
|
||||
f"{format_path(mamba_exe, interpreter)} shell init -s {interpreter} -p {format_path(tmp_root_prefix, interpreter)}"
|
||||
]
|
||||
call_interpreter(shell_init, tmp_path, interpreter)
|
||||
|
||||
if interpreter == "bash":
|
||||
assert (
|
||||
Path(tmp_root_prefix) / "etc" / "profile.d" / "micromamba.sh"
|
||||
).exists()
|
||||
|
||||
extra_start_code = []
|
||||
if interpreter == "powershell":
|
||||
extra_start_code = [
|
||||
f'$Env:MAMBA_EXE="{mamba_exe}"',
|
||||
"$MambaModuleArgs = @{ChangePs1 = $True}",
|
||||
f'Import-Module "{tmp_root_prefix}\\condabin\\Mamba.psm1" -ArgumentList $MambaModuleArgs',
|
||||
"Remove-Variable MambaModuleArgs",
|
||||
]
|
||||
elif interpreter == "bash":
|
||||
if plat == "linux":
|
||||
extra_start_code = ["source ~/.bashrc"]
|
||||
else:
|
||||
print(mamba_exe)
|
||||
extra_start_code = [
|
||||
"source ~/.bash_profile",
|
||||
"micromamba info",
|
||||
"echo $MAMBA_ROOT_PREFIX",
|
||||
"echo $HOME",
|
||||
"ls ~",
|
||||
"echo $MAMBA_EXE",
|
||||
]
|
||||
elif interpreter == "zsh":
|
||||
extra_start_code = ["source ~/.zshrc"]
|
||||
|
||||
call_interpreter(
|
||||
extra_start_code
|
||||
+ ["micromamba self-update --version 0.25.1 -c conda-forge"],
|
||||
tmp_path,
|
||||
interpreter,
|
||||
interactive=False,
|
||||
)
|
||||
|
||||
assert Path(mamba_exe).exists()
|
||||
|
||||
version = subprocess.check_output([mamba_exe, "--version"])
|
||||
assert version.decode("utf8").strip() == "0.25.1"
|
||||
|
||||
assert not Path(mamba_exe + ".bkup").exists()
|
||||
|
||||
shutil.copyfile(mamba_exe + ".orig", mamba_exe)
|
||||
os.chmod(mamba_exe, 0o755)
|
||||
|
|
|
@ -68,7 +68,7 @@ class TestShell:
|
|||
if shell_type == "powershell":
|
||||
assert f"$Env:MAMBA_EXE='{mamba_exe}'" in res
|
||||
elif shell_type in ("zsh", "bash", "posix"):
|
||||
assert res.count(mamba_exe) == 1
|
||||
assert res.count(mamba_exe) == 3
|
||||
elif shell_type == "xonsh":
|
||||
assert res.count(mamba_exe) == 8
|
||||
elif shell_type == "fish":
|
||||
|
|
Loading…
Reference in New Issue