From 795b5b6a20d330a293d236bccd2fb605b63957a9 Mon Sep 17 00:00:00 2001 From: Anne Redulla Date: Fri, 20 Sep 2024 00:04:32 +0000 Subject: [PATCH] Added check_git_version helper Not yet used. Bug: b/360206460 Change-Id: I233cdc17ec17d477ec78024edca2b87417a92258 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5869272 Reviewed-by: Josip Sokcevic Reviewed-by: Yiwei Zhang Commit-Queue: Anne Redulla --- git_common.py | 43 +++++++++++++++++++++++++++++--- tests/git_common_test.py | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/git_common.py b/git_common.py index 3d90d9ffd..2fde5ec84 100644 --- a/git_common.py +++ b/git_common.py @@ -81,6 +81,9 @@ def win_find_git(): GIT_EXE = 'git' if not IS_WIN else win_find_git() +# The recommended minimum version of Git, as (, , ). +GIT_MIN_VERSION = (2, 26, 0) + FREEZE = 'FREEZE' FREEZE_SECTIONS = {'indexed': 'soft', 'unindexed': 'mixed'} FREEZE_MATCHER = re.compile(r'%s.(%s)' % (FREEZE, '|'.join(FREEZE_SECTIONS))) @@ -1195,6 +1198,39 @@ def upstream(branch): return None +def check_git_version( + min_version: Tuple[int] = GIT_MIN_VERSION) -> Optional[str]: + """Checks whether git is installed, and its version meets the recommended + minimum version, which defaults to GIT_MIN_VERSION if not specified. + + Returns: + - the remediation action to take. + """ + min_tag = '.'.join(str(x) for x in min_version) + if shutil.which(GIT_EXE) is None: + # git command was not found. + return ('git command not found.\n' + f'Please install version >={min_tag} of git.\n' + 'See instructions at\n' + 'https://git-scm.com/book/en/v2/Getting-Started-Installing-Git') + + if meets_git_version(min_version): + # git version is sufficient; no remediation action necessary. + return None + + # git is installed but older than the recommended version. + tag = '.'.join(str(x) for x in get_git_version()) or 'unknown' + return ('git update is recommended.\n' + f'Installed git version is {tag};\n' + f'depot_tools recommends version {min_tag} or later.') + + +def meets_git_version(min_version: Tuple[int]) -> bool: + """Returns whether the current git version meets the minimum specified.""" + return get_git_version() >= min_version + + +@functools.lru_cache(maxsize=1) def get_git_version(): """Returns a tuple that contains the numeric components of the current git version.""" @@ -1204,9 +1240,10 @@ def get_git_version(): def _extract_git_tuple(version_string): version_match = re.search(r'(\d+.)+(\d+)', version_string) - version = version_match.group() if version_match else '' - - return tuple(int(x) for x in version.split('.')) + if version_match: + version = version_match.group() + return tuple(int(x) for x in version.split('.')) + return tuple() def get_num_commits(branch): diff --git a/tests/git_common_test.py b/tests/git_common_test.py index 03a826172..f95df5a01 100755 --- a/tests/git_common_test.py +++ b/tests/git_common_test.py @@ -1141,11 +1141,65 @@ class GitTestUtilsTest(git_test_utils.GitRepoReadOnlyTestBase): self.repo.show_commit('C', format_string='%cn %ce %ci')) +class CheckGitVersionTest(GitCommonTestBase): + + def setUp(self): + self.addCleanup(self.gc.get_git_version.cache_clear) + + @mock.patch('shutil.which') + def testGitNotInstalled(self, mockWhich): + mockWhich.return_value = None + + recommendation = self.gc.check_git_version() + self.assertIsNotNone(recommendation) + self.assertTrue('Please install' in recommendation) + + mockWhich.assert_called_once() + + @mock.patch('shutil.which') + @mock.patch('git_common.run') + def testGitOldVersion(self, mockRun, mockWhich): + mockWhich.return_value = '/example/bin/git' + mockRun.return_value = 'git version 2.2.40-abc' + + recommendation = self.gc.check_git_version() + self.assertIsNotNone(recommendation) + self.assertTrue('update is recommended' in recommendation) + + mockWhich.assert_called_once() + mockRun.assert_called_once() + + @mock.patch('shutil.which') + @mock.patch('git_common.run') + def testGitSufficientVersion(self, mockRun, mockWhich): + mockWhich.return_value = '/example/bin/git' + mockRun.return_value = 'git version 2.30.1.456' + + self.assertIsNone(self.gc.check_git_version()) + + mockWhich.assert_called_once() + mockRun.assert_called_once() + + @mock.patch('shutil.which') + @mock.patch('git_common.run') + def testHandlesErrorGettingVersion(self, mockRun, mockWhich): + mockWhich.return_value = '/example/bin/git' + mockRun.return_value = 'Error running git version' + + recommendation = self.gc.check_git_version() + self.assertIsNotNone(recommendation) + self.assertTrue('update is recommended' in recommendation) + + mockWhich.assert_called_once() + mockRun.assert_called_once() + + class WarnSubmoduleTest(unittest.TestCase): def setUp(self): import git_common self.warn_submodule = git_common.warn_submodule mock.patch('sys.stdout', StringIO()).start() + self.addCleanup(mock.patch.stopall) def testWarnFSMonitorOldVersion(self): mock.patch('git_common.is_fsmonitor_enabled', lambda: True).start()