From 4f0179a84cfadfcd622282134bfb0c60a2a4890d Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Jun 2023 13:58:32 +0200 Subject: [PATCH 1/5] bump minimum conda/mamba versions an old conda bug causes RemoveError: requests is a dependency of conda when installing other packages upgrading conda itself avoids this bug. It's unclear what the true minimum version is to fix this, but the important thing is that it will be upgraded if it's still the version in 0.2.0 (4.10), so pick the version from today which we know works --- tljh/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tljh/installer.py b/tljh/installer.py index dc787f4..b65ddac 100644 --- a/tljh/installer.py +++ b/tljh/installer.py @@ -148,8 +148,8 @@ MAMBAFORGE_CHECKSUMS = { # minimum versions of packages MINIMUM_VERSIONS = { # if conda/mamba/pip are lower than this, upgrade them before installing the user packages - "mamba": "0.16.0", - "conda": "4.10", + "mamba": "1.4.2", + "conda": "23.3.1", "pip": "23.1.2", # minimum Python version (if not matched, abort to avoid big disruptive updates) "python": "3.9", From 4cc55df2a446b0fcf60ae8777c7206bb8be0cf62 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Jun 2023 14:08:03 +0200 Subject: [PATCH 2/5] force upgrade of conda itself if conda upgrade conda is broken, we are in trouble this is required to avoid the RemoveError --- tljh/conda.py | 13 ++++++++----- tljh/installer.py | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tljh/conda.py b/tljh/conda.py index df9a027..ca849e8 100644 --- a/tljh/conda.py +++ b/tljh/conda.py @@ -98,7 +98,7 @@ def install_miniconda(installer_path, prefix): fix_permissions(prefix) -def ensure_conda_packages(prefix, packages): +def ensure_conda_packages(prefix, packages, force=False): """ Ensure packages (from conda-forge) are installed in the conda prefix. @@ -110,13 +110,16 @@ def ensure_conda_packages(prefix, packages): # fallback on conda if mamba is not present (e.g. for mamba to install itself) conda_executable = os.path.join(prefix, "bin", "conda") + cmd = [conda_executable, "install", "--yes"] + + if force: + cmd += ["--force"] + abspath = os.path.abspath(prefix) utils.run_subprocess( - [ - conda_executable, - "install", - "-y", + cmd + + [ "-c", "conda-forge", # Make customizable if we ever need to "--prefix", diff --git a/tljh/installer.py b/tljh/installer.py index b65ddac..8c429ec 100644 --- a/tljh/installer.py +++ b/tljh/installer.py @@ -249,6 +249,8 @@ def ensure_user_environment(user_requirements_txt_file): # we _could_ explicitly pin Python here, # but conda already does this by default cf_pkgs_to_upgrade, + # use force to avoid RemoveError: 'requests' is a dependency of conda + force=True, ) pypi_pkgs_to_upgrade = list(set(to_upgrade) & {"pip"}) if pypi_pkgs_to_upgrade: From 29b354b42b5e549d5a7c6b68ce3c7036a65a7cfc Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Jun 2023 14:16:27 +0200 Subject: [PATCH 3/5] use force-reinstall not deprecated --force --- tljh/conda.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tljh/conda.py b/tljh/conda.py index ca849e8..834b600 100644 --- a/tljh/conda.py +++ b/tljh/conda.py @@ -113,7 +113,9 @@ def ensure_conda_packages(prefix, packages, force=False): cmd = [conda_executable, "install", "--yes"] if force: - cmd += ["--force"] + # use force-reinstall, e.g. for conda/mamba to ensure everything is okay + # avoids problems with RemoveError upgrading conda from old versions + cmd += ["--force-reinstall"] abspath = os.path.abspath(prefix) From ee23e041dee6f081d7bb62ffb41f2e277c100f4a Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Jun 2023 14:45:30 +0200 Subject: [PATCH 4/5] reinstall conda/mamba in a separate, unconditional step makes it more likely that subsequent conda installs will succeed - fix indentation on the upgrade steps so they aren't run every iteration - no longer need to bump required versions --- tljh/conda.py | 4 ++-- tljh/installer.py | 51 +++++++++++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/tljh/conda.py b/tljh/conda.py index 834b600..a543645 100644 --- a/tljh/conda.py +++ b/tljh/conda.py @@ -98,7 +98,7 @@ def install_miniconda(installer_path, prefix): fix_permissions(prefix) -def ensure_conda_packages(prefix, packages, force=False): +def ensure_conda_packages(prefix, packages, force_reinstall=False): """ Ensure packages (from conda-forge) are installed in the conda prefix. @@ -112,7 +112,7 @@ def ensure_conda_packages(prefix, packages, force=False): cmd = [conda_executable, "install", "--yes"] - if force: + if force_reinstall: # use force-reinstall, e.g. for conda/mamba to ensure everything is okay # avoids problems with RemoveError upgrading conda from old versions cmd += ["--force-reinstall"] diff --git a/tljh/installer.py b/tljh/installer.py index 8c429ec..ab7a460 100644 --- a/tljh/installer.py +++ b/tljh/installer.py @@ -148,8 +148,8 @@ MAMBAFORGE_CHECKSUMS = { # minimum versions of packages MINIMUM_VERSIONS = { # if conda/mamba/pip are lower than this, upgrade them before installing the user packages - "mamba": "1.4.2", - "conda": "23.3.1", + "mamba": "0.16.0", + "conda": "4.10", "pip": "23.1.2", # minimum Python version (if not matched, abort to avoid big disruptive updates) "python": "3.9", @@ -242,23 +242,36 @@ def ensure_user_environment(user_requirements_txt_file): ) to_upgrade.append(pkg) - cf_pkgs_to_upgrade = list(set(to_upgrade) & {"conda", "mamba"}) - if cf_pkgs_to_upgrade: - conda.ensure_conda_packages( - USER_ENV_PREFIX, - # we _could_ explicitly pin Python here, - # but conda already does this by default - cf_pkgs_to_upgrade, - # use force to avoid RemoveError: 'requests' is a dependency of conda - force=True, - ) - pypi_pkgs_to_upgrade = list(set(to_upgrade) & {"pip"}) - if pypi_pkgs_to_upgrade: - conda.ensure_pip_packages( - USER_ENV_PREFIX, - pypi_pkgs_to_upgrade, - upgrade=True, - ) + # force reinstall conda/mamba to ensure a basically consistent env + # avoids issues with RemoveError: 'requests' is a dependency of conda + if not is_fresh_install: + # force-reinstall doesn't upgrade packages + # it reinstalls them in-place + # only include packages already installed here + to_reinstall = {"conda", "mamba"} & set(package_versions) + logger.info( + f"Reinstalling {', '.join(to_reinstall)} to ensure a consistent environment" + ) + conda.ensure_conda_packages( + USER_ENV_PREFIX, list(to_reinstall), force_reinstall=True + ) + + cf_pkgs_to_upgrade = list(set(to_upgrade) & {"conda", "mamba"}) + if cf_pkgs_to_upgrade: + conda.ensure_conda_packages( + USER_ENV_PREFIX, + # we _could_ explicitly pin Python here, + # but conda already does this by default + cf_pkgs_to_upgrade, + ) + + pypi_pkgs_to_upgrade = list(set(to_upgrade) & {"pip"}) + if pypi_pkgs_to_upgrade: + conda.ensure_pip_packages( + USER_ENV_PREFIX, + pypi_pkgs_to_upgrade, + upgrade=True, + ) # Install/upgrade the jupyterhub version in the user env based on the # version specification used for the hub env. From 39b1e1a7c718e882ecc34800402ce5a9f0c30dcd Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Jun 2023 15:33:36 +0200 Subject: [PATCH 5/5] preserve version pin when reinstalling conda avoids upgrade on older versions --- tljh/installer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tljh/installer.py b/tljh/installer.py index ab7a460..aff24fc 100644 --- a/tljh/installer.py +++ b/tljh/installer.py @@ -244,11 +244,17 @@ def ensure_user_environment(user_requirements_txt_file): # force reinstall conda/mamba to ensure a basically consistent env # avoids issues with RemoveError: 'requests' is a dependency of conda - if not is_fresh_install: + # only do this for 'old' conda versions known to have a problem + # we don't know how old, but we know 4.10 is affected and 23.1 is not + if not is_fresh_install and V(package_versions.get("conda", "0")) < V("23.1"): # force-reinstall doesn't upgrade packages # it reinstalls them in-place - # only include packages already installed here - to_reinstall = {"conda", "mamba"} & set(package_versions) + # only reinstall packages already present + to_reinstall = [] + for pkg in ["conda", "mamba"]: + if pkg in package_versions: + # add version pin to avoid upgrades + to_reinstall.append(f"{pkg}=={package_versions[pkg]}") logger.info( f"Reinstalling {', '.join(to_reinstall)} to ensure a consistent environment" )