def kraft_list_remove(ctx, origin=None): """ """ if isinstance(origin, list): for o in origin: kraft_list_remove(o) return existing_origins = ctx.obj.settings.get(KRAFTRC_LIST_ORIGINS) if existing_origins is None: existing_origins = list() new_uri = urlparse(origin) if os.path.exists(origin): origin = os.path.abspath(origin) for i, o in enumerate(existing_origins): cur_uri = urlparse(o) if (o == origin or (new_uri.netloc == cur_uri.netloc and new_uri.path == cur_uri.path)): logger.info("Removed: %s" % origin) del existing_origins[i] break ctx.obj.settings.set(KRAFTRC_LIST_ORIGINS, existing_origins)
def kraft_list_add(ctx, origin=None, update=False): """ """ if isinstance(origin, list): for o in origin: kraft_list_add(o, update=update) return existing_origins = ctx.obj.settings.get(KRAFTRC_LIST_ORIGINS) if existing_origins is None: existing_origins = list() new_uri = urlparse(origin) if os.path.exists(origin): origin = os.path.abspath(origin) for o in existing_origins: cur_uri = urlparse(o) if (o == origin or (new_uri.netloc == cur_uri.netloc and new_uri.path == cur_uri.path)): logger.warning("Origin already saved: %s" % o) return existing_origins.append(origin) ctx.obj.settings.set(KRAFTRC_LIST_ORIGINS, existing_origins) logger.info("Saved: %s" % origin) if update: with ctx: kraft_update(origin)
def execute(cmd="", env={}, dry_run=False, use_logger=False): if type(cmd) is list: cmd = " ".join(cmd) logger.debug("Running: %s" % cmd) if not dry_run: popen = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, env=merge_dicts(os.environ, env)) for line in popen.stdout: line = line.strip().decode('ascii') if use_logger: logger.info(line) else: print(line) popen.stdout.close() return_code = popen.wait() if return_code is not None and int(return_code) > 0: return return_code return 0
def remove_lib(ctx, self, lib=None, purge=False): if lib is None or str(lib) == "": logger.warn("No library to remove") return False elif isinstance(lib, six.string_types): _, name, _, _ = break_component_naming_format(lib) manifests = maniest_from_name(name) if len(manifests) == 0: logger.warn("Unknown library: %s" % lib) return False for manifest in manifests: if manifest.type != ComponentType.LIB: continue self.config.libraries.remove(name, purge) break if purge and os.path.exists(manifest.localdir): logger.info("Purging lib/%s..." % name) shutil.rmtree(manifest.localdir) self.save_yaml() return True
def execute(cmd="", env={}, dry_run=False): if type(cmd) is list: cmd = " ".join(cmd) logger.debug("Running: %s" % cmd) if not dry_run: cmd = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, env={**os.environ, **env}) for line in cmd.stdout: logger.info(line.strip().decode('ascii'))
def add(self, component=None): if component is None: raise ValueError("expected component") if not isinstance(component, Component): raise TypeError("expected Component") logger.info("Adding %s@%s..." % (component.manifest, component.version)) self._components.append(component) return True
def kraft_update(ctx): origins = ctx.obj.settings.get(KRAFTRC_LIST_ORIGINS) if origins is None or len(origins) == 0: logger.error( "No source origins available. Please see: kraft list add --help") sys.exit(1) try: for origin in origins: manifest = ctx.obj.cache.get(origin) if manifest is None: manifest = Manifest(manifest=origin) threads, items = kraft_update_from_source_threads(origin) for thread in threads: thread.join() # Check thread's return value while not items.empty(): result = items.get() if result is not None: manifest.add_item(result) logger.info("Found %s/%s via %s..." % (click.style(result.type.shortname, fg="blue"), click.style(result.name, fg="blue"), manifest.manifest)) ctx.obj.cache.save(origin, manifest) except RateLimitExceededException: logger.error("".join([ "GitHub rate limit exceeded. You can tell kraft to use a ", "personal access token by setting the UK_KRAFT_GITHUB_TOKEN ", "environmental variable." ])) except Exception as e: logger.critical(str(e)) if ctx.obj.verbose: import traceback logger.critical(traceback.format_exc()) sys.exit(1)
def kraft_list_remove(ctx, origin=None): """ """ if isinstance(origin, list): for o in origin: kraft_list_remove(o) return new_uri = urlparse(origin) existing_origins = ctx.obj.settings.get(KRAFTRC_LIST_ORIGINS) for i, o in enumerate(existing_origins): cur_uri = urlparse(o) if new_uri.netloc == cur_uri.netloc and new_uri.path == cur_uri.path: logger.info("Removed: %s" % origin) del existing_origins[i] break ctx.obj.settings.set(KRAFTRC_LIST_ORIGINS, existing_origins)
def remove(self, component=None, purge=False): if component is None: raise ValueError("expected component") if isinstance(component, six.string_types): for i, c in enumerate(self._components): if c.name == component: if purge and os.path.exists(c.localdir): logger.info("Purging lib/%s..." % component) shutil.rmtree(c.localdir) else: logger.info("Removing lib/%s..." % component) del self._components[i] return True return False
def kraft_list_add(ctx, origin=None): """ """ if isinstance(origin, list): for o in origin: kraft_list_add(o) return new_uri = urlparse(origin) existing_origins = ctx.obj.settings.get(KRAFTRC_LIST_ORIGINS) if existing_origins is None: existing_origins = list() for o in existing_origins: cur_uri = urlparse(o) if new_uri.netloc == cur_uri.netloc and new_uri.path == cur_uri.path: logger.warning("Origin already saved: %s" % o) return existing_origins.append(origin) ctx.obj.settings.set(KRAFTRC_LIST_ORIGINS, existing_origins) logger.info("Saved: %s" % origin)
def execute(cmd="", env={}, dry_run=False): if type(cmd) is list: cmd = " ".join(cmd) logger.debug("Running: %s" % cmd) if not dry_run: popen = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, env=merge_dicts(os.environ, env) ) for line in popen.stdout: logger.info(line.strip().decode('ascii')) popen.stdout.close() return_code = popen.wait() if return_code is not None and int(return_code) > 0: logger.error("Command '%s' returned %d" % (cmd, return_code)) sys.exit(return_code)
def add_bridge(self, bridge=None): if bridge: logger.info("Using networking bridge '%s'" % bridge) self._cmd.extend(('-b', bridge))
def kraft_app_init(ctx, appdir=None, name=None, plat=None, arch=None, template_app=None, force_init=False, pull_dependencies=False, dumps_local=False, create_makefile=False): """ """ if appdir is None: raise ValueError("Cannot initialize application at unset directory") # If we are using a template application, we can simply copy from the source # repository if template_app is not None: app_manifest = None _, template_name, _, version = break_component_naming_format( template_app) for manifest_origin in ctx.obj.cache.all(): manifest = ctx.obj.cache.get(manifest_origin) for _, item in manifest.items(): if item.name == template_name and item.type == ComponentType.APP: app_manifest = item if app_manifest is None: raise UnknownApplicationTemplateName(template_app) if version is not None: version = app_manifest.get_version(version) if version is None: raise UnknownVersionError(version, app_manifest) else: version = app_manifest.get_version(UNIKRAFT_RELEASE_STABLE) kraft_download_component(localdir=appdir, manifest=app_manifest, version=version.version) if pull_dependencies or dumps_local: workdir = None if dumps_local: workdir = os.path.join(appdir, UNIKRAFT_WORKDIR) kraft_list_pull( name=str(app_manifest), appdir=appdir, workdir=workdir, pull_dependencies=True, skip_app=True, force_pull=force_init, ) app = Application.from_workdir(appdir) # If no application is provided, we can initialize a template by dumping # a YAML file else: unikraft = ctx.obj.cache.find_item_by_name(type="core", name="unikraft") unikraft.download() app = Application(name=name, unikraft=unikraft, architectures=[arch], platforms=[plat], localdir=appdir, ignore_version=force_init) app.name = name app.init(create_makefile=create_makefile) logger.info('Initialized new unikraft application: %s' % appdir)
def configure(ctx, self, target_arch=None, target_plat=None): """Configure a Unikraft application.""" if not self.is_configured(): self.init() self.checkout() # Generate a dynamic .config to populate defconfig with based on # configure's parameterization. dotconfig = [] if 'kconfig' in self.config.unikraft: dotconfig.extend(self.config.unikraft['kconfig']) found_arch = False for arch in self.architectures.all(): if target_arch == arch.name: found_arch = True logger.info("Using %s" % arch.repository) kconfig_enable = arch.repository.kconfig_enabled_flag() if kconfig_enable: dotconfig.extend([kconfig_enable]) if isinstance(arch.config, (dict)) and 'kconfig' in arch.config: dotconfig.extend(arch.config['kconfig']) dotconfig.extend(arch.repository.kconfig_extra) if not found_arch: raise MismatchTargetArchitecture( target_arch, [arch.name for arch in self.architectures.all()]) found_plat = False for plat in self.platforms.all(): if target_plat == plat.name: found_plat = True logger.info("Using %s" % plat.repository) kconfig_enable = plat.repository.kconfig_enabled_flag() if kconfig_enable: dotconfig.extend([kconfig_enable]) if isinstance(plat.config, (dict)) and 'kconfig' in plat.config: dotconfig.extend(plat.config['kconfig']) dotconfig.extend(plat.repository.kconfig_extra) if not found_plat: raise MismatchTargetPlatform( target_plat, [plat.name for plat in self.platforms.all()]) for lib in self.libraries.all(): logger.info("Using %s" % lib.repository) kconfig_enable = lib.repository.kconfig_enabled_flag() if kconfig_enable: dotconfig.extend([kconfig_enable]) if isinstance(lib.config, (dict)) and 'kconfig' in lib.config: dotconfig.extend(lib.config['kconfig']) dotconfig.extend(lib.repository.kconfig_extra) # Create a temporary file with the kconfig written to it fd, path = tempfile.mkstemp() with os.fdopen(fd, 'w+') as tmp: logger.debug('Using the following defconfig:') for line in dotconfig: logger.debug(' > ' + line) tmp.write(line + '\n') try: self.make([('UK_DEFCONFIG=%s' % path), 'defconfig']) finally: os.remove(path)
def cmd_list(ctx, show_installed=False, show_core=False, show_plats=False, show_libs=False, show_apps=False, show_local=False, paginate=False, this=False, this_set=None, return_json=False): """ Retrieves lists of available architectures, platforms, libraries and applications supported by unikraft. Use this command if you wish to determine (and then later select) the possible targets for your unikraft application. By default, this subcommand will list all possible targets. """ if ctx.invoked_subcommand is None: kraft_list_preflight() show_archs = False # If no flags are set, show everything if (show_core is False and show_archs is False and show_plats is False and show_libs is False and show_apps is False): show_core = show_archs = show_plats = show_libs = show_apps = True # Populate a matrix with all relevant columns and rows for each # component. components = {} data = [] data_json = {} if this or this_set is not None: workdir = os.getcwd() if this_set is not None: workdir = this_set try: app = Application.from_workdir(workdir) for manifest in app.manifests: if manifest.type.shortname not in components: components[manifest.type.shortname] = [] components[manifest.type.shortname].append(manifest) except KraftError as e: logger.error(str(e)) sys.exit(1) else: for manifest_origin in ctx.obj.cache.all(): manifest = ctx.obj.cache.get(manifest_origin) for _, item in manifest.items(): if item.type.shortname not in components: components[item.type.shortname] = [] components[item.type.shortname].append(item) for type, member in ComponentType.__members__.items(): columns = [ click.style(member.plural.upper(), fg='white'), click.style('VERSION ', fg='white'), click.style('RELEASED', fg='white'), click.style('LAST CHECKED', fg='white') ] if show_local: columns.append(click.style('LOCATION', fg='white')) rows = [] components_showing = 0 if member.shortname in components and ( (show_core and member is ComponentType.CORE) or (show_archs and member is ComponentType.ARCH) or (show_plats and member is ComponentType.PLAT) or (show_libs and member is ComponentType.LIB) or (show_apps and member is ComponentType.APP)): rows = components[member.shortname] # if len(rows) > 0: data.append(columns) for row in rows: installed = False install_error = False localdir = row.localdir if os.path.isdir(localdir): installed = True if len(os.listdir(localdir)) == 0: install_error = True logger.warn("%s directory is empty: %s " % ( row.name, localdir )) latest_release = None if UNIKRAFT_RELEASE_STABLE in row.dists.keys(): latest_release = row.dists[UNIKRAFT_RELEASE_STABLE].latest elif UNIKRAFT_RELEASE_STAGING in row.dists.keys(): latest_release = row.dists[UNIKRAFT_RELEASE_STAGING].latest if return_json: if member.plural not in data_json: data_json[member.plural] = [] row_json = row.__getstate__() if not show_installed or (installed and show_installed): data_json[member.plural].append(row_json) components_showing += 1 else: line = [ click.style(row.name, fg='yellow' if install_error else 'green' if installed else 'red'), # noqa: E501 click.style(latest_release.version if latest_release is not None else "", fg='white'), # noqa: E501 click.style(prettydate(latest_release.timestamp) if latest_release is not None else "", fg='white'), # noqa: E501 click.style(prettydate(row.last_checked), fg='white'), ] if show_local: line.append(click.style(localdir if installed else '', fg='white')) # noqa: E501 if not show_installed or (installed and show_installed): data.append(line) components_showing += 1 # Delete component headers with no rows if components_showing == 0 and len(data) > 0: del data[-1] # Line break elif len(rows) > 0: data.append([click.style("", fg='white')] * len(columns)) if return_json: click.echo(json.dumps(data_json)) else: output = pretty_columns(data) if len(data) == 0: logger.info("Nothing to show") elif paginate: click.echo_via_pager(output) else: click.echo(output[:-1])
def kraft_run(ctx, plat, arch, initrd, background, paused, gdb, dbg, virtio_nic, bridge, interface, dry_run, args, memory, cpu_sockets, cpu_cores): """ Starts the unikraft application once it has been successfully built. """ try: project = Project.from_config( ctx.workdir, config.load(config.find(ctx.workdir, None, ctx.env))) except KraftError as e: logger.error(str(e)) sys.exit(1) target_platform = None for platform in project.platforms.all(): if plat == platform.name: target_platform = platform if target_platform is None: logger.error('Application platform not configured or set') sys.exit(1) target_architecture = None for architecture in project.architectures.all(): if arch == architecture.name: target_architecture = architecture if target_architecture is None: logger.error('Application architecture not configured or set') sys.exit(1) unikernel = UNIKERNEL_IMAGE_FORMAT % (ctx.workdir, project.name, target_platform.name, target_architecture.name) if not os.path.exists(unikernel): logger.error('Could not find unikernel: %s' % unikernel) logger.info('Have you tried running `kraft build`?') sys.exit(1) executor = target_platform.repository.executor executor.architecture = target_architecture.name executor.use_debug = dbg if initrd: executor.add_initrd(initrd) if virtio_nic: executor.add_virtio_nic(virtio_nic) if bridge: executor.add_bridge(bridge) if interface: executor.add_interface(interface) if gdb: executor.open_gdb(gdb) if memory: executor.set_memory(memory) if cpu_sockets: executor.set_cpu_sockets(cpu_sockets) if cpu_cores: executor.set_cpu_cores(cpu_cores) try: executor.unikernel = unikernel executor.execute( extra_args=args, background=background, paused=paused, dry_run=dry_run, ) except ExecutorError as e: logger.error("Cannot execute: %s" % e) sys.exit(1)
def init(ctx, self, extra_values=dict(), force_create=False, no_input=False): """ """ context = generate_context( context_file=get_template_config(TEMPLATE_LIB), default_context=self._template_values) # prompt the user to manually configure at the command line. # except when 'no-input' flag is set context['cookiecutter'] = prompt_for_config(context, no_input) self._description = context['cookiecutter']['description'] self._template_values = { **self._template_values, **dict(context['cookiecutter']) } # Set additional template values # Fix the starting "v" in the version string if context['cookiecutter']['version'].startswith('v'): context['cookiecutter']['version'] = context['cookiecutter'][ 'version'][1:] context['cookiecutter'][ 'source_archive'] = self.version_source_archive( 'v%s' % (UK_VERSION_VARNAME % self.kname)) else: context['cookiecutter'][ 'source_archive'] = self.version_source_archive() # include automatically generated content context['cookiecutter'][ 'kconfig_dependencies'] = self.determine_kconfig_dependencies() context['cookiecutter']['source_files'] = self.determine_source_files() # include template dir or url in the context dict context['cookiecutter']['_template'] = get_templates_path(TEMPLATE_LIB) # add all vars that were never prompted for key in self._template_values: if key not in context['cookiecutter']: context['cookiecutter'][key] = self._template_values[key] output_dir = Path(self.localdir).parent # if self.source.startswith("file://"): # output_dir = self.source[len("file://"):] logger.info("Generating files...") generate_files( repo_dir=get_templates_path(TEMPLATE_LIB), context=context, overwrite_if_exists=force_create, skip_if_file_exists=not force_create, output_dir=output_dir, ) delete_template_resources_of_disabled_features(self.localdir) # Save initial commit repo = GitRepo.init(self.localdir) repo.config_writer().set_value( "user", "name", self.template_value['author_name']).release() repo.config_writer().set_value( "user", "email", self.template_value['author_email']).release() repo.index.commit('Initial commit (blank)') logger.info("Generated new library: %s" % self.localdir)
def bump(ctx, self, version=None, fast_forward=False, force_version=False): """ Change the Unikraft library's source origin version. Usually this involves updating the LIBNAME_VERSION variable in the Makefile.uk file. Args: version: The version to set. If None, the latest version will be set. fast_forward: If True, choose the latest version. force_version: Whatever the specified version is, use it. Raises: NonCompatibleUnikraftLibrary: Provided path is not a Unikraft library. UnknownLibraryOriginVersion: The provided version does not match known versions from the origin. BumpLibraryDowngrade: Attempting to downgrade a library. NoRemoteVersionsAvailable: No remote versions to select from. CannotDetermineRemoteVersion: Unable to determine which version to upgrade to. UnknownLibraryProvider: Undetermined origin provider. KraftError: Miscellaneous error. """ if self.origin_provider is None: raise UnknownLibraryProvider(self.name) # Retrieve known versions versions = self.origin_provider.probe_remote_versions() semversions = [] if len(versions) == 0: raise NoRemoteVersionsAvailable(self.origin_provider.source) # filter out non-semver versions for known_version in list(versions.keys()): found = SEMVER_PATTERN.search(known_version) if found is not None: semversions.append(known_version) current_version = self.origin_version if version is None: # Pick the highest listed verson if ctx.obj.assume_yes: # There are no semversions if len(semversions) == 0: raise CannotDetermineRemoteVersion(self.localdir) current_not_semver = False try: semver.VersionInfo.parse(current_version) except ValueError as e: logger.warn(e) current_not_semver = True # Remove non-semvers latest_version = None _semversions = semversions semversions = list() for checkv in _semversions: try: semver.VersionInfo.parse(checkv) semversions.append(checkv) except ValueError: continue latest_version = sorted(semversions, reverse=True)[0] # Pick the latest version if fast_forward or current_not_semver: version = latest_version # Check if we're already at the latest version elif semver.compare(current_version, latest_version) == 0: version = latest_version # Find the next version else: semversions = sorted(semversions) for i in range(len(semversions)): try: comparison = semver.compare( semversions[i], current_version) except ValueError as e: logger.warn(e) continue if comparison == 0: # We should have never made it this far, but because we # did, we're at the latest version. if i + 1 == len(semversions): version = latest_version break # Select the next version else: version = semversions[i + 1] break # Prompt user for a version else: version = read_user_choice( 'version', sorted(list(versions.keys()), reverse=True)) if version not in versions.keys(): if ctx.obj.assume_yes: logger.warn("Provided version '%s' not known in: {%s}" % (version, ', '.join(versions.keys()))) else: raise UnknownLibraryOriginVersion(version, versions.keys()) if VSEMVER_PATTERN.search(version): version = version[1:] # Are we dealing with a semver pattern? try: if semver.compare(current_version, version) == 0: logger.info("Library already latest version: %s" % version) return version if semver.compare(current_version, version) > 0: if force_version: logger.warn("Downgrading library from %s to %s..." % (current_version, version)) else: raise BumpLibraryDowngrade(current_version, version) except ValueError: if current_version == version: logger.info("Library already at version: %s" % version) return version # Actually perform the bump makefile_uk = os.path.join(self.localdir, MAKEFILE_UK) logger.debug("Reading %s..." % makefile_uk) makefile_vars = make_list_vars(makefile_uk)['makefile'] version_var = None for var in makefile_vars: if var.endswith(UNIKRAFT_LIB_MAKEFILE_VERSION_EXT): version_var = var break logger.info('Upgrading library from %s to %s...' % (current_version, version)) for line in fileinput.input(makefile_uk, inplace=1): if line.startswith(version_var) and current_version in line: print('%s = %s' % (version_var, version)) else: print(line, end='') return version
def kraft_init(ctx, name, target_plat, target_arch, template_app, version, force_create): if name is None: name = os.path.basename(os.getcwd()) # Pre-flight check determines if we are trying to work with nothing if ctx.cache.is_stale() and click.confirm( 'kraft caches are out-of-date. Would you like to update?'): update() # Check if the directory is non-empty and prompt for validation if utils.is_dir_empty(ctx.workdir) is False and force_create is False: if click.confirm( '%s is a non-empty directory, would you like to continue?' % ctx.workdir): # It should be safe to set this now force_create = True else: logger.error('Cancelling!') sys.exit(1) # If we are using a template application, we can simply copy from the source # repository if template_app is not None: apps = {} for repo in ctx.cache.all(): repo = ctx.cache.get(repo) if repo.type is RepositoryType.APP: apps[repo.name] = repo if template_app not in apps.keys(): logger.error('Template application not found: %s' % template_app) logger.error('Supported templates: %s' % ', '.join(apps.keys())) sys.exit(1) app = apps[template_app] if version and version not in app.known_versions.keys(): logger.error('Unknown version \'%s\' for app: %s' % (version, template_app)) sys.exit(1) app.checkout(version) utils.recursively_copy( app.localdir, ctx.workdir, overwrite=force_create, ignore=['.git', 'build', '.config', '.config.old', '.config.orig']) logger.info('Initialized new unikraft application \'%s\' in %s' % (name, ctx.workdir)) # If no application is provided, we can initialize a template by dumping # a YAML file else: # Determine the version of unikraft that we should be using if version is None: # This simply sets the "source" to the unikraft core repository which, # once parsed through the internal cache, should pop out the latest # version. unikraft_source = UNIKRAFT_CORE unikraft_version = None else: unikraft_source, unikraft_version = interpolate_source_version( source=version, repository_type=RepositoryType.CORE) try: core = Core.from_source_string(source=unikraft_source, version=unikraft_version) preferred_arch = ctx.settings.get(KRAFTCONF_PREFERRED_ARCHITECTURE) if target_arch is None: if preferred_arch: target_arch = preferred_arch else: logger.error("Please provide an architecture.") sys.exit(1) arch_source, arch_version = interpolate_source_version( source=target_arch, repository_type=RepositoryType.ARCH) archs = Architectures([]) archs.add( target_arch, Architecture.from_source_string( name=target_arch, source=arch_source, ), {}) preferred_plat = ctx.settings.get(KRAFTCONF_PREFERRED_PLATFORM) if target_plat is None: if preferred_plat: target_plat = preferred_plat else: logger.error("Please provide a platform.") sys.exit(1) plat_source, plat_version = interpolate_source_version( source=target_plat, repository_type=RepositoryType.PLAT) plats = Platforms([]) plats.add( target_plat, Platform.from_source_string( name=target_plat, source=plat_source, ), {}) logger.info("Using %s..." % core) project = Project( path=ctx.workdir, name=name, core=core, architectures=archs, platforms=plats, ) project.init(force_create=force_create) logger.info('Initialized new unikraft application \'%s\' in %s' % (name, ctx.workdir)) except KraftError as e: logger.error(str(e)) sys.exit(1)
def probe(ctx, self, origin=None, items=None, return_threads=False): # TODO: There should be a work around to fix this import loop cycle from kraft.manifest import Manifest if self.is_type(origin) is False: return [] threads = list() if items is None: items = Queue() manifest = ctx.obj.cache.get(origin) if manifest is None: manifest = Manifest(manifest=origin) uri = urlparse(origin) # Is the origin from GitHub? if uri.netloc == GITHUB_ORIGIN: github_api = Github(ctx.obj.env.get('UK_KRAFT_GITHUB_TOKEN', None)) github_org = uri.path.split('/')[1] github_repo = uri.path.split('/')[2] if "*" in github_org: logger.warn( "Cannot use wildcard in GitHub organisation names!") return # Does the origin contain a wildcard in the repo name? if "*" in github_repo: logger.info("Populating via wildcard: %s" % origin) org = github_api.get_organization(github_org) repos = org.get_repos() for repo in repos: if return_threads: thread = ErrorPropagatingThread( target=lambda *arg: items.put( get_component_from_github(*arg)), args=( ctx, origin, github_org, repo.name, )) threads.append(thread) thread.start() else: items.put( get_component_from_github(ctx, origin, github_org, repo.name)) else: logger.info("Using direct repository: %s" % origin) if return_threads: thread = ErrorPropagatingThread( target=lambda *arg: items.put( get_component_from_github(*arg)), args=( ctx, origin, github_org, github_repo, )) threads.append(thread) thread.start() else: items.put( get_component_from_github(ctx, origin, github_org, github_repo)) return items, threads