summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico Sallembien <nsallembien@google.com>2010-04-06 10:40:01 -0700
committerNico Sallembien <nsallembien@google.com>2010-04-13 10:20:37 -0700
commita1bfd2cd7253b1662e08f5ec5be3d863430c756c (patch)
tree48cf4d0a983e37b50220cecc043793d6a5f0c319
parent6d7508b3d52781a3f8170a4257c65e2de176cc68 (diff)
downloadgit-repo-a1bfd2cd7253b1662e08f5ec5be3d863430c756c.tar.gz
Add a 'smart sync' option to repo syncv1.6.9.2
This option allows the user to specify a manifest server to use when syncing. This manifest server will provide a manifest pegging each project to a known green build. This allows developers to work on a known good tree that is known to build and pass tests, preventing failed builds to hamper productivity. The manifest used is not "sticky" so as to allow subsequent 'repo sync' calls to sync to the tip of the tree. Change-Id: Id0a24ece20f5a88034ad364b416a1dd2e394226d
-rw-r--r--docs/manifest-format.txt25
-rw-r--r--manifest_xml.py32
-rw-r--r--subcmds/sync.py52
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
89revision attribute will use this revision. 93revision attribute will use this revision.
90 94
91 95
96Element manifest-server
97-----------------------
98
99At most one manifest-server may be specified. The url attribute
100is used to specify the URL of a manifest server, which is an
101XML RPC service that will return a manifest in which each project
102is pegged to a known good revision for the current branch and
103target.
104
105The manifest server should implement:
106
107 GetApprovedManifest(branch, target)
108
109The target to use is defined by environment variables TARGET_PRODUCT
110and TARGET_BUILD_VARIANT. These variables are used to create a string
111of the form $TARGET_PRODUCT-$TARGET_BUILD_VARIANT, e.g. passion-userdebug.
112If one of those variables or both are not present, the program will call
113GetApprovedManifest without the target paramater and the manifest server
114should choose a reasonable default target.
115
116
92Element project 117Element 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
17import os 17import os
18import re 18import re
19import shutil 19import shutil
20import socket
20import subprocess 21import subprocess
21import sys 22import sys
22import time 23import time
24import xmlrpclib
23 25
24from git_command import GIT 26from git_command import GIT
25from project import HEAD 27from project import HEAD
@@ -57,6 +59,10 @@ back to the manifest revision. This option is especially helpful
57if the project is currently on a topic branch, but the manifest 59if the project is currently on a topic branch, but the manifest
58revision is temporarily needed. 60revision is temporarily needed.
59 61
62The -s/--smart-sync option can be used to sync to a known good
63build as specified by the manifest-server element in the current
64manifest.
65
60SSH Connections 66SSH 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