Commit 082267a6 authored by agable's avatar agable Committed by Commit bot

Delete lots of svn logic from bot_update

R=hinoka@chromium.org
BUG=472386

Review-Url: https://codereview.chromium.org/2280213002
parent 8da412cf
......@@ -86,13 +86,6 @@ BUILD_INTERNAL_DIR = check_dir(
CHROMIUM_GIT_HOST = 'https://chromium.googlesource.com'
CHROMIUM_SRC_URL = CHROMIUM_GIT_HOST + '/chromium/src.git'
# Official builds use buildspecs, so this is a special case.
BUILDSPEC_TYPE = collections.namedtuple('buildspec',
('container', 'version'))
BUILDSPEC_RE = (r'^/chrome-internal/trunk/tools/buildspec/'
'(build|branches|releases)/(.+)$')
GIT_BUILDSPEC_PATH = ('https://chrome-internal.googlesource.com/chrome/tools/'
'buildspec')
BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*'
BUILDSPEC_COMMIT_RE = (
......@@ -113,49 +106,10 @@ COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}')
# Regular expression to parse gclient's revinfo entries.
REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$')
# Used by 'ResolveSvnRevisionFromGitiles'
GIT_SVN_PROJECT_MAP = {
'webkit': {
'svn_url': 'svn://svn.chromium.org/blink',
'branch_map': [
(r'trunk', r'refs/heads/master'),
(r'branches/([^/]+)', r'refs/branch-heads/\1'),
],
},
'v8': {
'svn_url': 'https://v8.googlecode.com/svn',
'branch_map': [
(r'trunk', r'refs/heads/candidates'),
(r'branches/bleeding_edge', r'refs/heads/master'),
(r'branches/([^/]+)', r'refs/branch-heads/\1'),
],
},
'nacl': {
'svn_url': 'svn://svn.chromium.org/native_client',
'branch_map': [
(r'trunk/src/native_client', r'refs/heads/master'),
],
},
}
# Key for the 'git-svn' ID metadata commit footer entry.
GIT_SVN_ID_FOOTER_KEY = 'git-svn-id'
# e.g., git-svn-id: https://v8.googlecode.com/svn/trunk@23117
# ce2b1a6d-e550-0410-aec6-3dcde31c8c00
GIT_SVN_ID_RE = re.compile(r'((?:\w+)://[^@]+)@(\d+)\s+(?:[a-zA-Z0-9\-]+)')
# This is the git mirror of the buildspecs repository. We could rely on the svn
# checkout, now that the git buildspecs are checked in alongside the svn
# buildspecs, but we're going to want to pull all the buildspecs from here
# eventually anyhow, and there's already some logic to pull from git (for the
# old git_buildspecs.git repo), so just stick with that.
GIT_BUILDSPEC_REPO = (
'https://chrome-internal.googlesource.com/chrome/tools/buildspec')
# Copied from scripts/recipes/chromium.py.
GOT_REVISION_MAPPINGS = {
'/chrome/trunk/src': {
CHROMIUM_SRC_URL: {
'src/': 'got_revision',
'src/native_client/': 'got_nacl_revision',
'src/tools/swarm_client/': 'got_swarm_client_revision',
......@@ -183,18 +137,6 @@ step has two main advantages over them:
* it is a slave-side script, so its behavior can be modified without
restarting the master.
Why Git, you ask? Because that is the direction that the Chromium project is
heading. This step is an integral part of the transition from using the SVN repo
at chrome/trunk/src to using the Git repo src.git. Please pardon the dust while
we fully convert everything to Git. This message will get out of your way
eventually, and the waterfall will be a happier place because of it.
This step can be activated or deactivated independently on every builder on
every master. When it is active, the "gclient revert" and "update" steps become
no-ops. When it is inactive, it prints this message, cleans up after itself, and
lets everything else continue as though nothing has changed. Eventually, when
everything is stable enough, this step will replace them entirely.
Debugging information:
(master/builder/slave may be unspecified on recipes)
master: %(master)s
......@@ -244,16 +186,6 @@ if BUILD_INTERNAL_DIR:
print 'If this is an internal bot, this step may be erroneously inactive.'
internal_data = local_vars
RECOGNIZED_PATHS = {
# If SVN path matches key, the entire URL is rewritten to the Git url.
'/chrome/trunk/src':
CHROMIUM_SRC_URL,
'/chrome/trunk/src/tools/cros.DEPS':
CHROMIUM_GIT_HOST + '/chromium/src/tools/cros.DEPS.git',
'/chrome-internal/trunk/src-internal':
'https://chrome-internal.googlesource.com/chrome/src-internal.git',
}
RECOGNIZED_PATHS.update(internal_data.get('RECOGNIZED_PATHS', {}))
ENABLED_MASTERS = [
'bot_update.always_on',
......@@ -337,17 +269,6 @@ DISABLED_BUILDERS.update(internal_data.get('DISABLED_BUILDERS', {}))
DISABLED_SLAVES = {}
DISABLED_SLAVES.update(internal_data.get('DISABLED_SLAVES', {}))
# These masters work only in Git, meaning for got_revision, always output
# a git hash rather than a SVN rev.
GIT_MASTERS = [
'client.v8',
'client.v8.branches',
'client.v8.ports',
'tryserver.v8',
]
GIT_MASTERS += internal_data.get('GIT_MASTERS', [])
# How many times to try before giving up.
ATTEMPTS = 5
......@@ -384,19 +305,10 @@ class GclientSyncFailed(SubprocessFailed):
pass
class SVNRevisionNotFound(Exception):
pass
class InvalidDiff(Exception):
pass
class Inactive(Exception):
"""Not really an exception, just used to exit early cleanly."""
pass
RETRY = object()
OK = object()
FAIL = object()
......@@ -553,20 +465,6 @@ def check_valid_host(master, builder, slave):
and not check_disabled(master, builder, slave))
def maybe_ignore_revision(revision, buildspec):
"""Handle builders that don't care what buildbot tells them to build.
This is especially the case with branch builders that build from buildspecs
and/or trigger off multiple repositories, where the --revision passed in has
nothing to do with the solution being built. Clearing the revision in this
case causes bot_update to use HEAD rather that trying to checkout an
inappropriate version of the solution.
"""
if buildspec and buildspec.container == 'branches':
return []
return revision
def solutions_printer(solutions):
"""Prints gclient solution to stdout."""
print 'Gclient Solutions'
......@@ -601,52 +499,18 @@ def solutions_printer(solutions):
print
def solutions_to_git(input_solutions):
def modify_solutions(input_solutions):
"""Modifies urls in solutions to point at Git repos.
returns: (git solution, svn root of first solution) tuple.
returns: new solution dictionary
"""
assert input_solutions
solutions = copy.deepcopy(input_solutions)
first_solution = True
buildspec = None
for solution in solutions:
original_url = solution['url']
parsed_url = urlparse.urlparse(original_url)
parsed_path = parsed_url.path
# Rewrite SVN urls into Git urls.
buildspec_m = re.match(BUILDSPEC_RE, parsed_path)
if first_solution and buildspec_m:
solution['url'] = GIT_BUILDSPEC_PATH
buildspec = BUILDSPEC_TYPE(
container=buildspec_m.group(1),
version=buildspec_m.group(2),
)
solution['deps_file'] = path.join(buildspec.container, buildspec.version,
'DEPS')
elif parsed_path in RECOGNIZED_PATHS:
solution['url'] = RECOGNIZED_PATHS[parsed_path]
solution['deps_file'] = '.DEPS.git'
elif parsed_url.scheme == 'https' and 'googlesource' in parsed_url.netloc:
pass
else:
print 'Warning: %s' % ('path %r not recognized' % parsed_path,)
# Strip out deps containing $$V8_REV$$, etc.
if 'custom_deps' in solution:
new_custom_deps = {}
for deps_name, deps_value in solution['custom_deps'].iteritems():
if deps_value and '$$' in deps_value:
print 'Dropping %s:%s from custom deps' % (deps_name, deps_value)
else:
new_custom_deps[deps_name] = deps_value
solution['custom_deps'] = new_custom_deps
if first_solution:
root = parsed_path
first_solution = False
solution['managed'] = False
# We don't want gclient to be using a safesync URL. Instead it should
# using the lkgr/lkcr branch/tags.
......@@ -654,7 +518,8 @@ def solutions_to_git(input_solutions):
print 'Removing safesync url %s from %s' % (solution['safesync_url'],
parsed_path)
del solution['safesync_url']
return solutions, root, buildspec
return solutions
def remove(target):
......@@ -665,28 +530,15 @@ def remove(target):
os.rename(target, path.join(dead_folder, uuid.uuid4().hex))
def ensure_no_checkout(dir_names, scm_dirname):
"""Ensure that there is no undesired checkout under build/.
If there is an incorrect checkout under build/, then
move build/ to build.dead/
This function will check each directory in dir_names.
scm_dirname is expected to be either ['.svn', '.git']
"""
assert scm_dirname in ['.svn', '.git', '*']
has_checkout = any(path.exists(path.join(os.getcwd(), dir_name, scm_dirname))
def ensure_no_checkout(dir_names):
"""Ensure that there is no undesired checkout under build/."""
build_dir = os.getcwd()
has_checkout = any(path.exists(path.join(build_dir, dir_name, '.git'))
for dir_name in dir_names)
if has_checkout or scm_dirname == '*':
build_dir = os.getcwd()
prefix = ''
if scm_dirname != '*':
prefix = '%s detected in checkout, ' % scm_dirname
if has_checkout:
for filename in os.listdir(build_dir):
deletion_target = path.join(build_dir, filename)
print '%sdeleting %s...' % (prefix, deletion_target),
print '.git detected in checkout, deleting %s...' % deletion_target,
remove(deletion_target)
print 'done'
......@@ -780,32 +632,6 @@ def get_commit_message_footer(message, key):
return get_commit_message_footer_map(message).get(key)
def get_svn_rev(git_hash, dir_name):
log = git('log', '-1', git_hash, cwd=dir_name)
git_svn_id = get_commit_message_footer(log, GIT_SVN_ID_FOOTER_KEY)
if not git_svn_id:
return None
m = GIT_SVN_ID_RE.match(git_svn_id)
if not m:
return None
return int(m.group(2))
def get_git_hash(revision, branch, sln_dir):
"""We want to search for the SVN revision on the git-svn branch.
Note that git will search backwards from origin/master.
"""
match = "^%s: [^ ]*@%s " % (GIT_SVN_ID_FOOTER_KEY, revision)
ref = branch if branch.startswith('refs/') else 'origin/%s' % branch
cmd = ['log', '-E', '--grep', match, '--format=%H', '--max-count=1', ref]
result = git(*cmd, cwd=sln_dir).strip()
if result:
return result
raise SVNRevisionNotFound('We can\'t resolve svn r%s into a git hash in %s' %
(revision, sln_dir))
def emit_log_lines(name, lines):
for line in lines.splitlines():
print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line)
......@@ -860,17 +686,12 @@ def force_revision(folder_name, revision):
branch, revision = split_revision
if revision and revision.upper() != 'HEAD':
if revision and revision.isdigit() and len(revision) < 40:
# rev_num is really a svn revision number, convert it into a git hash.
git_ref = get_git_hash(int(revision), branch, folder_name)
else:
# rev_num is actually a git hash or ref, we can just use it.
git_ref = revision
git('checkout', '--force', git_ref, cwd=folder_name)
git('checkout', '--force', revision, cwd=folder_name)
else:
ref = branch if branch.startswith('refs/') else 'origin/%s' % branch
git('checkout', '--force', ref, cwd=folder_name)
def git_checkout(solutions, revisions, shallow, refs, git_cache_dir):
build_dir = os.getcwd()
# Before we do anything, break all git_cache locks.
......@@ -931,16 +752,6 @@ def git_checkout(solutions, revisions, shallow, refs, git_cache_dir):
else:
raise
remove(sln_dir)
except SVNRevisionNotFound:
tries_left -= 1
if tries_left > 0:
# If we don't have the correct revision, wait and try again.
print 'We can\'t find revision %s.' % revision
print 'The svn to git replicator is probably falling behind.'
print 'waiting 5 seconds and trying again...'
time.sleep(5)
else:
raise
git('clean', '-dff', cwd=sln_dir)
......@@ -961,51 +772,6 @@ def _download(url):
raise
def parse_diff(diff):
"""Takes a unified diff and returns a list of diffed files and their diffs.
The return format is a list of pairs of:
(<filename>, <diff contents>)
<diff contents> is inclusive of the diff line.
"""
result = []
current_diff = ''
current_header = None
for line in diff.splitlines():
# "diff" is for git style patches, and "Index: " is for SVN style patches.
if line.startswith('diff') or line.startswith('Index: '):
if current_header:
# If we are in a diff portion, then save the diff.
result.append((current_header, '%s\n' % current_diff))
git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line)
svn_header_match = re.match(r'Index: (.*)', line)
if git_header_match:
# First, see if its a git style header.
from_file = git_header_match.group(1)
to_file = git_header_match.group(2)
if from_file != to_file and from_file.startswith('a/'):
# Sometimes git prepends 'a/' and 'b/' in front of file paths.
from_file = from_file[2:]
current_header = from_file
elif svn_header_match:
# Otherwise, check if its an SVN style header.
current_header = svn_header_match.group(1)
else:
# Otherwise... I'm not really sure what to do with this.
raise InvalidDiff('Can\'t process header: %s\nFull diff:\n%s' %
(line, diff))
current_diff = ''
current_diff += '%s\n' % line
if current_header:
# We hit EOF, gotta save the last diff.
result.append((current_header, current_diff))
return result
def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision,
email_file, key_file, whitelist=None, blacklist=None):
apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win')
......@@ -1103,52 +869,13 @@ def emit_flag(flag_file):
f.write('Success!')
def get_commit_position_for_git_svn(url, revision):
"""Generates a commit position string for a 'git-svn' URL/revision.
If the 'git-svn' URL maps to a known project, we will construct a commit
position branch value by applying substitution on the SVN URL.
"""
# Identify the base URL so we can strip off trunk/branch name
project_config = branch = None
for _, project_config in GIT_SVN_PROJECT_MAP.iteritems():
if url.startswith(project_config['svn_url']):
branch = url[len(project_config['svn_url']):]
break
if branch:
# Strip any leading slashes
branch = branch.lstrip('/')
# Try and map the branch
for pattern, repl in project_config.get('branch_map', ()):
nbranch, subn = re.subn(pattern, repl, branch, count=1)
if subn:
print 'INFO: Mapped SVN branch to Git branch [%s] => [%s]' % (
branch, nbranch)
branch = nbranch
break
else:
# Use generic 'svn' branch
print 'INFO: Could not resolve project for SVN URL %r' % (url,)
branch = 'svn'
return '%s@{#%s}' % (branch, revision)
def get_commit_position(git_path, revision='HEAD'):
"""Dumps the 'git' log for a specific revision and parses out the commit
position.
If a commit position metadata key is found, its value will be returned.
Otherwise, we will search for a 'git-svn' metadata entry. If one is found,
we will compose a commit position from it, using its SVN revision value as
the revision.
If the 'git-svn' URL maps to a known project, we will construct a commit
position branch value by truncating the URL, mapping 'trunk' to
"refs/heads/master". Otherwise, we will return the generic branch, 'svn'.
"""
# TODO(iannucci): Use git-footers for this.
git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path)
footer_map = get_commit_message_footer_map(git_log)
......@@ -1157,23 +884,11 @@ def get_commit_position(git_path, revision='HEAD'):
footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY))
if value:
return value
# Compose a commit position from 'git-svn' metadata
value = footer_map.get(GIT_SVN_ID_FOOTER_KEY)
if value:
m = GIT_SVN_ID_RE.match(value)
if not m:
raise ValueError("Invalid 'git-svn' value: [%s]" % (value,))
return get_commit_position_for_git_svn(m.group(1), m.group(2))
return None
def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs):
"""Translate git gclient revision mapping to build properties.
If use_svn_revs is True, then translate git hashes in the revision mapping
to svn revision numbers.
"""
def parse_got_revision(gclient_output, got_revision_mapping):
"""Translate git gclient revision mapping to build properties."""
properties = {}
solutions_output = {
# Make sure path always ends with a single slash.
......@@ -1192,13 +907,7 @@ def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs):
else:
# Since we are using .DEPS.git, everything had better be git.
assert solution_output.get('scm') == 'git'
git_revision = git('rev-parse', 'HEAD', cwd=dir_name).strip()
if use_svn_revs:
revision = get_svn_rev(git_revision, dir_name)
if not revision:
revision = git_revision
else:
revision = git_revision
revision = git('rev-parse', 'HEAD', cwd=dir_name).strip()
commit_position = get_commit_position(dir_name)
properties[property_name] = revision
......@@ -1230,7 +939,6 @@ def ensure_deps_revisions(deps_url_mapping, solutions, revisions):
revisions)
if not revision:
continue
# TODO(hinoka): Catch SVNRevisionNotFound error maybe?
git('fetch', 'origin', cwd=deps_name)
force_revision(deps_name, revision)
......@@ -1239,7 +947,7 @@ def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only,
patch_root, issue, patchset, rietveld_server,
gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref,
revision_mapping, apply_issue_email_file,
apply_issue_key_file, buildspec, gyp_env, shallow, runhooks,
apply_issue_key_file, gyp_env, shallow, runhooks,
refs, git_cache_dir, gerrit_reset):
# Get a checkout of each solution, without DEPS or hooks.
# Calling git directly because there is no way to run Gclient without
......@@ -1276,21 +984,13 @@ def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only,
# Let gclient do the DEPS syncing.
# The branch-head refspec is a special case because its possible Chrome
# src, which contains the branch-head refspecs, is DEPSed in.
gclient_output = gclient_sync(buildspec or BRANCH_HEADS_REFSPEC in refs,
shallow)
gclient_output = gclient_sync(BRANCH_HEADS_REFSPEC in refs, shallow)
# Now that gclient_sync has finished, we should revert any .DEPS.git so that
# presubmit doesn't complain about it being modified.
if (not buildspec and
git('ls-files', '.DEPS.git', cwd=first_sln).strip()):
if git('ls-files', '.DEPS.git', cwd=first_sln).strip():
git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln)
if buildspec and runhooks:
# Run gclient runhooks if we're on an official builder.
# TODO(hinoka): Remove this when the official builders run their own
# runhooks step.
gclient_runhooks(gyp_env)
# Finally, ensure that all DEPS are pinned to the correct revision.
dir_names = [sln['name'] for sln in solutions]
ensure_deps_revisions(gclient_output.get('solutions', {}),
......@@ -1336,15 +1036,9 @@ def parse_revisions(revisions, root):
# This is an alt_root@revision argument.
current_root, current_rev = split_revision
# We want to normalize svn/git urls into .git urls.
parsed_root = urlparse.urlparse(current_root)
if parsed_root.scheme == 'svn':
if parsed_root.path in RECOGNIZED_PATHS:
normalized_root = RECOGNIZED_PATHS[parsed_root.path]
else:
print 'WARNING: SVN path %s not recognized, ignoring' % current_root
continue
elif parsed_root.scheme in ['http', 'https']:
if parsed_root.scheme in ['http', 'https']:
# We want to normalize git urls into .git urls.
normalized_root = 'https://%s/%s' % (parsed_root.netloc,
parsed_root.path)
if not normalized_root.endswith('.git'):
......@@ -1400,13 +1094,10 @@ def parse_args():
help=('Same as revision_mapping, except its a path to a json'
' file containing that format.'))
parse.add_option('--revision', action='append', default=[],
help='Revision to check out. Can be an SVN revision number, '
'git hash, or any form of git ref. Can prepend '
'root@<rev> to specify which repository, where root '
'is either a filesystem path, git https url, or '
'svn url. To specify Tip of Tree, set rev to HEAD.'
'To specify a git branch and an SVN rev, <rev> can be '
'set to <branch>:<revision>.')
help='Revision to check out. Can be any form of git ref. '
'Can prepend root@<rev> to specify which repository, '
'where root is either a filesystem path or git https '
'url. To specify Tip of Tree, set rev to HEAD. ')
parse.add_option('--output_manifest', action='store_true',
help=('Add manifest json to the json output.'))
parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0],
......@@ -1480,20 +1171,12 @@ def prepare(options, git_slns, active):
dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
# If we're active now, but the flag file doesn't exist (we weren't active
# last run) or vice versa, blow away all checkouts.
if bool(active) != bool(check_flag(options.flag_file)):
ensure_no_checkout(dir_names, '*')
if options.clobber or (bool(active) != bool(check_flag(options.flag_file))):
ensure_no_checkout(dir_names)
if options.output_json:
# Make sure we tell recipes that we didn't run if the script exits here.
emit_json(options.output_json, did_run=active)
if active:
if options.clobber:
ensure_no_checkout(dir_names, '*')
else:
ensure_no_checkout(dir_names, '.svn')
emit_flag(options.flag_file)
else:
delete_flag(options.flag_file)
raise Inactive # This is caught in main() and we exit cleanly.
emit_flag(options.flag_file)
# Do a shallow checkout if the disk is less than 100GB.
total_disk_space, free_disk_space = get_total_disk_space()
......@@ -1520,8 +1203,7 @@ def prepare(options, git_slns, active):
return revisions, step_text
def checkout(options, git_slns, specs, buildspec, master,
svn_root, revisions, step_text):
def checkout(options, git_slns, specs, master, revisions, step_text):
first_sln = git_slns[0]['name']
dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
try:
......@@ -1551,7 +1233,6 @@ def checkout(options, git_slns, specs, buildspec, master,
apply_issue_key_file=options.apply_issue_key_file,
# For official builders.
buildspec=buildspec,
gyp_env=options.gyp_env,
runhooks=not options.no_runhooks,
......@@ -1563,7 +1244,7 @@ def checkout(options, git_slns, specs, buildspec, master,
gclient_output = ensure_checkout(**checkout_parameters)
except GclientSyncFailed:
print 'We failed gclient sync, lets delete the checkout and retry.'
ensure_no_checkout(dir_names, '*')
ensure_no_checkout(dir_names)
gclient_output = ensure_checkout(**checkout_parameters)
except PatchFailed as e:
if options.output_json:
......@@ -1583,11 +1264,8 @@ def checkout(options, git_slns, specs, buildspec, master,
print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text
raise
# Revision is an svn revision, unless it's a git master.
use_svn_rev = master not in GIT_MASTERS
# Take care of got_revisions outputs.
revision_mapping = dict(GOT_REVISION_MAPPINGS.get(svn_root, {}))
revision_mapping = GOT_REVISION_MAPPINGS.get(git_slns[0]['url'], {})
if options.revision_mapping:
revision_mapping.update(options.revision_mapping)
......@@ -1597,8 +1275,7 @@ def checkout(options, git_slns, specs, buildspec, master,
if not revision_mapping:
revision_mapping[first_sln] = 'got_revision'
got_revisions = parse_got_revision(gclient_output, revision_mapping,
use_svn_rev)
got_revisions = parse_got_revision(gclient_output, revision_mapping)
if not got_revisions:
# TODO(hinoka): We should probably bail out here, but in the interest
......@@ -1672,21 +1349,16 @@ def main():
# Parse, munipulate, and print the gclient solutions.
specs = {}
exec(options.specs, specs)
svn_solutions = specs.get('solutions', [])
git_slns, svn_root, buildspec = solutions_to_git(svn_solutions)
options.revision = maybe_ignore_revision(options.revision, buildspec)
orig_solutions = specs.get('solutions', [])
git_slns = modify_solutions(orig_solutions)
solutions_printer(git_slns)
try:
# Dun dun dun, the main part of bot_update.
revisions, step_text = prepare(options, git_slns, active)
checkout(options, git_slns, specs, buildspec, master, svn_root, revisions,
step_text)
checkout(options, git_slns, specs, master, revisions, step_text)
except Inactive:
# Not active, should count as passing.
pass
except PatchFailed as e:
emit_flag(options.flag_file)
# Return a specific non-zero exit code for patch failure (because it is
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment