list-repos lists the git repos used by all projects and which branches those projects use.
Cc: Ashley E Desimone <ashley.e.desim...@intel.com> Signed-off-by: Nate DeSimone <nathaniel.l.desim...@intel.com> --- edkrepo/commands/arguments/list_repos_args.py | 16 ++ edkrepo/commands/humble/__init__.py | 8 + edkrepo/commands/humble/list_repos_humble.py | 26 ++ edkrepo/commands/list_repos_command.py | 243 ++++++++++++++++++ setup.py | 5 +- 5 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 edkrepo/commands/arguments/list_repos_args.py create mode 100644 edkrepo/commands/humble/__init__.py create mode 100644 edkrepo/commands/humble/list_repos_humble.py create mode 100644 edkrepo/commands/list_repos_command.py diff --git a/edkrepo/commands/arguments/list_repos_args.py b/edkrepo/commands/arguments/list_repos_args.py new file mode 100644 index 0000000..6ff9b9c --- /dev/null +++ b/edkrepo/commands/arguments/list_repos_args.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# +## @file +# list_repos_args.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +''' Contains the help and description strings for arguments in the +list-repos command meta data. +''' + +COMMAND_DESCRIPTION = 'Lists the git repos used by available projects and the branches that are used.' +ARCHIVED_HELP = 'Include a listing of archived projects.' +REPOS_HELP = 'Only show the given subset of repos instead of all repos.' diff --git a/edkrepo/commands/humble/__init__.py b/edkrepo/commands/humble/__init__.py new file mode 100644 index 0000000..dea6eb4 --- /dev/null +++ b/edkrepo/commands/humble/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# +## @file +# __init__.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# diff --git a/edkrepo/commands/humble/list_repos_humble.py b/edkrepo/commands/humble/list_repos_humble.py new file mode 100644 index 0000000..bbb05a7 --- /dev/null +++ b/edkrepo/commands/humble/list_repos_humble.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +## @file +# list_repos_humble.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +''' +Contains user visible strings printed by the list-repos command. +''' + +from colorama import Fore +from colorama import Style + +BRANCHES = 'Branches:' +BRANCH_FORMAT_STRING = ' {}{}{{}}{}'.format(Fore.BLUE, Style.BRIGHT, Style.RESET_ALL) +COMBO_FORMAT_STRING = ' {{}} {}{}({{}}){}'.format(Fore.CYAN, Style.BRIGHT, Style.RESET_ALL) +DEFAULT_COMBO_FORMAT_STRING = ' {{}} {}{}*({{}})*{}'.format(Fore.GREEN, Style.BRIGHT, Style.RESET_ALL) +MANIFEST_DIRECTORY = 'Manifest directory:' +PROJECT_NAME_FORMAT_STRING = '{}{}{{}}{}:'.format(Fore.YELLOW, Style.BRIGHT, Style.RESET_ALL) +REPOSITORIES = 'Repositories:' +REPO_NAME_AND_URL = '{}{}{{}}{} - [{}{}{{}}{}]'.format(Fore.MAGENTA, Style.BRIGHT, Style.RESET_ALL, Fore.RED, Style.BRIGHT, Style.RESET_ALL) +REPO_NAME_NOT_FOUND = 'repo_name not found' +REPO_NOT_FOUND_IN_MANIFEST = 'Repo {} not found in any manifest file' diff --git a/edkrepo/commands/list_repos_command.py b/edkrepo/commands/list_repos_command.py new file mode 100644 index 0000000..9035993 --- /dev/null +++ b/edkrepo/commands/list_repos_command.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# +## @file +# list_repos_command.py +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import collections +import os + +#from git import Repo +from colorama import Fore, Style + +# Our modules +from edkrepo.commands.edkrepo_command import EdkrepoCommand +from edkrepo.commands.edkrepo_command import ColorArgument +import edkrepo.commands.arguments.list_repos_args as arguments +import edkrepo.commands.humble.list_repos_humble as humble +from edkrepo.common.common_repo_functions import pull_latest_manifest_repo +from edkrepo.common.edkrepo_exception import EdkrepoInvalidParametersException, EdkrepoManifestInvalidException +from edkrepo.common.ui_functions import init_color_console +from edkrepo_manifest_parser.edk_manifest import CiIndexXml, ManifestXml + +class ListReposCommand(EdkrepoCommand): + def __init__(self): + super().__init__() + self.repo_names = None + + def get_metadata(self): + metadata = {} + metadata['name'] = 'list-repos' + metadata['help-text'] = arguments.COMMAND_DESCRIPTION + args = [] + metadata['arguments'] = args + args.append({'name': 'repos', + 'positional': False, + 'required': False, + 'action': 'store', + 'nargs': '+', + 'help-text': arguments.REPOS_HELP}) + args.append({'name': 'archived', + 'short-name': 'a', + 'positional': False, + 'required': False, + 'help-text': arguments.ARCHIVED_HELP}) + args.append(ColorArgument) + return metadata + + def run_command(self, args, config): + print() + init_color_console(args.color) + + # Get path to global manifest file + global_manifest_directory = config['cfg_file'].manifest_repo_abs_local_path + if args.verbose: + print(humble.MANIFEST_DIRECTORY) + print(global_manifest_directory) + print() + index_path = os.path.join(global_manifest_directory, 'CiIndex.xml') + + pull_latest_manifest_repo(args, config) + print() + + #Create a dictionary containing all the manifests listed in the CiIndex.xml file + ci_index_xml = CiIndexXml(index_path) + manifests = {} + repo_urls = set() + project_list = list(ci_index_xml.project_list) + if args.archived: + project_list.extend(ci_index_xml.archived_project_list) + for project in project_list: + xml_file = ci_index_xml.get_project_xml(project) + manifest = ManifestXml(os.path.normpath(os.path.join(global_manifest_directory, xml_file))) + manifests[project] = manifest + for combo in [c.name for c in manifest.combinations]: + sources = manifest.get_repo_sources(combo) + for source in sources: + repo_urls.add(self.get_repo_url(source.remote_url)) + + #Sort the manifests so projects will be displayed alphabetically + manifests = collections.OrderedDict(sorted(manifests.items())) + project_justify = len(max(manifests.keys(), key=len)) + + #Determine the names of the repositories + self.generate_repo_names(repo_urls, manifests) + print(humble.REPOSITORIES) + + #For each each git repository... + for repo_name in self.repo_names: + if args.repos and repo_name not in args.repos: + continue + repo = self.repo_names[repo_name][0] + print(humble.REPO_NAME_AND_URL.format(repo_name, repo)) + print(humble.BRANCHES) + + #Determine the list of branches that used by any branch combination in any manifest + branches = set() + for project_name in manifests: + for combo in [c.name for c in manifests[project_name].combinations]: + sources = manifests[project_name].get_repo_sources(combo) + for source in sources: + if self.get_repo_url(source.remote_url) == repo: + branches.add(source.branch) + + #Sort the branch names so they will be displayed alphabetically + #with the exception that if a branch named "master" exists, then it + #will be displayed first + branches = sorted(branches, key=str.casefold) + if 'master' in branches: + branches.remove('master') + branches.insert(0, 'master') + + #For each interesting branch in the current git repository... + for branch in branches: + print(humble.BRANCH_FORMAT_STRING.format(branch)) + + #Determine the branch combinations that use that branch + for project_name in manifests: + combos = [] + for combo in [c.name for c in manifests[project_name].combinations]: + sources = manifests[project_name].get_repo_sources(combo) + for source in sources: + if self.get_repo_url(source.remote_url) == repo and source.branch == branch: + combos.append(combo) + break + if len(combos) > 0: + #Sort the branch combinations so they will be displayed alphabetically + #with the exception that the default branch combination for the manifest + #file will be displayed first + combos = sorted(combos, key=str.casefold) + default_combo = manifests[project_name].general_config.default_combo + if default_combo in combos: + combos.remove(default_combo) + combos.insert(0, default_combo) + first_combo = True + for combo in combos: + #Print the project name + if first_combo: + project_name_print = humble.PROJECT_NAME_FORMAT_STRING.format(project_name.ljust(project_justify)) + first_combo = False + else: + project_name_print = '{} '.format((' ' * len(project_name)).ljust(project_justify)) + #Print the branch combination name, if this is the default branch combination, + #then print it in green color with *'s around it + if default_combo == combo: + print(humble.DEFAULT_COMBO_FORMAT_STRING.format(project_name_print, combo)) + else: + print(humble.COMBO_FORMAT_STRING.format(project_name_print, combo)) + + def get_repo_url(self, repo_url): + if repo_url[-4:].lower() == '.git': + return repo_url[:-4] + return repo_url + + def get_repo_name(self, repo_url, manifests): + for name in self.repo_names: + if self.repo_names[name][0] == repo_url: + return name + raise EdkrepoInvalidParametersException(humble.REPO_NAME_NOT_FOUND) + + def generate_repo_names(self, repo_urls, manifests): + #Determine the names of the repositories + self.repo_names = collections.OrderedDict() + for repo_url in repo_urls: + self.__repo_name_worker(repo_url, manifests) + + #Sort the git repositories so they will be displayed alphabetically + self.repo_names = collections.OrderedDict(sorted(self.repo_names.items())) + names_to_move = [] + for repo_name in self.repo_names: + if repo_name.lower().find('edk2') == 0: + names_to_move.append(repo_name) + names_to_move = sorted(names_to_move, reverse=True) + for name_to_move in names_to_move: + self.repo_names.move_to_end(name_to_move, False) + names_to_move = [] + for repo_name in self.repo_names: + if repo_name.lower().find('intel') == 0: + names_to_move.append(repo_name) + names_to_move = sorted(names_to_move, reverse=True) + for name_to_move in names_to_move: + self.repo_names.move_to_end(name_to_move, False) + + def __repo_name_worker(self, repo_url, manifests): + #This is a heuristic that guesses the "name" of a repository by looking + #at the name given to it by the most manifest files. + names = collections.defaultdict(int) + for project_name in manifests: + for combo in [c.name for c in manifests[project_name].combinations]: + sources = manifests[project_name].get_repo_sources(combo) + for source in sources: + if self.get_repo_url(source.remote_url) == repo_url: + names[source.root] += 1 + found_unique_name = False + original_best_name = None + original_best_name_frequency = 0 + while not found_unique_name: + best_name = None + best_name_frequency = 0 + if len(names) <= 0: + if original_best_name_frequency == 1: + #If only 1 project uses this name, then append the project + #name to the directory name to create the repo name + for project_name in manifests: + for combo in [c.name for c in manifests[project_name].combinations]: + sources = manifests[project_name].get_repo_sources(combo) + for source in sources: + if self.get_repo_url(source.remote_url) == repo_url and source.root == original_best_name: + best_name = "{}-{}".format(original_best_name, project_name) + best_name_frequency = original_best_name_frequency + else: + best_name = repo_url + best_name_frequency = 0 + break + for name in names: + if names[name] > best_name_frequency: + best_name = name + best_name_frequency = names[name] + if best_name is None: + raise EdkrepoManifestInvalidException(humble.REPO_NOT_FOUND_IN_MANIFEST.format(repo_url)) + if original_best_name is None: + original_best_name = best_name + original_best_name_frequency = best_name_frequency + if best_name in self.repo_names: + if self.repo_names[best_name][0] == repo_url: + found_unique_name = True + else: + #If there is a name collision, then which repo has the most + #Usage of the name owns the name + if best_name_frequency > self.repo_names[best_name][1]: + old_repo_url = self.repo_names[name][0] + del self.repo_names[best_name] + found_unique_name = True + self.repo_names[best_name] = (repo_url, best_name_frequency) + self.__repo_name_worker(old_repo_url, manifests) + else: + #Use the name given by the second most manifest files + del names[best_name] + else: + found_unique_name = True + self.repo_names[best_name] = (repo_url, best_name_frequency) diff --git a/setup.py b/setup.py index 73ce9a9..1b9edad 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ ## @file # setup.py # -# Copyright (c) 2017- 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -12,7 +12,7 @@ from setuptools import setup setup(name='edkrepo', version='2.0.0', description='The edkrepo tools', - packages=['edkrepo', 'edkrepo.commands', 'edkrepo.commands.arguments', 'edkrepo.common', 'edkrepo.config', 'edkrepo_manifest_parser', 'project_utils'], + packages=['edkrepo', 'edkrepo.commands', 'edkrepo.commands.arguments', 'edkrepo.commands.humble', 'edkrepo.common', 'edkrepo.config', 'edkrepo_manifest_parser', 'project_utils'], package_data={ }, include_package_data=True, @@ -22,4 +22,3 @@ setup(name='edkrepo', ] } ) - -- 2.24.0.windows.2 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#51673): https://edk2.groups.io/g/devel/message/51673 Mute This Topic: https://groups.io/mt/66074555/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-