diff options
-rw-r--r-- | docs/manifest-format.txt | 25 | ||||
-rw-r--r-- | manifest_xml.py | 32 | ||||
-rw-r--r-- | subcmds/sync.py | 52 |
3 files changed, 106 insertions, 3 deletions
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index da0e69ff..211344ee 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
@@ -22,6 +22,7 @@ following DTD: | |||
22 | <!DOCTYPE manifest [ | 22 | <!DOCTYPE manifest [ |
23 | <!ELEMENT manifest (remote*, | 23 | <!ELEMENT manifest (remote*, |
24 | default?, | 24 | default?, |
25 | manifest-server?, | ||
25 | remove-project*, | 26 | remove-project*, |
26 | project*)> | 27 | project*)> |
27 | 28 | ||
@@ -33,6 +34,9 @@ following DTD: | |||
33 | <!ELEMENT default (EMPTY)> | 34 | <!ELEMENT default (EMPTY)> |
34 | <!ATTLIST default remote IDREF #IMPLIED> | 35 | <!ATTLIST default remote IDREF #IMPLIED> |
35 | <!ATTLIST default revision CDATA #IMPLIED> | 36 | <!ATTLIST default revision CDATA #IMPLIED> |
37 | |||
38 | <!ELEMENT manifest-server (EMPTY)> | ||
39 | <!ATTLIST url CDATA #REQUIRED> | ||
36 | 40 | ||
37 | <!ELEMENT project (EMPTY)> | 41 | <!ELEMENT project (EMPTY)> |
38 | <!ATTLIST project name CDATA #REQUIRED> | 42 | <!ATTLIST project name CDATA #REQUIRED> |
@@ -89,6 +93,27 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or | |||
89 | revision attribute will use this revision. | 93 | revision attribute will use this revision. |
90 | 94 | ||
91 | 95 | ||
96 | Element manifest-server | ||
97 | ----------------------- | ||
98 | |||
99 | At most one manifest-server may be specified. The url attribute | ||
100 | is used to specify the URL of a manifest server, which is an | ||
101 | XML RPC service that will return a manifest in which each project | ||
102 | is pegged to a known good revision for the current branch and | ||
103 | target. | ||
104 | |||
105 | The manifest server should implement: | ||
106 | |||
107 | GetApprovedManifest(branch, target) | ||
108 | |||
109 | The target to use is defined by environment variables TARGET_PRODUCT | ||
110 | and TARGET_BUILD_VARIANT. These variables are used to create a string | ||
111 | of the form $TARGET_PRODUCT-$TARGET_BUILD_VARIANT, e.g. passion-userdebug. | ||
112 | If one of those variables or both are not present, the program will call | ||
113 | GetApprovedManifest without the target paramater and the manifest server | ||
114 | should choose a reasonable default target. | ||
115 | |||
116 | |||
92 | Element project | 117 | Element project |
93 | --------------- | 118 | --------------- |
94 | 119 | ||
diff --git a/manifest_xml.py b/manifest_xml.py index 7d02f9d6..d0c9debe 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -65,8 +65,8 @@ class XmlManifest(object): | |||
65 | 65 | ||
66 | self._Unload() | 66 | self._Unload() |
67 | 67 | ||
68 | def Link(self, name): | 68 | def Override(self, name): |
69 | """Update the repo metadata to use a different manifest. | 69 | """Use a different manifest, just for the current instantiation. |
70 | """ | 70 | """ |
71 | path = os.path.join(self.manifestProject.worktree, name) | 71 | path = os.path.join(self.manifestProject.worktree, name) |
72 | if not os.path.isfile(path): | 72 | if not os.path.isfile(path): |
@@ -80,6 +80,11 @@ class XmlManifest(object): | |||
80 | finally: | 80 | finally: |
81 | self.manifestFile = old | 81 | self.manifestFile = old |
82 | 82 | ||
83 | def Link(self, name): | ||
84 | """Update the repo metadata to use a different manifest. | ||
85 | """ | ||
86 | self.Override(name) | ||
87 | |||
83 | try: | 88 | try: |
84 | if os.path.exists(self.manifestFile): | 89 | if os.path.exists(self.manifestFile): |
85 | os.remove(self.manifestFile) | 90 | os.remove(self.manifestFile) |
@@ -123,6 +128,12 @@ class XmlManifest(object): | |||
123 | root.appendChild(e) | 128 | root.appendChild(e) |
124 | root.appendChild(doc.createTextNode('')) | 129 | root.appendChild(doc.createTextNode('')) |
125 | 130 | ||
131 | if self._manifest_server: | ||
132 | e = doc.createElement('manifest-server') | ||
133 | e.setAttribute('url', self._manifest_server) | ||
134 | root.appendChild(e) | ||
135 | root.appendChild(doc.createTextNode('')) | ||
136 | |||
126 | sort_projects = list(self.projects.keys()) | 137 | sort_projects = list(self.projects.keys()) |
127 | sort_projects.sort() | 138 | sort_projects.sort() |
128 | 139 | ||
@@ -169,6 +180,11 @@ class XmlManifest(object): | |||
169 | return self._default | 180 | return self._default |
170 | 181 | ||
171 | @property | 182 | @property |
183 | def manifest_server(self): | ||
184 | self._Load() | ||
185 | return self._manifest_server | ||
186 | |||
187 | @property | ||
172 | def IsMirror(self): | 188 | def IsMirror(self): |
173 | return self.manifestProject.config.GetBoolean('repo.mirror') | 189 | return self.manifestProject.config.GetBoolean('repo.mirror') |
174 | 190 | ||
@@ -178,6 +194,7 @@ class XmlManifest(object): | |||
178 | self._remotes = {} | 194 | self._remotes = {} |
179 | self._default = None | 195 | self._default = None |
180 | self.branch = None | 196 | self.branch = None |
197 | self._manifest_server = None | ||
181 | 198 | ||
182 | def _Load(self): | 199 | def _Load(self): |
183 | if not self._loaded: | 200 | if not self._loaded: |
@@ -247,6 +264,15 @@ class XmlManifest(object): | |||
247 | self._default = _Default() | 264 | self._default = _Default() |
248 | 265 | ||
249 | for node in config.childNodes: | 266 | for node in config.childNodes: |
267 | if node.nodeName == 'manifest-server': | ||
268 | url = self._reqatt(node, 'url') | ||
269 | if self._manifest_server is not None: | ||
270 | raise ManifestParseError, \ | ||
271 | 'duplicate manifest-server in %s' % \ | ||
272 | (self.manifestFile) | ||
273 | self._manifest_server = url | ||
274 | |||
275 | for node in config.childNodes: | ||
250 | if node.nodeName == 'project': | 276 | if node.nodeName == 'project': |
251 | project = self._ParseProject(node) | 277 | project = self._ParseProject(node) |
252 | if self._projects.get(project.name): | 278 | if self._projects.get(project.name): |
@@ -315,7 +341,7 @@ class XmlManifest(object): | |||
315 | def _ParseProject(self, node): | 341 | def _ParseProject(self, node): |
316 | """ | 342 | """ |
317 | reads a <project> element from the manifest file | 343 | reads a <project> element from the manifest file |
318 | """ | 344 | """ |
319 | name = self._reqatt(node, 'name') | 345 | name = self._reqatt(node, 'name') |
320 | 346 | ||
321 | remote = self._get_remote(node) | 347 | remote = self._get_remote(node) |
diff --git a/subcmds/sync.py b/subcmds/sync.py index ceb81eaa..deff171a 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -17,9 +17,11 @@ from optparse import SUPPRESS_HELP | |||
17 | import os | 17 | import os |
18 | import re | 18 | import re |
19 | import shutil | 19 | import shutil |
20 | import socket | ||
20 | import subprocess | 21 | import subprocess |
21 | import sys | 22 | import sys |
22 | import time | 23 | import time |
24 | import xmlrpclib | ||
23 | 25 | ||
24 | from git_command import GIT | 26 | from git_command import GIT |
25 | from project import HEAD | 27 | from project import HEAD |
@@ -57,6 +59,10 @@ back to the manifest revision. This option is especially helpful | |||
57 | if the project is currently on a topic branch, but the manifest | 59 | if the project is currently on a topic branch, but the manifest |
58 | revision is temporarily needed. | 60 | revision is temporarily needed. |
59 | 61 | ||
62 | The -s/--smart-sync option can be used to sync to a known good | ||
63 | build as specified by the manifest-server element in the current | ||
64 | manifest. | ||
65 | |||
60 | SSH Connections | 66 | SSH Connections |
61 | --------------- | 67 | --------------- |
62 | 68 | ||
@@ -97,6 +103,9 @@ later is required to fix a server side protocol bug. | |||
97 | p.add_option('-d','--detach', | 103 | p.add_option('-d','--detach', |
98 | dest='detach_head', action='store_true', | 104 | dest='detach_head', action='store_true', |
99 | help='detach projects back to manifest revision') | 105 | help='detach projects back to manifest revision') |
106 | p.add_option('-s', '--smart-sync', | ||
107 | dest='smart_sync', action='store_true', | ||
108 | help='smart sync using manifest from a known good build') | ||
100 | 109 | ||
101 | g = p.add_option_group('repo Version options') | 110 | g = p.add_option_group('repo Version options') |
102 | g.add_option('--no-repo-verify', | 111 | g.add_option('--no-repo-verify', |
@@ -182,6 +191,49 @@ uncommitted changes are present' % project.relpath | |||
182 | print >>sys.stderr, 'error: cannot combine -n and -l' | 191 | print >>sys.stderr, 'error: cannot combine -n and -l' |
183 | sys.exit(1) | 192 | sys.exit(1) |
184 | 193 | ||
194 | if opt.smart_sync: | ||
195 | if not self.manifest.manifest_server: | ||
196 | print >>sys.stderr, \ | ||
197 | 'error: cannot smart sync: no manifest server defined in manifest' | ||
198 | sys.exit(1) | ||
199 | try: | ||
200 | server = xmlrpclib.Server(self.manifest.manifest_server) | ||
201 | p = self.manifest.manifestProject | ||
202 | b = p.GetBranch(p.CurrentBranch) | ||
203 | branch = b.merge | ||
204 | |||
205 | env = dict(os.environ) | ||
206 | if (env.has_key('TARGET_PRODUCT') and | ||
207 | env.has_key('TARGET_BUILD_VARIANT')): | ||
208 | target = '%s-%s' % (env['TARGET_PRODUCT'], | ||
209 | env['TARGET_BUILD_VARIANT']) | ||
210 | [success, manifest_str] = server.GetApprovedManifest(branch, target) | ||
211 | else: | ||
212 | [success, manifest_str] = server.GetApprovedManifest(branch) | ||
213 | |||
214 | if success: | ||
215 | manifest_name = "smart_sync_override.xml" | ||
216 | manifest_path = os.path.join(self.manifest.manifestProject.worktree, | ||
217 | manifest_name) | ||
218 | try: | ||
219 | f = open(manifest_path, 'w') | ||
220 | try: | ||
221 | f.write(manifest_str) | ||
222 | self.manifest.Override(manifest_name) | ||
223 | finally: | ||
224 | f.close() | ||
225 | except IOError: | ||
226 | print >>sys.stderr, 'error: cannot write manifest to %s' % \ | ||
227 | manifest_path | ||
228 | sys.exit(1) | ||
229 | else: | ||
230 | print >>sys.stderr, 'error: %s' % manifest_str | ||
231 | sys.exit(1) | ||
232 | except socket.error: | ||
233 | print >>sys.stderr, 'error: cannot connect to manifest server %s' % ( | ||
234 | self.manifest.manifest_server) | ||
235 | sys.exit(1) | ||
236 | |||
185 | rp = self.manifest.repoProject | 237 | rp = self.manifest.repoProject |
186 | rp.PreSync() | 238 | rp.PreSync() |
187 | 239 | ||