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

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