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: - + + ]> ``` @@ -368,6 +369,10 @@ target manifest to include - it must be a usable manifest on its own. Attribute `name`: the manifest to include, specified relative to the manifest repository's root. +Attribute `groups`: List of additional groups to which all projects +in the included manifest belong. This appends and recurses, meaning +all projects in sub-manifests carry all parent include groups. +Same syntax as the corresponding element of `project`. ## Local Manifests 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 self._loaded = True - def _ParseManifestXml(self, path, include_root): + def _ParseManifestXml(self, path, include_root, parent_groups=''): try: root = xml.dom.minidom.parse(path) except (OSError, xml.parsers.expat.ExpatError) as e: @@ -656,12 +656,17 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md for node in manifest.childNodes: if node.nodeName == 'include': name = self._reqatt(node, 'name') + include_groups = '' + if parent_groups: + include_groups = parent_groups + if node.hasAttribute('groups'): + include_groups = node.getAttribute('groups') + ',' + include_groups fp = os.path.join(include_root, name) if not os.path.isfile(fp): raise ManifestParseError("include %s doesn't exist or isn't a file" % (name,)) try: - nodes.extend(self._ParseManifestXml(fp, include_root)) + nodes.extend(self._ParseManifestXml(fp, include_root, include_groups)) # should isolate this to the exact exception, but that's # tricky. actual parsing implementation may vary. except (KeyboardInterrupt, RuntimeError, SystemExit): @@ -670,6 +675,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md raise ManifestParseError( "failed parsing included manifest %s: %s" % (name, e)) else: + if parent_groups and node.nodeName == 'project': + nodeGroups = parent_groups + if node.hasAttribute('groups'): + nodeGroups = node.getAttribute('groups') + ',' + nodeGroups + node.setAttribute('groups', nodeGroups) nodes.append(node) return nodes 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): self.assertCountEqual( result['extras'], ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) + + def test_include_levels(self): + root_m = os.path.join(self.manifest_dir, 'root.xml') + with open(root_m, 'w') as fp: + fp.write(""" + + + + + + + +""") + with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: + fp.write(""" + + + + +""") + with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: + fp.write(""" + + + +""") + include_m = manifest_xml.XmlManifest(self.repodir, root_m) + for proj in include_m.projects: + if proj.name == 'root-name1': + # Check include group not set on root level proj. + self.assertNotIn('level1-group', proj.groups) + if proj.name == 'root-name2': + # Check root proj group not removed. + self.assertIn('r2g1', proj.groups) + if proj.name == 'level1-name1': + # Check level1 proj has inherited group level 1. + self.assertIn('level1-group', proj.groups) + if proj.name == 'level2-name1': + # Check level2 proj has inherited group levels 1 and 2. + self.assertIn('level1-group', proj.groups) + self.assertIn('level2-group', proj.groups) + # Check level2 proj group not removed. + self.assertIn('l2g1', proj.groups)