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: @@ -33,6 +34,9 @@ following DTD: + + + @@ -89,6 +93,27 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or revision attribute will use this revision. +Element manifest-server +----------------------- + +At most one manifest-server may be specified. The url attribute +is used to specify the URL of a manifest server, which is an +XML RPC service that will return a manifest in which each project +is pegged to a known good revision for the current branch and +target. + +The manifest server should implement: + + GetApprovedManifest(branch, target) + +The target to use is defined by environment variables TARGET_PRODUCT +and TARGET_BUILD_VARIANT. These variables are used to create a string +of the form $TARGET_PRODUCT-$TARGET_BUILD_VARIANT, e.g. passion-userdebug. +If one of those variables or both are not present, the program will call +GetApprovedManifest without the target paramater and the manifest server +should choose a reasonable default target. + + Element project --------------- 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): self._Unload() - def Link(self, name): - """Update the repo metadata to use a different manifest. + def Override(self, name): + """Use a different manifest, just for the current instantiation. """ path = os.path.join(self.manifestProject.worktree, name) if not os.path.isfile(path): @@ -80,6 +80,11 @@ class XmlManifest(object): finally: self.manifestFile = old + def Link(self, name): + """Update the repo metadata to use a different manifest. + """ + self.Override(name) + try: if os.path.exists(self.manifestFile): os.remove(self.manifestFile) @@ -123,6 +128,12 @@ class XmlManifest(object): root.appendChild(e) root.appendChild(doc.createTextNode('')) + if self._manifest_server: + e = doc.createElement('manifest-server') + e.setAttribute('url', self._manifest_server) + root.appendChild(e) + root.appendChild(doc.createTextNode('')) + sort_projects = list(self.projects.keys()) sort_projects.sort() @@ -168,6 +179,11 @@ class XmlManifest(object): self._Load() return self._default + @property + def manifest_server(self): + self._Load() + return self._manifest_server + @property def IsMirror(self): return self.manifestProject.config.GetBoolean('repo.mirror') @@ -178,6 +194,7 @@ class XmlManifest(object): self._remotes = {} self._default = None self.branch = None + self._manifest_server = None def _Load(self): if not self._loaded: @@ -246,6 +263,15 @@ class XmlManifest(object): if self._default is None: self._default = _Default() + for node in config.childNodes: + if node.nodeName == 'manifest-server': + url = self._reqatt(node, 'url') + if self._manifest_server is not None: + raise ManifestParseError, \ + 'duplicate manifest-server in %s' % \ + (self.manifestFile) + self._manifest_server = url + for node in config.childNodes: if node.nodeName == 'project': project = self._ParseProject(node) @@ -315,7 +341,7 @@ class XmlManifest(object): def _ParseProject(self, node): """ reads a element from the manifest file - """ + """ name = self._reqatt(node, 'name') 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 import os import re import shutil +import socket import subprocess import sys import time +import xmlrpclib from git_command import GIT from project import HEAD @@ -57,6 +59,10 @@ back to the manifest revision. This option is especially helpful if the project is currently on a topic branch, but the manifest revision is temporarily needed. +The -s/--smart-sync option can be used to sync to a known good +build as specified by the manifest-server element in the current +manifest. + SSH Connections --------------- @@ -97,6 +103,9 @@ later is required to fix a server side protocol bug. p.add_option('-d','--detach', dest='detach_head', action='store_true', help='detach projects back to manifest revision') + p.add_option('-s', '--smart-sync', + dest='smart_sync', action='store_true', + help='smart sync using manifest from a known good build') g = p.add_option_group('repo Version options') g.add_option('--no-repo-verify', @@ -182,6 +191,49 @@ uncommitted changes are present' % project.relpath print >>sys.stderr, 'error: cannot combine -n and -l' sys.exit(1) + if opt.smart_sync: + if not self.manifest.manifest_server: + print >>sys.stderr, \ + 'error: cannot smart sync: no manifest server defined in manifest' + sys.exit(1) + try: + server = xmlrpclib.Server(self.manifest.manifest_server) + p = self.manifest.manifestProject + b = p.GetBranch(p.CurrentBranch) + branch = b.merge + + env = dict(os.environ) + if (env.has_key('TARGET_PRODUCT') and + env.has_key('TARGET_BUILD_VARIANT')): + target = '%s-%s' % (env['TARGET_PRODUCT'], + env['TARGET_BUILD_VARIANT']) + [success, manifest_str] = server.GetApprovedManifest(branch, target) + else: + [success, manifest_str] = server.GetApprovedManifest(branch) + + if success: + manifest_name = "smart_sync_override.xml" + manifest_path = os.path.join(self.manifest.manifestProject.worktree, + manifest_name) + try: + f = open(manifest_path, 'w') + try: + f.write(manifest_str) + self.manifest.Override(manifest_name) + finally: + f.close() + except IOError: + print >>sys.stderr, 'error: cannot write manifest to %s' % \ + manifest_path + sys.exit(1) + else: + print >>sys.stderr, 'error: %s' % manifest_str + sys.exit(1) + except socket.error: + print >>sys.stderr, 'error: cannot connect to manifest server %s' % ( + self.manifest.manifest_server) + sys.exit(1) + rp = self.manifest.repoProject rp.PreSync()