diff --git a/superflore/generate_installers.py b/superflore/generate_installers.py new file mode 100644 index 00000000..61c905bb --- /dev/null +++ b/superflore/generate_installers.py @@ -0,0 +1,81 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rosinstall_generator.distro import get_distro +from rosinstall_generator.distro import get_package_names + +from superflore.utils import err +from superflore.utils import get_pkg_version +from superflore.utils import info +from superflore.utils import ok +from superflore.utils import warn + + +def generate_installers( + distro_name, # ros distro name + overlay, # repo instance + gen_pkg_func, # function to call for generating + preserve_existing=True # don't regenerate if installer exists +): + distro = get_distro(distro_name) + pkg_names = get_package_names(distro) + total = float(len(pkg_names[0])) + borkd_pkgs = dict() + changes = [] + installers = [] + bad_installers = [] + succeeded = 0 + failed = 0 + + info("Generating installers for distro '%s'" % distro_name) + for i, pkg in enumerate(sorted(pkg_names[0])): + version = get_pkg_version(distro, pkg) + percent = '%.1f' % (100 * (float(i) / total)) + try: + current, bad_deps = gen_pkg_func( + overlay, pkg, distro, preserve_existing + ) + if not current and bad_deps: + # we are missing dependencies + failed_msg = "{0}%: Failed to generate".format(percent) + failed_msg += " installer for package '%s'!" % pkg + err(failed_msg) + borkd_pkgs[pkg] = bad_deps + failed = failed + 1 + continue + elif not current and preserve_existing: + # don't replace the installer + succeeded = succeeded + 1 + continue + success_msg = 'Successfully generated installer for package' + ok('{0}%: {1} \'{2}\'.'.format(percent, success_msg, pkg)) + succeeded = succeeded + 1 + changes.append('*{0} --> {1}*'.format(pkg, version)) + installers.append(pkg) + except KeyError: + failed_msg = 'Failed to generate installer' + err("{0}%: {1} for package {2}!".format(percent, failed_msg, pkg)) + bad_installers.append(pkg) + failed = failed + 1 + results = 'Generated {0} / {1}'.format(succeeded, failed + succeeded) + results += ' for distro {0}'.format(distro_name) + info("------ {0} ------\n".format(results)) + + if len(borkd_pkgs) > 0: + warn("Unresolved:") + for broken in borkd_pkgs.keys(): + warn("{}:".format(broken)) + warn(" {}".format(borkd_pkgs[broken])) + + return installers, borkd_pkgs, changes diff --git a/superflore/generators/ebuild/gen_packages.py b/superflore/generators/ebuild/gen_packages.py index 7fa84c45..543ad9e4 100644 --- a/superflore/generators/ebuild/gen_packages.py +++ b/superflore/generators/ebuild/gen_packages.py @@ -21,19 +21,20 @@ from rosdistro.rosdistro import RosPackage from rosinstall_generator.distro import _generate_rosinstall -from rosinstall_generator.distro import get_distro from rosinstall_generator.distro import get_package_names -from termcolor import colored +from superflore.exceptions import UnresolvedDependency -import xmltodict - -from .ebuild import Ebuild, UnresolvedDependency -from .metadata_xml import metadata_xml +from superflore.generators.ebuild.ebuild import Ebuild +from superflore.generators.ebuild.metadata_xml import metadata_xml +from superflore.utils import err +from superflore.utils import get_pkg_version +from superflore.utils import make_dir +from superflore.utils import ok +from superflore.utils import warn -org = "Open Source Robotics Foundation" -org_license = "BSD" +import xmltodict # TODO(allenh1): This is a blacklist of things that # do not yet support Python 3. This will be updated @@ -42,145 +43,80 @@ no_python3 = ['tf'] - -def warn(string): - print(colored('>>>> {0}'.format(string), 'yellow')) - - -def ok(string): - print(colored('>>>> {0}'.format(string), 'green')) - - -def err(string): - print(colored('!!!! {0}'.format(string), 'red')) +org = "Open Source Robotics Foundation" +org_license = "BSD" -def make_dir(dirname): +def regenerate_pkg(overlay, pkg, distro, preserve_existing=False): + version = get_pkg_version(distro, pkg) + ebuild_name =\ + '/ros-{0}/{1}/{1}-{2}.ebuild'.format(distro.name, pkg, version) + ebuild_name = overlay.repo.repo_dir + ebuild_name + patch_path = '/ros-{}/{}/files'.format(distro.name, pkg) + patch_path = overlay.repo.repo_dir + patch_path + has_patches = os.path.exists(patch_path) + pkg_names = get_package_names(distro)[0] + + if pkg not in pkg_names: + raise RuntimeError("Unknown package '%s'" % (pkg)) + # otherwise, remove a (potentially) existing ebuild. + existing = glob.glob( + '{0}/ros-{1}/{2}/*.ebuild'.format( + overlay.repo.repo_dir, + distro.name, pkg + ) + ) + if preserve_existing and existing: + ok("ebuild for package '%s' up to date, skipping..." % pkg) + return None, [] + elif existing: + overlay.repo.remove_file(existing[0]) + manifest_file = '{0}/ros-{1}/{2}/Manifest'.format( + overlay.repo.repo_dir, distro.name, pkg + ) + overlay.repo.remove_file(manifest_file) try: - os.makedirs(dirname) - except Exception: - pass - - -def get_pkg_version(distro, pkg_name): - pkg = distro.release_packages[pkg_name] - repo = distro.repositories[pkg.repository_name].release_repository - maj_min_patch, deb_inc = repo.version.split('-') - if deb_inc != '0': - return '{0}-r{1}'.format(maj_min_patch, deb_inc) - return maj_min_patch - + current = gentoo_installer(distro, pkg, has_patches) + current.ebuild.name = pkg + except Exception as e: + err('Failed to generate installer for package {}!'.format(pkg)) + raise e + try: + ebuild_text = current.ebuild_text() + metadata_text = current.metadata_text() + except UnresolvedDependency: + dep_err = 'Failed to resolve required dependencies for' + err("{0} package {1}!".format(dep_err, pkg)) + unresolved = current.ebuild.get_unresolved() + for dep in unresolved: + err(" unresolved: \"{}\"".format(dep)) + return None, current.ebuild.get_unresolved() + except KeyError as ke: + err("Failed to parse data for package {}!".format(pkg)) + raise ke + make_dir( + "{}/ros-{}/{}".format(overlay.repo.repo_dir, distro.name, pkg) + ) + success_msg = 'Successfully generated installer for package' + ok('{0} \'{1}\'.'.format(success_msg, pkg)) -def generate_installers(distro_name, overlay, preserve_existing=True): - distro = get_distro(distro_name) - pkg_names = get_package_names(distro) - total = float(len(pkg_names[0])) - borkd_pkgs = dict() - changes = [] - installers = [] - bad_installers = [] - succeeded = 0 - failed = 0 - - for i, pkg in enumerate(sorted(pkg_names[0])): - version = get_pkg_version(distro, pkg) - ebuild_name =\ - '/ros-{0}/{1}/{1}-{2}.ebuild'.format(distro_name, pkg, version) - ebuild_name = overlay.repo.repo_dir + ebuild_name - ebuild_exists = os.path.exists(ebuild_name) - patch_path = '/ros-{}/{}/files'.format(distro_name, pkg) - patch_path = overlay.repo.repo_dir + patch_path - has_patches = os.path.exists(patch_path) - percent = '%.1f' % (100 * (float(i) / total)) - - if preserve_existing and ebuild_exists: - skip_msg = 'Ebuild for package ' - skip_msg += '{0} up to date, skipping...'.format(pkg) - status = '{0}%: {1}'.format(percent, skip_msg) - ok(status) - succeeded = succeeded + 1 - continue - # otherwise, remove a (potentially) existing ebuild. - existing = glob.glob( - '{0}/ros-{1}/{2}/*.ebuild'.format( - overlay.repo.repo_dir, - distro_name, pkg - ) + try: + ebuild_file = '{0}/ros-{1}/{2}/{2}-{3}.ebuild'.format( + overlay.repo.repo_dir, + distro.name, pkg, version ) - if existing: - overlay.repo.remove_file(existing[0]) - manifest_file = '{0}/ros-{1}/{2}/Manifest'.format( - overlay.repo.repo_dir, distro_name, pkg - ) - overlay.repo.remove_file(manifest_file) - try: - current = gentoo_installer(distro, pkg, has_patches) - current.ebuild.name = pkg - except Exception as e: - err('Failed to generate installer for package {}!'.format(pkg)) - err(' exception: {0}'.format(e)) - failed = failed + 1 - continue - try: - ebuild_text = current.ebuild_text() - metadata_text = current.metadata_text() - except UnresolvedDependency: - dep_err = 'Failed to resolve required dependencies for' - err("{0} package {1}!".format(dep_err, pkg)) - unresolved = current.ebuild.get_unresolved() - borkd_pkgs[pkg] = list() - for dep in unresolved: - err(" unresolved: \"{}\"".format(dep)) - borkd_pkgs[pkg].append(dep) - err("Failed to generate installer for package {}!".format(pkg)) - failed = failed + 1 - continue # do not generate an incomplete ebuild - except KeyError: - err("Failed to parse data for package {}!".format(pkg)) - unresolved = current.ebuild.get_unresolved() - err("Failed to generate installer for package {}!".format(pkg)) - failed = failed + 1 - continue # do not generate an incomplete ebuild - make_dir( - "{}/ros-{}/{}".format(overlay.repo.repo_dir, distro_name, pkg) + ebuild_file = open(ebuild_file, "w") + metadata_file = '{0}/ros-{1}/{2}/metadata.xml'.format( + overlay.repo.repo_dir, + distro.name, pkg ) - success_msg = 'Successfully generated installer for package' - ok('{0}%: {1} \'{2}\'.'.format(percent, success_msg, pkg)) - succeeded = succeeded + 1 - - try: - ebuild_file = '{0}/ros-{1}/{2}/{2}-{3}.ebuild'.format( - overlay.repo.repo_dir, - distro_name, pkg, version - ) - ebuild_file = open(ebuild_file, "w") - metadata_file = '{0}/ros-{1}/{2}/metadata.xml'.format( - overlay.repo.repo_dir, - distro_name, pkg - ) - metadata_file = open(metadata_file, "w") - ebuild_file.write(ebuild_text) - metadata_file.write(metadata_text) - changes.append('*{0} --> {1}*'.format(pkg, version)) - except Exception: - err("Failed to write ebuild/metadata to disk!") - installers.append(current) - failed_msg = 'Failed to generate installer' - err("{0}%: {1} for package {2}!".format(percent, failed_msg, pkg)) - bad_installers.append(current) - failed = failed + 1 - results = 'Generated {0} / {1}'.format(succeeded, failed + succeeded) - results += ' for distro {0}'.format(distro_name) - print("------ {0} ------".format(results)) - print() - - if len(borkd_pkgs) > 0: - warn("Unresolved:") - for broken in borkd_pkgs.keys(): - warn("{}:".format(broken)) - warn(" {}".format(borkd_pkgs[broken])) - - return installers, borkd_pkgs, changes + metadata_file = open(metadata_file, "w") + ebuild_file.write(ebuild_text) + metadata_file.write(metadata_text) + except Exception as e: + err("Failed to write ebuild/metadata to disk!") + raise e + return current, [] def _gen_metadata_for_package(distro, pkg_name, pkg, diff --git a/superflore/generators/ebuild/overlay_instance.py b/superflore/generators/ebuild/overlay_instance.py index 1b9f25ee..367cc6f7 100644 --- a/superflore/generators/ebuild/overlay_instance.py +++ b/superflore/generators/ebuild/overlay_instance.py @@ -20,6 +20,8 @@ from superflore.docker import Docker from superflore.repo_instance import RepoInstance +from superflore.utils import info + def get_random_tmp_dir(): rand_str = ''.join(random.choice(string.ascii_letters) for x in range(10)) @@ -45,25 +47,23 @@ def __init__(self, repo_dir=None): self.branch_name = get_random_branch_name() if do_clone: self.repo.clone() - branch_msg = 'Creating new branch {0}...'.format(self.branch_name) - self.repo.info(branch_msg) + info('Creating new branch {0}...'.format(self.branch_name)) self.repo.create_branch(self.branch_name) def clean_ros_ebuild_dirs(self, distro=None): - if distro is not None: - self.repo.info('Cleaning up ros-{0} directory...'.format(distro)) + if distro: + info('Cleaning up ros-{0} directory...'.format(distro)) self.repo.git.rm('-rf', 'ros-{0}'.format(distro)) else: - self.repo.info('Cleaning up ros-* directories...') + info('Cleaning up ros-* directories...') self.repo.git.rm('-rf', 'ros-*') def commit_changes(self, distro): - self.repo.info('Adding changes...') - if not distro: - self.repo.git.add('ros-*') - else: + info('Adding changes...') + if distro: self.repo.git.add('ros-{0}'.format(distro)) - if not distro: + else: + self.repo.git.add('ros-*') distro = 'update' commit_msg = { 'update': 'rosdistro sync, {0}', @@ -72,26 +72,26 @@ def commit_changes(self, distro): 'indigo': 'regenerate ros-indigo, {0}', 'kinetic': 'regenerate ros-kinetic, {0}', }[distro].format(time.ctime()) - self.repo.info('Committing to branch {0}...'.format(self.branch_name)) + info('Committing to branch {0}...'.format(self.branch_name)) self.repo.git.commit(m='{0}'.format(commit_msg)) - def regenerate_manifests(self, mode): - self.repo.info('Building docker image...') + def regenerate_manifests(self, regen_dict): + info('Building docker image...') dock = Docker('repoman_docker', 'gentoo_repoman') dock.build() - self.repo.info('Running docker image...') - self.repo.info('Generating manifests...') + info('Running docker image...') + info('Generating manifests...') dock.map_directory( '/home/%s/.gnupg' % os.getenv('USER'), '/root/.gnupg' ) - if mode == 'all' or not mode: - dock.map_directory(self.repo.repo_dir, '/tmp/ros-overlay') - else: - ros_dir = '{0}/ros-{1}'.format(self.repo.repo_dir, mode) - dock.map_directory(ros_dir, '/tmp/ros-overlay') - dock.add_bash_command('cd {0}'.format('/tmp/ros-overlay')) - dock.add_bash_command('repoman manifest') + dock.map_directory(self.repo.repo_dir, '/tmp/ros-overlay') + for key in regen_dict.keys(): + for pkg in regen_dict[key]: + pkg_dir = '/tmp/ros-overlay/ros-{0}/{1}'.format(key, pkg) + dock.add_bash_command('cd {0}'.format(pkg_dir)) + dock.add_bash_command('repoman manifest') + dock.add_bash_command('cd /tmp/ros-overlay') dock.run(show_cmd=True) def pull_request(self, message): diff --git a/superflore/generators/ebuild/run.py b/superflore/generators/ebuild/run.py index 128dba81..ea8954bb 100755 --- a/superflore/generators/ebuild/run.py +++ b/superflore/generators/ebuild/run.py @@ -19,10 +19,20 @@ import sys import time -from superflore.generators.ebuild.gen_packages import generate_installers +from rosinstall_generator.distro import get_distro + +from superflore.generate_installers import generate_installers + +from superflore.generators.ebuild.gen_packages import regenerate_pkg from superflore.generators.ebuild.overlay_instance import RosOverlay + from superflore.repo_instance import RepoInstance +from superflore.utils import err +from superflore.utils import info +from superflore.utils import ok +from superflore.utils import warn + # Modify if a new distro is added active_distros = ['indigo', 'kinetic', 'lunar'] # just update packages, by default. @@ -35,7 +45,7 @@ def clean_up(distro, preserve_repo=False): if not preserve_repo: clean_msg = \ 'Cleaning up tmp directory {0}...'.format(overlay.repo.repo_dir) - RepoInstance.info(clean_msg) + info(clean_msg) shutil.rmtree(overlay.repo.repo_dir) if os.path.exists('.pr-message.tmp'): os.remove('.pr-message.tmp') @@ -47,8 +57,8 @@ def file_pr(overlay, delta, missing_deps): try: overlay.pull_request('{0}\n{1}'.format(delta, missing_deps)) except Exception as e: - overlay.error('Failed to file PR with ros/ros-overlay repo!') - overlay.error('Exception: {0}'.format(e)) + err('Failed to file PR with ros/ros-overlay repo!') + err('Exception: {0}'.format(e)) sys.exit(1) @@ -82,20 +92,25 @@ def main(): help='location of the Git repo', type=str ) + parser.add_argument( + '--only', + nargs='+', + help='generate only the specified packages' + ) args = parser.parse_args(sys.argv[1:]) if args.all: - RepoInstance.warn('"All" mode detected... This may take a while!') + warn('"All" mode detected... This may take a while!') preserve_existing = False elif args.ros_distro: selected_targets = [args.ros_distro] preserve_existing = False elif args.dry_run and args.pr_only: - RepoInstance.error('Invalid args! cannot dry-run and file PR') + err('Invalid args! cannot dry-run and file PR') sys.exit(1) elif args.pr_only and not args.output_repository_path: - RepoInstance.error('Invalid args! no repository specified') + err('Invalid args! no repository specified') elif args.pr_only: try: with open('.pr-message.tmp', 'r') as msg_file: @@ -103,8 +118,8 @@ def main(): with open('.pr-title.tmp', 'r') as title_file: title = title_file.read().rstrip('\n') except OSError: - RepoInstance.error('Failed to open PR title/message file!') - RepoInstance.RepoInstance.error( + err('Failed to open PR title/message file!') + err( 'Please supply the %s and %s files' % ( '.pr_message.tmp', '.pr_title.tmp' @@ -113,14 +128,14 @@ def main(): raise try: prev_overlay = RepoInstance(args.output_repository_path) - RepoInstance.info('PR message:\n"%s"\n' % msg) - RepoInstance.info('PR title:\n"%s"\n' % title) + info('PR message:\n"%s"\n' % msg) + info('PR title:\n"%s"\n' % title) prev_overlay.pull_request(msg, title) clean_up('all') sys.exit(0) except Exception as e: - RepoInstance.error('Failed to file PR!') - RepoInstance.error('reason: {0}'.format(e)) + err('Failed to file PR!') + err('reason: {0}'.format(e)) sys.exit(1) # clone current repo overlay = RosOverlay(args.output_repository_path) @@ -130,9 +145,42 @@ def main(): total_broken = set() total_changes = dict() + if args.only: + for pkg in args.only: + info("Regenerating package '%s'..." % pkg) + regenerate_pkg( + overlay, + pkg=pkg, + distro=get_distro(args.ros_distro) + ) + # Commit changes and file pull request + regen_dict = dict() + regen_dict[args.ros_distro] = args.only + overlay.regenerate_manifests(regen_dict) + overlay.commit_changes(args.ros_distro) + delta = "Regenerated: '%s'\n" % args.only + missing_deps = '' + if args.dry_run: + info('Running in dry mode, not filing PR') + title_file = open('.pr-title.tmp', 'w') + title_file.write('rosdistro sync, {0}\n'.format(time.ctime())) + pr_message_file = open('.pr-message.tmp', 'w') + pr_message_file.write('%s\n%s\n' % (delta, missing_deps)) + sys.exit(0) + file_pr(overlay, delta, missing_deps) + + clean_up(args.ros_distro, args.output_repository_path) + ok('Successfully synchronized repositories!') + sys.exit(0) + for distro in selected_targets: distro_installers, distro_broken, distro_changes =\ - generate_installers(distro, overlay, preserve_existing) + generate_installers( + distro_name=distro, + overlay=overlay, + gen_pkg_func=regenerate_pkg, + preserve_existing=preserve_existing + ) for key in distro_broken.keys(): for pkg in distro_broken[key]: total_broken.add(pkg) @@ -145,8 +193,8 @@ def main(): num_changes += len(total_changes[distro_name]) if num_changes == 0: - RepoInstance.info('ROS distro is up to date.') - RepoInstance.info('Exiting...') + info('ROS distro is up to date.') + info('Exiting...') clean_up(args.ros_distro) sys.exit(0) @@ -189,11 +237,11 @@ def main(): missing_deps += " * [ ] {0}\n".format(pkg) # Commit changes and file pull request - overlay.regenerate_manifests(args.ros_distro) + overlay.regenerate_manifests(total_installers) overlay.commit_changes(args.ros_distro) if args.dry_run: - RepoInstance.info('Running in dry mode, not filing PR') + info('Running in dry mode, not filing PR') title_file = open('.pr-title.tmp', 'w') title_file.write('rosdistro sync, {0}\n'.format(time.ctime())) pr_message_file = open('.pr-message.tmp', 'w') @@ -202,4 +250,4 @@ def main(): file_pr(overlay, delta, missing_deps) clean_up(args.ros_distro, args.output_repository_path) - RepoInstance.happy('Successfully synchronized repositories!') + ok('Successfully synchronized repositories!') diff --git a/superflore/repo_instance.py b/superflore/repo_instance.py index 67f300d6..a8c74238 100644 --- a/superflore/repo_instance.py +++ b/superflore/repo_instance.py @@ -17,7 +17,9 @@ from git import Repo from git.exc import GitCommandError as GitGotGot -from termcolor import colored +from superflore.utils import err as error +from superflore.utils import info +from superflore.utils import ok class RepoInstance(object): @@ -39,9 +41,9 @@ def clone(self, branch=None): if self.repo_dir != self.repo_name: msg += (' into directory {0}'.format(self.repo_dir)) msg += '...' - RepoInstance.info(msg) + info(msg) self.repo = Repo.clone_from(self.repo_url, self.repo_dir) - if branch is not None: + if branch: self.git.checkout(branch) def remove_file(self, filename, ignore_fail=False): @@ -52,14 +54,14 @@ def remove_file(self, filename, ignore_fail=False): return fail_msg = 'Failed to remove file {0}'.format(filename) fail_msg += 'from source control.' - self.error(fail_msg) - self.error(' Exception: {0}'.format(g)) + error(fail_msg) + error(' Exception: {0}'.format(g)) def create_branch(self, branch_name): """ @todo: error checking """ - RepoInstance.info(self.git.checkout('HEAD', b=branch_name)) + info(self.git.checkout('HEAD', b=branch_name)) def remove_branch(self, branch_name): """ @@ -80,27 +82,10 @@ def rebase(self, target): self.git.rebase(i=target) def pull_request(self, message, title): - self.info('Filing pull-request...') + info('Filing pull-request...') self.git.pull_request(m='{0}'.format(message), title='{0}'.format(title)) - good_msg = 'Successfully filed a pull request.' - self.happy(good_msg) - - @staticmethod - def info(string): - print(colored(string, 'cyan')) - - @staticmethod - def error(string): - print(colored(string, 'red')) - - @staticmethod - def warn(string): - print(colored(string, 'yellow')) - - @staticmethod - def happy(string): - print(colored(string, 'green')) + ok('Successfully filed a pull request.') class CloneException(Exception): diff --git a/superflore/utils.py b/superflore/utils.py index 41e418df..0f58180d 100644 --- a/superflore/utils.py +++ b/superflore/utils.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import errno +import os import re import sys @@ -36,6 +38,41 @@ def get_http(url): return response.read() +def warn(string): + print(colored('>>>> {0}'.format(string), 'yellow')) + + +def ok(string): + print(colored('>>>> {0}'.format(string), 'green')) + + +def err(string): + print(colored('!!!! {0}'.format(string), 'red')) + + +def info(string): + print(colored('>>> {0}'.format(string), 'cyan')) + + +def make_dir(dirname): + try: + os.makedirs(dirname) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dirname): + pass + else: + raise e + + +def get_pkg_version(distro, pkg_name): + pkg = distro.release_packages[pkg_name] + repo = distro.repositories[pkg.repository_name].release_repository + maj_min_patch, deb_inc = repo.version.split('-') + if deb_inc != '0': + return '{0}-r{1}'.format(maj_min_patch, deb_inc) + return maj_min_patch + + def download_yamls(): global base_yml global python_yml @@ -46,11 +83,11 @@ def download_yamls(): python_yaml = "{0}/python.yaml".format(base_url) ruby_yaml = "{0}/ruby.yaml".format(base_url) - print(colored("Downloading latest base yml...", 'cyan')) + info("Downloading latest base yml...") base_yml = yaml.load(get_http(base_yaml)) - print(colored("Downloading latest python yml...", 'cyan')) + info("Downloading latest python yml...", 'cyan') python_yml = yaml.load(get_http(python_yaml)) - print(colored("Downloading latest ruby yml...", 'cyan')) + info("Downloading latest ruby yml...", 'cyan') ruby_yml = yaml.load(get_http(ruby_yaml))