From 8a766fa33703622d5d47439e7f176c8ecc3dbe23 Mon Sep 17 00:00:00 2001
From: jlaura <jlaura@asu.edu>
Date: Fri, 29 Jul 2016 08:52:23 -0700
Subject: [PATCH] AppVeyor Upload Support

---
 appveyor.yml                                |  81 ++-
 ci_support/upload_or_check_non_existence.py | 119 +++
 ci_tools/condaci.py                         | 764 --------------------
 3 files changed, 178 insertions(+), 786 deletions(-)
 create mode 100644 ci_support/upload_or_check_non_existence.py
 delete mode 100644 ci_tools/condaci.py

diff --git a/appveyor.yml b/appveyor.yml
index 57a174b..e82a0c9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,35 +1,72 @@
 environment:
-   BINSTAR_USER: jlaura
-   PYTHON_VERSION: 3.4
 
+  CONDA_INSTALL_LOCN: "C:\\conda"
+
+  # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
+  # /E:ON and /V:ON options are not enabled in the batch script intepreter
+  # See: http://stackoverflow.com/a/13751649/163740
+  CMD_IN_ENV: "cmd /E:ON /V:ON /C obvci_appveyor_python_build_env.cmd"
+
+  # We set a default Python version for the miniconda that is to be installed. This can be
+  # overridden in the matrix definition where appropriate.
+  CONDA_PY: "27"
+
+  matrix:
+
+    - TARGET_ARCH: x64
+      CONDA_PY: 34
+
+# We always use a 64-bit machine, but can build x86 distributions
+# with the TARGET_ARCH variable.
 platform:
-   - x64
+    - x64
 
 install:
-   - cmd: set PATH=C:\Miniconda3;C:\Miniconda3\Scripts;%PATH%
-   - cmd: conda config --set always_yes yes --set changeps1 no
-   - cmd: conda update -q conda
+    # If there is a newer build queued for the same PR, cancel this one.
+    # The AppVeyor 'rollout builds' option is supposed to serve the same
+    # purpose but it is problematic because it tends to cancel builds pushed
+    # directly to master instead of just PR builds (or the converse).
+    # credits: JuliaLang developers.
+    - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
+        https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
+        Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
+          throw "There are newer queued builds for this pull request, failing early." }
+
+    # Cywing's git breaks conda-build. (See https://github.com/conda-forge/conda-smithy-feedstock/pull/2.)
+    - cmd: rmdir C:\cygwin /s /q
+    - appveyor DownloadFile "https://raw.githubusercontent.com/pelson/Obvious-CI/master/bootstrap-obvious-ci-and-miniconda.py"
+    - cmd: python bootstrap-obvious-ci-and-miniconda.py %CONDA_INSTALL_LOCN% %TARGET_ARCH% %CONDA_PY:~0,1% --without-obvci
+    - cmd: set PATH=%CONDA_INSTALL_LOCN%;%CONDA_INSTALL_LOCN%\scripts;%PATH%
+    - cmd: set PYTHONUNBUFFERED=1
 
-   # Useful for debugging any issues with conda
-   - cmd: conda info -a
+    - cmd: conda config --set show_channel_urls true
+    - cmd: conda install -c pelson/channel/development --yes --quiet obvious-ci
+    - cmd: conda config --add channels conda-forge
+    - cmd: conda info
+    - cmd: conda install -n root --quiet --yes conda-build anaconda-client jinja2 setuptools
+    # Workaround for Python 3.4 and x64 bug in latest conda-build.
+    # FIXME: Remove once there is a release that fixes the upstream issue
+    # ( https://github.com/conda/conda-build/issues/895 ).
+    - cmd: if "%TARGET_ARCH%" == "x64" if "%CONDA_PY%" == "34" conda install conda-build=1.20.0 --yes
 
-   # Install not using env because build needs to be in root env
-   - cmd: conda config --add channels conda-forge
-   - cmd: conda config --add channels jlaura
-   - cmd: conda install -c conda-forge gdal h5py
-   - cmd: conda install pandas sqlalchemy pyyaml
-   - cmd: pip install pvl
-   - cmd: pip install protobuf==3.0.0b2
+    # Now install the pacakge dependencies
+    - cmd: conda config --add channels conda-forge
+    - cmd: conda config --add channels jlaura
+    - cmd: conda install -c conda-forge gdal h5py
+    - cmd: conda install pandas sqlalchemy pyyaml
+    - cmd: conda install -c jlaura protobuf pvl
 
-   # Development installation
-   - cmd: conda install nose coverage anaconda-client
-   - cmd: pip install coveralls
+    # Development installation
+    - cmd: conda install nose coverage
+    - cmd: pip install coveralls
 
+# Skip .NET project specific build phase.
 build: off
 
 test_script:
-   - cmd: nosetests --with-coverage --cover-package=plio
+    - cmd: nosetests --with-coverage --cover-package=plio
+    - "%CMD_IN_ENV% conda build conda --quiet"
+
+deploy_script:
 
-after_test:
-   # Afte test success, package and upload to Anaconda
-   - cmd: python ci_tools/condaci.py build ./conda
+    - 'python ci_support\upload_or_check_non_existence.py .\conda jlaura --channel=main'
diff --git a/ci_support/upload_or_check_non_existence.py b/ci_support/upload_or_check_non_existence.py
new file mode 100644
index 0000000..9cedfdd
--- /dev/null
+++ b/ci_support/upload_or_check_non_existence.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import argparse
+import hashlib
+import os
+import subprocess
+import sys
+
+from binstar_client.utils import get_binstar
+import binstar_client.errors
+import conda.config
+from conda_build.metadata import MetaData
+from conda_build.build import bldpkg_path
+
+
+def built_distribution_already_exists(cli, meta, owner):
+    """
+    Checks to see whether the built recipe (aka distribution) already
+    exists on the owner/user's binstar account.
+    """
+    distro_name = '{}/{}.tar.bz2'.format(conda.config.subdir, meta.dist())
+    fname = bldpkg_path(meta)
+    try:
+        dist_info = cli.distribution(owner, meta.name(), meta.version(),
+                                     distro_name)
+    except binstar_client.errors.NotFound:
+        dist_info = {}
+
+    exists = bool(dist_info)
+    # Unfortunately, we cannot check the md5 quality of the built distribution, as
+    # this will depend on fstat information such as modification date (because
+    # distributions are tar files). Therefore we can only assume that the distribution
+    # just built, and the one on anaconda.org are the same.
+#    if exists:
+#        md5_on_binstar = dist_info.get('md5')
+#        with open(fname, 'rb') as fh:
+#            md5_of_build = hashlib.md5(fh.read()).hexdigest()
+#
+#        if md5_on_binstar != md5_of_build:
+#            raise ValueError('This build ({}), and the build already on binstar '
+#                             '({}) are different.'.format(md5_of_build, md5_on_binstar))
+    return exists
+
+
+def upload(cli, meta, owner, channels):
+    try:
+        with open('binstar.token', 'w') as fh:
+            fh.write(cli.token)
+        subprocess.check_call(['anaconda', '--quiet', '-t', 'binstar.token',
+                               'upload', bldpkg_path(meta),
+                               '--user={}'.format(owner),
+                               '--channel={}'.format(channels)],
+                              env=os.environ)
+    finally:
+        os.remove('binstar.token')
+
+
+def distribution_exists_on_channel(binstar_cli, meta, owner, channel='main'):
+    """
+    Determine whether a distribution exists on a specific channel.
+    Note from @pelson: As far as I can see, there is no easy way to do this on binstar.
+    """
+    fname = '{}/{}.tar.bz2'.format(conda.config.subdir, meta.dist())
+    distributions_on_channel = [dist['basename'] for dist in
+                                binstar_cli.show_channel(owner=owner, channel=channel)['files']]
+    return fname in distributions_on_channel
+
+
+def add_distribution_to_channel(binstar_cli, meta, owner, channel='main'):
+    """
+    Add a(n already existing) distribution on binstar to another channel.
+    Note - the addition is done based on name and version - no build strings etc.
+    so if you have a foo-0.1-np18 and foo-0.1-np19 *both* will be added to the channel.
+    """
+    package_fname = '{}/{}.tar.bz2'.format(conda.config.subdir, meta.dist())
+    binstar_cli.add_channel(channel, owner, meta.name(), meta.version())
+
+
+def main():
+    token = os.environ.get('BINSTAR_KEY')
+
+    description = ('Upload or check consistency of a built version of a '
+                   'conda recipe with binstar. Note: The existence of the '
+                   'BINSTAR_KEY environment variable determines '
+                   'whether the upload should actually take place.')
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument('recipe_dir', help='the conda recipe directory')
+    parser.add_argument('owner', help='the binstar owner/user')
+    parser.add_argument('--channel', help='the binstar channel', default='main')
+    args = parser.parse_args()
+    recipe_dir, owner, channel = args.recipe_dir, args.owner, args.channel
+
+    cli = get_binstar(argparse.Namespace(token=token, site=None))
+    meta = MetaData(recipe_dir)
+    if meta.skip():
+        print("No upload to take place - this configuration was skipped in build/skip.")
+        return
+    exists = built_distribution_already_exists(cli, meta, owner)
+    if token:
+        on_channel = distribution_exists_on_channel(cli, meta, owner, channel)
+        if not exists:
+            upload(cli, meta, owner, channel)
+            print('Uploaded {}'.format(bldpkg_path(meta)))
+        elif not on_channel:
+            print('Adding distribution {} to {}\'s {} channel'
+                  ''.format(bldpkg_path(meta), owner, channel))
+            add_distribution_to_channel(cli, meta, owner, channel)
+        else:
+            print('Distribution {} already \nexists on {}\'s {} channel.'
+                  ''.format(bldpkg_path(meta), owner, channel))
+    else:
+        print("No BINSTAR_KEY present, so no upload is taking place. "
+              "The distribution just built {} already available on {}'s "
+              "{} channel.".format('is' if exists else 'is not',
+                                   owner, channel))
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/ci_tools/condaci.py b/ci_tools/condaci.py
deleted file mode 100644
index 30ab480..0000000
--- a/ci_tools/condaci.py
+++ /dev/null
@@ -1,764 +0,0 @@
-#!/usr/bin/env python
-import subprocess
-import os
-import shutil
-import os.path as p
-from functools import partial
-import platform as stdplatform
-import uuid
-import sys
-from pprint import pprint
-
-# on windows we have to download a small secondary script that configures
-# Python 3 64-bit extensions. Here we define the URL and the local path that
-# we will use for this script.
-import zipfile
-
-MAGIC_WIN_SCRIPT_URL = 'https://raw.githubusercontent.com/menpo/condaci/master/run_with_env.cmd'
-MAGIC_WIN_SCRIPT_PATH = r'C:\run_with_env.cmd'
-VS2008_PATCH_URL = 'https://raw.githubusercontent.com/menpo/condaci/master/vs2008_patch.zip'
-VS2008_PATCH_PATH = r'C:\vs2008_patch.zip'
-VS2008_PATCH_FOLDER_PATH = r'C:\vs2008_patch'
-
-VS2008_PATH = r'C:\Program Files (x86)\Microsoft Visual Studio 9.0'
-VS2008_BIN_PATH = os.path.join(VS2008_PATH, 'VC', 'bin')
-VS2010_PATH = r'C:\Program Files (x86)\Microsoft Visual Studio 10.0'
-VS2010_BIN_PATH = os.path.join(VS2010_PATH, 'VC', 'bin')
-VS2010_AMD64_VCVARS_CMD = r'CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64'
-
-# a random string we can use for the miniconda installer
-# (to avoid name collisions)
-RANDOM_UUID = uuid.uuid4()
-
-# -------------------------------- STATE ------------------------------------ #
-
-# Key globals that need to be set for the rest of the script.
-PYTHON_VERSION = None
-PYTHON_VERSION_NO_DOT = None
-BINSTAR_USER = None
-BINSTAR_KEY = None
-
-
-def set_globals_from_environ(verbose=True):
-    global PYTHON_VERSION, BINSTAR_KEY, BINSTAR_USER, PYTHON_VERSION_NO_DOT
-
-    PYTHON_VERSION = os.environ.get('PYTHON_VERSION')
-    BINSTAR_USER = os.environ.get('BINSTAR_USER')
-    BINSTAR_KEY = os.environ.get('BINSTAR_KEY')
-
-    if verbose:
-        print('Environment variables extracted:')
-        print('  PYTHON_VERSION: {}'.format(PYTHON_VERSION))
-        print('  BINSTAR_USER:   {}'.format(BINSTAR_USER))
-        print('  BINSTAR_KEY:    {}'.format('*****' if BINSTAR_KEY is not None
-                                            else '-'))
-
-    if PYTHON_VERSION is None:
-        raise ValueError('Fatal: PYTHON_VERSION is not set.')
-    if PYTHON_VERSION not in ['2.7', '3.4', '3.5']:
-        raise ValueError("Fatal: PYTHON_VERSION '{}' is invalid - must be "
-                         "either '2.7', '3.4' or '3.5'".format(PYTHON_VERSION))
-
-    # Required when setting Python version in conda
-    PYTHON_VERSION_NO_DOT = PYTHON_VERSION.replace('.', '')
-
-
-# ------------------------------ UTILITIES ---------------------------------- #
-
-# forward stderr to stdout
-check = partial(subprocess.check_call, stderr=subprocess.STDOUT)
-
-
-def execute(cmd, verbose=True, env_additions=None):
-    r""" Runs a command, printing the command and it's output to screen.
-    """
-    env_for_p = os.environ.copy()
-    if env_additions is not None:
-        env_for_p.update(env_additions)
-    if verbose:
-        print('> {}'.format(' '.join(cmd)))
-        if env_additions is not None:
-            print('Additional environment variables: '
-                  '{}'.format(', '.join(['{}={}'.format(k, v)
-                                         for k, v in env_additions.items()])))
-    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT, env=env_for_p)
-    sentinal = ''
-    if sys.version_info.major == 3:
-        sentinal = b''
-    for line in iter(proc.stdout.readline, sentinal):
-        if verbose:
-            if sys.version_info.major == 3:
-                # convert bytes to string
-                line = line.decode("utf-8")
-            sys.stdout.write(line)
-            sys.stdout.flush()
-    output = proc.communicate()[0]
-    if proc.returncode == 0:
-        return
-    else:
-        e = subprocess.CalledProcessError(proc.returncode, cmd, output=output)
-        print(' -> {}'.format(e.output))
-        raise e
-
-
-def execute_sequence(*cmds, **kwargs):
-    r""" Execute a sequence of commands. If any fails, display an error.
-    """
-    verbose = kwargs.get('verbose', True)
-    for cmd in cmds:
-        execute(cmd, verbose)
-
-
-def extract_zip(zip_path, dest_dir):
-    r"""
-    Extract a zip file to a destination
-    """
-    with zipfile.PyZipFile(str(zip_path)) as z:
-        z.extractall(path=str(dest_dir))
-
-
-def download_file(url, path_to_download):
-    try:
-        from urllib2 import urlopen
-    except ImportError:
-        from urllib.request import urlopen
-    f = urlopen(url)
-    with open(path_to_download, "wb") as fp:
-        fp.write(f.read())
-    fp.close()
-
-
-def dirs_containing_file(fname, root=os.curdir):
-    for path, dirs, files in os.walk(os.path.abspath(root)):
-        if fname in files:
-            yield path
-
-
-def host_platform():
-    return stdplatform.system()
-
-
-def host_arch():
-    arch = stdplatform.architecture()[0]
-    # need to be a little more sneaky to check the platform on Windows:
-    # http://stackoverflow.com/questions/2208828/detect-64bit-os-windows-in-python
-    if host_platform() == 'Windows':
-        if 'APPVEYOR' in os.environ:
-            av_platform = os.environ['PLATFORM']
-            if av_platform == 'x86':
-                arch = '32bit'
-            elif av_platform == 'x64':
-                arch = '64bit'
-            else:
-                print('Was unable to interpret the platform "{}"'.format())
-    return arch
-
-
-# ------------------------ MINICONDA INTEGRATION ---------------------------- #
-
-def url_for_platform_version(platform, py_version, arch):
-    version = 'latest'
-    base_url = 'http://repo.continuum.io/miniconda/Miniconda'
-    platform_str = {'Linux': 'Linux',
-                    'Darwin': 'MacOSX',
-                    'Windows': 'Windows'}
-    arch_str = {'64bit': 'x86_64',
-                '32bit': 'x86'}
-    ext = {'Linux': '.sh',
-           'Darwin': '.sh',
-           'Windows': '.exe'}
-
-    if py_version in ['3.4', '3.5']:
-        base_url += '3'
-    elif py_version != '2.7':
-        raise ValueError("Python version must be '2.7', '3.4' or '3.5'")
-    return '-'.join([base_url, version,
-                     platform_str[platform],
-                     arch_str[arch]]) + ext[platform]
-
-
-def appveyor_miniconda_dir():
-    if PYTHON_VERSION in ['3.4', '3.5']:
-        conda_dir = r'C:\Miniconda3'
-    elif PYTHON_VERSION == '2.7':
-        conda_dir = r'C:\Miniconda'
-    else:
-        raise ValueError("Python version must be '2.7', '3.4' or '3.5'")
-
-    if host_arch() == '64bit':
-        conda_dir += '-x64'
-
-    return conda_dir
-
-
-def temp_installer_path():
-    # we need a place to download the miniconda installer too. use a random
-    # string for the filename to avoid collisions, but choose the dir based
-    # on platform
-    return ('C:\{}.exe'.format(RANDOM_UUID) if host_platform() == 'Windows'
-            else p.expanduser('~/{}.sh'.format(RANDOM_UUID)))
-
-
-def miniconda_dir():
-    # the directory where miniconda will be installed too/is
-    if host_platform() == 'Windows':
-        path = appveyor_miniconda_dir()
-    else:  # Unix
-        path = p.expanduser('~/miniconda')
-    if is_on_jenkins():
-        # jenkins persists miniconda installs between builds, but we want a
-        # unique miniconda env for each executor
-        if not os.path.isdir(path):
-                os.mkdir(path)
-        exec_no = os.environ['EXECUTOR_NUMBER']
-        j_path = os.path.join(path, exec_no)
-        if not os.path.isdir(j_path):
-            os.mkdir(j_path)
-        path = os.path.join(j_path, PYTHON_VERSION)
-    return path
-
-
-# the script directory inside a miniconda install varies based on platform
-def miniconda_script_dir_name():
-    return 'Scripts' if host_platform() == 'Windows' else 'bin'
-
-
-# handles to binaries from a miniconda install
-exec_ext = '.exe' if host_platform() == 'Windows' else ''
-miniconda_script_dir = lambda mc: p.join(mc, miniconda_script_dir_name())
-conda = lambda mc: p.join(miniconda_script_dir(mc), 'conda' + exec_ext)
-binstar = lambda mc: p.join(miniconda_script_dir(mc), 'anaconda' + exec_ext)
-
-
-def acquire_miniconda(url, path_to_download):
-    print('Downloading miniconda from {} to {}'.format(url, path_to_download))
-    download_file(url, path_to_download)
-
-
-def install_miniconda(path_to_installer, path_to_install):
-    print('Installing miniconda to {}'.format(path_to_install))
-    if host_platform() == 'Windows':
-        execute([path_to_installer, '/S', '/D={}'.format(path_to_install)])
-    else:
-        execute(['chmod', '+x', path_to_installer])
-        execute([path_to_installer, '-b', '-p', path_to_install])
-
-
-def setup_miniconda(python_version, installation_path, binstar_user=None):
-    conda_cmd = conda(installation_path)
-    if os.path.exists(conda_cmd):
-        print('conda is already setup at {}'.format(installation_path))
-    else:
-        print('No existing conda install detected at {}'.format(installation_path))
-        url = url_for_platform_version(host_platform(), python_version,
-                                       host_arch())
-        print('Setting up miniconda from URL {}'.format(url))
-        print("(Installing to '{}')".format(installation_path))
-        acquire_miniconda(url, temp_installer_path())
-        install_miniconda(temp_installer_path(), installation_path)
-        # delete the installer now we are done
-        os.unlink(temp_installer_path())
-    cmds = [[conda_cmd, 'update', '-q', '--yes', 'conda'],
-            [conda_cmd, 'install', '-q', '--yes', 'conda-build', 'jinja2',
-             'anaconda-client']]
-    root_config = os.path.join(installation_path, '.condarc')
-    if os.path.exists(root_config):
-        print('existing root config at present at {} - removing'.format(root_config))
-        os.unlink(root_config)
-    if binstar_user is not None:
-        print("(adding user channel '{}' for dependencies to root config)".format(binstar_user))
-        cmds.append([conda_cmd, 'config', '--system', '--add', 'channels', binstar_user])
-    else:
-        print("No user channels have been configured (all dependencies have to be "
-              "sourced from anaconda)")
-    execute_sequence(*cmds)
-
-
-# ------------------------ CONDA BUILD INTEGRATION -------------------------- #
-
-def get_conda_build_path(mc_dir, recipe_dir):
-    path_bytes = subprocess.check_output([conda(mc_dir), 'build',
-                                          recipe_dir, '--output'])
-    return path_bytes.decode("utf-8").strip()
-
-
-def conda_build_package_win(mc, path):
-    if 'BINSTAR_KEY' in os.environ:
-        print('found BINSTAR_KEY in environment on Windows - deleting to '
-              'stop vcvarsall from telling the world')
-        del os.environ['BINSTAR_KEY']
-    os.environ['PYTHON_ARCH'] = host_arch()[:2]
-    os.environ['PYTHON_VERSION'] = PYTHON_VERSION
-    print('PYTHON_ARCH={} PYTHON_VERSION={}'.format(os.environ['PYTHON_ARCH'],
-                                                    os.environ['PYTHON_VERSION']))
-    execute(['cmd', '/E:ON', '/V:ON', '/C', MAGIC_WIN_SCRIPT_PATH,
-             conda(mc), 'build', '-q', path,
-             '--py={}'.format(PYTHON_VERSION_NO_DOT)])
-
-
-def windows_setup_compiler():
-    arch = host_arch()
-    if PYTHON_VERSION == '2.7':
-        download_file(VS2008_PATCH_URL, VS2008_PATCH_PATH)
-        if not os.path.exists(VS2008_PATCH_FOLDER_PATH):
-            os.makedirs(VS2008_PATCH_FOLDER_PATH)
-        extract_zip(VS2008_PATCH_PATH, VS2008_PATCH_FOLDER_PATH)
-
-        if arch == '64bit':
-            execute([os.path.join(VS2008_PATCH_FOLDER_PATH, 'setup_x64.bat')])
-
-            VS2008_AMD64_PATH = os.path.join(VS2008_BIN_PATH, 'amd64')
-            if not os.path.exists(VS2008_AMD64_PATH):
-                os.makedirs(VS2008_AMD64_PATH)
-            shutil.copyfile(os.path.join(VS2008_BIN_PATH, 'vcvars64.bat'),
-                            os.path.join(VS2008_AMD64_PATH, 'vcvarsamd64.bat'))
-        elif arch == '32bit':
-            # For some reason these files seems to be missing on Appveyor
-            # execute([os.path.join(VS2008_PATCH_FOLDER_PATH, 'setup_x86.bat')])
-            pass
-        else:
-            raise ValueError('Unexpected architecture {}'.format(arch))
-    elif PYTHON_VERSION == '3.4' and arch == '64bit':
-        VS2010_AMD64_PATH = os.path.join(VS2010_BIN_PATH, 'amd64')
-        if not os.path.exists(VS2010_AMD64_PATH):
-            os.makedirs(VS2010_AMD64_PATH)
-        VS2010_AMD64_VCVARS_PATH = os.path.join(VS2010_AMD64_PATH,
-                                                'vcvars64.bat')
-        with open(VS2010_AMD64_VCVARS_PATH, 'w') as f:
-            f.write(VS2010_AMD64_VCVARS_CMD)
-
-
-def build_conda_package(mc, path, binstar_user=None):
-    print('Building package at path {}'.format(path))
-    v = get_version(path)
-    print('Detected version: {}'.format(v))
-    print('Setting CONDACI_VERSION environment variable to {}'.format(v))
-    os.environ['CONDACI_VERSION'] = v
-    print('Setting CONDA_PY environment variable to {}'.format(
-        PYTHON_VERSION_NO_DOT))
-    os.environ['CONDA_PY'] = PYTHON_VERSION_NO_DOT
-
-    # we want to add the master channel when doing dev builds to source our
-    # other dev dependencies
-    if not (is_release_tag(v) or is_rc_tag(v)):
-        print('building a non-release non-RC build - adding master channel.')
-        if binstar_user is None:
-            print('warning - no binstar user provided - cannot add master channel')
-        else:
-            execute([conda(mc), 'config', '--system', '--add', 'channels',
-                     binstar_user + '/channel/master'])
-    else:
-        print('building a RC or tag release - no master channel added.')
-
-    if host_platform() == 'Windows':
-        # Before building the package, we may need to edit the environment a bit
-        # to handle the nightmare that is Visual Studio compilation
-        windows_setup_compiler()
-        conda_build_package_win(mc, path)
-    else:
-        execute([conda(mc), 'build', '-q', path,
-                 '--py={}'.format(PYTHON_VERSION_NO_DOT)])
-
-
-# ------------------------- VERSIONING INTEGRATION -------------------------- #
-
-# versions that match up to master changes (anything after a '+')
-same_version_different_build = lambda v1, v2: v2.startswith(v1.split('+')[0])
-
-
-def versions_from_versioneer():
-    # Ideally, we will interrogate versioneer to find out the version of the
-    # project we are building. Note that we can't simply look at
-    # project.__version__ as we need the version string pre-build, so the
-    # package may not be importable.
-    for dir_ in dirs_containing_file('_version.py'):
-        sys.path.insert(0, dir_)
-
-        try:
-            import _version
-            yield _version.get_versions()['version']
-        except Exception as e:
-            print(e)
-        finally:
-            if '_version' in sys.modules:
-                sys.modules.pop('_version')
-
-            sys.path.pop(0)
-
-
-def version_from_meta_yaml(path):
-    meta_yaml_path = os.path.join(path, 'meta.yaml')
-    with open(meta_yaml_path, 'rt') as f:
-        s = f.read()
-    v = s.split('version:', 1)[1].split('\n', 1)[0].strip().strip("'").strip('"')
-    if '{{' in v:
-        raise ValueError('Trying to establish version from meta.yaml'
-                         ' and it seems to be dynamic: {}'.format(v))
-    return v
-
-
-def get_version(path):
-    # search for versioneer versions in our subdirs
-    versions = list(versions_from_versioneer())
-
-    if len(versions) == 1:
-        version = versions[0]
-        print('Found unambiguous versioneer version: {}'.format(version))
-    elif len(versions) > 1:
-        raise ValueError('Multiple versioneer _version.py files - cannot '
-                         'resolve unambiguous version. '
-                         'Versions found are: {}'.format(versions))
-    else:
-        # this project doesn't seem to be versioneer controlled - maybe the
-        # version is hardcoded? Interrogate meta.yaml
-        version = version_from_meta_yaml(path)
-    return version
-
-# booleans about the state of the the PEP440 tags.
-is_tag = lambda v: '+' not in v
-is_dev_tag = lambda v: v.split('.')[-1].startswith('dev')
-is_rc_tag = lambda v: 'rc' in v.split('+')[0]
-is_release_tag = lambda v: is_tag(v) and not (is_rc_tag(v) or is_dev_tag(v))
-
-
-# -------------------------- BINSTAR INTEGRATION ---------------------------- #
-
-
-class LetMeIn:
-    def __init__(self, key):
-        self.token = key
-        self.site = False
-
-
-def login_to_binstar():
-    from binstar_client.utils import get_binstar
-    return get_binstar()
-
-
-def login_to_binstar_with_key(key):
-    from binstar_client.utils import get_binstar
-    return get_binstar(args=LetMeIn(key))
-
-
-class BinstarFile(object):
-
-    def __init__(self, full_name):
-        self.full_name = full_name
-
-    @property
-    def user(self):
-        return self.full_name.split('/')[0]
-
-    @property
-    def name(self):
-        return self.full_name.split('/')[1]
-
-    @property
-    def basename(self):
-        return '/'.join(self.full_name.split('/')[3:])
-
-    @property
-    def version(self):
-        return self.full_name.split('/')[2]
-
-    @property
-    def platform(self):
-        return self.full_name.replace('\\', '/').split('/')[3]
-
-    @property
-    def configuration(self):
-        return self.full_name.replace('\\', '/').split('/')[4].split('-')[2].split('.')[0]
-
-    def __str__(self):
-        return self.full_name
-
-    def __repr__(self):
-        return self.full_name
-
-    def all_info(self):
-        s = ["         user: {}".format(self.user),
-             "         name: {}".format(self.name),
-             "     basename: {}".format(self.basename),
-             "      version: {}".format(self.version),
-             "     platform: {}".format(self.platform),
-             "configuration: {}".format(self.configuration)]
-        return "\n".join(s)
-
-
-configuration_from_binstar_filename = lambda fn: fn.split('-')[-1].split('.')[0]
-name_from_binstar_filename = lambda fn: fn.split('-')[0]
-version_from_binstar_filename = lambda fn: fn.split('-')[1]
-platform_from_binstar_filepath = lambda fp: p.split(p.split(fp)[0])[-1]
-
-
-def binstar_channels_for_user(b, user):
-    return b.list_channels(user).keys()
-
-
-def binstar_files_on_channel(b, user, channel):
-    info = b.show_channel(channel, user)
-    return [BinstarFile(i['full_name']) for i in info['files']]
-
-
-def binstar_remove_file(b, bfile):
-    b.remove_dist(bfile.user, bfile.name, bfile.version, bfile.basename)
-
-
-def files_to_remove(b, user, channel, filepath):
-    platform_ = platform_from_binstar_filepath(filepath)
-    filename = p.split(filepath)[-1]
-    name = name_from_binstar_filename(filename)
-    version = version_from_binstar_filename(filename)
-    configuration = configuration_from_binstar_filename(filename)
-    # find all the files on this channel
-    all_files = binstar_files_on_channel(b, user, channel)
-    # other versions of this exact setup that are not tagged versions should
-    # be removed
-    print('Removing old releases matching:'
-          '\nname: {}\nconfiguration: {}\nplatform: {}'
-          '\nversion: {}'.format(name, configuration, platform_, version))
-    print('candidate releases with same name are:')
-    pprint([f.all_info() for f in all_files if f.name == name])
-    return [f for f in all_files if
-            f.name == name and
-            f.configuration == configuration and
-            f.platform == platform_ and
-            f.version != version and
-            not is_release_tag(f.version) and
-            same_version_different_build(version, f.version)]
-
-
-def purge_old_binstar_files(b, user, channel, filepath):
-    to_remove = files_to_remove(b, user, channel, filepath)
-    print("Found {} releases to remove".format(len(to_remove)))
-    for old_file in to_remove:
-        print("Removing '{}'".format(old_file))
-        binstar_remove_file(b, old_file)
-
-
-def binstar_upload_unchecked(mc, key, user, channel, path):
-    print('Uploading from {} using {}'.format(path, binstar(mc)))
-    try:
-        # TODO - could this safely be co? then we would get the binstar error..
-        check([binstar(mc), '-t', key, 'upload',
-               '--force', '-u', user, '-c', channel, path])
-    except subprocess.CalledProcessError as e:
-        # mask the binstar key...
-        cmd = e.cmd
-        cmd[2] = 'BINSTAR_KEY'
-        # ...then raise the error
-        raise subprocess.CalledProcessError(e.returncode, cmd)
-
-
-def binstar_upload_if_appropriate(mc, path, user, key):
-    if key is None:
-        print('No binstar key provided')
-    if user is None:
-        print('No binstar user provided')
-    if user is None or key is None:
-        print('-> Unable to upload to binstar')
-        return
-    print('Have a user ({}) and key - can upload if suitable'.format(user))
-
-    # decide if we should attempt an upload (if it's a PR we can't)
-    if resolve_can_upload_from_ci():
-        print('Auto resolving channel based on release type and CI status')
-        channel = binstar_channel_from_ci(path)
-        print("Fit to upload to channel '{}'".format(channel))
-        binstar_upload_and_purge(mc, key, user, channel,
-                                 get_conda_build_path(mc, path))
-    else:
-        print("Cannot upload to binstar - must be a PR.")
-
-
-def binstar_upload_and_purge(mc, key, user, channel, filepath):
-    if not os.path.exists(filepath):
-        raise ValueError('Built file {} does not exist. '
-                         'UPLOAD FAILED.'.format(filepath))
-    else:
-        print('Uploading to {}/{}'.format(user, channel))
-        binstar_upload_unchecked(mc, key, user, channel, filepath)
-        b = login_to_binstar_with_key(key)
-        if channel != 'main':
-            print("Purging old releases from channel '{}'".format(channel))
-            purge_old_binstar_files(b, user, channel, filepath)
-        else:
-            print("On main channel - no purging of releases will be done.")
-
-
-# -------------- CONTINUOUS INTEGRATION-SPECIFIC FUNCTIONALITY -------------- #
-
-is_on_appveyor = lambda: 'APPVEYOR' in os.environ
-is_on_travis = lambda: 'TRAVIS' in os.environ
-is_on_jenkins = lambda: 'JENKINS_URL' in os.environ
-
-is_pr_from_travis = lambda: os.environ['TRAVIS_PULL_REQUEST'] != 'false'
-is_pr_from_appveyor = lambda: 'APPVEYOR_PULL_REQUEST_NUMBER' in os.environ
-is_pr_from_jenkins = lambda: 'ghprbSourceBranch' in os.environ
-
-branch_from_appveyor = lambda: os.environ['APPVEYOR_REPO_BRANCH']
-
-
-def branch_from_jenkins():
-    branch = os.environ['GIT_BRANCH']
-    print('Jenkins has set GIT_BRANCH={}'.format(branch))
-    if branch.startswith('origin/tags/'):
-        print('WARNING - on jenkins and GIT_BRANCH starts with origin/tags/. '
-              'This suggests that we are building a tag.')
-        print('Jenkins obscures the branch in this scenario, so we assume that'
-              ' the branch is "master"')
-        return 'master'
-    elif branch.startswith('origin/'):
-        return branch.split('origin/', 1)[-1]
-    else:
-        raise ValueError('Error: jenkins branch name seems '
-                         'suspicious: {}'.format(branch))
-
-
-def branch_from_travis():
-    tag = os.environ['TRAVIS_TAG']
-    branch = os.environ['TRAVIS_BRANCH']
-    if tag == branch:
-        print('WARNING - on travis and TRAVIS_TAG == TRAVIS_BRANCH. This '
-              'suggests that we are building a tag.')
-        print('Travis obscures the branch in this scenario, so we assume that'
-              ' the branch is "master"')
-        return 'master'
-    else:
-        return branch
-
-
-def is_pr_on_ci():
-    if is_on_travis():
-        return is_pr_from_travis()
-    elif is_on_appveyor():
-        return is_pr_from_appveyor()
-    elif is_on_jenkins():
-        return is_pr_from_jenkins()
-    else:
-        raise ValueError("Not on appveyor, travis or jenkins, so can't "
-                         "resolve whether we are on a PR or not")
-
-
-def branch_from_ci():
-    if is_on_travis():
-        return branch_from_travis()
-    elif is_on_appveyor():
-        return branch_from_appveyor()
-    elif is_on_jenkins():
-        return branch_from_jenkins()
-    else:
-        raise ValueError("We aren't on jenkins, "
-                         "Appveyor or Travis so can't "
-                         "decide on branch")
-
-
-def resolve_can_upload_from_ci():
-    # can upload as long as this isn't a PR
-    can_upload = not is_pr_on_ci()
-    print("Can we can upload (i.e. is this not a PR)? : {}".format(can_upload))
-    return can_upload
-
-
-def binstar_channel_from_ci(path):
-    v = get_version(path)
-    if is_release_tag(v):
-        # tagged releases always go to main
-        print("current head is a tagged release ({}), "
-              "uploading to 'main' channel".format(v))
-        return 'main'
-    else:
-        print('current head is not a release - interrogating CI to decide on '
-              'channel to upload to (based on branch)')
-        return branch_from_ci()
-
-
-# -------------------- [EXPERIMENTAL] PYPI INTEGRATION ---------------------- #
-
-# pypirc_path = p.join(p.expanduser('~'), '.pypirc')
-# pypi_upload_allowed = (host_platform() == 'Linux' and
-#                        host_arch() == '64bit' and
-#                        sys.version_info.major == 2)
-#
-# pypi_template = """[distutils]
-# index-servers = pypi
-#
-# [pypi]
-# username:{}
-# password:{}"""
-#
-#
-# def pypi_setup_dotfile(username, password):
-#     with open(pypirc_path, 'wb') as f:
-#         f.write(pypi_template.format(username, password))
-#
-#
-# def upload_to_pypi_if_appropriate(mc, username, password):
-#     if username is None or password is None:
-#         print('Missing PyPI username or password, skipping upload')
-#         return
-#     v = get_version()
-#     if not is_release_tag(v):
-#         print('Not on a tagged release - not uploading to PyPI')
-#         return
-#     if not pypi_upload_allowed:
-#         print('Not on key node (Linux 64 Py2) - no PyPI upload')
-#     print('Setting up .pypirc file..')
-#     pypi_setup_dotfile(username, password)
-#     print("Uploading to PyPI user '{}'".format(username))
-#     execute_sequence([python(mc), 'setup.py', 'sdist', 'upload'])
-
-
-# --------------------------- ARGPARSE COMMANDS ----------------------------- #
-
-def miniconda_dir_cmd(_):
-    set_globals_from_environ(verbose=False)
-    print(miniconda_dir())
-
-
-def setup_cmd(_):
-    set_globals_from_environ()
-    mc = miniconda_dir()
-    setup_miniconda(PYTHON_VERSION, mc, binstar_user=BINSTAR_USER)
-
-
-def build_cmd(args):
-    set_globals_from_environ()
-    mc = miniconda_dir()
-    conda_meta = args.meta_yaml_dir
-
-    if host_platform() == 'Windows':
-        print('downloading magical Windows SDK configuration'
-              ' script to {}'.format(MAGIC_WIN_SCRIPT_PATH))
-        download_file(MAGIC_WIN_SCRIPT_URL, MAGIC_WIN_SCRIPT_PATH)
-
-    build_conda_package(mc, conda_meta, binstar_user=BINSTAR_USER)
-    print('successfully built conda package, proceeding to upload')
-    binstar_upload_if_appropriate(mc, conda_meta, BINSTAR_USER, BINSTAR_KEY)
-    # upload_to_pypi_if_appropriate(mc, args.pypiuser, args.pypipassword)
-
-
-if __name__ == "__main__":
-    from argparse import ArgumentParser
-    pa = ArgumentParser(
-        description=r"""
-        Sets up miniconda, builds, and uploads to Binstar.
-        """)
-    subp = pa.add_subparsers()
-
-    sp = subp.add_parser('setup', help='setup a miniconda environment')
-    sp.set_defaults(func=setup_cmd)
-
-    bp = subp.add_parser('build', help='run a conda build')
-    bp.add_argument('meta_yaml_dir',
-                    help="path to the dir containing the conda 'meta.yaml'"
-                         "build script")
-
-    mp = subp.add_parser('miniconda_dir',
-                         help='path to the miniconda root directory')
-    mp.set_defaults(func=miniconda_dir_cmd)
-
-    bp.set_defaults(func=build_cmd)
-    args = pa.parse_args()
-    args.func(args)
-- 
GitLab