diff --git a/bioconda_utils/build.py b/bioconda_utils/build.py index ef2ff0ccd8..f2a76c9d8e 100644 --- a/bioconda_utils/build.py +++ b/bioconda_utils/build.py @@ -182,6 +182,7 @@ def build_recipes( anaconda_upload=False, mulled_upload_target=None, check_channels=None, + latest_only=False, ): """ Build one or many bioconda packages. @@ -244,11 +245,15 @@ def build_recipes( logger.info('blacklist: %s', ', '.join(sorted(blacklist))) + get_recipes = utils.get_recipes + if latest_only: + get_recipes = utils.get_latest_recipes + if packages == "*": packages = ["*"] recipes = [] for package in packages: - for recipe in utils.get_recipes(recipe_folder, package): + for recipe in get_recipes(recipe_folder, package): if os.path.relpath(recipe, recipe_folder) in blacklist: logger.debug('blacklisted: %s', recipe) continue diff --git a/bioconda_utils/cli.py b/bioconda_utils/cli.py index 739b85d517..c7d35fd860 100644 --- a/bioconda_utils/cli.py +++ b/bioconda_utils/cli.py @@ -331,6 +331,8 @@ def lint(recipe_folder, config, packages="*", cache=None, list_funcs=False, in a docker container. Has no effect otherwise.''') @arg('--anaconda-upload', action='store_true', help='''After building recipes, upload them to Anaconda. This requires $ANACONDA_TOKEN to be set.''') +@arg('--latest-only', action='store_true', help='''Build only latest version + of each recipe.''') def build(recipe_folder, config, packages="*", @@ -345,6 +347,7 @@ def build(recipe_folder, conda_build_version=docker_utils.DEFAULT_CONDA_BUILD_VERSION, anaconda_upload=False, mulled_upload_target=None, + latest_only=False, ): setup_logger(loglevel) @@ -402,6 +405,7 @@ def build(recipe_folder, docker_builder=docker_builder, anaconda_upload=anaconda_upload, mulled_upload_target=mulled_upload_target, + latest_only=latest_only, ) exit(0 if success else 1) diff --git a/bioconda_utils/utils.py b/bioconda_utils/utils.py index 347eaa548f..97198ba2b6 100644 --- a/bioconda_utils/utils.py +++ b/bioconda_utils/utils.py @@ -17,13 +17,14 @@ from jsonschema import validate import datetime import tempfile -from distutils.version import LooseVersion +from distutils.version import LooseVersion, StrictVersion import time import threading from conda_build.exceptions import UnableToParse from conda_build import api from conda_build.metadata import MetaData +from conda.version import VersionOrder import yaml from jinja2 import Environment, PackageLoader, select_autoescape @@ -382,6 +383,38 @@ def get_recipes(recipe_folder, package="*"): glob.glob(os.path.join(path, "*", "meta.yaml"))) +def get_latest_recipes(recipe_folder, package="*"): + """ + Generator of recipes. + + Finds (possibly nested) directories containing a `meta.yaml` file and returns + the latest version of each recipe. + + Parameters + ---------- + recipe_folder : str + Top-level dir of the recipes + + package : str or iterable + Pattern or patterns to restrict the results. + """ + if isinstance(package, str): + package = [package] + for p in package: + path = os.path.join(recipe_folder, p) + main_recipe = os.path.join(path, "meta.yaml") + if os.path.exists(main_recipe): + yield os.path.dirname(main_recipe) + else: + get_version = lambda p: VersionOrder(os.path.basename(p)) + sorted_versions = sorted( + map(os.path.dirname, + glob.glob(os.path.join(path, "*", "meta.yaml"))), + key=get_version) + if sorted_versions: + yield sorted_versions[-1] + + def get_channel_repodata(channel='bioconda', platform=None): """ Returns the parsed JSON repodata for a channel from conda.anaconda.org.