Index: scripts/slave/bot_update.py |
diff --git a/scripts/slave/bot_update.py b/scripts/slave/bot_update.py |
index 53c281a7157c423491f755181f87132a07c02279..5049b6cbc538991142863f34805e885a09bbba38 100644 |
--- a/scripts/slave/bot_update.py |
+++ b/scripts/slave/bot_update.py |
@@ -5,14 +5,18 @@ |
import codecs |
import copy |
+import cStringIO |
+import json |
import optparse |
import os |
import pprint |
+import re |
import shutil |
import socket |
import subprocess |
import sys |
import time |
+import urllib2 |
import urlparse |
import os.path as path |
@@ -116,6 +120,15 @@ CACHE_DIR = path.join(SLAVE_DIR, 'cache_dir') |
if sys.platform.startswith('win'): |
CACHE_DIR = CACHE_DIR.replace('\\', '\\\\') |
+# Find the patch tool. |
+ROOT_BUILD_DIR = path.dirname(SLAVE_DIR) |
+ROOT_B_DIR = path.dirname(ROOT_BUILD_DIR) |
+BUILD_INTERNAL_DIR = path.join(ROOT_B_DIR, 'build_internal') |
+if sys.platform.startswith('win'): |
+ PATCH_TOOL = path.join(BUILD_INTERNAL_DIR, 'tools', 'patch.EXE') |
+else: |
+ PATCH_TOOL = '/usr/bin/patch' |
+ |
class SubprocessFailed(Exception): |
def __init__(self, message, code): |
@@ -127,11 +140,18 @@ def call(*args, **kwargs): |
"""Interactive subprocess call.""" |
kwargs['stdout'] = subprocess.PIPE |
kwargs['stderr'] = subprocess.STDOUT |
+ stdin_data = kwargs.pop('stdin_data', None) |
+ if stdin_data: |
+ kwargs['stdin'] = subprocess.PIPE |
+ out = cStringIO.StringIO() |
for attempt in xrange(RETRIES): |
attempt_msg = ' (retry #%d)' % attempt if attempt else '' |
print '===Running %s%s===' % (' '.join(args), attempt_msg) |
start_time = time.time() |
proc = subprocess.Popen(args, **kwargs) |
+ if stdin_data: |
+ proc.stdin.write(stdin_data) |
+ proc.stdin.close() |
# This is here because passing 'sys.stdout' into stdout for proc will |
# produce out of order output. |
while True: |
@@ -139,12 +159,13 @@ def call(*args, **kwargs): |
if not buf: |
break |
sys.stdout.write(buf) |
+ out.write(buf) |
code = proc.wait() |
elapsed_time = ((time.time() - start_time) / 60.0) |
if not code: |
print '===Succeeded in %.1f mins===' % elapsed_time |
- return 0 |
+ return out.getvalue() |
print '===Failed in %.1f mins===' % elapsed_time |
@@ -160,7 +181,7 @@ def git(*args, **kwargs): |
if sys.platform.startswith('win'): |
git_executable += '.bat' |
cmd = (git_executable,) + args |
- call(*cmd, **kwargs) |
+ return call(*cmd, **kwargs) |
def get_gclient_spec(solutions): |
@@ -284,10 +305,79 @@ def gclient_sync(): |
'--nohooks', '--noprehooks') |
+def create_less_than_or_equal_regex(number): |
agable
2014/02/20 00:13:06
Nooooooooooooo.
At the very least can we update t
Ryan Tseng
2014/02/20 01:04:37
Imagine if you set chrome's src repo to some pinne
|
+ """ Return a regular expression to test whether an integer less than or equal |
+ to 'number' is present in a given string. |
+ """ |
+ |
+ # In three parts, build a regular expression that match any numbers smaller |
+ # than 'number'. |
+ # For example, 78656 would give a regular expression that looks like: |
+ # Part 1 |
+ # (78356| # 78356 |
+ # Part 2 |
+ # 7835[0-5]| # 78350-78355 |
+ # 783[0-4][0-9]| # 78300-78349 |
+ # 78[0-2][0-9][0-9]| # 78000-78299 |
+ # 7[0-7][0-9][0-9][0-9]| # 70000-77999 |
+ # [0-6][0-9][0-9][0-9][0-9]| # 10000-69999 |
+ # Part 3 |
+ # [0-9][0-9][0-9][0-9]| # 1000-9999 |
+ # [0-9][0-9][0-9]| # 100-999 |
+ # [0-9][0-9]| # 10-99 |
+ # [0-9]) # 0-9 |
+ |
+ # Part 1: Create an array with all the regexes, as described above. |
+ # Prepopulate it with the number itself. |
+ number = str(number) |
+ expressions = [number] |
+ |
+ # Convert the number to a list, so we can translate digits in it to |
+ # expressions. |
+ num_list = list(number) |
+ num_len = len(num_list) |
+ |
+ # Part 2: Go through all the digits in the number, starting from the end. |
+ # Each iteration appends a line to 'expressions'. |
+ for index in range (num_len - 1, -1, -1): |
+ # Convert this digit back to an integer. |
+ digit = int(num_list[index]) |
+ |
+ # Part 2.1: No processing if this digit is a zero. |
+ if digit == 0: |
+ continue |
+ |
+ # Part 2.2: We switch the current digit X by a range "[0-(X-1)]". |
+ num_list[index] = '[0-%d]' % (digit - 1) |
+ |
+ # Part 2.3: We set all following digits to be "[0-9]". |
+ # Since we just decrementented a digit in a most important position, all |
+ # following digits don't matter. The possible numbers will always be smaller |
+ # than before we decremented. |
+ for next_digit in range(index + 1, num_len): |
+ num_list[next_digit] = '[0-9]' |
+ |
+ # Part 2.4: Add this new sub-expression to the list. |
+ expressions.append(''.join(num_list)) |
+ |
+ # Part 3: We add all the full ranges to match all numbers that are at least |
+ # one order of magnitude smaller than the original numbers. |
+ for index in range(1, num_len): |
+ expressions.append('[0-9]'*index) |
+ |
+ # All done. We now have our final regular expression. |
+ regex = '(%s)' % ('|'.join(expressions)) |
+ return regex |
+ |
+ |
def get_git_hash(revision, dir_name): |
- match = "^git-svn-id: [^ ]*@%d" % revision |
- cmd = ['git', 'log', '--grep', match, '--format=%H', dir_name] |
- return subprocess.check_output(cmd).strip() or None |
+ match = "^git-svn-id: [^ ]*@%s " % create_less_than_or_equal_regex(revision) |
+ cmd = ['git', 'log', '-E', '--grep', match, '--format=%H', '--max-count=1'] |
+ results = call(*cmd, cwd=dir_name).strip().splitlines() |
+ if results: |
+ return results[0] |
+ raise Exception('We can\'t resolve svn revision %s into a git hash' % |
+ revision) |
def deps2git(sln_dirs): |
@@ -303,6 +393,9 @@ def deps2git(sln_dirs): |
'--deps=%s' % deps_file, '--out=%s' % deps_git_file) |
+def emit_got_revision(revision): |
+ print '@@@SET_BUILD_PROPERTY@got_revision@%s@@@' % revision |
+ |
def git_checkout(solutions, revision): |
build_dir = os.getcwd() |
# Revision only applies to the first solution. |
@@ -331,21 +424,56 @@ def git_checkout(solutions, revision): |
git('pull', 'origin', 'master', cwd=sln_dir) |
# TODO(hinoka): We probably have to make use of revision mapping. |
if first_solution and revision and revision.lower() != 'head': |
+ emit_got_revision(revision) |
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(revision, name) |
+ git_ref = get_git_hash(int(revision), name) |
else: |
# rev_num is actually a git hash or ref, we can just use it. |
git_ref = revision |
git('checkout', git_ref, cwd=sln_dir) |
else: |
git('checkout', 'origin/master', cwd=sln_dir) |
+ if first_solution: |
+ git_ref = git('log', '--format=%H', '--max-count=1', |
+ cwd=sln_dir).strip() |
first_solution = False |
+ return git_ref |
-def apply_issue(issue, patchset, root, server): |
- pass |
+def _download(url): |
+ """Fetch url and return content, with retries for flake.""" |
+ for attempt in xrange(RETRIES): |
+ try: |
+ return urllib2.urlopen(url).read() |
+ except Exception: |
+ if attempt == RETRIES - 1: |
+ raise |
+ pass |
+ |
+ |
+def apply_issue_svn(root, patch_url): |
+ patch_data = call('svn', 'cat', patch_url) |
+ call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', |
+ stdin_data=patch_data, cwd=root) |
+ |
+ |
+def apply_issue_rietveld(issue, patchset, root, server, rev_map, revision): |
+ apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') |
agable
2014/02/20 00:13:06
It's really truly necessary to be using apply_issu
Ryan Tseng
2014/02/20 01:04:37
apply_issue has a crapton of logic to deal with ev
|
+ else 'apply_issue') |
+ rev_map = json.loads(rev_map) |
+ if root in rev_map and rev_map[root] == 'got_revision': |
+ rev_map[root] = revision |
+ call(apply_issue_bin, |
+ '--root_dir', root, |
+ '--issue', issue, |
+ '--patchset', patchset, |
+ '--no-auth', |
+ '--server', server, |
+ '--revision-mapping', json.dumps(rev_map), |
+ '--base_ref', revision, |
+ '--force') |
def check_flag(flag_file): |
@@ -374,14 +502,15 @@ def parse_args(): |
help='Patchset from issue to patch from, if applicable.') |
parse.add_option('--patch_url', help='Optional URL to SVN patch.') |
parse.add_option('--root', help='Repository root.') |
- parse.add_option('--rietveld_server', help='Rietveld server.') |
+ parse.add_option('--rietveld_server', |
+ default='codereview.chromium.org', |
+ help='Rietveld server.') |
parse.add_option('--specs', help='Gcilent spec.') |
parse.add_option('--master', help='Master name.') |
parse.add_option('-f', '--force', action='store_true', |
help='Bypass check to see if we want to be run. ' |
'Should ONLY be used locally.') |
- # TODO(hinoka): We don't actually use this yet, we should factor this in. |
- parse.add_option('--revision-mapping') |
+ parse.add_option('--revision_mapping') |
parse.add_option('--revision') |
parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], |
help='Hostname of the current machine, ' |
@@ -437,11 +566,15 @@ def main(): |
# Calling git directory because there is no way to run Gclient without |
# invoking DEPS. |
print 'Fetching Git checkout' |
- git_checkout(git_solutions, options.revision) |
- |
- # TODO(hinoka): This must be implemented before we can turn this on for TS. |
- # if options.issue: |
- # apply_issue(options.issue, options.patchset, options.root, options.server) |
+ got_revision = git_checkout(git_solutions, options.revision) |
+ |
+ options.root = options.root or dir_names[0] |
+ if options.patch_url: |
+ apply_issue_svn(options.root, options.patch_url) |
+ elif options.issue: |
+ apply_issue_rietveld(options.issue, options.patchset, options.root, |
+ options.rietveld_server, options.revision_mapping, |
+ got_revision) |
# Magic to get deps2git to work with internal DEPS. |
shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) |