diff options
-rw-r--r-- | docs/manifest-format.md | 7 | ||||
-rw-r--r-- | manifest_xml.py | 14 | ||||
-rw-r--r-- | tests/test_manifest_xml.py | 43 |
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. | |||
368 | Attribute `name`: the manifest to include, specified relative to | 369 | Attribute `name`: the manifest to include, specified relative to |
369 | the manifest repository's root. | 370 | the manifest repository's root. |
370 | 371 | ||
372 | Attribute `groups`: List of additional groups to which all projects | ||
373 | in the included manifest belong. This appends and recurses, meaning | ||
374 | all projects in sub-manifests carry all parent include groups. | ||
375 | Same 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) | ||