diff --git a/bob/devtools/build.py b/bob/devtools/build.py index 7ad4acb69ca00c9878dc68b725f651aa03b1e02d..d71e6bfb694e197a7e88ab518c07f58ad3a58b56 100644 --- a/bob/devtools/build.py +++ b/bob/devtools/build.py @@ -48,58 +48,54 @@ def should_skip_build(metadata_tuples): return all(m[0].skip() for m in metadata_tuples) -def next_build_number(channel_url, name, version, python): +def next_build_number(channel_url, basename): """Calculates the next build number of a package given the channel This function returns the next build number (integer) for a package given its - recipe, dependencies, name, version and python version. It looks on the - channel URL provided and figures out if any clash would happen and what would - be the highest build number available for that configuration. + resulting tarball base filename (can be obtained with + :py:func:`get_output_path`). Args: channel_url: The URL where to look for packages clashes (normally a beta channel) - name: The name of the package - version: The version of the package - python: The version of python as 2 digits (e.g.: "2.7" or "3.6") + basename: The tarball basename to check on the channel Returns: The next build number with the current configuration. Zero (0) is returned if no match is found. Also returns the URLs of the packages it - finds with matches on the name, version and python-version. + finds with matches on the name, version and python-version, ordered by + (reversed) build-number. """ from conda.exports import get_index - # no dot in py_ver - py_ver = python.replace('.', '') - # get the channel index logger.debug('Downloading channel index from %s', channel_url) index = get_index(channel_urls=[channel_url], prepend=False) - # search if package with the same version exists - build_number = 0 - urls = [] - for dist in index: + # remove .tar.bz2 from name, then split from the end twice, on '-' + name, version, build = basename[:-8].rsplit('-', 2) - if dist.name == name and dist.version == version: - if py_ver: - match = re.match('py[2-9][0-9]+', dist.build_string) - else: - match = re.match('py', dist.build_string) + # remove the build number as we're looking for the next value + build_variant = build.rsplit('_', 1)[0] - if match and match.group() == 'py{}'.format(py_ver): - logger.debug("Found match at %s for %s-%s-py%s", index[dist].url, - name, version, py_ver) - build_number = max(build_number, dist.build_number + 1) - urls.append(index[dist].url) + # search if package with the same characteristics + urls = {} + build_number = 0 + for dist in index: + if dist.name == name and dist.version == version and \ + dist.build_string.startswith(build_variant): #match! + url = index[dist].url + logger.debug("Found match at %s for %s-%s-%s", url, + name, version, build_variant) + build_number = max(build_number, dist.build_number + 1) + urls[dist.build_number] = url.replace(channel_url, '') - urls = [url.replace(channel_url, '') for url in urls] + sorted_urls = [urls[k] for k in reversed(list(urls.keys()))] - return build_number, urls + return build_number, sorted_urls def make_conda_config(config, python, append_file, condarc_options): @@ -142,6 +138,13 @@ def make_conda_config(config, python, append_file, condarc_options): return retval +def get_output_path(metadata, config): + '''Renders the recipe and returns the interpreted YAML file''' + + from conda_build.api import get_output_file_paths + return get_output_file_paths(metadata, config=config)[0] + + def get_rendered_metadata(recipe_dir, config): '''Renders the recipe and returns the interpreted YAML file''' @@ -157,66 +160,35 @@ def get_parsed_recipe(metadata): return yaml.load(output) -def exists_on_channel(channel_url, name, version, build_number, - python_version): +def exists_on_channel(channel_url, basename): """Checks on the given channel if a package with the specs exist Args: channel_url: The URL where to look for packages clashes (normally a beta channel) - name: The name of the package - version: The version of the package - build_number: The build number of the package - python_version: The current version of python we're building for. May be - ``noarch``, to check for "noarch" packages or ``None``, in which case we - don't check for the python version + basename: The basename of the tarball to search for - Returns: A complete package name, version and build string, if the package - already exists in the channel or ``None`` otherwise. + Returns: A complete package url, if the package already exists in the channel + or ``None`` otherwise. """ from conda.exports import get_index - # handles different cases as explained on the description of - # ``python_version`` - py_ver = python_version.replace('.', '') if python_version else None - if py_ver == 'noarch': py_ver = '' - # get the channel index logger.debug('Downloading channel index from %s', channel_url) index = get_index(channel_urls=[channel_url], prepend=False) - logger.info('Checking for %s-%s-%s...', name, version, build_number) + logger.info('Checking for %s...', basename) for dist in index: + url = index[dist].url + if url.endswith(basename): + logger.debug('Found matching package (%s) at %s', basename, url) + return url - if dist.name == name and dist.version == version and \ - dist.build_string.endswith('_%s' % build_number): - - # two possible options must be checked - (i) the package build_string - # starts with ``py``, which means it is a python specific package so we - # must also check for the matching python version. (ii) the package is - # not a python-specific package and a simple match will do - if dist.build_string.startswith('py'): - match = re.match('py[2-9][0-9]+', dist.build_string) - if match and match.group() == 'py{}'.format(py_ver): - logger.debug('Found matching package (%s-%s-%s)', dist.name, - dist.version, dist.build_string) - return (dist.name, dist.version, dist.build_string) - - else: - logger.debug('Found matching package (%s-%s-%s)', dist.name, - dist.version, dist.build_string) - return (dist.name, dist.version, dist.build_string) - - if py_ver is None: - logger.info('No matches for %s-%s-%s found among %d packages', - name, version, build_number, len(index)) - else: - logger.info('No matches for %s-%s-py%s_%s found among %d packages', - name, version, py_ver, build_number, len(index)) + logger.debug('No matches for %s', path) return @@ -543,26 +515,16 @@ def base_build(bootstrap, server, intranet, group, recipe_dir, 'on %s', recipe_dir, python_version, arch) return - recipe = get_parsed_recipe(metadata) + path = get_output_path(metadata, conda_config) - candidate = exists_on_channel(public_channels[0], recipe['package']['name'], - recipe['package']['version'], recipe['build']['number'], - python_version) - if candidate is not None: - logger.info('Skipping build for %s-%s-%s for %s - exists ' \ - 'on channel', candidate[0], candidate[1], candidate[2], arch) + url = exists_on_channel(public_channels[0], os.path.basename(path)) + if url is not None: + logger.info('Skipping build for %s as it exists (at %s)', path, url) return # if you get to this point, just builds the package - if py_ver is None: - logger.info('Building %s-%s-%s for %s', - recipe['package']['name'], recipe['package']['version'], - recipe['build']['number'], arch) - else: - logger.info('Building %s-%s-py%s_%s for %s', - recipe['package']['name'], recipe['package']['version'], py_ver, - recipe['build']['number'], arch) - conda_build.api.build(recipe_dir, config=conda_config) + logger.info('Building %s', path) + conda_build.api.build(metadata, config=conda_config) if __name__ == '__main__': @@ -659,20 +621,36 @@ if __name__ == '__main__': '\n - '.join(channels + ['defaults'])) condarc_options['channels'] = channels + ['defaults'] - # retrieve the current build number for this build - build_number, _ = next_build_number(channels[0], args.name, version, - args.python_version) - bootstrap.set_environment('BOB_BUILD_NUMBER', str(build_number)) - logger.info('Merging conda configuration files...') conda_config = make_conda_config(conda_build_config, args.python_version, recipe_append, condarc_options) + metadata = get_rendered_metadata(os.path.join(args.work_dir, 'conda'), + conda_config) + path = get_output_path(metadata, conda_config) + + # asserts we're building at the right location + assert path.startswith(os.path.join(args.conda_root, 'conda-bld')), \ + 'Output path for build (%s) does not start with "%s" - this ' \ + 'typically means this build is running on a shared builder and ' \ + 'the file ~/.conda/environments.txt is polluted with other ' \ + 'environment paths. To fix, empty that file and set its mode ' \ + 'to read-only for all.' % (path, os.path.join(args.conda_root, + 'conda-bld')) + + # retrieve the current build number for this build + build_number, _ = next_build_number(channels[0], os.path.basename(path)) + # runs the build using the conda-build API arch = conda_arch() logger.info('Building %s-%s-py%s (build: %d) for %s', args.name, version, args.python_version.replace('.',''), build_number, arch) + + # notice we cannot build from the pre-parsed metadata because it has already + # resolved the "wrong" build number. We'll have to reparse after setting the + # environment variable BOB_BUILD_NUMBER. + bootstrap.set_environment('BOB_BUILD_NUMBER', str(build_number)) conda_build.api.build(os.path.join(args.work_dir, 'conda'), config=conda_config) diff --git a/bob/devtools/data/gitlab-ci/nightlies.yaml b/bob/devtools/data/gitlab-ci/nightlies.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1309dfdbc0c3a3fbd045838c6eae8b25e7e705a2 --- /dev/null +++ b/bob/devtools/data/gitlab-ci/nightlies.yaml @@ -0,0 +1,35 @@ +# This YAML file contains descriptions for the CI of nightly builds of Bob and +# BEAT. + +stages: + - build + +.build_template: + variables: + CONDA_ROOT: "${CI_PROJECT_DIR}/miniconda" + PYTHON_VERSION: "3.6" + PYTHONUNBUFFERED: 1 + stage: build + script: + - curl --silent "${BOOTSTRAP}" --output "bootstrap.py" + - python3 bootstrap.py -vv channel base + - source ${CONDA_ROOT}/etc/profile.d/conda.sh + - conda activate base + - bdt ci nightlies -vv order.txt + - bdt ci clean -vv + cache: + key: "$CI_JOB_NAME" + paths: + - miniconda.sh + - ${CONDA_ROOT}/pkgs/*.tar.bz2 + - ${CONDA_ROOT}/pkgs/urls.txt + +linux: + extends: .build_template + tags: + - docker + +macosx: + extends: .build_template + tags: + - macosx diff --git a/bob/devtools/scripts/build.py b/bob/devtools/scripts/build.py index 29aef6465c1866f1dd888cf0ac6249dd1e33cb5c..059aae25a14e08b3c648a51f0962690060dcad10 100644 --- a/bob/devtools/scripts/build.py +++ b/bob/devtools/scripts/build.py @@ -12,7 +12,7 @@ import conda_build.api from . import bdt from ..build import next_build_number, conda_arch, should_skip_build, \ get_rendered_metadata, get_parsed_recipe, make_conda_config, \ - get_docserver_setup, get_env_directory + get_docserver_setup, get_env_directory, get_output_path from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND, \ SERVER, MATPLOTLIB_RCDIR, BASE_CONDARC from ..bootstrap import set_environment, get_channels @@ -125,6 +125,8 @@ def build(recipe_dir, python, condarc, config, no_test, append_file, server=server, intranet=ci, group=group) set_environment('BOB_DOCUMENTATION_SERVER', doc_urls) + arch = conda_arch() + for d in recipe_dir: if not os.path.exists(d): @@ -135,24 +137,27 @@ def build(recipe_dir, python, condarc, config, no_test, append_file, version = open(version_candidate).read().rstrip() set_environment('BOB_PACKAGE_VERSION', version) - # pre-renders the recipe - figures out package name and version + # pre-renders the recipe - figures out the destination metadata = get_rendered_metadata(d, conda_config) - # checks we should actually build this recipe - arch = conda_arch() - if should_skip_build(metadata): - logger.warn('Skipping UNSUPPORTED build of "%s" for py%s on %s', - d, python.replace('.',''), arch) - return 0 - - # converts the metadata output into parsed yaml and continues the process rendered_recipe = get_parsed_recipe(metadata) - # if a channel URL was passed, set the build number - build_number, _ = next_build_number(channels[0], - rendered_recipe['package']['name'], - rendered_recipe['package']['version'], python) + path = get_output_path(metadata, conda_config) + # checks if we should actually build this recipe + if should_skip_build(metadata): + logger.info('Skipping UNSUPPORTED build of %s-%s-py%s for %s', + rendered_recipe['package']['name'], + rendered_recipe['package']['version'], python.replace('.',''), + arch) + continue + + # gets the next build number + build_number, _ = next_build_number(channels[0], os.path.basename(path)) + + # notice we cannot build from the pre-parsed metadata because it has + # already resolved the "wrong" build number. We'll have to reparse after + # setting the environment variable BOB_BUILD_NUMBER. set_environment('BOB_BUILD_NUMBER', str(build_number)) logger.info('Building %s-%s-py%s (build: %d) for %s', diff --git a/bob/devtools/scripts/ci.py b/bob/devtools/scripts/ci.py index 0a21c74106fb9022ed5e1e3d47b2732d07a3e5ab..6139da6a72625cc0105195b502801530cf0334f6 100644 --- a/bob/devtools/scripts/ci.py +++ b/bob/devtools/scripts/ci.py @@ -11,7 +11,7 @@ import conda_build.api from click_plugins import with_plugins from . import bdt -from ..constants import SERVER +from ..constants import SERVER, CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND from ..log import verbosity_option, get_logger, echo_normal logger = get_logger(__name__) @@ -317,8 +317,7 @@ Examples: ''') @click.argument('order', required=True, type=click.Path(file_okay=True, dir_okay=False, exists=True), nargs=1) -@click.option('-g', '--group', show_default=True, - default=os.environ['CI_PROJECT_NAMESPACE'], +@click.option('-g', '--group', show_default=True, default='bob', help='Group of packages (gitlab namespace) this package belongs to') @click.option('-p', '--python', multiple=True, help='Versions of python in the format "x.y" we should build for. Pass ' \ @@ -337,8 +336,6 @@ def base_build(order, group, python, dry_run): this context. """ - from ..constants import CONDA_BUILD_CONFIG - condarc = os.path.join(os.environ['CONDA_ROOT'], 'condarc') logger.info('Loading (this build\'s) CONDARC file from %s...', condarc) with open(condarc, 'rb') as f: @@ -400,8 +397,6 @@ def test(ctx, dry_run): to be used outside this context. """ - from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND - group = os.environ['CI_PROJECT_NAMESPACE'] if group not in ('bob', 'beat'): # defaults back to bob - no other server setups are available as of now @@ -445,8 +440,6 @@ def build(ctx, dry_run): to be used outside this context. """ - from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND - group = os.environ['CI_PROJECT_NAMESPACE'] if group not in ('bob', 'beat'): # defaults back to bob - no other server setups are available as of now @@ -491,3 +484,95 @@ def clean(ctx): from ..bootstrap import run_cmdline git_clean_build(run_cmdline, verbose=(ctx.meta['verbosity']>=3)) + + +@ci.command(epilog=''' +Examples: + + 1. Runs the nightly builds following a list of packages in a file: + + $ bdt ci nightlies -vv order.txt + +''') +@click.argument('order', required=True, type=click.Path(file_okay=True, + dir_okay=False, exists=True), nargs=1) +@click.option('-d', '--dry-run/--no-dry-run', default=False, + help='Only goes through the actions, but does not execute them ' \ + '(combine with the verbosity flags - e.g. ``-vvv``) to enable ' \ + 'printing to help you understand what will be done') +@verbosity_option() +@bdt.raise_on_error +@click.pass_context +def nightlies(ctx, order, dry_run): + """Runs nightly builds + + This command can run nightly builds for packages listed on a file. + + The build or each package happens in a few phases: + + 1. Package is checked out and switched to the requested branch (master if not + set otherwise) + 2. A build string is calculated from current dependencies. If the package + has already been compiled, it is downloaded from the respective conda + channel and tested. If the test does not pass, the package is completely + rebuilt + 3. If the rebuild is successful, the new package is uploaded to the + respective conda channel, and the program continues with the next package + + Dependencies are searched with priority to locally built packages. For this + reason, the input file **must** be provided in the right dependence order. + """ + + # loads dirnames from order file (accepts # comments and empty lines) + packages = [] + with open(order, 'rt') as f: + for line in f: + line = line.partition('#')[0].strip() + if line: + if ',' in line: #user specified a branch + path, branch = [k.strip() for k in line.split(',', 1)] + packages.append((path, branch)) + else: + packages.apend((line, 'master')) + + import git + from .rebuild import rebuild + from urllib.request import urlopen + + # loaded all recipes, now cycle through them implementing what is described + # in the documentation of this function + for n, (package, branch) in enumerate(packages): + + echo_normal('\n' + (80*'=')) + echo_normal('Testing/Re-building %s@%s (%d/%d)' % (package, branch, n+1, + len(packages)) + echo_normal((80*'=') + '\n') + + group, name = package.split('/', 1) + + clone_to = os.path.join(os.environ['CI_PROJECT_DIR'], 'src', group, name) + dirname = os.path.dirname(clone_to) + if not os.path.exists(dirname): + os.makedirs(dirname) + + # clone the repo, shallow version, on the specified branch + logger.info('Cloning "%s", branch "%s" (depth=1)...', package, branch) + git.Repo.clone_from('https://gitlab-ci-token:%s@gitlab.idiap.ch/%s' % \ + (token, package), clone_to, branch=branch, depth=1) + + # determine package visibility + private = urlopen('https://gitlab.idiap.ch/%s' % package).getcode() != 200 + + ctx.invoke(rebuild, + recipe_dir=[os.path.join(clone_to, 'conda')], + python=os.environ['PYTHON_VERSION'], #python version + condarc=None, #custom build configuration + config=CONDA_BUILD_CONFIG, + append_file=CONDA_RECIPE_APPEND, + server=SERVER, + group=group, + private=private, + stable='STABLE' in os.environ, + dry_run=dry_run, + ci=True, + ) diff --git a/bob/devtools/scripts/rebuild.py b/bob/devtools/scripts/rebuild.py new file mode 100644 index 0000000000000000000000000000000000000000..4187b1950f94e0f4f81501f695d0f10de0a536c5 --- /dev/null +++ b/bob/devtools/scripts/rebuild.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import urllib.request + +import yaml +import click +import pkg_resources +import conda_build.api + +from . import bdt +from ..build import next_build_number, conda_arch, should_skip_build, \ + get_rendered_metadata, get_parsed_recipe, make_conda_config, \ + get_docserver_setup, get_env_directory, get_output_path +from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND, \ + SERVER, MATPLOTLIB_RCDIR, BASE_CONDARC +from ..bootstrap import set_environment, get_channels + +from ..log import verbosity_option, get_logger, echo_normal +logger = get_logger(__name__) + + +@click.command(epilog=''' +Examples: + 1. Rebuilds a recipe from one of our packages, checked-out at "bob/bob.extension", for python 3.6: + +\b + $ bdt rebuild -vv --python=3.6 bob/bob.extension/conda + + + 2. To rebuild multiple recipes, just pass the paths to them: + +\b + $ bdt rebuild -vv --python=3.6 path/to/recipe-dir1 path/to/recipe-dir2 +''') +@click.argument('recipe-dir', required=False, type=click.Path(file_okay=False, + dir_okay=True, exists=True), nargs=-1) +@click.option('-p', '--python', default=('%d.%d' % sys.version_info[:2]), + show_default=True, help='Version of python to build the environment for') +@click.option('-r', '--condarc', + help='Use custom conda configuration file instead of our own',) +@click.option('-m', '--config', '--variant-config-files', show_default=True, + default=CONDA_BUILD_CONFIG, help='overwrites the path leading to ' \ + 'variant configuration file to use') +@click.option('-a', '--append-file', show_default=True, + default=CONDA_RECIPE_APPEND, help='overwrites the path leading to ' \ + 'appended configuration file to use') +@click.option('-S', '--server', show_default=True, default=SERVER, + help='Server used for downloading conda packages and documentation ' \ + 'indexes of required packages') +@click.option('-g', '--group', show_default=True, default='bob', + help='Group of packages (gitlab namespace) this package belongs to') +@click.option('-P', '--private/--no-private', default=False, + help='Set this to **include** private channels on your build - ' \ + 'you **must** be at Idiap to execute this build in this case - ' \ + 'you **must** also use the correct server name through --server - ' \ + 'notice this option has no effect to conda if you also pass --condarc') +@click.option('-X', '--stable/--no-stable', default=False, + help='Set this to **exclude** beta channels from your build - ' \ + 'notice this option has no effect if you also pass --condarc') +@click.option('-d', '--dry-run/--no-dry-run', default=False, + help='Only goes through the actions, but does not execute them ' \ + '(combine with the verbosity flags - e.g. ``-vvv``) to enable ' \ + 'printing to help you understand what will be done') +@click.option('-C', '--ci/--no-ci', default=False, hidden=True, + help='Use this flag to indicate the build will be running on the CI') +@verbosity_option() +@bdt.raise_on_error +def rebuild(recipe_dir, python, condarc, config, append_file, + server, group, private, stable, dry_run, ci): + """Tests and rebuilds packages through conda-build with stock configuration + + This command wraps the execution of conda-build in two stages: first, from + the original package recipe and some channel look-ups, it figures out what is + the lastest version of the package available. It downloads such file and + runs a test. If the test suceeds, then it proceeds to the next recipe. + Otherwise, it rebuilds the package and uploads a new version to the channel. + """ + + # if we are in a dry-run mode, let's let it be known + if dry_run: + logger.warn('!!!! DRY RUN MODE !!!!') + logger.warn('Nothing will be really built') + + recipe_dir = recipe_dir or [os.path.join(os.path.realpath('.'), 'conda')] + + logger.debug('This package is considered part of group "%s" - tunning ' \ + 'conda package and documentation URLs for this...', group) + + # get potential channel upload and other auxiliary channels + channels = get_channels(public=(not private), stable=stable, server=server, + intranet=ci, group=group) + + if condarc is not None: + logger.info('Loading CONDARC file from %s...', condarc) + with open(condarc, 'rb') as f: + condarc_options = yaml.load(f) + else: + # use default and add channels + condarc_options = yaml.load(BASE_CONDARC) #n.b.: no channels + logger.info('Using the following channels during build:\n - %s', + '\n - '.join(channels + ['defaults'])) + condarc_options['channels'] = channels + ['defaults'] + + # dump packages at base environment + prefix = get_env_directory(os.environ['CONDA_EXE'], 'base') + condarc_options['croot'] = os.path.join(prefix, 'conda-bld') + + conda_config = make_conda_config(config, python, append_file, + condarc_options) + + set_environment('MATPLOTLIBRC', MATPLOTLIB_RCDIR) + + # setup BOB_DOCUMENTATION_SERVER environment variable (used for bob.extension + # and derived documentation building via Sphinx) + set_environment('DOCSERVER', server) + doc_urls = get_docserver_setup(public=(not private), stable=stable, + server=server, intranet=ci, group=group) + set_environment('BOB_DOCUMENTATION_SERVER', doc_urls) + + arch = conda_arch() + + for d in recipe_dir: + + if not os.path.exists(d): + raise RuntimeError("The directory %s does not exist" % recipe_dir) + + version_candidate = os.path.join(d, '..', 'version.txt') + if os.path.exists(version_candidate): + version = open(version_candidate).read().rstrip() + set_environment('BOB_PACKAGE_VERSION', version) + + # pre-renders the recipe - figures out the destination + metadata = get_rendered_metadata(d, conda_config) + + rendered_recipe = get_parsed_recipe(metadata) + + path = get_output_path(metadata, conda_config) + + # checks if we should actually build this recipe + if should_skip_build(metadata): + logger.info('Skipping UNSUPPORTED build of %s-%s-py%s for %s', + rendered_recipe['package']['name'], + rendered_recipe['package']['version'], python.replace('.',''), + arch) + continue + + # Get the latest build number + build_number, existing = next_build_number(channels[0], + os.path.basename(path)) + + should_build = True + + if existing: #other builds exist, get the latest and see if it still works + + destpath = os.path.join(condarc_options['croot'], arch, + os.path.basename(existing[0])) + logger.info('Downloading %s -> %s', existing[0], destpath) + urllib.request.urlretrieve(existing[0], destpath) + + try: + logger.info('Testing %s', existing[0]) + conda_build.api.test(destpath, config=conda_config) + should_build = False + logger.info('Test for %s: SUCCESS', existing[0]) + except Exception as error: + logger.exception(error) + logger.warn('Test for %s: FAILED. Building...', existing[0]) + + + if should_build: #something wrong happened, run a full build + + logger.info('Building %s-%s-py%s (build: %d) for %s', + rendered_recipe['package']['name'], + rendered_recipe['package']['version'], python.replace('.',''), + build_number, arch) + + # notice we cannot build from the pre-parsed metadata because it has + # already resolved the "wrong" build number. We'll have to reparse after + # setting the environment variable BOB_BUILD_NUMBER. + set_environment('BOB_BUILD_NUMBER', str(build_number)) + + if not dry_run: + conda_build.api.build(d, config=conda_config, notest=no_test) + + else: #skip build, test worked + logger.info('Skipping build of %s-%s-py%s (build: %d) for %s', + rendered_recipe['package']['name'], + rendered_recipe['package']['version'], python.replace('.',''), + build_number, arch) diff --git a/conda/meta.yaml b/conda/meta.yaml index f5fdc7b3731619c9a812b8246d213fedfaa62f76..6424fb310e0c0e5c16240c3a894264d8b454cce5 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -59,6 +59,7 @@ test: - bdt dumpsphinx https://docs.python.org/3/objects.inv > /dev/null - bdt create --help - bdt build --help + - bdt rebuild --help - bdt test --help - bdt caupdate --help - bdt new --help @@ -87,6 +88,7 @@ test: - bdt ci pypi --help - bdt ci readme --help - bdt ci clean --help + - bdt ci nightlies --help - sphinx-build -aEW ${PREFIX}/share/doc/{{ name }}/doc sphinx - if [ -n "${CI_PROJECT_DIR}" ]; then mv sphinx "${CI_PROJECT_DIR}/"; fi diff --git a/setup.py b/setup.py index 2e852fa1440d33e3c4c3c188c450cb6c018bdb05..6866872593d60f48862aabe7fc63edc8926075b3 100644 --- a/setup.py +++ b/setup.py @@ -50,6 +50,7 @@ setup( 'dumpsphinx = bob.devtools.scripts.dumpsphinx:dumpsphinx', 'create = bob.devtools.scripts.create:create', 'build = bob.devtools.scripts.build:build', + 'rebuild = bob.devtools.scripts.rebuild:rebuild', 'test = bob.devtools.scripts.test:test', 'caupdate = bob.devtools.scripts.caupdate:caupdate', 'ci = bob.devtools.scripts.ci:ci', @@ -76,6 +77,7 @@ setup( 'deploy = bob.devtools.scripts.ci:deploy', 'readme = bob.devtools.scripts.ci:readme', 'pypi = bob.devtools.scripts.ci:pypi', + 'nightlies = bob.devtools.scripts.ci:nightlies', ], },