#!/usr/bin/env python3 import itertools import subprocess import sys import logging LOG = logging.getLogger(__name__) REGISTRY = 'logilab/cubicweb' CWREPO = 'https://hg.logilab.org/master/cubicweb' MATRIX = [ (['py27'], ['stretch', 'buster'], [None, '3.25', '3.26'], [None, 'onbuild']), (['py35'], ['stretch'], [None, '3.26'], [None, 'onbuild']), (['py37'], ['buster'], [None, '3.26', 'dev'], [None, 'onbuild']), ] ALIASES = ( ('3.25', 'py27-buster-3.25'), ('3.26', 'py37-buster-3.26'), ('dev', 'py37-buster-dev'), ('latest', 'py37-buster-3.26'), ('buildpackage', 'buster-buildpackage'), ) def run(*args): LOG.info('%s', ' '.join(args)) return subprocess.run(args, stdout=subprocess.PIPE) def check_call(*args): LOG.info('%s', ' '.join(args)) return subprocess.check_call(args) def check_output(*args): LOG.info('%s', ' '.join(args)) return subprocess.check_output(args).decode().strip() def _cwdev(_cache={}): if not _cache: rev = check_output('hg', 'id', '-r', 'default', CWREPO) _cache[None] = '{}/archive/{}.tar.gz#egg=cubicweb[pyramid]'.format( CWREPO, rev) return _cache[None] def _tag(*args, registry=None): tag_part = '-'.join([x for x in args if x is not None]) if tag_part: return '{}:{}'.format(registry or REGISTRY, tag_part) return REGISTRY def tag_aliases(): for alias, source in ALIASES: if alias == 'buildpackage': check_call('docker', 'tag', _tag(source), _tag(alias)) continue for onbuild in [None, 'onbuild']: if alias == 'latest' and onbuild == 'onbuild': tag = _tag('onbuild') else: tag = _tag(alias, onbuild) src = _tag(source, onbuild) check_call('docker', 'tag', src, tag) def get_cubicweb_arg(cw): return { '3.25': 'cubicweb[pyramid]>=3.25,<3.26', '3.26': 'cubicweb[pyramid]>=3.26,<3.27', 'dev': _cwdev(), }[cw] def build_image(python, dist, cw, onbuild, no_cache=False, registry=None): tag = _tag(python, dist, cw, onbuild, registry=registry) args = {} dockerfile = 'Dockerfile' if onbuild is not None: dockerfile = 'Dockerfile.onbuild' args['FROM'] = _tag(python, dist, cw) elif cw is not None: dockerfile = 'Dockerfile.cubicweb' args['FROM'] = _tag(python, dist) args['CUBICWEB'] = get_cubicweb_arg(cw) else: args['DIST'] = dist args['PYTHON'] = 'python' if python == 'py27' else 'python3' cmd = [ 'docker', 'build', '-t', tag, '-f', dockerfile, ] if no_cache: cmd += ['--no-cache'] for key, value in args.items(): cmd += ['--build-arg', '{}={}'.format(key, value)] cmd += ['.'] check_call(*cmd) def green(text): return '\033[0;32m' + text + '\033[0m' def red(text): return '\033[0;31m' + text + '\033[0m' def build_buildpackage(dist): tag = '{}:{}-buildpackage'.format(REGISTRY, dist) check_call('docker', 'build', '-t', tag, '--build-arg', 'DIST={}'.format(dist), '-f', 'Dockerfile.buildpackage', '.') def build(rebuild=False): if rebuild: # pull base images check_call('docker', 'pull', 'debian:stretch-slim') check_call('docker', 'pull', 'debian:buster-slim') build_buildpackage('stretch') build_buildpackage('buster') for matrix in MATRIX: for python, dist, cw, onbuild in itertools.product(*matrix): build_image(python, dist, cw, onbuild) if rebuild and cw is None and onbuild is None: tag = _tag(python, dist) out = run( 'docker', 'run', '--rm', '-t', '--user', 'root', '--entrypoint', 'check-docker-updates.sh', tag, 'apt') if out.returncode == 1: LOG.warning(red( '%s debian packages updates are available: %s'), tag, out.stdout) build_image(python, dist, cw, onbuild, no_cache=True) else: assert (out.returncode, out.stdout) == (0, b''), out LOG.info(green('%s debian packages are up-to-date'), tag) if rebuild and cw is not None and onbuild is None: tag = _tag(python, dist, cw) out = run( 'docker', 'run', '--rm', '-t', '--user', 'root', '--entrypoint', 'check-docker-updates.sh', tag, 'pip', get_cubicweb_arg(cw)) if out.returncode == 1: LOG.info( red('%s python packages updates are available: %s'), tag, out.stdout) build_image(python, dist, cw, onbuild, no_cache=True) else: assert (out.returncode, out.stdout) == (0, b''), out LOG.info(green('%s python packages are up-to-date'), tag) def push(): for matrix in MATRIX: for python, dist, cw, onbuild in itertools.product(*matrix): tag = _tag(python, dist, cw, onbuild) check_call('docker', 'push', tag) for alias, source in ALIASES: for onbuild in [None, 'onbuild']: if alias == 'latest' and onbuild == 'onbuild': tag = _tag('onbuild') else: tag = _tag(alias, onbuild) check_call('docker', 'push', tag) if __name__ == '__main__': import argparse logging.basicConfig(format='%(asctime)-15s %(message)s') LOG.setLevel(logging.INFO) parser = argparse.ArgumentParser(sys.argv[0]) parser.add_argument('--registry', action='store', default=REGISTRY) parser.add_argument( '--checkrebuild', action='store_true', default=False, help='Rebuild images in case of outdated debian or python package') parser.add_argument( '--push', action='store_true', default=False, help='push images') args = parser.parse_args() REGISTRY = args.registry if args.push: push() else: build(args.checkrebuild) tag_aliases()