summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Neus <jackneus@google.com>2021-07-20 20:52:33 +0000
committerJack Neus <jackneus@google.com>2021-07-23 18:03:11 +0000
commit6ea0caea86f4c6b1f934b682a3aa7722e98a46f9 (patch)
treef54707aa6778f60078aef727210669f22f87de4e
parent8e983bbc0f5f48aa38d0e1c5a37766ce121d28eb (diff)
downloadgit-repo-6ea0caea86f4c6b1f934b682a3aa7722e98a46f9.tar.gz
repo: properly handle remote annotations in manifest_xml
BUG=b:192664812 TEST=tests/ Change-Id: I1aa50260f4a00d3cebbd531141e1626825e70127 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/312643 Tested-by: Jack Neus <jackneus@google.com> Reviewed-by: Mike Frysinger <vapier@google.com>
-rw-r--r--docs/manifest-format.md14
-rw-r--r--manifest_xml.py35
-rw-r--r--project.py20
-rw-r--r--tests/test_manifest_xml.py29
4 files changed, 80 insertions, 18 deletions
diff --git a/docs/manifest-format.md b/docs/manifest-format.md
index c3bfcff0..854e5e1b 100644
--- a/docs/manifest-format.md
+++ b/docs/manifest-format.md
@@ -36,7 +36,7 @@ following DTD:
36 36
37 <!ELEMENT notice (#PCDATA)> 37 <!ELEMENT notice (#PCDATA)>
38 38
39 <!ELEMENT remote EMPTY> 39 <!ELEMENT remote (annotation*)>
40 <!ATTLIST remote name ID #REQUIRED> 40 <!ATTLIST remote name ID #REQUIRED>
41 <!ATTLIST remote alias CDATA #IMPLIED> 41 <!ATTLIST remote alias CDATA #IMPLIED>
42 <!ATTLIST remote fetch CDATA #REQUIRED> 42 <!ATTLIST remote fetch CDATA #REQUIRED>
@@ -348,12 +348,12 @@ project. Same syntax as the corresponding element of `project`.
348### Element annotation 348### Element annotation
349 349
350Zero or more annotation elements may be specified as children of a 350Zero or more annotation elements may be specified as children of a
351project element. Each element describes a name-value pair that will be 351project or remote element. Each element describes a name-value pair.
352exported into each project's environment during a 'forall' command, 352For projects, this name-value pair will be exported into each project's
353prefixed with REPO__. In addition, there is an optional attribute 353environment during a 'forall' command, prefixed with `REPO__`. In addition,
354"keep" which accepts the case insensitive values "true" (default) or 354there is an optional attribute "keep" which accepts the case insensitive values
355"false". This attribute determines whether or not the annotation will 355"true" (default) or "false". This attribute determines whether or not the
356be kept when exported with the manifest subcommand. 356annotation will be kept when exported with the manifest subcommand.
357 357
358### Element copyfile 358### Element copyfile
359 359
diff --git a/manifest_xml.py b/manifest_xml.py
index 22758cfd..55ad6c08 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -25,7 +25,7 @@ import gitc_utils
25from git_config import GitConfig, IsId 25from git_config import GitConfig, IsId
26from git_refs import R_HEADS, HEAD 26from git_refs import R_HEADS, HEAD
27import platform_utils 27import platform_utils
28from project import RemoteSpec, Project, MetaProject 28from project import Annotation, RemoteSpec, Project, MetaProject
29from error import (ManifestParseError, ManifestInvalidPathError, 29from error import (ManifestParseError, ManifestInvalidPathError,
30 ManifestInvalidRevisionError) 30 ManifestInvalidRevisionError)
31from wrapper import Wrapper 31from wrapper import Wrapper
@@ -149,16 +149,18 @@ class _XmlRemote(object):
149 self.reviewUrl = review 149 self.reviewUrl = review
150 self.revision = revision 150 self.revision = revision
151 self.resolvedFetchUrl = self._resolveFetchUrl() 151 self.resolvedFetchUrl = self._resolveFetchUrl()
152 self.annotations = []
152 153
153 def __eq__(self, other): 154 def __eq__(self, other):
154 if not isinstance(other, _XmlRemote): 155 if not isinstance(other, _XmlRemote):
155 return False 156 return False
156 return self.__dict__ == other.__dict__ 157 return (sorted(self.annotations) == sorted(other.annotations) and
158 self.name == other.name and self.fetchUrl == other.fetchUrl and
159 self.pushUrl == other.pushUrl and self.remoteAlias == other.remoteAlias
160 and self.reviewUrl == other.reviewUrl and self.revision == other.revision)
157 161
158 def __ne__(self, other): 162 def __ne__(self, other):
159 if not isinstance(other, _XmlRemote): 163 return not self.__eq__(other)
160 return True
161 return self.__dict__ != other.__dict__
162 164
163 def _resolveFetchUrl(self): 165 def _resolveFetchUrl(self):
164 if self.fetchUrl is None: 166 if self.fetchUrl is None:
@@ -191,6 +193,9 @@ class _XmlRemote(object):
191 orig_name=self.name, 193 orig_name=self.name,
192 fetchUrl=self.fetchUrl) 194 fetchUrl=self.fetchUrl)
193 195
196 def AddAnnotation(self, name, value, keep):
197 self.annotations.append(Annotation(name, value, keep))
198
194 199
195class XmlManifest(object): 200class XmlManifest(object):
196 """manages the repo configuration file""" 201 """manages the repo configuration file"""
@@ -300,6 +305,13 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
300 if r.revision is not None: 305 if r.revision is not None:
301 e.setAttribute('revision', r.revision) 306 e.setAttribute('revision', r.revision)
302 307
308 for a in r.annotations:
309 if a.keep == 'true':
310 ae = doc.createElement('annotation')
311 ae.setAttribute('name', a.name)
312 ae.setAttribute('value', a.value)
313 e.appendChild(ae)
314
303 def _ParseList(self, field): 315 def _ParseList(self, field):
304 """Parse fields that contain flattened lists. 316 """Parse fields that contain flattened lists.
305 317
@@ -995,7 +1007,14 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
995 if revision == '': 1007 if revision == '':
996 revision = None 1008 revision = None
997 manifestUrl = self.manifestProject.config.GetString('remote.origin.url') 1009 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
998 return _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision) 1010
1011 remote = _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision)
1012
1013 for n in node.childNodes:
1014 if n.nodeName == 'annotation':
1015 self._ParseAnnotation(remote, n)
1016
1017 return remote
999 1018
1000 def _ParseDefault(self, node): 1019 def _ParseDefault(self, node):
1001 """ 1020 """
@@ -1362,7 +1381,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
1362 self._ValidateFilePaths('linkfile', src, dest) 1381 self._ValidateFilePaths('linkfile', src, dest)
1363 project.AddLinkFile(src, dest, self.topdir) 1382 project.AddLinkFile(src, dest, self.topdir)
1364 1383
1365 def _ParseAnnotation(self, project, node): 1384 def _ParseAnnotation(self, element, node):
1366 name = self._reqatt(node, 'name') 1385 name = self._reqatt(node, 'name')
1367 value = self._reqatt(node, 'value') 1386 value = self._reqatt(node, 'value')
1368 try: 1387 try:
@@ -1372,7 +1391,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
1372 if keep != "true" and keep != "false": 1391 if keep != "true" and keep != "false":
1373 raise ManifestParseError('optional "keep" attribute must be ' 1392 raise ManifestParseError('optional "keep" attribute must be '
1374 '"true" or "false"') 1393 '"true" or "false"')
1375 project.AddAnnotation(name, value, keep) 1394 element.AddAnnotation(name, value, keep)
1376 1395
1377 def _get_remote(self, node): 1396 def _get_remote(self, node):
1378 name = node.getAttribute('remote') 1397 name = node.getAttribute('remote')
diff --git a/project.py b/project.py
index a55e7600..fe88a505 100644
--- a/project.py
+++ b/project.py
@@ -251,13 +251,29 @@ class DiffColoring(Coloring):
251 self.fail = self.printer('fail', fg='red') 251 self.fail = self.printer('fail', fg='red')
252 252
253 253
254class _Annotation(object): 254class Annotation(object):
255 255
256 def __init__(self, name, value, keep): 256 def __init__(self, name, value, keep):
257 self.name = name 257 self.name = name
258 self.value = value 258 self.value = value
259 self.keep = keep 259 self.keep = keep
260 260
261 def __eq__(self, other):
262 if not isinstance(other, Annotation):
263 return False
264 return self.__dict__ == other.__dict__
265
266 def __lt__(self, other):
267 # This exists just so that lists of Annotation objects can be sorted, for
268 # use in comparisons.
269 if not isinstance(other, Annotation):
270 raise ValueError('comparison is not between two Annotation objects')
271 if self.name == other.name:
272 if self.value == other.value:
273 return self.keep < other.keep
274 return self.value < other.value
275 return self.name < other.name
276
261 277
262def _SafeExpandPath(base, subpath, skipfinal=False): 278def _SafeExpandPath(base, subpath, skipfinal=False):
263 """Make sure |subpath| is completely safe under |base|. 279 """Make sure |subpath| is completely safe under |base|.
@@ -1448,7 +1464,7 @@ class Project(object):
1448 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest)) 1464 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
1449 1465
1450 def AddAnnotation(self, name, value, keep): 1466 def AddAnnotation(self, name, value, keep):
1451 self.annotations.append(_Annotation(name, value, keep)) 1467 self.annotations.append(Annotation(name, value, keep))
1452 1468
1453 def DownloadPatchSet(self, change_id, patch_id): 1469 def DownloadPatchSet(self, change_id, patch_id):
1454 """Download a single patch set of a single change to FETCH_HEAD. 1470 """Download a single patch set of a single change to FETCH_HEAD.
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index 96ee4c4a..59f2a779 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -286,6 +286,25 @@ class XmlManifestTests(ManifestParseTestCase):
286 '<superproject name="superproject"/>' 286 '<superproject name="superproject"/>'
287 '</manifest>') 287 '</manifest>')
288 288
289 def test_remote_annotations(self):
290 """Check remote settings."""
291 manifest = self.getXmlManifest("""
292<manifest>
293 <remote name="test-remote" fetch="http://localhost">
294 <annotation name="foo" value="bar"/>
295 </remote>
296</manifest>
297""")
298 self.assertEqual(manifest.remotes['test-remote'].annotations[0].name, 'foo')
299 self.assertEqual(manifest.remotes['test-remote'].annotations[0].value, 'bar')
300 self.assertEqual(
301 sort_attributes(manifest.ToXml().toxml()),
302 '<?xml version="1.0" ?><manifest>'
303 '<remote fetch="http://localhost" name="test-remote">'
304 '<annotation name="foo" value="bar"/>'
305 '</remote>'
306 '</manifest>')
307
289 308
290class IncludeElementTests(ManifestParseTestCase): 309class IncludeElementTests(ManifestParseTestCase):
291 """Tests for <include>.""" 310 """Tests for <include>."""
@@ -632,9 +651,17 @@ class RemoteElementTests(ManifestParseTestCase):
632 def test_remote(self): 651 def test_remote(self):
633 """Check remote settings.""" 652 """Check remote settings."""
634 a = manifest_xml._XmlRemote(name='foo') 653 a = manifest_xml._XmlRemote(name='foo')
635 b = manifest_xml._XmlRemote(name='bar') 654 a.AddAnnotation('key1', 'value1', 'true')
655 b = manifest_xml._XmlRemote(name='foo')
656 b.AddAnnotation('key2', 'value1', 'true')
657 c = manifest_xml._XmlRemote(name='foo')
658 c.AddAnnotation('key1', 'value2', 'true')
659 d = manifest_xml._XmlRemote(name='foo')
660 d.AddAnnotation('key1', 'value1', 'false')
636 self.assertEqual(a, a) 661 self.assertEqual(a, a)
637 self.assertNotEqual(a, b) 662 self.assertNotEqual(a, b)
663 self.assertNotEqual(a, c)
664 self.assertNotEqual(a, d)
638 self.assertNotEqual(a, manifest_xml._Default()) 665 self.assertNotEqual(a, manifest_xml._Default())
639 self.assertNotEqual(a, 123) 666 self.assertNotEqual(a, 123)
640 self.assertNotEqual(a, None) 667 self.assertNotEqual(a, None)