public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-staging/EdkRepo] [PATCH] EdkRepo: Add list-repos command
@ 2019-11-27  4:37 Nate DeSimone
  2019-12-03 22:36 ` Desimone, Ashley E
  0 siblings, 1 reply; 2+ messages in thread
From: Nate DeSimone @ 2019-11-27  4:37 UTC (permalink / raw)
  To: devel; +Cc: Ashley E Desimone

list-repos lists the git repos used by all projects
and which branches those projects use.

Cc: Ashley E Desimone <ashley.e.desimone@intel.com>
Signed-off-by: Nate DeSimone <nathaniel.l.desimone@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        | 218 ++++++++++++++++++
 4 files changed, 268 insertions(+)
 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..de2de03
--- /dev/null
+++ b/edkrepo/commands/list_repos_command.py
@@ -0,0 +1,218 @@
+#!/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()
+
+        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))
+        manifests = collections.OrderedDict(sorted(manifests.items()))
+        project_justify = len(max(manifests.keys(), key=len))
+        self.generate_repo_names(repo_urls, manifests)
+        print(humble.REPOSITORIES)
+        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)
+            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)
+            branches = sorted(branches, key=str.casefold)
+            if 'master' in branches:
+                branches.remove('master')
+                branches.insert(0, 'master')
+            #length = len(max(branches, key=len)) .ljust(length)
+            for branch in branches:
+                print(humble.BRANCH_FORMAT_STRING.format(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:
+                        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:
+                            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))
+                            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):
+        self.repo_names = collections.OrderedDict()
+        for repo_url in repo_urls:
+            self.__repo_name_worker(repo_url, manifests)
+        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)
-- 
2.24.0.windows.2


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-12-03 22:36 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-11-27  4:37 [edk2-staging/EdkRepo] [PATCH] EdkRepo: Add list-repos command Nate DeSimone
2019-12-03 22:36 ` Desimone, Ashley E

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox