mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
Add a 'smart sync' option to repo sync
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
This commit is contained in:
parent
6d7508b3d5
commit
a1bfd2cd72
@ -22,6 +22,7 @@ following DTD:
|
|||||||
<!DOCTYPE manifest [
|
<!DOCTYPE manifest [
|
||||||
<!ELEMENT manifest (remote*,
|
<!ELEMENT manifest (remote*,
|
||||||
default?,
|
default?,
|
||||||
|
manifest-server?,
|
||||||
remove-project*,
|
remove-project*,
|
||||||
project*)>
|
project*)>
|
||||||
|
|
||||||
@ -34,6 +35,9 @@ following DTD:
|
|||||||
<!ATTLIST default remote IDREF #IMPLIED>
|
<!ATTLIST default remote IDREF #IMPLIED>
|
||||||
<!ATTLIST default revision CDATA #IMPLIED>
|
<!ATTLIST default revision CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT manifest-server (EMPTY)>
|
||||||
|
<!ATTLIST url CDATA #REQUIRED>
|
||||||
|
|
||||||
<!ELEMENT project (EMPTY)>
|
<!ELEMENT project (EMPTY)>
|
||||||
<!ATTLIST project name CDATA #REQUIRED>
|
<!ATTLIST project name CDATA #REQUIRED>
|
||||||
<!ATTLIST project path CDATA #IMPLIED>
|
<!ATTLIST project path CDATA #IMPLIED>
|
||||||
@ -89,6 +93,27 @@ Attribute `revision`: Name of a Git branch (e.g. `master` or
|
|||||||
revision attribute will use this revision.
|
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
|
Element project
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ class XmlManifest(object):
|
|||||||
|
|
||||||
self._Unload()
|
self._Unload()
|
||||||
|
|
||||||
def Link(self, name):
|
def Override(self, name):
|
||||||
"""Update the repo metadata to use a different manifest.
|
"""Use a different manifest, just for the current instantiation.
|
||||||
"""
|
"""
|
||||||
path = os.path.join(self.manifestProject.worktree, name)
|
path = os.path.join(self.manifestProject.worktree, name)
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
@ -80,6 +80,11 @@ class XmlManifest(object):
|
|||||||
finally:
|
finally:
|
||||||
self.manifestFile = old
|
self.manifestFile = old
|
||||||
|
|
||||||
|
def Link(self, name):
|
||||||
|
"""Update the repo metadata to use a different manifest.
|
||||||
|
"""
|
||||||
|
self.Override(name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.manifestFile):
|
if os.path.exists(self.manifestFile):
|
||||||
os.remove(self.manifestFile)
|
os.remove(self.manifestFile)
|
||||||
@ -123,6 +128,12 @@ class XmlManifest(object):
|
|||||||
root.appendChild(e)
|
root.appendChild(e)
|
||||||
root.appendChild(doc.createTextNode(''))
|
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 = list(self.projects.keys())
|
||||||
sort_projects.sort()
|
sort_projects.sort()
|
||||||
|
|
||||||
@ -168,6 +179,11 @@ class XmlManifest(object):
|
|||||||
self._Load()
|
self._Load()
|
||||||
return self._default
|
return self._default
|
||||||
|
|
||||||
|
@property
|
||||||
|
def manifest_server(self):
|
||||||
|
self._Load()
|
||||||
|
return self._manifest_server
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def IsMirror(self):
|
def IsMirror(self):
|
||||||
return self.manifestProject.config.GetBoolean('repo.mirror')
|
return self.manifestProject.config.GetBoolean('repo.mirror')
|
||||||
@ -178,6 +194,7 @@ class XmlManifest(object):
|
|||||||
self._remotes = {}
|
self._remotes = {}
|
||||||
self._default = None
|
self._default = None
|
||||||
self.branch = None
|
self.branch = None
|
||||||
|
self._manifest_server = None
|
||||||
|
|
||||||
def _Load(self):
|
def _Load(self):
|
||||||
if not self._loaded:
|
if not self._loaded:
|
||||||
@ -246,6 +263,15 @@ class XmlManifest(object):
|
|||||||
if self._default is None:
|
if self._default is None:
|
||||||
self._default = _Default()
|
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:
|
for node in config.childNodes:
|
||||||
if node.nodeName == 'project':
|
if node.nodeName == 'project':
|
||||||
project = self._ParseProject(node)
|
project = self._ParseProject(node)
|
||||||
|
@ -17,9 +17,11 @@ from optparse import SUPPRESS_HELP
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
from git_command import GIT
|
from git_command import GIT
|
||||||
from project import HEAD
|
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
|
if the project is currently on a topic branch, but the manifest
|
||||||
revision is temporarily needed.
|
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
|
SSH Connections
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -97,6 +103,9 @@ later is required to fix a server side protocol bug.
|
|||||||
p.add_option('-d','--detach',
|
p.add_option('-d','--detach',
|
||||||
dest='detach_head', action='store_true',
|
dest='detach_head', action='store_true',
|
||||||
help='detach projects back to manifest revision')
|
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 = p.add_option_group('repo Version options')
|
||||||
g.add_option('--no-repo-verify',
|
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'
|
print >>sys.stderr, 'error: cannot combine -n and -l'
|
||||||
sys.exit(1)
|
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 = self.manifest.repoProject
|
||||||
rp.PreSync()
|
rp.PreSync()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user