Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: scripts/slave/bot_update.py

Issue 169173009: Revert of Apply patch for bot_update (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « scripts/master/factory/gclient_factory.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import codecs 6 import codecs
7 import copy 7 import copy
8 import cStringIO
9 import json
10 import optparse 8 import optparse
11 import os 9 import os
12 import pprint 10 import pprint
13 import shutil 11 import shutil
14 import socket 12 import socket
15 import subprocess 13 import subprocess
16 import sys 14 import sys
17 import time 15 import time
18 import urllib2
19 import urlparse 16 import urlparse
20 17
21 import os.path as path 18 import os.path as path
22 19
23 from common import chromium_utils 20 from common import chromium_utils
24 21
25 22
26 RECOGNIZED_PATHS = { 23 RECOGNIZED_PATHS = {
27 # If SVN path matches key, the entire URL is rewritten to the Git url. 24 # If SVN path matches key, the entire URL is rewritten to the Git url.
28 '/chrome/trunk/src': 25 '/chrome/trunk/src':
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 THIS_DIR = path.abspath(os.getcwd()) 109 THIS_DIR = path.abspath(os.getcwd())
113 BUILDER_DIR = path.dirname(THIS_DIR) 110 BUILDER_DIR = path.dirname(THIS_DIR)
114 SLAVE_DIR = path.dirname(BUILDER_DIR) 111 SLAVE_DIR = path.dirname(BUILDER_DIR)
115 CACHE_DIR = path.join(SLAVE_DIR, 'cache_dir') 112 CACHE_DIR = path.join(SLAVE_DIR, 'cache_dir')
116 # Because we print CACHE_DIR out into a .gclient file, and then later run 113 # Because we print CACHE_DIR out into a .gclient file, and then later run
117 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets 114 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets
118 # parsed as "E:[\x08][\x08]uild". 115 # parsed as "E:[\x08][\x08]uild".
119 if sys.platform.startswith('win'): 116 if sys.platform.startswith('win'):
120 CACHE_DIR = CACHE_DIR.replace('\\', '\\\\') 117 CACHE_DIR = CACHE_DIR.replace('\\', '\\\\')
121 118
122 # Find the patch tool.
123 ROOT_BUILD_DIR = path.dirname(SLAVE_DIR)
124 ROOT_B_DIR = path.dirname(ROOT_BUILD_DIR)
125 BUILD_INTERNAL_DIR = path.join(ROOT_B_DIR, 'build_internal')
126 if sys.platform.startswith('win'):
127 PATCH_TOOL = path.join(BUILD_INTERNAL_DIR, 'tools', 'patch.EXE')
128 else:
129 PATCH_TOOL = '/usr/bin/patch'
130
131 119
132 class SubprocessFailed(Exception): 120 class SubprocessFailed(Exception):
133 def __init__(self, message, code): 121 def __init__(self, message, code):
134 Exception.__init__(self, message) 122 Exception.__init__(self, message)
135 self.code = code 123 self.code = code
136 124
137 125
138 def call(*args, **kwargs): 126 def call(*args, **kwargs):
139 """Interactive subprocess call.""" 127 """Interactive subprocess call."""
140 kwargs['stdout'] = subprocess.PIPE 128 kwargs['stdout'] = subprocess.PIPE
141 kwargs['stderr'] = subprocess.STDOUT 129 kwargs['stderr'] = subprocess.STDOUT
142 stdin_data = kwargs.pop('stdin_data', None)
143 if stdin_data:
144 kwargs['stdin'] = subprocess.PIPE
145 out = cStringIO.StringIO()
146 for attempt in xrange(RETRIES): 130 for attempt in xrange(RETRIES):
147 attempt_msg = ' (retry #%d)' % attempt if attempt else '' 131 attempt_msg = ' (retry #%d)' % attempt if attempt else ''
148 print '===Running %s%s===' % (' '.join(args), attempt_msg) 132 print '===Running %s%s===' % (' '.join(args), attempt_msg)
149 start_time = time.time() 133 start_time = time.time()
150 proc = subprocess.Popen(args, **kwargs) 134 proc = subprocess.Popen(args, **kwargs)
151 if stdin_data:
152 proc.stdin.write(stdin_data)
153 proc.stdin.close()
154 # This is here because passing 'sys.stdout' into stdout for proc will 135 # This is here because passing 'sys.stdout' into stdout for proc will
155 # produce out of order output. 136 # produce out of order output.
156 while True: 137 while True:
157 buf = proc.stdout.read(1) 138 buf = proc.stdout.read(1)
158 if not buf: 139 if not buf:
159 break 140 break
160 sys.stdout.write(buf) 141 sys.stdout.write(buf)
161 out.write(buf)
162 code = proc.wait() 142 code = proc.wait()
163 elapsed_time = ((time.time() - start_time) / 60.0) 143 elapsed_time = ((time.time() - start_time) / 60.0)
164 if not code: 144 if not code:
165 print '===Succeeded in %.1f mins===' % elapsed_time 145 print '===Succeeded in %.1f mins===' % elapsed_time
166 print 146 print
167 return out.getvalue() 147 return 0
168 print '===Failed in %.1f mins===' % elapsed_time 148 print '===Failed in %.1f mins===' % elapsed_time
169 print 149 print
170 150
171 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % 151 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' %
172 (' '.join(args), code, os.getcwd(), RETRIES), code) 152 (' '.join(args), code, os.getcwd(), RETRIES), code)
173 153
174 154
175 def git(*args, **kwargs): 155 def git(*args, **kwargs):
176 """Wrapper around call specifically for Git commands.""" 156 """Wrapper around call specifically for Git commands."""
177 git_executable = 'git' 157 git_executable = 'git'
178 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we 158 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we
179 # have to do it explicitly. This is better than passing shell=True. 159 # have to do it explicitly. This is better than passing shell=True.
180 if sys.platform.startswith('win'): 160 if sys.platform.startswith('win'):
181 git_executable += '.bat' 161 git_executable += '.bat'
182 cmd = (git_executable,) + args 162 cmd = (git_executable,) + args
183 return call(*cmd, **kwargs) 163 call(*cmd, **kwargs)
184 164
185 165
186 def get_gclient_spec(solutions): 166 def get_gclient_spec(solutions):
187 return GCLIENT_TEMPLATE % { 167 return GCLIENT_TEMPLATE % {
188 'solutions': pprint.pformat(solutions, indent=4), 168 'solutions': pprint.pformat(solutions, indent=4),
189 'cache_dir': '"%s"' % CACHE_DIR 169 'cache_dir': '"%s"' % CACHE_DIR
190 } 170 }
191 171
192 172
193 def check_enabled(master, builder, slave): 173 def check_enabled(master, builder, slave):
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: 277 with codecs.open('.gclient', mode='w', encoding='utf-8') as f:
298 f.write(get_gclient_spec(solutions)) 278 f.write(get_gclient_spec(solutions))
299 279
300 280
301 def gclient_sync(): 281 def gclient_sync():
302 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient' 282 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient'
303 call(gclient_bin, 'sync', '--verbose', '--reset', '--force', 283 call(gclient_bin, 'sync', '--verbose', '--reset', '--force',
304 '--nohooks', '--noprehooks') 284 '--nohooks', '--noprehooks')
305 285
306 286
307 def create_less_than_or_equal_regex(number):
308 """ Return a regular expression to test whether an integer less than or equal
309 to 'number' is present in a given string.
310 """
311
312 # In three parts, build a regular expression that match any numbers smaller
313 # than 'number'.
314 # For example, 78656 would give a regular expression that looks like:
315 # Part 1
316 # (78356| # 78356
317 # Part 2
318 # 7835[0-5]| # 78350-78355
319 # 783[0-4][0-9]| # 78300-78349
320 # 78[0-2][0-9]{2}| # 78000-78299
321 # 7[0-7][0-9]{3}| # 70000-77999
322 # [0-6][0-9]{4}| # 10000-69999
323 # Part 3
324 # [0-9]{1,4} # 0-9999
325
326 # Part 1: Create an array with all the regexes, as described above.
327 # Prepopulate it with the number itself.
328 number = str(number)
329 expressions = [number]
330
331 # Convert the number to a list, so we can translate digits in it to
332 # expressions.
333 num_list = list(number)
334 num_len = len(num_list)
335
336 # Part 2: Go through all the digits in the number, starting from the end.
337 # Each iteration appends a line to 'expressions'.
338 for index in range (num_len - 1, -1, -1):
339 # Convert this digit back to an integer.
340 digit = int(num_list[index])
341
342 # Part 2.1: No processing if this digit is a zero.
343 if digit == 0:
344 continue
345
346 # Part 2.2: We switch the current digit X by a range "[0-(X-1)]".
347 if digit == 1:
348 num_list[index] = '0'
349 else:
350 num_list[index] = '[0-%d]' % (digit - 1)
351
352 # Part 2.3: We set all following digits to be "[0-9]".
353 # Since we just decrementented a digit in a most important position, all
354 # following digits don't matter. The possible numbers will always be smaller
355 # than before we decremented.
356 if (index + 1) < num_len:
357 if (num_len - (index + 1)) == 1:
358 num_list[index + 1] = '[0-9]'
359 else:
360 num_list[index + 1] = '[0-9]{%s}' % (num_len - (index + 1))
361
362 # Part 2.4: Add this new sub-expression to the list.
363 expressions.append(''.join(num_list[:min(index+2, num_len)]))
364
365 # Part 3: We add all the full ranges to match all numbers that are at least
366 # one order of magnitude smaller than the original numbers.
367 if num_len == 2:
368 expressions.append('[0-9]')
369 elif num_len > 2:
370 expressions.append('[0-9]{1,%s}' % (num_len - 1))
371
372 # All done. We now have our final regular expression.
373 regex = '(%s)' % ('|'.join(expressions))
374 return regex
375
376
377 def get_git_hash(revision, dir_name): 287 def get_git_hash(revision, dir_name):
378 match = "^git-svn-id: [^ ]*@%s " % create_less_than_or_equal_regex(revision) 288 match = "^git-svn-id: [^ ]*@%d" % revision
379 cmd = ['git', 'log', '-E', '--grep', match, '--format=%H', '--max-count=1'] 289 cmd = ['git', 'log', '--grep', match, '--format=%H', dir_name]
380 results = call(*cmd, cwd=dir_name).strip().splitlines() 290 return subprocess.check_output(cmd).strip() or None
381 if results:
382 return results[0]
383 raise Exception('We can\'t resolve svn revision %s into a git hash' %
384 revision)
385 291
386 292
387 def deps2git(sln_dirs): 293 def deps2git(sln_dirs):
388 for sln_dir in sln_dirs: 294 for sln_dir in sln_dirs:
389 deps_file = path.join(os.getcwd(), sln_dir, 'DEPS') 295 deps_file = path.join(os.getcwd(), sln_dir, 'DEPS')
390 deps_git_file = path.join(os.getcwd(), sln_dir, '.DEPS.git') 296 deps_git_file = path.join(os.getcwd(), sln_dir, '.DEPS.git')
391 if not path.isfile(deps_file): 297 if not path.isfile(deps_file):
392 return 298 return
393 # Do we have a better way of doing this....? 299 # Do we have a better way of doing this....?
394 repo_type = 'internal' if 'internal' in sln_dir else 'public' 300 repo_type = 'internal' if 'internal' in sln_dir else 'public'
395 call(sys.executable, DEPS2GIT_PATH, '-t', repo_type, 301 call(sys.executable, DEPS2GIT_PATH, '-t', repo_type,
396 '--cache_dir=%s' % CACHE_DIR, 302 '--cache_dir=%s' % CACHE_DIR,
397 '--deps=%s' % deps_file, '--out=%s' % deps_git_file) 303 '--deps=%s' % deps_file, '--out=%s' % deps_git_file)
398 304
399 305
400 def emit_got_revision(revision):
401 print '@@@SET_BUILD_PROPERTY@got_revision@%s@@@' % revision
402
403 def git_checkout(solutions, revision): 306 def git_checkout(solutions, revision):
404 build_dir = os.getcwd() 307 build_dir = os.getcwd()
405 # Revision only applies to the first solution. 308 # Revision only applies to the first solution.
406 first_solution = True 309 first_solution = True
407 for sln in solutions: 310 for sln in solutions:
408 name = sln['name'] 311 name = sln['name']
409 url = sln['url'] 312 url = sln['url']
410 sln_dir = path.join(build_dir, name) 313 sln_dir = path.join(build_dir, name)
411 if not path.isdir(sln_dir): 314 if not path.isdir(sln_dir):
412 git('clone', url, sln_dir) 315 git('clone', url, sln_dir)
413 316
414 # Clean out .DEPS.git changes first. 317 # Clean out .DEPS.git changes first.
415 try: 318 try:
416 git('reset', '--hard', cwd=sln_dir) 319 git('reset', '--hard', cwd=sln_dir)
417 except SubprocessFailed as e: 320 except SubprocessFailed as e:
418 if e.code == 128: 321 if e.code == 128:
419 # Exited abnormally, theres probably something wrong with the checkout. 322 # Exited abnormally, theres probably something wrong with the checkout.
420 # Lets wipe the checkout and try again. 323 # Lets wipe the checkout and try again.
421 chromium_utils.RemoveDirectory(sln_dir) 324 chromium_utils.RemoveDirectory(sln_dir)
422 git('clone', url, sln_dir) 325 git('clone', url, sln_dir)
423 git('reset', '--hard', cwd=sln_dir) 326 git('reset', '--hard', cwd=sln_dir)
424 else: 327 else:
425 raise 328 raise
426 329
427 git('clean', '-df', cwd=sln_dir) 330 git('clean', '-df', cwd=sln_dir)
428 git('pull', 'origin', 'master', cwd=sln_dir) 331 git('pull', 'origin', 'master', cwd=sln_dir)
429 # TODO(hinoka): We probably have to make use of revision mapping. 332 # TODO(hinoka): We probably have to make use of revision mapping.
430 if first_solution and revision and revision.lower() != 'head': 333 if first_solution and revision and revision.lower() != 'head':
431 emit_got_revision(revision)
432 if revision and revision.isdigit() and len(revision) < 40: 334 if revision and revision.isdigit() and len(revision) < 40:
433 # rev_num is really a svn revision number, convert it into a git hash. 335 # rev_num is really a svn revision number, convert it into a git hash.
434 git_ref = get_git_hash(int(revision), name) 336 git_ref = get_git_hash(revision, name)
435 else: 337 else:
436 # rev_num is actually a git hash or ref, we can just use it. 338 # rev_num is actually a git hash or ref, we can just use it.
437 git_ref = revision 339 git_ref = revision
438 git('checkout', git_ref, cwd=sln_dir) 340 git('checkout', git_ref, cwd=sln_dir)
439 else: 341 else:
440 git('checkout', 'origin/master', cwd=sln_dir) 342 git('checkout', 'origin/master', cwd=sln_dir)
441 if first_solution:
442 git_ref = git('log', '--format=%H', '--max-count=1',
443 cwd=sln_dir).strip()
444 343
445 first_solution = False 344 first_solution = False
446 return git_ref
447 345
448 346
449 def _download(url): 347 def apply_issue(issue, patchset, root, server):
450 """Fetch url and return content, with retries for flake.""" 348 pass
451 for attempt in xrange(RETRIES):
452 try:
453 return urllib2.urlopen(url).read()
454 except Exception:
455 if attempt == RETRIES - 1:
456 raise
457
458
459 def apply_issue_svn(root, patch_url):
460 patch_data = call('svn', 'cat', patch_url)
461 call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward',
462 stdin_data=patch_data, cwd=root)
463
464
465 def apply_issue_rietveld(issue, patchset, root, server, rev_map, revision):
466 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win')
467 else 'apply_issue')
468 rev_map = json.loads(rev_map)
469 if root in rev_map and rev_map[root] == 'got_revision':
470 rev_map[root] = revision
471 call(apply_issue_bin,
472 '--root_dir', root,
473 '--issue', issue,
474 '--patchset', patchset,
475 '--no-auth',
476 '--server', server,
477 '--revision-mapping', json.dumps(rev_map),
478 '--base_ref', revision,
479 '--force')
480 349
481 350
482 def check_flag(flag_file): 351 def check_flag(flag_file):
483 """Returns True if the flag file is present.""" 352 """Returns True if the flag file is present."""
484 return os.path.isfile(flag_file) 353 return os.path.isfile(flag_file)
485 354
486 355
487 def delete_flag(flag_file): 356 def delete_flag(flag_file):
488 """Remove bot update flag.""" 357 """Remove bot update flag."""
489 if os.path.isfile(flag_file): 358 if os.path.isfile(flag_file):
490 os.remove(flag_file) 359 os.remove(flag_file)
491 360
492 361
493 def emit_flag(flag_file): 362 def emit_flag(flag_file):
494 """Deposit a bot update flag on the system to tell gclient not to run.""" 363 """Deposit a bot update flag on the system to tell gclient not to run."""
495 print 'Emitting flag file at %s' % flag_file 364 print 'Emitting flag file at %s' % flag_file
496 with open(flag_file, 'wb') as f: 365 with open(flag_file, 'wb') as f:
497 f.write('Success!') 366 f.write('Success!')
498 367
499 368
500 def parse_args(): 369 def parse_args():
501 parse = optparse.OptionParser() 370 parse = optparse.OptionParser()
502 371
503 parse.add_option('--issue', help='Issue number to patch from.') 372 parse.add_option('--issue', help='Issue number to patch from.')
504 parse.add_option('--patchset', 373 parse.add_option('--patchset',
505 help='Patchset from issue to patch from, if applicable.') 374 help='Patchset from issue to patch from, if applicable.')
506 parse.add_option('--patch_url', help='Optional URL to SVN patch.') 375 parse.add_option('--patch_url', help='Optional URL to SVN patch.')
507 parse.add_option('--root', help='Repository root.') 376 parse.add_option('--root', help='Repository root.')
508 parse.add_option('--rietveld_server', 377 parse.add_option('--rietveld_server', help='Rietveld server.')
509 default='codereview.chromium.org',
510 help='Rietveld server.')
511 parse.add_option('--specs', help='Gcilent spec.') 378 parse.add_option('--specs', help='Gcilent spec.')
512 parse.add_option('--master', help='Master name.') 379 parse.add_option('--master', help='Master name.')
513 parse.add_option('-f', '--force', action='store_true', 380 parse.add_option('-f', '--force', action='store_true',
514 help='Bypass check to see if we want to be run. ' 381 help='Bypass check to see if we want to be run. '
515 'Should ONLY be used locally.') 382 'Should ONLY be used locally.')
516 parse.add_option('--revision_mapping') 383 # TODO(hinoka): We don't actually use this yet, we should factor this in.
384 parse.add_option('--revision-mapping')
517 parse.add_option('--revision') 385 parse.add_option('--revision')
518 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], 386 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0],
519 help='Hostname of the current machine, ' 387 help='Hostname of the current machine, '
520 'used for determining whether or not to activate.') 388 'used for determining whether or not to activate.')
521 parse.add_option('--builder_name', help='Name of the builder, ' 389 parse.add_option('--builder_name', help='Name of the builder, '
522 'used for determining whether or not to activate.') 390 'used for determining whether or not to activate.')
523 parse.add_option('--build_dir', default=os.getcwd()) 391 parse.add_option('--build_dir', default=os.getcwd())
524 parse.add_option('--flag_file', default=path.join(os.getcwd(), 392 parse.add_option('--flag_file', default=path.join(os.getcwd(),
525 'update.flag')) 393 'update.flag'))
526 394
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
562 ensure_no_checkout(dir_names, '.svn') 430 ensure_no_checkout(dir_names, '.svn')
563 emit_flag(options.flag_file) 431 emit_flag(options.flag_file)
564 else: 432 else:
565 delete_flag(options.flag_file) 433 delete_flag(options.flag_file)
566 return 434 return
567 435
568 # Get a checkout of each solution, without DEPS or hooks. 436 # Get a checkout of each solution, without DEPS or hooks.
569 # Calling git directory because there is no way to run Gclient without 437 # Calling git directory because there is no way to run Gclient without
570 # invoking DEPS. 438 # invoking DEPS.
571 print 'Fetching Git checkout' 439 print 'Fetching Git checkout'
572 got_revision = git_checkout(git_solutions, options.revision) 440 git_checkout(git_solutions, options.revision)
573 441
574 options.root = options.root or dir_names[0] 442 # TODO(hinoka): This must be implemented before we can turn this on for TS.
575 if options.patch_url: 443 # if options.issue:
576 apply_issue_svn(options.root, options.patch_url) 444 # apply_issue(options.issue, options.patchset, options.root, options.server)
577 elif options.issue:
578 apply_issue_rietveld(options.issue, options.patchset, options.root,
579 options.rietveld_server, options.revision_mapping,
580 got_revision)
581 445
582 # Magic to get deps2git to work with internal DEPS. 446 # Magic to get deps2git to work with internal DEPS.
583 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) 447 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH)
584 deps2git(dir_names) 448 deps2git(dir_names)
585 449
586 gclient_configure(git_solutions) 450 gclient_configure(git_solutions)
587 gclient_sync() 451 gclient_sync()
588 452
589 453
590 if __name__ == '__main__': 454 if __name__ == '__main__':
591 sys.exit(main()) 455 sys.exit(main())
OLDNEW
« no previous file with comments | « scripts/master/factory/gclient_factory.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698