manifest_xml: initial support for <superproject>

At most one superproject may be specified. It will be used
to specify the URL of superproject.

It would have 3 attributes: remote, name, and default.
Only "name" is required while the others have reasonable defaults.

<remote name="superproject-url" review="<url>" />
<superproject remote="superproject-url" name="platform/superproject"/>

TODO: This CL only implements the parsing logic and further work
will be in followup CLs.

Tested the code with the following commands.

$ ./run_tests tests/test_manifest_xml.py
$ ./run_tests -v

Bug: https://crbug.com/gerrit/13709
Tested-by: Raman Tenneti <rtenneti@google.com>
Change-Id: I5b4bba02c8b59601c754cf6b5e4d07a1e16ce167
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/292982
Reviewed-by: Mike Frysinger <vapier@google.com>
This commit is contained in:
Raman Tenneti 2021-01-07 16:50:45 -08:00
parent b64bec6acc
commit 1bb4fb222d
3 changed files with 123 additions and 0 deletions

View File

@ -29,6 +29,7 @@ following DTD:
project*, project*,
extend-project*, extend-project*,
repo-hooks?, repo-hooks?,
superproject?,
include*)> include*)>
<!ELEMENT notice (#PCDATA)> <!ELEMENT notice (#PCDATA)>
@ -98,6 +99,10 @@ following DTD:
<!ATTLIST repo-hooks in-project CDATA #REQUIRED> <!ATTLIST repo-hooks in-project CDATA #REQUIRED>
<!ATTLIST repo-hooks enabled-list CDATA #REQUIRED> <!ATTLIST repo-hooks enabled-list CDATA #REQUIRED>
<!ELEMENT superproject (EMPTY)>
<!ATTLIST superproject name CDATA #REQUIRED>
<!ATTLIST superproject remote IDREF #IMPLIED>
<!ELEMENT include EMPTY> <!ELEMENT include EMPTY>
<!ATTLIST include name CDATA #REQUIRED> <!ATTLIST include name CDATA #REQUIRED>
<!ATTLIST include groups CDATA #IMPLIED> <!ATTLIST include groups CDATA #IMPLIED>
@ -377,6 +382,28 @@ defined `project` element.
Attribute `enabled-list`: List of hooks to use, whitespace or comma separated. Attribute `enabled-list`: List of hooks to use, whitespace or comma separated.
### Element superproject
***
*Note*: This is currently a WIP.
***
NB: See the [git superprojects documentation](
https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects) for background
information.
This element is used to specify the URL of the superproject. It has "name" and
"remote" as atrributes. Only "name" is required while the others have
reasonable defaults. At most one superproject may be specified.
Attempting to redefine it will fail to parse.
Attribute `name`: A unique name for the superproject. This attribute has the
same meaning as project's name attribute. See the
[element project](#element-project) for more information.
Attribute `remote`: Name of a previously defined remote element.
If not supplied the remote given by the default element is used.
### Element include ### Element include
This element provides the capability of including another manifest This element provides the capability of including another manifest

View File

@ -463,6 +463,19 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
' '.join(self._repo_hooks_project.enabled_repo_hooks)) ' '.join(self._repo_hooks_project.enabled_repo_hooks))
root.appendChild(e) root.appendChild(e)
if self._superproject:
root.appendChild(doc.createTextNode(''))
e = doc.createElement('superproject')
e.setAttribute('name', self._superproject['name'])
remoteName = None
if d.remote:
remoteName = d.remote.name
remote = self._superproject.get('remote')
if not d.remote or remote.orig_name != remoteName:
remoteName = remote.orig_name
e.setAttribute('remote', remoteName)
root.appendChild(e)
return doc return doc
def ToDict(self, **kwargs): def ToDict(self, **kwargs):
@ -473,6 +486,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
'default', 'default',
'manifest-server', 'manifest-server',
'repo-hooks', 'repo-hooks',
'superproject',
} }
# Elements that may be repeated. # Elements that may be repeated.
MULTI_ELEMENTS = { MULTI_ELEMENTS = {
@ -544,6 +558,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
self._Load() self._Load()
return self._repo_hooks_project return self._repo_hooks_project
@property
def superproject(self):
self._Load()
return self._superproject
@property @property
def notice(self): def notice(self):
self._Load() self._Load()
@ -591,6 +610,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
self._remotes = {} self._remotes = {}
self._default = None self._default = None
self._repo_hooks_project = None self._repo_hooks_project = None
self._superproject = {}
self._notice = None self._notice = None
self.branch = None self.branch = None
self._manifest_server = None self._manifest_server = None
@ -793,6 +813,23 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
# Store the enabled hooks in the Project object. # Store the enabled hooks in the Project object.
self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks
if node.nodeName == 'superproject':
name = self._reqatt(node, 'name')
# There can only be one superproject.
if self._superproject.get('name'):
raise ManifestParseError(
'duplicate superproject in %s' %
(self.manifestFile))
self._superproject['name'] = name
remote_name = node.getAttribute('remote')
if not remote_name:
remote = self._default.remote
else:
remote = self._get_remote(node)
if remote is None:
raise ManifestParseError("no remote for superproject %s within %s" %
(name, self.manifestFile))
self._superproject['remote'] = remote.ToRemoteSpec(name)
if node.nodeName == 'remove-project': if node.nodeName == 'remove-project':
name = self._reqatt(node, 'name') name = self._reqatt(node, 'name')

View File

@ -221,6 +221,65 @@ class XmlManifestTests(unittest.TestCase):
self.assertEqual(manifest.repo_hooks_project.name, 'repohooks') self.assertEqual(manifest.repo_hooks_project.name, 'repohooks')
self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b']) self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b'])
def test_superproject(self):
"""Check superproject settings."""
manifest = self.getXmlManifest("""
<manifest>
<remote name="test-remote" fetch="http://localhost" />
<default remote="test-remote" revision="refs/heads/main" />
<superproject name="superproject"/>
</manifest>
""")
self.assertEqual(manifest.superproject['name'], 'superproject')
self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
self.assertEqual(
manifest.ToXml().toxml(),
'<?xml version="1.0" ?><manifest>' +
'<remote name="test-remote" fetch="http://localhost"/>' +
'<default remote="test-remote" revision="refs/heads/main"/>' +
'<superproject name="superproject"/>' +
'</manifest>')
def test_superproject_with_remote(self):
"""Check superproject settings."""
manifest = self.getXmlManifest("""
<manifest>
<remote name="default-remote" fetch="http://localhost" />
<remote name="test-remote" fetch="http://localhost" />
<default remote="default-remote" revision="refs/heads/main" />
<superproject name="superproject" remote="test-remote"/>
</manifest>
""")
self.assertEqual(manifest.superproject['name'], 'superproject')
self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
self.assertEqual(
manifest.ToXml().toxml(),
'<?xml version="1.0" ?><manifest>' +
'<remote name="default-remote" fetch="http://localhost"/>' +
'<remote name="test-remote" fetch="http://localhost"/>' +
'<default remote="default-remote" revision="refs/heads/main"/>' +
'<superproject name="superproject" remote="test-remote"/>' +
'</manifest>')
def test_superproject_with_defalut_remote(self):
"""Check superproject settings."""
manifest = self.getXmlManifest("""
<manifest>
<remote name="default-remote" fetch="http://localhost" />
<default remote="default-remote" revision="refs/heads/main" />
<superproject name="superproject" remote="default-remote"/>
</manifest>
""")
self.assertEqual(manifest.superproject['name'], 'superproject')
self.assertEqual(manifest.superproject['remote'].name, 'default-remote')
self.assertEqual(
manifest.ToXml().toxml(),
'<?xml version="1.0" ?><manifest>' +
'<remote name="default-remote" fetch="http://localhost"/>' +
'<default remote="default-remote" revision="refs/heads/main"/>' +
'<superproject name="superproject"/>' +
'</manifest>')
def test_project_group(self): def test_project_group(self):
"""Check project group settings.""" """Check project group settings."""
manifest = self.getXmlManifest(""" manifest = self.getXmlManifest("""