From 319633c0a9288667b29f04b87d4eb88493ed438a Mon Sep 17 00:00:00 2001 From: jaimergp Date: Sat, 22 Jun 2024 12:49:09 +0200 Subject: [PATCH] Fix `channels_remap` handling in `extra_envs` configuration (#808) * Fix channels_remap handling in extra_envs configuration * add news * add channels_remap to examples * map to itself * to itself * just use the name * this time with https * Mention that the channels must be passed as URLs in channels_remap * Mention conda/conda-standalone in this error message * more news * fix TypeError in tests * fix dep on Windows * strip str for good measure * do pass the dest * do test channel remaps --- CONSTRUCT.md | 2 +- constructor/construct.py | 2 +- constructor/main.py | 15 ++++++++++++--- docs/source/construct-yaml.md | 2 +- examples/extra_envs/construct.yaml | 7 +++++-- news/808-fix-env-channels-remap | 19 +++++++++++++++++++ tests/test_examples.py | 21 +++++++++++++++------ 7 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 news/808-fix-env-channels-remap diff --git a/CONSTRUCT.md b/CONSTRUCT.md index 9e02ff8e2..0fdf19f73 100644 --- a/CONSTRUCT.md +++ b/CONSTRUCT.md @@ -67,7 +67,7 @@ See notes in `channels_remap` for details about local channels. _required:_ no
_type:_ list
-A list of `src/dest` channel pairs. When building the installer, conda will +A list of `src/dest` channel URL pairs. When building the installer, conda will use the `src` channels to solve and fetch the packages. However, the resulting installation will see the packages as coming from the `dest` equivalent. This allows an installer to be built against a different set of channels than diff --git a/constructor/construct.py b/constructor/construct.py index 2fe4c8978..1585fb9ca 100644 --- a/constructor/construct.py +++ b/constructor/construct.py @@ -42,7 +42,7 @@ '''), ('channels_remap', False, list, ''' -A list of `src/dest` channel pairs. When building the installer, conda will +A list of `src/dest` channel URL pairs. When building the installer, conda will use the `src` channels to solve and fetch the packages. However, the resulting installation will see the packages as coming from the `dest` equivalent. This allows an installer to be built against a different set of channels than diff --git a/constructor/main.py b/constructor/main.py index c2223a6bf..13bac0630 100644 --- a/constructor/main.py +++ b/constructor/main.py @@ -149,10 +149,18 @@ def main_build(dir_path, output_dir='.', platform=cc_platform, if env_name in ("base", "root"): raise ValueError(f"Environment name '{env_name}' cannot be used") for config_key, value in env_config.copy().items(): - if isinstance(value, (list, tuple)): - env_config[config_key] = [val.strip() for val in value] if config_key == "environment_file": env_config[config_key] = abspath(join(dir_path, value)) + elif config_key == "channels_remap": + env_config[config_key] = [ + {"src": item["src"].strip(), "dest": item["dest"].strip()} for item in value + ] + elif isinstance(value, (list, tuple)): + env_config[config_key] = [val.strip() for val in value] + elif isinstance(value, str): + env_config[config_key] = value.strip() + else: + env_config[config_key] = value try: exe_name, exe_version = identify_conda_exe(info.get("_conda_exe")) @@ -418,7 +426,8 @@ def main(): easiest way to obtain one is to install the 'conda-standalone' package. Alternatively, you can download an executable manually and supply its path with the --conda-exe argument. Self-contained executables can be -downloaded from https://repo.anaconda.com/pkgs/misc/conda-execs/""".lstrip()) +downloaded from https://repo.anaconda.com/pkgs/misc/conda-execs/ and/or +https://github.com/conda/conda-standalone/releases""".lstrip()) out_dir = normalize_path(args.output_dir) main_build(dir_path, output_dir=out_dir, platform=args.platform, diff --git a/docs/source/construct-yaml.md b/docs/source/construct-yaml.md index 9e02ff8e2..0fdf19f73 100644 --- a/docs/source/construct-yaml.md +++ b/docs/source/construct-yaml.md @@ -67,7 +67,7 @@ See notes in `channels_remap` for details about local channels. _required:_ no
_type:_ list
-A list of `src/dest` channel pairs. When building the installer, conda will +A list of `src/dest` channel URL pairs. When building the installer, conda will use the `src` channels to solve and fetch the packages. However, the resulting installation will see the packages as coming from the `dest` equivalent. This allows an installer to be built against a different set of channels than diff --git a/examples/extra_envs/construct.yaml b/examples/extra_envs/construct.yaml index 2152e9717..d39c72292 100644 --- a/examples/extra_envs/construct.yaml +++ b/examples/extra_envs/construct.yaml @@ -2,11 +2,11 @@ name: ExtraEnvs version: X installer_type: all channels: - - http://repo.anaconda.com/pkgs/main/ + - https://conda.anaconda.org/conda-forge specs: - python=3.9 - conda # conda is required for extra_envs - - console_shortcut # [win] + - miniforge_console_shortcut # [win] exclude: # [unix] - tk # [unix] extra_envs: @@ -16,6 +16,9 @@ extra_envs: - pip channels: - conda-forge + channels_remap: + - src: https://conda.anaconda.org/conda-forge + dest: conda-forge exclude: - setuptools dav1d: diff --git a/news/808-fix-env-channels-remap b/news/808-fix-env-channels-remap new file mode 100644 index 000000000..202d797a8 --- /dev/null +++ b/news/808-fix-env-channels-remap @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* Fix `channels_remap` sanitization when included as part of an item in `extra_envs`. (#808) + +### Deprecations + +* + +### Docs + +* Clarify that channels must be passed as URLs in `channels_remap`. (#808) + +### Other + +* Mention `conda/conda-standalone` repository as a source for conda-standalone downloads in relevant error messages. (#808) diff --git a/tests/test_examples.py b/tests/test_examples.py index 66edb7895..b383af00f 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,4 +1,5 @@ import getpass +import json import os import shutil import subprocess @@ -219,7 +220,7 @@ def _sentinel_file_checks(example_path, install_dir): if (example_path / script).exists() and not (install_dir / sentinel).exists(): raise AssertionError( f"Sentinel file for {script_prefix}_install not found! " - f"{install_dir} contents:\n" + "\n".join(sorted(install_dir.iterdir())) + f"{install_dir} contents:\n" + "\n".join(sorted(map(str, install_dir.iterdir()))) ) @@ -511,12 +512,12 @@ def test_example_signing(tmp_path, request): @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") @pytest.mark.skipif( - not shutil.which("azuresigntool") and not os.environ.get("AZURE_SIGNTOOL_PATH"), - reason="AzureSignTool not available" + not shutil.which("azuresigntool") and not os.environ.get("AZURE_SIGNTOOL_PATH"), + reason="AzureSignTool not available", ) @pytest.mark.parametrize( - "auth_method", - os.environ.get("AZURE_SIGNTOOL_TEST_AUTH_METHODS", "token,secret").split(","), + "auth_method", + os.environ.get("AZURE_SIGNTOOL_TEST_AUTH_METHODS", "token,secret").split(","), ) def test_azure_signtool(tmp_path, request, monkeypatch, auth_method): """Test signing installers with AzureSignTool. @@ -549,7 +550,15 @@ def test_azure_signtool(tmp_path, request, monkeypatch, auth_method): def test_example_use_channel_remap(tmp_path, request): input_path = _example_path("use_channel_remap") for installer, install_dir in create_installer(input_path, tmp_path): - _run_installer(input_path, installer, install_dir, request=request) + _run_installer(input_path, installer, install_dir, request=request, uninstall=False) + p = subprocess.run( + [sys.executable, "-m", "conda", "list", "--prefix", install_dir, "--json"], + capture_output=True, + text=True, + ) + packages = json.loads(p.stdout) + for pkg in packages: + assert pkg["channel"] == "private_repo" def test_example_from_existing_env(tmp_path, request):