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

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

Issue 238063010: Bot Update support on official builders (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: Rebase Created 6 years, 8 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 | « no previous file | 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 8 import cStringIO
9 import ctypes 9 import ctypes
10 import json 10 import json
(...skipping 15 matching lines...) Expand all
26 from common import chromium_utils 26 from common import chromium_utils
27 27
28 28
29 CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git' 29 CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
30 30
31 RECOGNIZED_PATHS = { 31 RECOGNIZED_PATHS = {
32 # If SVN path matches key, the entire URL is rewritten to the Git url. 32 # If SVN path matches key, the entire URL is rewritten to the Git url.
33 '/chrome/trunk/src': 33 '/chrome/trunk/src':
34 CHROMIUM_SRC_URL, 34 CHROMIUM_SRC_URL,
35 '/chrome-internal/trunk/src-internal': 35 '/chrome-internal/trunk/src-internal':
36 'https://chrome-internal.googlesource.com/chrome/src-internal.git' 36 'https://chrome-internal.googlesource.com/chrome/src-internal.git',
37 '/chrome-internal/trunk/tools/buildspec/build/chrome-official':
38 'https://chrome-internal.googlesource.com/chrome/tools/buildspec',
37 } 39 }
38 40
41 CUSTOM_DEPS_FILE = {
42 '/chrome-internal/trunk/tools/buildspec/build/chrome-official':
43 'build/chrome-official/DEPS'
44 }
45
46 # Solution names that use git_buildspecs to resolve .DEPS.git.
47 BUILDSPEC_SOLUTIONS = [
48 'chrome-official',
agable 2014/04/18 03:45:07 I think this needs to be more than chrome-official
Ryan Tseng 2014/04/18 18:42:38 Done.
49 ]
50
51 GIT_BUILDSPEC_REPO = (
52 'https://chrome-internal.googlesource.com/chrome/tools/git_buildspecs')
53
39 # Copied from scripts/recipes/chromium.py. 54 # Copied from scripts/recipes/chromium.py.
40 GOT_REVISION_MAPPINGS = { 55 GOT_REVISION_MAPPINGS = {
41 '/chrome/trunk/src': { 56 '/chrome/trunk/src': {
42 'src/': 'got_revision', 57 'src/': 'got_revision',
43 'src/native_client/': 'got_nacl_revision', 58 'src/native_client/': 'got_nacl_revision',
44 'src/tools/swarm_client/': 'got_swarm_client_revision', 59 'src/tools/swarm_client/': 'got_swarm_client_revision',
45 'src/tools/swarming_client/': 'got_swarming_client_revision', 60 'src/tools/swarming_client/': 'got_swarming_client_revision',
46 'src/third_party/WebKit/': 'got_webkit_revision', 61 'src/third_party/WebKit/': 'got_webkit_revision',
47 'src/third_party/webrtc/': 'got_webrtc_revision', 62 'src/third_party/webrtc/': 'got_webrtc_revision',
48 } 63 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 114
100 115
101 GCLIENT_TEMPLATE = """solutions = %(solutions)s 116 GCLIENT_TEMPLATE = """solutions = %(solutions)s
102 117
103 cache_dir = %(cache_dir)s 118 cache_dir = %(cache_dir)s
104 %(target_os)s 119 %(target_os)s
105 """ 120 """
106 121
107 # IMPORTANT: If you're trying to enable a RECIPE bot, you'll need to 122 # IMPORTANT: If you're trying to enable a RECIPE bot, you'll need to
108 # edit recipe_modules/bot_update/api.py instead. 123 # edit recipe_modules/bot_update/api.py instead.
109 ENABLED_MASTERS = ['chromium.git'] 124 ENABLED_MASTERS = ['chromium.git', 'chrome_git']
110 ENABLED_BUILDERS = { 125 ENABLED_BUILDERS = {
111 'tryserver.chromium': ['linux_rel_alt'], 126 'tryserver.chromium': ['linux_rel_alt'],
112 } 127 }
113 ENABLED_SLAVES = { 128 ENABLED_SLAVES = {
114 # This is enabled on a bot-to-bot basis to ensure that we don't have 129 # This is enabled on a bot-to-bot basis to ensure that we don't have
115 # bots that have mixed configs. 130 # bots that have mixed configs.
116 'chromium.fyi': [ 131 'chromium.fyi': [
117 'build1-m1', # Chromium Builder / Chromium Builder (dbg) 132 'build1-m1', # Chromium Builder / Chromium Builder (dbg)
118 'vm928-m1', # Chromium Linux Buildrunner 133 'vm928-m1', # Chromium Linux Buildrunner
119 'vm859-m1', # Chromium Linux Redux 134 'vm859-m1', # Chromium Linux Redux
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 """ 316 """
302 assert input_solutions 317 assert input_solutions
303 solutions = copy.deepcopy(input_solutions) 318 solutions = copy.deepcopy(input_solutions)
304 first_solution = True 319 first_solution = True
305 for solution in solutions: 320 for solution in solutions:
306 original_url = solution['url'] 321 original_url = solution['url']
307 parsed_url = urlparse.urlparse(original_url) 322 parsed_url = urlparse.urlparse(original_url)
308 parsed_path = parsed_url.path 323 parsed_path = parsed_url.path
309 if first_solution: 324 if first_solution:
310 root = parsed_path 325 root = parsed_path
326 deps_file = CUSTOM_DEPS_FILE.get(parsed_path, 'DEPS')
311 first_solution = False 327 first_solution = False
328
329 # Rewrite SVN urls into Git urls.
312 if parsed_path in RECOGNIZED_PATHS: 330 if parsed_path in RECOGNIZED_PATHS:
313 solution['url'] = RECOGNIZED_PATHS[parsed_path] 331 solution['url'] = RECOGNIZED_PATHS[parsed_path]
314 else: 332 else:
315 print 'Warning: path %s not recognized' % parsed_path 333 print 'Warning: path %s not recognized' % parsed_path
316 if solution.get('deps_file', 'DEPS') == 'DEPS': 334
317 solution['deps_file'] = '.DEPS.git' 335 if parsed_path in CUSTOM_DEPS_FILE:
336 # For some bots (eg. official), we want to specify what DEPS file
337 # to look at.
338 solution['deps_file'] = CUSTOM_DEPS_FILE[parsed_path]
339
340 if solution.get('deps_file', 'DEPS').endswith('DEPS'):
341 # Point .DEPS.git is the git version of the DEPS file.
342 solution['deps_file'] = '.DEPS.git'.join(
343 solution['deps_file'].rsplit('DEPS', 1))
344
318 solution['managed'] = False 345 solution['managed'] = False
319 # We don't want gclient to be using a safesync URL. Instead it should 346 # We don't want gclient to be using a safesync URL. Instead it should
320 # using the lkgr/lkcr branch/tags. 347 # using the lkgr/lkcr branch/tags.
321 if 'safesync_url' in solution: 348 if 'safesync_url' in solution:
322 print 'Removing safesync url %s from %s' % (solution['safesync_url'], 349 print 'Removing safesync url %s from %s' % (solution['safesync_url'],
323 parsed_path) 350 parsed_path)
324 del solution['safesync_url'] 351 del solution['safesync_url']
325 return solutions, root 352 return solutions, root, deps_file
326 353
327 354
328 def ensure_no_checkout(dir_names, scm_dirname): 355 def ensure_no_checkout(dir_names, scm_dirname):
329 """Ensure that there is no undesired checkout under build/. 356 """Ensure that there is no undesired checkout under build/.
330 357
331 If there is an incorrect checkout under build/, then 358 If there is an incorrect checkout under build/, then
332 move build/ to build.dead/ 359 move build/ to build.dead/
333 This function will check each directory in dir_names. 360 This function will check each directory in dir_names.
334 361
335 scm_dirname is expected to be either ['.svn', '.git'] 362 scm_dirname is expected to be either ['.svn', '.git']
(...skipping 17 matching lines...) Expand all
353 chromium_utils.RemoveFile(deletion_target) 380 chromium_utils.RemoveFile(deletion_target)
354 print 'done' 381 print 'done'
355 382
356 383
357 def gclient_configure(solutions, target_os): 384 def gclient_configure(solutions, target_os):
358 """Should do the same thing as gclient --spec='...'.""" 385 """Should do the same thing as gclient --spec='...'."""
359 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: 386 with codecs.open('.gclient', mode='w', encoding='utf-8') as f:
360 f.write(get_gclient_spec(solutions, target_os)) 387 f.write(get_gclient_spec(solutions, target_os))
361 388
362 389
363 def gclient_sync(output_json): 390 def gclient_sync(output_json, buildspec_mode):
364 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient' 391 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient'
365 call(gclient_bin, 'sync', '--verbose', '--reset', '--force', 392 cmd = [gclient_bin, 'sync', '--verbose', '--reset', '--force',
366 '--nohooks', '--noprehooks', '--output-json', output_json) 393 '--output-json', output_json]
394 if buildspec_mode:
395 cmd += ['--with_branch_heads']
396 else:
397 cmd += ['--nohooks', '--noprehooks']
398 call(*cmd)
367 with open(output_json) as f: 399 with open(output_json) as f:
368 return json.load(f) 400 return json.load(f)
369 401
370 402
371 def create_less_than_or_equal_regex(number): 403 def create_less_than_or_equal_regex(number):
372 """ Return a regular expression to test whether an integer less than or equal 404 """ Return a regular expression to test whether an integer less than or equal
373 to 'number' is present in a given string. 405 to 'number' is present in a given string.
374 """ 406 """
375 407
376 # In three parts, build a regular expression that match any numbers smaller 408 # In three parts, build a regular expression that match any numbers smaller
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 def _last_commit_for_file(filename, repo_base): 501 def _last_commit_for_file(filename, repo_base):
470 cmd = ['log', '--format=%H', '--max-count=1', '--', filename] 502 cmd = ['log', '--format=%H', '--max-count=1', '--', filename]
471 return git(*cmd, cwd=repo_base).strip() 503 return git(*cmd, cwd=repo_base).strip()
472 504
473 505
474 def need_to_run_deps2git(repo_base, deps_file, deps_git_file): 506 def need_to_run_deps2git(repo_base, deps_file, deps_git_file):
475 """Checks to see if we need to run deps2git. 507 """Checks to see if we need to run deps2git.
476 508
477 Returns True if there was a DEPS change after the last .DEPS.git update. 509 Returns True if there was a DEPS change after the last .DEPS.git update.
478 """ 510 """
511 print 'Checking if %s exists' % deps_git_file
479 if not path.isfile(deps_git_file): 512 if not path.isfile(deps_git_file):
480 # .DEPS.git doesn't exist but DEPS does? We probably want to generate one. 513 # .DEPS.git doesn't exist but DEPS does? We probably want to generate one.
514 print 'it exists!'
481 return True 515 return True
482 516
483 last_known_deps_ref = _last_commit_for_file(deps_file, repo_base) 517 last_known_deps_ref = _last_commit_for_file(deps_file, repo_base)
484 last_known_deps_git_ref = _last_commit_for_file(deps_git_file, repo_base) 518 last_known_deps_git_ref = _last_commit_for_file(deps_git_file, repo_base)
485 merge_base_ref = git('merge-base', last_known_deps_ref, 519 merge_base_ref = git('merge-base', last_known_deps_ref,
486 last_known_deps_git_ref, cwd=repo_base).strip() 520 last_known_deps_git_ref, cwd=repo_base).strip()
487 521
488 # If the merge base of the last DEPS and last .DEPS.git file is not 522 # If the merge base of the last DEPS and last .DEPS.git file is not
489 # equivilent to the hash of the last DEPS file, that means the DEPS file 523 # equivilent to the hash of the last DEPS file, that means the DEPS file
490 # was committed after the last .DEPS.git file. 524 # was committed after the last .DEPS.git file.
491 return last_known_deps_ref != merge_base_ref 525 return last_known_deps_ref != merge_base_ref
492 526
493 527
494 def ensure_deps2git(sln_dir, shallow): 528 def get_git_buildspec(version):
529 """Get the git buildspec of a version, return its contents.
530
531 The contents are returned instead of the file so that we can check the
532 repository into a temp directory and confine the cleanup logic here."""
533 git('cache', 'populate', '-v', '--cache-dir', CACHE_DIR, GIT_BUILDSPEC_REPO)
534 mirror_dir = git(
535 'cache', 'exists', '--cache-dir', CACHE_DIR, GIT_BUILDSPEC_REPO).strip()
536 tempdir = tempfile.mkdtemp()
537 git('clone', mirror_dir, tempdir)
538 while True:
539 if path.isdir(path.join(tempdir, version)):
540 with open(path.join(tempdir, version, 'DEPS'), 'rb') as f:
541 git_buildspec = f.read()
542 chromium_utils.RemoveDirectory(tempdir)
543 return git_buildspec
544 print 'Buildspec for %s not committed yet, waiting 5 seconds...'
545 time.sleep(5)
546 git('cache', 'populate', '-v', '--cache-dir',
547 CACHE_DIR, GIT_BUILDSPEC_REPO)
548 git('fetch', 'origin' , cwd=tempdir)
549
550
551 def buildspecs2git(repo_base, deps_file, deps_git_file):
552 """This is like deps2git, but for buildspecs.
553
554 Because buildspecs are vastly different than normal DEPS files, we cannot
555 use deps2git.py to generate git versions of the git DEPS. Fortunately
556 we don't have buildspec trybots, and there is already a service that
557 generates git DEPS for every buildspec commit already, so we can leverage
558 that service so that we don't need to run buildspec2git.py serially.
559
560 This checks the commit message of the current DEPS file for the release
561 number, waits in a busy loop for the coorisponding .DEPS.git file to be
562 committed into the git_buildspecs repository."""
563 deps_log = git('log', '-1', deps_file, cwd=repo_base)
564 m = re.search(r'Buildspec for\s+version (\d+\.\d+\.\d+\.\d+)', deps_log)
565 version = m.group(1)
566 git_buildspec = get_git_buildspec(version)
567 with open(deps_git_file, 'wb') as f:
568 f.write(git_buildspec)
569
570
571 def ensure_deps2git(sln_dir, deps_file, shallow, buildspec_mode):
495 repo_base = path.join(os.getcwd(), sln_dir) 572 repo_base = path.join(os.getcwd(), sln_dir)
496 deps_file = path.join(repo_base, 'DEPS') 573 deps_file = path.join(repo_base, deps_file)
497 deps_git_file = path.join(repo_base, '.DEPS.git') 574 deps_git_file = path.join(path.dirname(deps_file), '.DEPS.git')
575 if buildspec_mode:
576 return buildspecs2git(repo_base, deps_file, deps_git_file)
577 print 'Checking if %s is newer than %s' % (deps_file, deps_git_file)
498 if not path.isfile(deps_file): 578 if not path.isfile(deps_file):
499 return 579 return
500 580
501 if not need_to_run_deps2git(repo_base, deps_file, deps_git_file): 581 if not need_to_run_deps2git(repo_base, deps_file, deps_git_file):
502 return 582 return
503 583
504 print '===DEPS file modified, need to run deps2git===' 584 print '===DEPS file modified, need to run deps2git==='
505 # Magic to get deps2git to work with internal DEPS. 585 # Magic to get deps2git to work with internal DEPS.
506 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) 586 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH)
507 587
508 # TODO(hinoka): This might need to be smarter if we need to deal with 588 # TODO(hinoka): This might need to be smarter if we need to deal with
509 # DEPS changes that are in an internal repository. 589 # DEPS changes that are in an internal repository.
510 repo_type = 'internal' if 'internal' in sln_dir else 'public' 590 repo_type = 'public'
591 if sln_dir in ['src-internal', 'chrome-official']:
592 repo_type = 'internal'
511 cmd = [sys.executable, DEPS2GIT_PATH, 593 cmd = [sys.executable, DEPS2GIT_PATH,
512 '-t', repo_type, 594 '-t', repo_type,
513 '--cache_dir=%s' % CACHE_DIR, 595 '--cache_dir=%s' % CACHE_DIR,
514 '--deps=%s' % deps_file, 596 '--deps=%s' % deps_file,
515 '--out=%s' % deps_git_file] 597 '--out=%s' % deps_git_file]
516 if shallow: 598 if shallow:
517 cmd.append('--shallow') 599 cmd.append('--shallow')
518 call(*cmd) 600 call(*cmd)
519 601
520 602
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after
768 'slave': slave or 'Not specified', 850 'slave': slave or 'Not specified',
769 'recipe': recipe_force, 851 'recipe': recipe_force,
770 }, 852 },
771 # Print to stderr so that it shows up red on win/mac. 853 # Print to stderr so that it shows up red on win/mac.
772 print ACTIVATED_MESSAGE if active else NOT_ACTIVATED_MESSAGE 854 print ACTIVATED_MESSAGE if active else NOT_ACTIVATED_MESSAGE
773 855
774 # Parse, munipulate, and print the gclient solutions. 856 # Parse, munipulate, and print the gclient solutions.
775 specs = {} 857 specs = {}
776 exec(options.specs, specs) 858 exec(options.specs, specs)
777 svn_solutions = specs.get('solutions', []) 859 svn_solutions = specs.get('solutions', [])
778 git_solutions, svn_root = solutions_to_git(svn_solutions) 860 git_solutions, svn_root, deps_file = solutions_to_git(svn_solutions)
779 solutions_printer(git_solutions) 861 solutions_printer(git_solutions)
780 862
781 dir_names = [sln.get('name') for sln in svn_solutions if 'name' in sln] 863 dir_names = [sln.get('name') for sln in svn_solutions if 'name' in sln]
782 # If we're active now, but the flag file doesn't exist (we weren't active last 864 # If we're active now, but the flag file doesn't exist (we weren't active last
783 # run) or vice versa, blow away all checkouts. 865 # run) or vice versa, blow away all checkouts.
784 if bool(active) != bool(check_flag(options.flag_file)): 866 if bool(active) != bool(check_flag(options.flag_file)):
785 ensure_no_checkout(dir_names, '*') 867 ensure_no_checkout(dir_names, '*')
786 if options.output_json: 868 if options.output_json:
787 # Make sure we tell recipes that we didn't run if the script exits here. 869 # Make sure we tell recipes that we didn't run if the script exits here.
788 emit_json(options.output_json, did_run=active) 870 emit_json(options.output_json, did_run=active)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 # If either patch_url or issue is passed in, then we need to apply a patch. 902 # If either patch_url or issue is passed in, then we need to apply a patch.
821 if options.patch_url: 903 if options.patch_url:
822 # patch_url takes precidence since its only passed in on gcl try/git try. 904 # patch_url takes precidence since its only passed in on gcl try/git try.
823 apply_issue_svn(options.root, options.patch_url) 905 apply_issue_svn(options.root, options.patch_url)
824 elif options.issue: 906 elif options.issue:
825 apply_issue_rietveld(options.issue, options.patchset, options.root, 907 apply_issue_rietveld(options.issue, options.patchset, options.root,
826 options.rietveld_server, options.revision_mapping, 908 options.rietveld_server, options.revision_mapping,
827 git_ref) 909 git_ref)
828 910
829 # Run deps2git if there is a DEPS commit after the last .DEPS.git commit. 911 # Run deps2git if there is a DEPS commit after the last .DEPS.git commit.
830 ensure_deps2git(options.root, options.shallow) 912 buildspec_mode = options.root in BUILDSPEC_SOLUTIONS
913 ensure_deps2git(options.root, deps_file, options.shallow, buildspec_mode)
831 914
832 # Ensure our build/ directory is set up with the correct .gclient file. 915 # Ensure our build/ directory is set up with the correct .gclient file.
833 gclient_configure(git_solutions, specs.get('target_os', [])) 916 gclient_configure(git_solutions, specs.get('target_os', []))
834 917
835 # Let gclient do the DEPS syncing. Also we can get "got revision" data 918 # Let gclient do the DEPS syncing. Also we can get "got revision" data
836 # from gclient by passing in --output-json. In our case, we can just reuse 919 # from gclient by passing in --output-json. In our case, we can just reuse
837 # the temp file that 920 # the temp file that
838 _, gclient_output_file = tempfile.mkstemp(suffix='.json') 921 _, gclient_output_file = tempfile.mkstemp(suffix='.json')
839 gclient_output = gclient_sync(gclient_output_file) 922 gclient_output = gclient_sync(gclient_output_file, buildspec_mode)
840 923
841 # If we're fed an svn revision number as --revision, then our got_revision 924 # If we're fed an svn revision number as --revision, then our got_revision
842 # output should be in svn revs. Otherwise it'll be in git hashes. 925 # output should be in svn revs. Otherwise it'll be in git hashes.
843 use_svn_rev = (options.revision and options.revision.isdigit() and 926 use_svn_rev = (options.revision and options.revision.isdigit() and
844 len(options.revision) < 40) 927 len(options.revision) < 40)
845 # Take care of got_revisions outputs. 928 # Take care of got_revisions outputs.
846 revision_mapping = get_revision_mapping(svn_root, options.revision_mapping) 929 revision_mapping = get_revision_mapping(svn_root, options.revision_mapping)
847 got_revisions = parse_got_revision(gclient_output, revision_mapping, 930 got_revisions = parse_got_revision(gclient_output, revision_mapping,
848 use_svn_rev) 931 use_svn_rev)
849 932
850 if options.output_json: 933 if options.output_json:
851 # Tell recipes information such as root, got_revision, etc. 934 # Tell recipes information such as root, got_revision, etc.
852 emit_json(options.output_json, 935 emit_json(options.output_json,
853 did_run=True, 936 did_run=True,
854 root=options.root, 937 root=options.root,
855 step_text=step_text, 938 step_text=step_text,
856 properties=got_revisions) 939 properties=got_revisions)
857 else: 940 else:
858 # If we're not on recipes, tell annotator about our got_revisions. 941 # If we're not on recipes, tell annotator about our got_revisions.
859 emit_properties(got_revisions) 942 emit_properties(got_revisions)
860 943
861 944
862 if __name__ == '__main__': 945 if __name__ == '__main__':
863 sys.exit(main()) 946 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698