summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFredrik de Groot <fredrik.de.groot@volvocars.com>2020-10-06 12:55:14 +0200
committerMike Frysinger <vapier@google.com>2020-11-26 09:13:14 +0000
commit352c93b68084e97ec7a3c09f0bd3f2a161290211 (patch)
treecf2e19805c6046a12aa221dd6c9747da570994a9
parent7f7acfe9fd93cfd4a697f2bc851d1b8182f6336e (diff)
downloadgit-repo-2.10.tar.gz
manifest: add support for groups in includev2.10
Attrib groups can now be added to manifest include, thus all projects in an included manifest file can easily be tagged with a group without modifying all projects in that manifest file. Include groups will add and recurse, meaning included manifest projects will carry all parent includes. Intentionally, no support added for group remove, to keep complexity down. Group handling for projects is untouched, meaning a group set on a project will still append to whatever was or was not inherited in parent manifest includes, resulting in union of groups inherited and set for the project itself. Test: manual multi-level manifest include structure, in serial and parallel, with different groups set on init Test: added unit tests to cover the inheritance Change-Id: Id2229aa6fd78d355ba598cc15c701b2ee71e5c6f Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/283587 Tested-by: Fredrik de Groot <fredrik.de.groot@volvocars.com> Reviewed-by: Mike Frysinger <vapier@google.com>
-rw-r--r--docs/manifest-format.md7
-rw-r--r--manifest_xml.py14
-rw-r--r--tests/test_manifest_xml.py43
3 files changed, 61 insertions, 3 deletions
diff --git a/docs/manifest-format.md b/docs/manifest-format.md
index b35a065f..2af34ac2 100644
--- a/docs/manifest-format.md
+++ b/docs/manifest-format.md
@@ -99,7 +99,8 @@ following DTD:
99 <!ATTLIST repo-hooks enabled-list CDATA #REQUIRED> 99 <!ATTLIST repo-hooks enabled-list CDATA #REQUIRED>
100 100
101 <!ELEMENT include EMPTY> 101 <!ELEMENT include EMPTY>
102 <!ATTLIST include name CDATA #REQUIRED> 102 <!ATTLIST include name CDATA #REQUIRED>
103 <!ATTLIST include groups CDATA #IMPLIED>
103]> 104]>
104``` 105```
105 106
@@ -368,6 +369,10 @@ target manifest to include - it must be a usable manifest on its own.
368Attribute `name`: the manifest to include, specified relative to 369Attribute `name`: the manifest to include, specified relative to
369the manifest repository's root. 370the manifest repository's root.
370 371
372Attribute `groups`: List of additional groups to which all projects
373in the included manifest belong. This appends and recurses, meaning
374all projects in sub-manifests carry all parent include groups.
375Same syntax as the corresponding element of `project`.
371 376
372## Local Manifests 377## Local Manifests
373 378
diff --git a/manifest_xml.py b/manifest_xml.py
index 95c67d73..ad0017cc 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -637,7 +637,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
637 637
638 self._loaded = True 638 self._loaded = True
639 639
640 def _ParseManifestXml(self, path, include_root): 640 def _ParseManifestXml(self, path, include_root, parent_groups=''):
641 try: 641 try:
642 root = xml.dom.minidom.parse(path) 642 root = xml.dom.minidom.parse(path)
643 except (OSError, xml.parsers.expat.ExpatError) as e: 643 except (OSError, xml.parsers.expat.ExpatError) as e:
@@ -656,12 +656,17 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
656 for node in manifest.childNodes: 656 for node in manifest.childNodes:
657 if node.nodeName == 'include': 657 if node.nodeName == 'include':
658 name = self._reqatt(node, 'name') 658 name = self._reqatt(node, 'name')
659 include_groups = ''
660 if parent_groups:
661 include_groups = parent_groups
662 if node.hasAttribute('groups'):
663 include_groups = node.getAttribute('groups') + ',' + include_groups
659 fp = os.path.join(include_root, name) 664 fp = os.path.join(include_root, name)
660 if not os.path.isfile(fp): 665 if not os.path.isfile(fp):
661 raise ManifestParseError("include %s doesn't exist or isn't a file" 666 raise ManifestParseError("include %s doesn't exist or isn't a file"
662 % (name,)) 667 % (name,))
663 try: 668 try:
664 nodes.extend(self._ParseManifestXml(fp, include_root)) 669 nodes.extend(self._ParseManifestXml(fp, include_root, include_groups))
665 # should isolate this to the exact exception, but that's 670 # should isolate this to the exact exception, but that's
666 # tricky. actual parsing implementation may vary. 671 # tricky. actual parsing implementation may vary.
667 except (KeyboardInterrupt, RuntimeError, SystemExit): 672 except (KeyboardInterrupt, RuntimeError, SystemExit):
@@ -670,6 +675,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
670 raise ManifestParseError( 675 raise ManifestParseError(
671 "failed parsing included manifest %s: %s" % (name, e)) 676 "failed parsing included manifest %s: %s" % (name, e))
672 else: 677 else:
678 if parent_groups and node.nodeName == 'project':
679 nodeGroups = parent_groups
680 if node.hasAttribute('groups'):
681 nodeGroups = node.getAttribute('groups') + ',' + nodeGroups
682 node.setAttribute('groups', nodeGroups)
673 nodes.append(node) 683 nodes.append(node)
674 return nodes 684 return nodes
675 685
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index 40385cce..939717be 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -235,3 +235,46 @@ class XmlManifestTests(unittest.TestCase):
235 self.assertCountEqual( 235 self.assertCountEqual(
236 result['extras'], 236 result['extras'],
237 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) 237 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path'])
238
239 def test_include_levels(self):
240 root_m = os.path.join(self.manifest_dir, 'root.xml')
241 with open(root_m, 'w') as fp:
242 fp.write("""
243<manifest>
244 <remote name="test-remote" fetch="http://localhost" />
245 <default remote="test-remote" revision="refs/heads/main" />
246 <include name="level1.xml" groups="level1-group" />
247 <project name="root-name1" path="root-path1" />
248 <project name="root-name2" path="root-path2" groups="r2g1,r2g2" />
249</manifest>
250""")
251 with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp:
252 fp.write("""
253<manifest>
254 <include name="level2.xml" groups="level2-group" />
255 <project name="level1-name1" path="level1-path1" />
256</manifest>
257""")
258 with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp:
259 fp.write("""
260<manifest>
261 <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" />
262</manifest>
263""")
264 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
265 for proj in include_m.projects:
266 if proj.name == 'root-name1':
267 # Check include group not set on root level proj.
268 self.assertNotIn('level1-group', proj.groups)
269 if proj.name == 'root-name2':
270 # Check root proj group not removed.
271 self.assertIn('r2g1', proj.groups)
272 if proj.name == 'level1-name1':
273 # Check level1 proj has inherited group level 1.
274 self.assertIn('level1-group', proj.groups)
275 if proj.name == 'level2-name1':
276 # Check level2 proj has inherited group levels 1 and 2.
277 self.assertIn('level1-group', proj.groups)
278 self.assertIn('level2-group', proj.groups)
279 # Check level2 proj group not removed.
280 self.assertIn('l2g1', proj.groups)