#!/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()