diff options
| -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) |
