diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 04859603..e20845b0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: install mamba uses: mamba-org/setup-micromamba@v1 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 742f2818..e3cd5b83 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,13 +16,27 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7] + conda-channel: ['conda-forge', 'conda-canary/label/dev'] + include: + # Lowest versions to test for. + - conda-channel: 'conda-forge' + conda-build-version: '3.25' + python-version: '3.8' + # Unbound/dev versions to test for. + - conda-channel: 'conda-canary/label/dev' + conda-build-version: '*' + python-version: '*' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: install mamba uses: mamba-org/setup-micromamba@v1 with: environment-file: tests/env.yml + create-args: >- + --channel-priority=flexible + python=${{ matrix.python-version }} + ${{ matrix.conda-channel }}::conda + ${{ matrix.conda-channel }}::conda-build=${{ matrix.conda-build-version }} - name: Install boa shell: bash -l {0} run: | diff --git a/boa/cli/boa.py b/boa/cli/boa.py index 1a26ec8e..89a68fb5 100644 --- a/boa/cli/boa.py +++ b/boa/cli/boa.py @@ -4,6 +4,8 @@ import sys import argparse +from conda.base.context import context + from boa.core import monkey_patch_emscripten if any("emscripten" in arg for arg in sys.argv): @@ -14,7 +16,7 @@ from boa._version import __version__ from boa.core.utils import init_api_context -from conda_build.conda_interface import cc_conda_build +cc_conda_build = context.conda_build if hasattr(context, "conda_build") else {} banner = r""" _ diff --git a/boa/cli/mambabuild.py b/boa/cli/mambabuild.py index 65aaa4e5..00ca3a63 100644 --- a/boa/cli/mambabuild.py +++ b/boa/cli/mambabuild.py @@ -105,7 +105,7 @@ def _get_solver(channel_urls, subdir, output_folder): return solver -def mamba_get_install_actions( +def mamba_get_package_records( prefix, specs, env, @@ -138,7 +138,8 @@ def mamba_get_install_actions( _specs = [s.conda_build_form() for s in _specs] try: - solution = solver.solve_for_action(_specs, prefix) + # We only create fresh environments in builds and can ignore unlink precs. + _, link_precs = solver.solve_for_unlink_link_precs(_specs, prefix) except RuntimeError as e: conflict_packages = parse_problems(str(e)) @@ -149,10 +150,54 @@ def mamba_get_install_actions( err.subdir = subdir raise err - return solution - + return link_precs + + +if hasattr(conda_build.environ, "get_package_records"): + # conda-build>=24.1 avoids the legacy "actions"/"Dist"-based installs. + conda_build.environ.get_package_records = mamba_get_package_records +else: + # conda-build<24.1 needs get_package_records' result wrapped in "actions" dict. + def mamba_get_install_actions( + prefix, + specs, + env, + retries=0, + subdir=None, + verbose=True, + debug=False, + locking=True, + bldpkgs_dirs=None, + timeout=900, + disable_pip=False, + max_env_retry=3, + output_folder=None, + channel_urls=None, + ): + from conda.models.dist import Dist + from conda.plan import get_blank_actions + + link_precs = mamba_get_package_records( + prefix=prefix, + specs=specs, + env=env, + retries=retries, + subdir=subdir, + verbose=verbose, + debug=debug, + locking=locking, + bldpkgs_dirs=bldpkgs_dirs, + timeout=timeout, + disable_pip=disable_pip, + max_env_retry=max_env_retry, + output_folder=output_folder, + channel_urls=channel_urls, + ) + actions = get_blank_actions(prefix) + actions["LINK"].extend(Dist(prec) for prec in link_precs) + return actions -conda_build.environ.get_install_actions = mamba_get_install_actions + conda_build.environ.get_install_actions = mamba_get_install_actions def prepare(**kwargs): diff --git a/boa/core/build.py b/boa/core/build.py index 33c43d81..2f6a2e7c 100644 --- a/boa/core/build.py +++ b/boa/core/build.py @@ -25,7 +25,7 @@ import conda_package_handling.api # used to get version -from conda_build.conda_interface import env_path_backup_var_exists, TemporaryDirectory +from conda.gateways.disk.create import TemporaryDirectory from conda_build.utils import tmp_chdir from conda_build import source, utils @@ -44,7 +44,11 @@ if sys.platform == "win32": import boa.core.windows as windows -from boa.core.utils import shell_path, get_sys_vars_stubs +from boa.core.utils import ( + env_path_backup_var_exists, + get_sys_vars_stubs, + shell_path, +) from boa.core.recipe_handling import copy_recipe from boa.core.config import boa_config from boa.tui.exceptions import BoaRunBuildException diff --git a/boa/core/metadata.py b/boa/core/metadata.py index ecf92145..c82aaf86 100644 --- a/boa/core/metadata.py +++ b/boa/core/metadata.py @@ -149,7 +149,7 @@ def get_value(self, in_key: str, default: Any = None, autotype=True) -> Any: if autotype and default is None and FIELDS.get(section, {}).get(key): default = FIELDS[section][key]() - section = self.output.sections.get(section, {}) + section = self.meta.get(section, {}) if isinstance(section, list): return section[int(num)].get(key, default) else: diff --git a/boa/core/monkey_patch_emscripten.py b/boa/core/monkey_patch_emscripten.py index 4d7e97e8..0b58967e 100644 --- a/boa/core/monkey_patch_emscripten.py +++ b/boa/core/monkey_patch_emscripten.py @@ -32,8 +32,9 @@ def patch(): # CONDA-BUILD MONKEY-PATCH ############################################### + from conda.base.context import non_x86_machines as non_x86_linux_machines + from conda_build import utils, variants, environ - from conda_build.conda_interface import non_x86_linux_machines from conda_build import metadata from conda_build.features import feature_list diff --git a/boa/core/solver.py b/boa/core/solver.py index 7779c7a9..b23027a1 100644 --- a/boa/core/solver.py +++ b/boa/core/solver.py @@ -4,24 +4,27 @@ import os import tempfile +from boltons.setutils import IndexedSet + from conda.base.constants import ChannelPriority from conda.core.solve import diff_for_unlink_link_precs from conda.common.serialize import json_dump from conda.models.prefix_graph import PrefixGraph from conda.core.prefix_data import PrefixData -from conda._vendor.boltons.setutils import IndexedSet from conda.models.match_spec import MatchSpec from conda.common.url import remove_auth, split_anaconda_token from conda.core.index import _supplement_index_with_system from conda.base.context import context -from conda.plan import get_blank_actions -from conda.models.dist import Dist -from conda_build.conda_interface import pkgs_dirs from conda.core.package_cache_data import PackageCacheData import libmambapy -from boa.core.utils import get_index, load_channels, to_package_record_from_subjson +from boa.core.utils import ( + get_index, + load_channels, + pkgs_dirs, + to_package_record_from_subjson, +) from boa.core.config import boa_config console = boa_config.console @@ -59,7 +62,9 @@ def get_url_from_channel(c): return split_anaconda_token(remove_auth(c))[0] -def to_action(specs_to_add, specs_to_remove, prefix, to_link, to_unlink, index): +def to_unlink_link_precs( + specs_to_add, specs_to_remove, prefix, to_link, to_unlink, index +): to_link_records = [] prefix_data = PrefixData(prefix) @@ -85,10 +90,7 @@ def to_action(specs_to_add, specs_to_remove, prefix, to_link, to_unlink, index): specs_to_add=specs_to_add, ) - actions = get_blank_actions(prefix) - actions["UNLINK"].extend(Dist(prec) for prec in unlink_precs) - actions["LINK"].extend(Dist(prec) for prec in link_precs) - return actions + return unlink_precs, link_precs def get_virtual_packages(): @@ -238,7 +240,7 @@ def solve(self, specs, pkg_cache_path=None): package_cache = libmambapy.MultiPackageCache(pkg_cache_path) return libmambapy.Transaction(api_solver, package_cache) - def solve_for_action(self, specs, prefix): + def solve_for_unlink_link_precs(self, specs, prefix): t = self.solve(specs) if not boa_config.quiet and not boa_config.is_mambabuild: t.print() @@ -247,7 +249,7 @@ def solve_for_action(self, specs, prefix): specs_to_add = [MatchSpec(m) for m in mmb_specs[0]] specs_to_remove = [MatchSpec(m) for m in mmb_specs[1]] - return to_action( + return to_unlink_link_precs( specs_to_add, specs_to_remove, prefix, diff --git a/boa/core/test.py b/boa/core/test.py index 525a13a4..7fed5848 100644 --- a/boa/core/test.py +++ b/boa/core/test.py @@ -16,6 +16,7 @@ from libmambapy import PrefixData from libmambapy import Context as MambaContext +from conda.common.url import path_to_url from conda.gateways.disk.create import mkdir_p from conda_build.utils import CONDA_PACKAGE_EXTENSIONS, get_site_packages @@ -25,11 +26,6 @@ get_all_replacements, log_stats, ) -from conda_build.conda_interface import ( - url_path, - env_path_backup_var_exists, - pkgs_dirs, -) from conda_build.create_test import create_all_test_files from conda_build.post import post_build from conda_build.render import bldpkg_path, try_download @@ -40,7 +36,7 @@ from conda_build import utils from conda_build.environ import clean_pkg_cache -from boa.core.utils import shell_path +from boa.core.utils import env_path_backup_var_exists, pkgs_dirs, shell_path from boa.core.recipe_output import Output from boa.core.metadata import MetaData from boa.core import environ @@ -359,7 +355,7 @@ def _construct_metadata_for_test_from_package(package, config): metadata.config.used_vars = list(hash_input.keys()) urls = list(utils.ensure_list(metadata.config.channel_urls)) - local_path = url_path(local_channel) + local_path = path_to_url(local_channel) # replace local with the appropriate real channel. Order is maintained. urls = [url if url != "local" else local_path for url in urls] if local_path not in urls: @@ -714,7 +710,10 @@ def run_test( not hasattr(recipedir_or_package_or_metadata, "config") and os.path.isfile(recipedir_or_package_or_metadata) and recipedir_or_package_or_metadata.endswith(CONDA_PACKAGE_EXTENSIONS) - and os.path.dirname(recipedir_or_package_or_metadata) in pkgs_dirs[0] + and any( + os.path.dirname(recipedir_or_package_or_metadata) in pkgs_dir + for pkgs_dir in pkgs_dirs + ) ) if not in_pkg_cache: clean_pkg_cache(metadata.dist(), metadata.config) diff --git a/boa/core/utils.py b/boa/core/utils.py index 927590b7..89516fd4 100644 --- a/boa/core/utils.py +++ b/boa/core/utils.py @@ -14,7 +14,6 @@ from conda_build import utils from conda_build.config import get_or_merge_config from conda_build.variants import find_config_files, parse_config_file, combine_specs -from conda_build import __version__ as cb_version from conda.base.constants import ChannelPriority from conda.gateways.connection.session import CondaHttpAuth from conda.core.index import check_allowlist @@ -33,10 +32,8 @@ console = boa_config.console -if "+" in cb_version: - cb_version = cb_version[: cb_version.index("+")] -cb_split_version = tuple(int(x) for x in cb_version.split(".")) - +env_path_backup_var_exists = os.environ.get("CONDA_PATH_BACKUP", None) +pkgs_dirs = list(context.pkgs_dirs) if "bsd" in sys.platform: shell_path = "/bin/sh" @@ -58,11 +55,7 @@ def get_config( variant = {} config = get_or_merge_config(config, variant) - if cb_split_version >= (3, 20, 5): - config_files = find_config_files(folder, config) - else: - config_files = find_config_files(folder) - + config_files = find_config_files(folder, config) all_files = [os.path.abspath(p) for p in config_files + additional_files] # reverse files an uniquify diff --git a/setup.py b/setup.py index 28c6dd47..7cf1d867 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ "prompt-toolkit", "joblib", "beautifulsoup4", + "boltons", ] setup( diff --git a/tests/env.yml b/tests/env.yml index cb9dfb5c..f869910b 100644 --- a/tests/env.yml +++ b/tests/env.yml @@ -4,10 +4,11 @@ channels: dependencies: - python>=3.7 - pip + - boltons - conda - libmambapy >=1.5,<1.6 - pytest - - "conda-build>=3.20" + - conda-build >=3.25 - conda-index - ruamel - ruamel.yaml