Fix git submodule status pattern

The ParseGitModules() uses `git submodule status` result to get the
commit hashes for the submodules. It assumes the command's result to not
have a `git describe` at the end of each line, and hence only works in
certain scenarios. This change fixes this by updating the parser.

Change-Id: I8f4e3fae6562dafd0e127255a8f26e7336a75683
Bug: 1471241
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4763348
Auto-Submit: Aravind Vasudevan <aravindvasudev@google.com>
Commit-Queue: Josip Sokcevic <sokcevic@chromium.org>
Commit-Queue: Aravind Vasudevan <aravindvasudev@google.com>
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
changes/48/4763348/10
Aravind Vasudevan 2 years ago committed by LUCI CQ
parent 3ab2f21289
commit 18c726d5b1

@ -942,16 +942,23 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
return {}
# Get submodule commit hashes
result = subprocess2.check_output(['git', 'submodule', 'status'],
# Output Format: `<mode> <commit_hash> <path>`.
result = subprocess2.check_output([
'git', 'ls-tree', '-r', 'HEAD', '--format',
'%(objectmode) %(objectname) %(path)'
],
cwd=cwd).decode('utf-8')
commit_hashes = {}
for record in result.splitlines():
commit, module = record.split(maxsplit=1)
commit_hashes[module] = commit[1:]
for r in result.splitlines():
# ['<mode>', '<commit_hash>', '<path>'].
record = r.strip().split()
if record[0] == '160000': # Only add gitlinks
commit_hashes[record[2]] = record[1]
# Get .gitmodules fields
gitmodules_entries = subprocess2.check_output(
['git', 'config', '--file', filepath, '-l']).decode('utf-8')
['git', 'config', '--file', filepath, '-l'], cwd=cwd).decode('utf-8')
gitmodules = {}
for entry in gitmodules_entries.splitlines():
@ -961,7 +968,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
section, submodule_key = key.split('.', maxsplit=1)
# Only parse [submodule "foo"] sections from .gitmodules.
if section != 'submodule':
if section.strip() != 'submodule':
continue
# The name of the submodule can contain '.', hence split from the back.
@ -975,14 +982,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
# Structure git submodules into a dict of DEPS git url entries.
submodules = {}
for name, module in gitmodules.items():
for module in gitmodules.values():
if self._use_relative_paths:
path = module['path']
else:
path = f'{self.name}/{module["path"]}'
submodules[path] = {
'dep_type': 'git',
'url': '{}@{}'.format(module['url'], commit_hashes[name])
'url': '{}@{}'.format(module['url'], commit_hashes[module['path']])
}
if 'gclient-condition' in module:

@ -1406,6 +1406,157 @@ class GclientTest(trial_dir.TestCase):
self.assertFalse(sol.dependencies)
self.assertFalse(sol._deps_hooks)
def testParseGitSubmodules_NoSubmodules(self):
"""ParseGitSubmodules should return {} when the dep doesn't have
submodules."""
solutions = [{
'name': 'foobar',
'url': 'https://example.com/foobar',
'deps_file': '.DEPS.git',
}]
write('.gclient', 'solutions = %s' % repr(solutions))
options, _ = gclient.OptionParser().parse_args([])
client = gclient.GClient.LoadCurrentConfig(options)
self.assertEqual(1, len(client.dependencies))
sol = client.dependencies[0]
self.assertEqual(sol.ParseGitSubmodules(), {})
def testParseGitSubmodules_ParsesSubmodules(self):
"""ParseGitSubmodules returns submodules when present. """
solutions = [{
'name': 'foobar',
'url': 'https://example.com/foobar',
'deps_file': '.DEPS.git',
}]
write('.gclient', 'solutions = %s' % repr(solutions))
ls_tree = """160000 be8c5114d606692dc783b60cf256690b62fbad17 foo/bar
123456 daf2de9caad4a70e6bb1047a6b50c412066f68b1 README.txt
160000 3ad3b564f8ae456f286446d091709f5a09fa4a93 aaaaaa
160000 956df937508b65b5e72a4cf02696255be3631b78 a.a.a/a
160000 b9f77763f0fab67eeeb6371492166567a8b7a3d2 a_b/c"""
git_config = """submodule.foo/bar.path=foo/bar
submodule.foo/bar.url=http://example.com/foo/bar
submodule.foo/bar.gclient-condition=checkout_linux
submodule.aaaaaa.path=aaaaaa
submodule.aaaaaa.url=http://example.com/aaaaaa
submodule.a.a.a/a.path=a.a.a/a
submodule.a.a.a/a.url=http://example.com/a.a.a/a
submodule.a_b/c.path=a_b/c
submodule.a_b/c.url=http://example.com/a_b/c"""
os_path_isfile_mock = mock.MagicMock(return_value=True)
subprocess2_check_output_mock = mock.MagicMock(
side_effect=[ls_tree.encode(), git_config.encode()])
options, _ = gclient.OptionParser().parse_args([])
client = gclient.GClient.LoadCurrentConfig(options)
self.assertEqual(1, len(client.dependencies))
sol = client.dependencies[0]
sol._use_relative_paths = True
with mock.patch('os.path.isfile', os_path_isfile_mock), mock.patch(
'subprocess2.check_output', subprocess2_check_output_mock):
self.assertEqual(
sol.ParseGitSubmodules(), {
'foo/bar': {
'dep_type':
'git',
'url': ('http://example.com/foo/bar@' +
'be8c5114d606692dc783b60cf256690b62fbad17'),
'condition':
'checkout_linux',
},
'aaaaaa': {
'dep_type':
'git',
'url': ('http://example.com/aaaaaa@' +
'3ad3b564f8ae456f286446d091709f5a09fa4a93'),
},
'a.a.a/a': {
'dep_type':
'git',
'url': ('http://example.com/a.a.a/a@' +
'956df937508b65b5e72a4cf02696255be3631b78'),
},
'a_b/c': {
'dep_type':
'git',
'url': ('http://example.com/a_b/c@' +
'b9f77763f0fab67eeeb6371492166567a8b7a3d2')
}
})
def testParseGitSubmodules_UsesAbsolutePath(self):
"""ParseGitSubmodules uses absolute path when use_relative_path is not
set."""
solutions = [{
'name': 'foobar',
'url': 'https://example.com/foobar',
'deps_file': '.DEPS.git',
}]
write('.gclient', 'solutions = %s' % repr(solutions))
ls_tree = """160000 be8c5114d606692dc783b60cf256690b62fbad17 foo/bar
123456 daf2de9caad4a70e6bb1047a6b50c412066f68b1 README.txt
160000 3ad3b564f8ae456f286446d091709f5a09fa4a93 aaaaaa
160000 956df937508b65b5e72a4cf02696255be3631b78 a.a.a/a
160000 b9f77763f0fab67eeeb6371492166567a8b7a3d2 a_b/c"""
git_config = """submodule.foo/bar.path=foo/bar
submodule.foo/bar.url=http://example.com/foo/bar
submodule.foo/bar.gclient-condition=checkout_linux
submodule.aaaaaa.path=aaaaaa
submodule.aaaaaa.url=http://example.com/aaaaaa
submodule.a.a.a/a.path=a.a.a/a
submodule.a.a.a/a.url=http://example.com/a.a.a/a
submodule.a_b/c.path=a_b/c
submodule.a_b/c.url=http://example.com/a_b/c"""
os_path_isfile_mock = mock.MagicMock(return_value=True)
subprocess2_check_output_mock = mock.MagicMock(
side_effect=[ls_tree.encode(), git_config.encode()])
options, _ = gclient.OptionParser().parse_args([])
client = gclient.GClient.LoadCurrentConfig(options)
self.assertEqual(1, len(client.dependencies))
sol = client.dependencies[0]
with mock.patch('os.path.isfile', os_path_isfile_mock), mock.patch(
'subprocess2.check_output', subprocess2_check_output_mock):
self.assertEqual(
sol.ParseGitSubmodules(), {
'foobar/foo/bar': {
'dep_type':
'git',
'url': ('http://example.com/foo/bar@' +
'be8c5114d606692dc783b60cf256690b62fbad17'),
'condition':
'checkout_linux',
},
'foobar/aaaaaa': {
'dep_type':
'git',
'url': ('http://example.com/aaaaaa@' +
'3ad3b564f8ae456f286446d091709f5a09fa4a93'),
},
'foobar/a.a.a/a': {
'dep_type':
'git',
'url': ('http://example.com/a.a.a/a@' +
'956df937508b65b5e72a4cf02696255be3631b78'),
},
'foobar/a_b/c': {
'dep_type':
'git',
'url': ('http://example.com/a_b/c@' +
'b9f77763f0fab67eeeb6371492166567a8b7a3d2')
}
})
def testSameDirAllowMultipleCipdDeps(self):
"""Verifies gclient allow multiple cipd deps under same directory."""
parser = gclient.OptionParser()

Loading…
Cancel
Save