diff options
author | Jack Neus <jackneus@google.com> | 2021-07-20 20:52:33 +0000 |
---|---|---|
committer | Jack Neus <jackneus@google.com> | 2021-07-23 18:03:11 +0000 |
commit | 6ea0caea86f4c6b1f934b682a3aa7722e98a46f9 (patch) | |
tree | f54707aa6778f60078aef727210669f22f87de4e | |
parent | 8e983bbc0f5f48aa38d0e1c5a37766ce121d28eb (diff) | |
download | git-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.md | 14 | ||||
-rw-r--r-- | manifest_xml.py | 35 | ||||
-rw-r--r-- | project.py | 20 | ||||
-rw-r--r-- | tests/test_manifest_xml.py | 29 |
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 | ||
350 | Zero or more annotation elements may be specified as children of a | 350 | Zero or more annotation elements may be specified as children of a |
351 | project element. Each element describes a name-value pair that will be | 351 | project or remote element. Each element describes a name-value pair. |
352 | exported into each project's environment during a 'forall' command, | 352 | For projects, this name-value pair will be exported into each project's |
353 | prefixed with REPO__. In addition, there is an optional attribute | 353 | environment during a 'forall' command, prefixed with `REPO__`. In addition, |
354 | "keep" which accepts the case insensitive values "true" (default) or | 354 | there 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 |
356 | be kept when exported with the manifest subcommand. | 356 | annotation 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 | |||
25 | from git_config import GitConfig, IsId | 25 | from git_config import GitConfig, IsId |
26 | from git_refs import R_HEADS, HEAD | 26 | from git_refs import R_HEADS, HEAD |
27 | import platform_utils | 27 | import platform_utils |
28 | from project import RemoteSpec, Project, MetaProject | 28 | from project import Annotation, RemoteSpec, Project, MetaProject |
29 | from error import (ManifestParseError, ManifestInvalidPathError, | 29 | from error import (ManifestParseError, ManifestInvalidPathError, |
30 | ManifestInvalidRevisionError) | 30 | ManifestInvalidRevisionError) |
31 | from wrapper import Wrapper | 31 | from 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 | ||
195 | class XmlManifest(object): | 200 | class 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') |
@@ -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 | ||
254 | class _Annotation(object): | 254 | class 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 | ||
262 | def _SafeExpandPath(base, subpath, skipfinal=False): | 278 | def _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 | ||
290 | class IncludeElementTests(ManifestParseTestCase): | 309 | class 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) |