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

Side by Side Diff: recipe_modules/bot_update/resources/bot_update.py

Issue 2320653003: Revert of Delete lots of svn logic from bot_update (Closed)
Patch Set: Created 4 years, 3 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 # TODO(hinoka): Use logging. 6 # TODO(hinoka): Use logging.
7 7
8 import cStringIO 8 import cStringIO
9 import codecs 9 import codecs
10 import collections 10 import collections
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 path.join(ROOT_DIR, # .recipe_deps 79 path.join(ROOT_DIR, # .recipe_deps
80 path.pardir, # slave 80 path.pardir, # slave
81 path.pardir, # scripts 81 path.pardir, # scripts
82 path.pardir), # build_internal 82 path.pardir), # build_internal
83 ]) 83 ])
84 84
85 85
86 CHROMIUM_GIT_HOST = 'https://chromium.googlesource.com' 86 CHROMIUM_GIT_HOST = 'https://chromium.googlesource.com'
87 CHROMIUM_SRC_URL = CHROMIUM_GIT_HOST + '/chromium/src.git' 87 CHROMIUM_SRC_URL = CHROMIUM_GIT_HOST + '/chromium/src.git'
88 88
89 # Official builds use buildspecs, so this is a special case.
90 BUILDSPEC_TYPE = collections.namedtuple('buildspec',
91 ('container', 'version'))
92 BUILDSPEC_RE = (r'^/chrome-internal/trunk/tools/buildspec/'
93 '(build|branches|releases)/(.+)$')
94 GIT_BUILDSPEC_PATH = ('https://chrome-internal.googlesource.com/chrome/tools/'
95 'buildspec')
89 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' 96 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*'
90 97
91 BUILDSPEC_COMMIT_RE = ( 98 BUILDSPEC_COMMIT_RE = (
92 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'), 99 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'),
93 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'), 100 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'),
94 re.compile(r'Auto-converted (\d+\.\d+\.\d+\.\d+) buildspec to git'), 101 re.compile(r'Auto-converted (\d+\.\d+\.\d+\.\d+) buildspec to git'),
95 ) 102 )
96 103
97 # Regular expression that matches a single commit footer line. 104 # Regular expression that matches a single commit footer line.
98 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') 105 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)')
99 106
100 # Footer metadata keys for regular and gsubtreed mirrored commit positions. 107 # Footer metadata keys for regular and gsubtreed mirrored commit positions.
101 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' 108 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position'
102 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' 109 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position'
103 # Regular expression to parse a commit position 110 # Regular expression to parse a commit position
104 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}') 111 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}')
105 112
106 # Regular expression to parse gclient's revinfo entries. 113 # Regular expression to parse gclient's revinfo entries.
107 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') 114 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$')
108 115
116 # Used by 'ResolveSvnRevisionFromGitiles'
117 GIT_SVN_PROJECT_MAP = {
118 'webkit': {
119 'svn_url': 'svn://svn.chromium.org/blink',
120 'branch_map': [
121 (r'trunk', r'refs/heads/master'),
122 (r'branches/([^/]+)', r'refs/branch-heads/\1'),
123 ],
124 },
125 'v8': {
126 'svn_url': 'https://v8.googlecode.com/svn',
127 'branch_map': [
128 (r'trunk', r'refs/heads/candidates'),
129 (r'branches/bleeding_edge', r'refs/heads/master'),
130 (r'branches/([^/]+)', r'refs/branch-heads/\1'),
131 ],
132 },
133 'nacl': {
134 'svn_url': 'svn://svn.chromium.org/native_client',
135 'branch_map': [
136 (r'trunk/src/native_client', r'refs/heads/master'),
137 ],
138 },
139 }
140
141 # Key for the 'git-svn' ID metadata commit footer entry.
142 GIT_SVN_ID_FOOTER_KEY = 'git-svn-id'
143 # e.g., git-svn-id: https://v8.googlecode.com/svn/trunk@23117
144 # ce2b1a6d-e550-0410-aec6-3dcde31c8c00
145 GIT_SVN_ID_RE = re.compile(r'((?:\w+)://[^@]+)@(\d+)\s+(?:[a-zA-Z0-9\-]+)')
146
147
148 # This is the git mirror of the buildspecs repository. We could rely on the svn
149 # checkout, now that the git buildspecs are checked in alongside the svn
150 # buildspecs, but we're going to want to pull all the buildspecs from here
151 # eventually anyhow, and there's already some logic to pull from git (for the
152 # old git_buildspecs.git repo), so just stick with that.
153 GIT_BUILDSPEC_REPO = (
154 'https://chrome-internal.googlesource.com/chrome/tools/buildspec')
109 155
110 # Copied from scripts/recipes/chromium.py. 156 # Copied from scripts/recipes/chromium.py.
111 GOT_REVISION_MAPPINGS = { 157 GOT_REVISION_MAPPINGS = {
112 CHROMIUM_SRC_URL: { 158 '/chrome/trunk/src': {
113 'src/': 'got_revision', 159 'src/': 'got_revision',
114 'src/native_client/': 'got_nacl_revision', 160 'src/native_client/': 'got_nacl_revision',
115 'src/tools/swarm_client/': 'got_swarm_client_revision', 161 'src/tools/swarm_client/': 'got_swarm_client_revision',
116 'src/tools/swarming_client/': 'got_swarming_client_revision', 162 'src/tools/swarming_client/': 'got_swarming_client_revision',
117 'src/third_party/WebKit/': 'got_webkit_revision', 163 'src/third_party/WebKit/': 'got_webkit_revision',
118 'src/third_party/webrtc/': 'got_webrtc_revision', 164 'src/third_party/webrtc/': 'got_webrtc_revision',
119 'src/v8/': 'got_v8_revision', 165 'src/v8/': 'got_v8_revision',
120 } 166 }
121 } 167 }
122 168
123 169
124 BOT_UPDATE_MESSAGE = """ 170 BOT_UPDATE_MESSAGE = """
125 What is the "Bot Update" step? 171 What is the "Bot Update" step?
126 ============================== 172 ==============================
127 173
128 This step ensures that the source checkout on the bot (e.g. Chromium's src/ and 174 This step ensures that the source checkout on the bot (e.g. Chromium's src/ and
129 its dependencies) is checked out in a consistent state. This means that all of 175 its dependencies) is checked out in a consistent state. This means that all of
130 the necessary repositories are checked out, no extra repositories are checked 176 the necessary repositories are checked out, no extra repositories are checked
131 out, and no locally modified files are present. 177 out, and no locally modified files are present.
132 178
133 These actions used to be taken care of by the "gclient revert" and "update" 179 These actions used to be taken care of by the "gclient revert" and "update"
134 steps. However, those steps are known to be buggy and occasionally flaky. This 180 steps. However, those steps are known to be buggy and occasionally flaky. This
135 step has two main advantages over them: 181 step has two main advantages over them:
136 * it only operates in Git, so the logic can be clearer and cleaner; and 182 * it only operates in Git, so the logic can be clearer and cleaner; and
137 * it is a slave-side script, so its behavior can be modified without 183 * it is a slave-side script, so its behavior can be modified without
138 restarting the master. 184 restarting the master.
139 185
186 Why Git, you ask? Because that is the direction that the Chromium project is
187 heading. This step is an integral part of the transition from using the SVN repo
188 at chrome/trunk/src to using the Git repo src.git. Please pardon the dust while
189 we fully convert everything to Git. This message will get out of your way
190 eventually, and the waterfall will be a happier place because of it.
191
192 This step can be activated or deactivated independently on every builder on
193 every master. When it is active, the "gclient revert" and "update" steps become
194 no-ops. When it is inactive, it prints this message, cleans up after itself, and
195 lets everything else continue as though nothing has changed. Eventually, when
196 everything is stable enough, this step will replace them entirely.
197
140 Debugging information: 198 Debugging information:
141 (master/builder/slave may be unspecified on recipes) 199 (master/builder/slave may be unspecified on recipes)
142 master: %(master)s 200 master: %(master)s
143 builder: %(builder)s 201 builder: %(builder)s
144 slave: %(slave)s 202 slave: %(slave)s
145 forced by recipes: %(recipe)s 203 forced by recipes: %(recipe)s
146 CURRENT_DIR: %(CURRENT_DIR)s 204 CURRENT_DIR: %(CURRENT_DIR)s
147 BUILDER_DIR: %(BUILDER_DIR)s 205 BUILDER_DIR: %(BUILDER_DIR)s
148 SLAVE_DIR: %(SLAVE_DIR)s 206 SLAVE_DIR: %(SLAVE_DIR)s
149 THIS_DIR: %(THIS_DIR)s 207 THIS_DIR: %(THIS_DIR)s
(...skipping 29 matching lines...) Expand all
179 try: 237 try:
180 execfile(os.path.join( 238 execfile(os.path.join(
181 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), 239 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'),
182 local_vars) 240 local_vars)
183 except Exception: 241 except Exception:
184 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. 242 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place.
185 print 'Warning: unable to read internal configuration file.' 243 print 'Warning: unable to read internal configuration file.'
186 print 'If this is an internal bot, this step may be erroneously inactive.' 244 print 'If this is an internal bot, this step may be erroneously inactive.'
187 internal_data = local_vars 245 internal_data = local_vars
188 246
247 RECOGNIZED_PATHS = {
248 # If SVN path matches key, the entire URL is rewritten to the Git url.
249 '/chrome/trunk/src':
250 CHROMIUM_SRC_URL,
251 '/chrome/trunk/src/tools/cros.DEPS':
252 CHROMIUM_GIT_HOST + '/chromium/src/tools/cros.DEPS.git',
253 '/chrome-internal/trunk/src-internal':
254 'https://chrome-internal.googlesource.com/chrome/src-internal.git',
255 }
256 RECOGNIZED_PATHS.update(internal_data.get('RECOGNIZED_PATHS', {}))
189 257
190 ENABLED_MASTERS = [ 258 ENABLED_MASTERS = [
191 'bot_update.always_on', 259 'bot_update.always_on',
192 'chromium.android', 260 'chromium.android',
193 'chromium.angle', 261 'chromium.angle',
194 'chromium.chrome', 262 'chromium.chrome',
195 'chromium.chromedriver', 263 'chromium.chromedriver',
196 'chromium.chromiumos', 264 'chromium.chromiumos',
197 'chromium', 265 'chromium',
198 'chromium.fyi', 266 'chromium.fyi',
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 330
263 # Disabled filters get run AFTER enabled filters, so for example if a builder 331 # Disabled filters get run AFTER enabled filters, so for example if a builder
264 # config is enabled, but a bot on that builder is disabled, that bot will 332 # config is enabled, but a bot on that builder is disabled, that bot will
265 # be disabled. 333 # be disabled.
266 DISABLED_BUILDERS = {} 334 DISABLED_BUILDERS = {}
267 DISABLED_BUILDERS.update(internal_data.get('DISABLED_BUILDERS', {})) 335 DISABLED_BUILDERS.update(internal_data.get('DISABLED_BUILDERS', {}))
268 336
269 DISABLED_SLAVES = {} 337 DISABLED_SLAVES = {}
270 DISABLED_SLAVES.update(internal_data.get('DISABLED_SLAVES', {})) 338 DISABLED_SLAVES.update(internal_data.get('DISABLED_SLAVES', {}))
271 339
340 # These masters work only in Git, meaning for got_revision, always output
341 # a git hash rather than a SVN rev.
342 GIT_MASTERS = [
343 'client.v8',
344 'client.v8.branches',
345 'client.v8.ports',
346 'tryserver.v8',
347 ]
348 GIT_MASTERS += internal_data.get('GIT_MASTERS', [])
349
350
272 # How many times to try before giving up. 351 # How many times to try before giving up.
273 ATTEMPTS = 5 352 ATTEMPTS = 5
274 353
275 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') 354 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py')
276 355
277 # Find the patch tool. 356 # Find the patch tool.
278 if sys.platform.startswith('win'): 357 if sys.platform.startswith('win'):
279 if not BUILD_INTERNAL_DIR: 358 if not BUILD_INTERNAL_DIR:
280 print 'Warning: could not find patch tool because there is no ' 359 print 'Warning: could not find patch tool because there is no '
281 print 'build_internal present.' 360 print 'build_internal present.'
(...skipping 16 matching lines...) Expand all
298 377
299 378
300 class PatchFailed(SubprocessFailed): 379 class PatchFailed(SubprocessFailed):
301 pass 380 pass
302 381
303 382
304 class GclientSyncFailed(SubprocessFailed): 383 class GclientSyncFailed(SubprocessFailed):
305 pass 384 pass
306 385
307 386
387 class SVNRevisionNotFound(Exception):
388 pass
389
390
308 class InvalidDiff(Exception): 391 class InvalidDiff(Exception):
309 pass 392 pass
310 393
311 394
395 class Inactive(Exception):
396 """Not really an exception, just used to exit early cleanly."""
397 pass
398
399
312 RETRY = object() 400 RETRY = object()
313 OK = object() 401 OK = object()
314 FAIL = object() 402 FAIL = object()
315 403
316 404
317 class PsPrinter(object): 405 class PsPrinter(object):
318 def __init__(self, interval=300): 406 def __init__(self, interval=300):
319 self.interval = interval 407 self.interval = interval
320 self.active = sys.platform.startswith('linux2') 408 self.active = sys.platform.startswith('linux2')
321 self.thread = None 409 self.thread = None
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 if slave_list and slave in slave_list: 546 if slave_list and slave in slave_list:
459 return True 547 return True
460 return False 548 return False
461 549
462 550
463 def check_valid_host(master, builder, slave): 551 def check_valid_host(master, builder, slave):
464 return (check_enabled(master, builder, slave) 552 return (check_enabled(master, builder, slave)
465 and not check_disabled(master, builder, slave)) 553 and not check_disabled(master, builder, slave))
466 554
467 555
556 def maybe_ignore_revision(revision, buildspec):
557 """Handle builders that don't care what buildbot tells them to build.
558
559 This is especially the case with branch builders that build from buildspecs
560 and/or trigger off multiple repositories, where the --revision passed in has
561 nothing to do with the solution being built. Clearing the revision in this
562 case causes bot_update to use HEAD rather that trying to checkout an
563 inappropriate version of the solution.
564 """
565 if buildspec and buildspec.container == 'branches':
566 return []
567 return revision
568
569
468 def solutions_printer(solutions): 570 def solutions_printer(solutions):
469 """Prints gclient solution to stdout.""" 571 """Prints gclient solution to stdout."""
470 print 'Gclient Solutions' 572 print 'Gclient Solutions'
471 print '=================' 573 print '================='
472 for solution in solutions: 574 for solution in solutions:
473 name = solution.get('name') 575 name = solution.get('name')
474 url = solution.get('url') 576 url = solution.get('url')
475 print '%s (%s)' % (name, url) 577 print '%s (%s)' % (name, url)
476 if solution.get('deps_file'): 578 if solution.get('deps_file'):
477 print ' Dependencies file is %s' % solution['deps_file'] 579 print ' Dependencies file is %s' % solution['deps_file']
(...skipping 14 matching lines...) Expand all
492 print ' %s: Ignore' % deps_name 594 print ' %s: Ignore' % deps_name
493 for k, v in solution.iteritems(): 595 for k, v in solution.iteritems():
494 # Print out all the keys we don't know about. 596 # Print out all the keys we don't know about.
495 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', 597 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps',
496 'managed']: 598 'managed']:
497 continue 599 continue
498 print ' %s is %s' % (k, v) 600 print ' %s is %s' % (k, v)
499 print 601 print
500 602
501 603
502 def modify_solutions(input_solutions): 604 def solutions_to_git(input_solutions):
503 """Modifies urls in solutions to point at Git repos. 605 """Modifies urls in solutions to point at Git repos.
504 606
505 returns: new solution dictionary 607 returns: (git solution, svn root of first solution) tuple.
506 """ 608 """
507 assert input_solutions 609 assert input_solutions
508 solutions = copy.deepcopy(input_solutions) 610 solutions = copy.deepcopy(input_solutions)
611 first_solution = True
612 buildspec = None
509 for solution in solutions: 613 for solution in solutions:
510 original_url = solution['url'] 614 original_url = solution['url']
511 parsed_url = urlparse.urlparse(original_url) 615 parsed_url = urlparse.urlparse(original_url)
512 parsed_path = parsed_url.path 616 parsed_path = parsed_url.path
513 617
618 # Rewrite SVN urls into Git urls.
619 buildspec_m = re.match(BUILDSPEC_RE, parsed_path)
620 if first_solution and buildspec_m:
621 solution['url'] = GIT_BUILDSPEC_PATH
622 buildspec = BUILDSPEC_TYPE(
623 container=buildspec_m.group(1),
624 version=buildspec_m.group(2),
625 )
626 solution['deps_file'] = path.join(buildspec.container, buildspec.version,
627 'DEPS')
628 elif parsed_path in RECOGNIZED_PATHS:
629 solution['url'] = RECOGNIZED_PATHS[parsed_path]
630 solution['deps_file'] = '.DEPS.git'
631 elif parsed_url.scheme == 'https' and 'googlesource' in parsed_url.netloc:
632 pass
633 else:
634 print 'Warning: %s' % ('path %r not recognized' % parsed_path,)
635
636 # Strip out deps containing $$V8_REV$$, etc.
637 if 'custom_deps' in solution:
638 new_custom_deps = {}
639 for deps_name, deps_value in solution['custom_deps'].iteritems():
640 if deps_value and '$$' in deps_value:
641 print 'Dropping %s:%s from custom deps' % (deps_name, deps_value)
642 else:
643 new_custom_deps[deps_name] = deps_value
644 solution['custom_deps'] = new_custom_deps
645
646 if first_solution:
647 root = parsed_path
648 first_solution = False
649
514 solution['managed'] = False 650 solution['managed'] = False
515 # We don't want gclient to be using a safesync URL. Instead it should 651 # We don't want gclient to be using a safesync URL. Instead it should
516 # using the lkgr/lkcr branch/tags. 652 # using the lkgr/lkcr branch/tags.
517 if 'safesync_url' in solution: 653 if 'safesync_url' in solution:
518 print 'Removing safesync url %s from %s' % (solution['safesync_url'], 654 print 'Removing safesync url %s from %s' % (solution['safesync_url'],
519 parsed_path) 655 parsed_path)
520 del solution['safesync_url'] 656 del solution['safesync_url']
521 657 return solutions, root, buildspec
522 return solutions
523 658
524 659
525 def remove(target): 660 def remove(target):
526 """Remove a target by moving it into build.dead.""" 661 """Remove a target by moving it into build.dead."""
527 dead_folder = path.join(BUILDER_DIR, 'build.dead') 662 dead_folder = path.join(BUILDER_DIR, 'build.dead')
528 if not path.exists(dead_folder): 663 if not path.exists(dead_folder):
529 os.makedirs(dead_folder) 664 os.makedirs(dead_folder)
530 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) 665 os.rename(target, path.join(dead_folder, uuid.uuid4().hex))
531 666
532 667
533 def ensure_no_checkout(dir_names): 668 def ensure_no_checkout(dir_names, scm_dirname):
534 """Ensure that there is no undesired checkout under build/.""" 669 """Ensure that there is no undesired checkout under build/.
535 build_dir = os.getcwd() 670
536 has_checkout = any(path.exists(path.join(build_dir, dir_name, '.git')) 671 If there is an incorrect checkout under build/, then
672 move build/ to build.dead/
673 This function will check each directory in dir_names.
674
675 scm_dirname is expected to be either ['.svn', '.git']
676 """
677 assert scm_dirname in ['.svn', '.git', '*']
678 has_checkout = any(path.exists(path.join(os.getcwd(), dir_name, scm_dirname))
537 for dir_name in dir_names) 679 for dir_name in dir_names)
538 if has_checkout: 680
681 if has_checkout or scm_dirname == '*':
682 build_dir = os.getcwd()
683 prefix = ''
684 if scm_dirname != '*':
685 prefix = '%s detected in checkout, ' % scm_dirname
686
539 for filename in os.listdir(build_dir): 687 for filename in os.listdir(build_dir):
540 deletion_target = path.join(build_dir, filename) 688 deletion_target = path.join(build_dir, filename)
541 print '.git detected in checkout, deleting %s...' % deletion_target, 689 print '%sdeleting %s...' % (prefix, deletion_target),
542 remove(deletion_target) 690 remove(deletion_target)
543 print 'done' 691 print 'done'
544 692
545 693
546 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): 694 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir):
547 """Should do the same thing as gclient --spec='...'.""" 695 """Should do the same thing as gclient --spec='...'."""
548 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: 696 with codecs.open('.gclient', mode='w', encoding='utf-8') as f:
549 f.write(get_gclient_spec( 697 f.write(get_gclient_spec(
550 solutions, target_os, target_os_only, git_cache_dir)) 698 solutions, target_os, target_os_only, git_cache_dir))
551 699
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
625 footers[m.group(1)] = m.group(2).strip() 773 footers[m.group(1)] = m.group(2).strip()
626 return footers 774 return footers
627 775
628 776
629 def get_commit_message_footer(message, key): 777 def get_commit_message_footer(message, key):
630 """Returns: (str/None) The footer value for 'key', or None if none was found. 778 """Returns: (str/None) The footer value for 'key', or None if none was found.
631 """ 779 """
632 return get_commit_message_footer_map(message).get(key) 780 return get_commit_message_footer_map(message).get(key)
633 781
634 782
783 def get_svn_rev(git_hash, dir_name):
784 log = git('log', '-1', git_hash, cwd=dir_name)
785 git_svn_id = get_commit_message_footer(log, GIT_SVN_ID_FOOTER_KEY)
786 if not git_svn_id:
787 return None
788 m = GIT_SVN_ID_RE.match(git_svn_id)
789 if not m:
790 return None
791 return int(m.group(2))
792
793
794 def get_git_hash(revision, branch, sln_dir):
795 """We want to search for the SVN revision on the git-svn branch.
796
797 Note that git will search backwards from origin/master.
798 """
799 match = "^%s: [^ ]*@%s " % (GIT_SVN_ID_FOOTER_KEY, revision)
800 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch
801 cmd = ['log', '-E', '--grep', match, '--format=%H', '--max-count=1', ref]
802 result = git(*cmd, cwd=sln_dir).strip()
803 if result:
804 return result
805 raise SVNRevisionNotFound('We can\'t resolve svn r%s into a git hash in %s' %
806 (revision, sln_dir))
807
808
635 def emit_log_lines(name, lines): 809 def emit_log_lines(name, lines):
636 for line in lines.splitlines(): 810 for line in lines.splitlines():
637 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) 811 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line)
638 print '@@@STEP_LOG_END@%s@@@' % name 812 print '@@@STEP_LOG_END@%s@@@' % name
639 813
640 814
641 def emit_properties(properties): 815 def emit_properties(properties):
642 for property_name, property_value in sorted(properties.items()): 816 for property_name, property_value in sorted(properties.items()):
643 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) 817 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value)
644 818
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
679 853
680 854
681 def force_revision(folder_name, revision): 855 def force_revision(folder_name, revision):
682 split_revision = revision.split(':', 1) 856 split_revision = revision.split(':', 1)
683 branch = 'master' 857 branch = 'master'
684 if len(split_revision) == 2: 858 if len(split_revision) == 2:
685 # Support for "branch:revision" syntax. 859 # Support for "branch:revision" syntax.
686 branch, revision = split_revision 860 branch, revision = split_revision
687 861
688 if revision and revision.upper() != 'HEAD': 862 if revision and revision.upper() != 'HEAD':
689 git('checkout', '--force', revision, cwd=folder_name) 863 if revision and revision.isdigit() and len(revision) < 40:
864 # rev_num is really a svn revision number, convert it into a git hash.
865 git_ref = get_git_hash(int(revision), branch, folder_name)
866 else:
867 # rev_num is actually a git hash or ref, we can just use it.
868 git_ref = revision
869 git('checkout', '--force', git_ref, cwd=folder_name)
690 else: 870 else:
691 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch 871 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch
692 git('checkout', '--force', ref, cwd=folder_name) 872 git('checkout', '--force', ref, cwd=folder_name)
693 873
694
695 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): 874 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir):
696 build_dir = os.getcwd() 875 build_dir = os.getcwd()
697 # Before we do anything, break all git_cache locks. 876 # Before we do anything, break all git_cache locks.
698 if path.isdir(git_cache_dir): 877 if path.isdir(git_cache_dir):
699 git('cache', 'unlock', '-vv', '--force', '--all', 878 git('cache', 'unlock', '-vv', '--force', '--all',
700 '--cache-dir', git_cache_dir) 879 '--cache-dir', git_cache_dir)
701 for item in os.listdir(git_cache_dir): 880 for item in os.listdir(git_cache_dir):
702 filename = os.path.join(git_cache_dir, item) 881 filename = os.path.join(git_cache_dir, item)
703 if item.endswith('.lock'): 882 if item.endswith('.lock'):
704 raise Exception('%s exists after cache unlock' % filename) 883 raise Exception('%s exists after cache unlock' % filename)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 # Exited abnormally, theres probably something wrong. 924 # Exited abnormally, theres probably something wrong.
746 # Lets wipe the checkout and try again. 925 # Lets wipe the checkout and try again.
747 tries_left -= 1 926 tries_left -= 1
748 if tries_left > 0: 927 if tries_left > 0:
749 print 'Something failed: %s.' % str(e) 928 print 'Something failed: %s.' % str(e)
750 print 'waiting 5 seconds and trying again...' 929 print 'waiting 5 seconds and trying again...'
751 time.sleep(5) 930 time.sleep(5)
752 else: 931 else:
753 raise 932 raise
754 remove(sln_dir) 933 remove(sln_dir)
934 except SVNRevisionNotFound:
935 tries_left -= 1
936 if tries_left > 0:
937 # If we don't have the correct revision, wait and try again.
938 print 'We can\'t find revision %s.' % revision
939 print 'The svn to git replicator is probably falling behind.'
940 print 'waiting 5 seconds and trying again...'
941 time.sleep(5)
942 else:
943 raise
755 944
756 git('clean', '-dff', cwd=sln_dir) 945 git('clean', '-dff', cwd=sln_dir)
757 946
758 if first_solution: 947 if first_solution:
759 git_ref = git('log', '--format=%H', '--max-count=1', 948 git_ref = git('log', '--format=%H', '--max-count=1',
760 cwd=sln_dir).strip() 949 cwd=sln_dir).strip()
761 first_solution = False 950 first_solution = False
762 return git_ref 951 return git_ref
763 952
764 953
765 def _download(url): 954 def _download(url):
766 """Fetch url and return content, with retries for flake.""" 955 """Fetch url and return content, with retries for flake."""
767 for attempt in xrange(ATTEMPTS): 956 for attempt in xrange(ATTEMPTS):
768 try: 957 try:
769 return urllib2.urlopen(url).read() 958 return urllib2.urlopen(url).read()
770 except Exception: 959 except Exception:
771 if attempt == ATTEMPTS - 1: 960 if attempt == ATTEMPTS - 1:
772 raise 961 raise
773 962
774 963
964 def parse_diff(diff):
965 """Takes a unified diff and returns a list of diffed files and their diffs.
966
967 The return format is a list of pairs of:
968 (<filename>, <diff contents>)
969 <diff contents> is inclusive of the diff line.
970 """
971 result = []
972 current_diff = ''
973 current_header = None
974 for line in diff.splitlines():
975 # "diff" is for git style patches, and "Index: " is for SVN style patches.
976 if line.startswith('diff') or line.startswith('Index: '):
977 if current_header:
978 # If we are in a diff portion, then save the diff.
979 result.append((current_header, '%s\n' % current_diff))
980 git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line)
981 svn_header_match = re.match(r'Index: (.*)', line)
982
983 if git_header_match:
984 # First, see if its a git style header.
985 from_file = git_header_match.group(1)
986 to_file = git_header_match.group(2)
987 if from_file != to_file and from_file.startswith('a/'):
988 # Sometimes git prepends 'a/' and 'b/' in front of file paths.
989 from_file = from_file[2:]
990 current_header = from_file
991
992 elif svn_header_match:
993 # Otherwise, check if its an SVN style header.
994 current_header = svn_header_match.group(1)
995
996 else:
997 # Otherwise... I'm not really sure what to do with this.
998 raise InvalidDiff('Can\'t process header: %s\nFull diff:\n%s' %
999 (line, diff))
1000
1001 current_diff = ''
1002 current_diff += '%s\n' % line
1003 if current_header:
1004 # We hit EOF, gotta save the last diff.
1005 result.append((current_header, current_diff))
1006 return result
1007
1008
775 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, 1009 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision,
776 email_file, key_file, whitelist=None, blacklist=None): 1010 email_file, key_file, whitelist=None, blacklist=None):
777 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') 1011 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win')
778 else 'apply_issue') 1012 else 'apply_issue')
779 cmd = [apply_issue_bin, 1013 cmd = [apply_issue_bin,
780 # The patch will be applied on top of this directory. 1014 # The patch will be applied on top of this directory.
781 '--root_dir', root, 1015 '--root_dir', root,
782 # Tell apply_issue how to fetch the patch. 1016 # Tell apply_issue how to fetch the patch.
783 '--issue', issue, 1017 '--issue', issue,
784 '--server', server, 1018 '--server', server,
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 os.remove(flag_file) 1096 os.remove(flag_file)
863 1097
864 1098
865 def emit_flag(flag_file): 1099 def emit_flag(flag_file):
866 """Deposit a bot update flag on the system to tell gclient not to run.""" 1100 """Deposit a bot update flag on the system to tell gclient not to run."""
867 print 'Emitting flag file at %s' % flag_file 1101 print 'Emitting flag file at %s' % flag_file
868 with open(flag_file, 'wb') as f: 1102 with open(flag_file, 'wb') as f:
869 f.write('Success!') 1103 f.write('Success!')
870 1104
871 1105
1106 def get_commit_position_for_git_svn(url, revision):
1107 """Generates a commit position string for a 'git-svn' URL/revision.
1108
1109 If the 'git-svn' URL maps to a known project, we will construct a commit
1110 position branch value by applying substitution on the SVN URL.
1111 """
1112 # Identify the base URL so we can strip off trunk/branch name
1113 project_config = branch = None
1114 for _, project_config in GIT_SVN_PROJECT_MAP.iteritems():
1115 if url.startswith(project_config['svn_url']):
1116 branch = url[len(project_config['svn_url']):]
1117 break
1118
1119 if branch:
1120 # Strip any leading slashes
1121 branch = branch.lstrip('/')
1122
1123 # Try and map the branch
1124 for pattern, repl in project_config.get('branch_map', ()):
1125 nbranch, subn = re.subn(pattern, repl, branch, count=1)
1126 if subn:
1127 print 'INFO: Mapped SVN branch to Git branch [%s] => [%s]' % (
1128 branch, nbranch)
1129 branch = nbranch
1130 break
1131 else:
1132 # Use generic 'svn' branch
1133 print 'INFO: Could not resolve project for SVN URL %r' % (url,)
1134 branch = 'svn'
1135 return '%s@{#%s}' % (branch, revision)
1136
1137
872 def get_commit_position(git_path, revision='HEAD'): 1138 def get_commit_position(git_path, revision='HEAD'):
873 """Dumps the 'git' log for a specific revision and parses out the commit 1139 """Dumps the 'git' log for a specific revision and parses out the commit
874 position. 1140 position.
875 1141
876 If a commit position metadata key is found, its value will be returned. 1142 If a commit position metadata key is found, its value will be returned.
1143
1144 Otherwise, we will search for a 'git-svn' metadata entry. If one is found,
1145 we will compose a commit position from it, using its SVN revision value as
1146 the revision.
1147
1148 If the 'git-svn' URL maps to a known project, we will construct a commit
1149 position branch value by truncating the URL, mapping 'trunk' to
1150 "refs/heads/master". Otherwise, we will return the generic branch, 'svn'.
877 """ 1151 """
878 # TODO(iannucci): Use git-footers for this.
879 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) 1152 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path)
880 footer_map = get_commit_message_footer_map(git_log) 1153 footer_map = get_commit_message_footer_map(git_log)
881 1154
882 # Search for commit position metadata 1155 # Search for commit position metadata
883 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or 1156 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or
884 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) 1157 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY))
885 if value: 1158 if value:
886 return value 1159 return value
1160
1161 # Compose a commit position from 'git-svn' metadata
1162 value = footer_map.get(GIT_SVN_ID_FOOTER_KEY)
1163 if value:
1164 m = GIT_SVN_ID_RE.match(value)
1165 if not m:
1166 raise ValueError("Invalid 'git-svn' value: [%s]" % (value,))
1167 return get_commit_position_for_git_svn(m.group(1), m.group(2))
887 return None 1168 return None
888 1169
889 1170
890 def parse_got_revision(gclient_output, got_revision_mapping): 1171 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs):
891 """Translate git gclient revision mapping to build properties.""" 1172 """Translate git gclient revision mapping to build properties.
1173
1174 If use_svn_revs is True, then translate git hashes in the revision mapping
1175 to svn revision numbers.
1176 """
892 properties = {} 1177 properties = {}
893 solutions_output = { 1178 solutions_output = {
894 # Make sure path always ends with a single slash. 1179 # Make sure path always ends with a single slash.
895 '%s/' % path.rstrip('/') : solution_output for path, solution_output 1180 '%s/' % path.rstrip('/') : solution_output for path, solution_output
896 in gclient_output['solutions'].iteritems() 1181 in gclient_output['solutions'].iteritems()
897 } 1182 }
898 for dir_name, property_name in got_revision_mapping.iteritems(): 1183 for dir_name, property_name in got_revision_mapping.iteritems():
899 # Make sure dir_name always ends with a single slash. 1184 # Make sure dir_name always ends with a single slash.
900 dir_name = '%s/' % dir_name.rstrip('/') 1185 dir_name = '%s/' % dir_name.rstrip('/')
901 if dir_name not in solutions_output: 1186 if dir_name not in solutions_output:
902 continue 1187 continue
903 solution_output = solutions_output[dir_name] 1188 solution_output = solutions_output[dir_name]
904 if solution_output.get('scm') is None: 1189 if solution_output.get('scm') is None:
905 # This is an ignored DEPS, so the output got_revision should be 'None'. 1190 # This is an ignored DEPS, so the output got_revision should be 'None'.
906 git_revision = revision = commit_position = None 1191 git_revision = revision = commit_position = None
907 else: 1192 else:
908 # Since we are using .DEPS.git, everything had better be git. 1193 # Since we are using .DEPS.git, everything had better be git.
909 assert solution_output.get('scm') == 'git' 1194 assert solution_output.get('scm') == 'git'
910 revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() 1195 git_revision = git('rev-parse', 'HEAD', cwd=dir_name).strip()
1196 if use_svn_revs:
1197 revision = get_svn_rev(git_revision, dir_name)
1198 if not revision:
1199 revision = git_revision
1200 else:
1201 revision = git_revision
911 commit_position = get_commit_position(dir_name) 1202 commit_position = get_commit_position(dir_name)
912 1203
913 properties[property_name] = revision 1204 properties[property_name] = revision
914 if revision != git_revision: 1205 if revision != git_revision:
915 properties['%s_git' % property_name] = git_revision 1206 properties['%s_git' % property_name] = git_revision
916 if commit_position: 1207 if commit_position:
917 properties['%s_cp' % property_name] = commit_position 1208 properties['%s_cp' % property_name] = commit_position
918 1209
919 return properties 1210 return properties
920 1211
(...skipping 11 matching lines...) Expand all
932 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): 1223 def ensure_deps_revisions(deps_url_mapping, solutions, revisions):
933 """Ensure correct DEPS revisions, ignores solutions.""" 1224 """Ensure correct DEPS revisions, ignores solutions."""
934 for deps_name, deps_data in sorted(deps_url_mapping.items()): 1225 for deps_name, deps_data in sorted(deps_url_mapping.items()):
935 if deps_name.strip('/') in solutions: 1226 if deps_name.strip('/') in solutions:
936 # This has already been forced to the correct solution by git_checkout(). 1227 # This has already been forced to the correct solution by git_checkout().
937 continue 1228 continue
938 revision = get_target_revision(deps_name, deps_data.get('url', None), 1229 revision = get_target_revision(deps_name, deps_data.get('url', None),
939 revisions) 1230 revisions)
940 if not revision: 1231 if not revision:
941 continue 1232 continue
1233 # TODO(hinoka): Catch SVNRevisionNotFound error maybe?
942 git('fetch', 'origin', cwd=deps_name) 1234 git('fetch', 'origin', cwd=deps_name)
943 force_revision(deps_name, revision) 1235 force_revision(deps_name, revision)
944 1236
945 1237
946 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, 1238 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only,
947 patch_root, issue, patchset, rietveld_server, 1239 patch_root, issue, patchset, rietveld_server,
948 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, 1240 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref,
949 revision_mapping, apply_issue_email_file, 1241 revision_mapping, apply_issue_email_file,
950 apply_issue_key_file, gyp_env, shallow, runhooks, 1242 apply_issue_key_file, buildspec, gyp_env, shallow, runhooks,
951 refs, git_cache_dir, gerrit_reset): 1243 refs, git_cache_dir, gerrit_reset):
952 # Get a checkout of each solution, without DEPS or hooks. 1244 # Get a checkout of each solution, without DEPS or hooks.
953 # Calling git directly because there is no way to run Gclient without 1245 # Calling git directly because there is no way to run Gclient without
954 # invoking DEPS. 1246 # invoking DEPS.
955 print 'Fetching Git checkout' 1247 print 'Fetching Git checkout'
956 1248
957 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) 1249 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir)
958 1250
959 print '===Processing patch solutions===' 1251 print '===Processing patch solutions==='
960 already_patched = [] 1252 already_patched = []
(...skipping 16 matching lines...) Expand all
977 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, 1269 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset,
978 gerrit_rebase_patch_ref) 1270 gerrit_rebase_patch_ref)
979 applied_gerrit_patch = True 1271 applied_gerrit_patch = True
980 1272
981 # Ensure our build/ directory is set up with the correct .gclient file. 1273 # Ensure our build/ directory is set up with the correct .gclient file.
982 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) 1274 gclient_configure(solutions, target_os, target_os_only, git_cache_dir)
983 1275
984 # Let gclient do the DEPS syncing. 1276 # Let gclient do the DEPS syncing.
985 # The branch-head refspec is a special case because its possible Chrome 1277 # The branch-head refspec is a special case because its possible Chrome
986 # src, which contains the branch-head refspecs, is DEPSed in. 1278 # src, which contains the branch-head refspecs, is DEPSed in.
987 gclient_output = gclient_sync(BRANCH_HEADS_REFSPEC in refs, shallow) 1279 gclient_output = gclient_sync(buildspec or BRANCH_HEADS_REFSPEC in refs,
1280 shallow)
988 1281
989 # Now that gclient_sync has finished, we should revert any .DEPS.git so that 1282 # Now that gclient_sync has finished, we should revert any .DEPS.git so that
990 # presubmit doesn't complain about it being modified. 1283 # presubmit doesn't complain about it being modified.
991 if git('ls-files', '.DEPS.git', cwd=first_sln).strip(): 1284 if (not buildspec and
1285 git('ls-files', '.DEPS.git', cwd=first_sln).strip()):
992 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln) 1286 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln)
993 1287
1288 if buildspec and runhooks:
1289 # Run gclient runhooks if we're on an official builder.
1290 # TODO(hinoka): Remove this when the official builders run their own
1291 # runhooks step.
1292 gclient_runhooks(gyp_env)
1293
994 # Finally, ensure that all DEPS are pinned to the correct revision. 1294 # Finally, ensure that all DEPS are pinned to the correct revision.
995 dir_names = [sln['name'] for sln in solutions] 1295 dir_names = [sln['name'] for sln in solutions]
996 ensure_deps_revisions(gclient_output.get('solutions', {}), 1296 ensure_deps_revisions(gclient_output.get('solutions', {}),
997 dir_names, revisions) 1297 dir_names, revisions)
998 # Apply the rest of the patch here (sans DEPS) 1298 # Apply the rest of the patch here (sans DEPS)
999 if issue: 1299 if issue:
1000 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, 1300 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server,
1001 revision_mapping, git_ref, apply_issue_email_file, 1301 revision_mapping, git_ref, apply_issue_email_file,
1002 apply_issue_key_file, blacklist=already_patched) 1302 apply_issue_key_file, blacklist=already_patched)
1003 elif gerrit_ref and not applied_gerrit_patch: 1303 elif gerrit_ref and not applied_gerrit_patch:
(...skipping 25 matching lines...) Expand all
1029 expanded_revisions.extend(revision.split(',')) 1329 expanded_revisions.extend(revision.split(','))
1030 for revision in expanded_revisions: 1330 for revision in expanded_revisions:
1031 split_revision = revision.split('@') 1331 split_revision = revision.split('@')
1032 if len(split_revision) == 1: 1332 if len(split_revision) == 1:
1033 # This is just a plain revision, set it as the revision for root. 1333 # This is just a plain revision, set it as the revision for root.
1034 results[root] = split_revision[0] 1334 results[root] = split_revision[0]
1035 elif len(split_revision) == 2: 1335 elif len(split_revision) == 2:
1036 # This is an alt_root@revision argument. 1336 # This is an alt_root@revision argument.
1037 current_root, current_rev = split_revision 1337 current_root, current_rev = split_revision
1038 1338
1339 # We want to normalize svn/git urls into .git urls.
1039 parsed_root = urlparse.urlparse(current_root) 1340 parsed_root = urlparse.urlparse(current_root)
1040 if parsed_root.scheme in ['http', 'https']: 1341 if parsed_root.scheme == 'svn':
1041 # We want to normalize git urls into .git urls. 1342 if parsed_root.path in RECOGNIZED_PATHS:
1343 normalized_root = RECOGNIZED_PATHS[parsed_root.path]
1344 else:
1345 print 'WARNING: SVN path %s not recognized, ignoring' % current_root
1346 continue
1347 elif parsed_root.scheme in ['http', 'https']:
1042 normalized_root = 'https://%s/%s' % (parsed_root.netloc, 1348 normalized_root = 'https://%s/%s' % (parsed_root.netloc,
1043 parsed_root.path) 1349 parsed_root.path)
1044 if not normalized_root.endswith('.git'): 1350 if not normalized_root.endswith('.git'):
1045 normalized_root = '%s.git' % normalized_root 1351 normalized_root = '%s.git' % normalized_root
1046 elif parsed_root.scheme: 1352 elif parsed_root.scheme:
1047 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme 1353 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme
1048 continue 1354 continue
1049 else: 1355 else:
1050 # This is probably a local path. 1356 # This is probably a local path.
1051 normalized_root = current_root.strip('/') 1357 normalized_root = current_root.strip('/')
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1087 'bot_update\'s whitelist, bot_update will be noop.') 1393 'bot_update\'s whitelist, bot_update will be noop.')
1088 parse.add_option('-f', '--force', action='store_true', 1394 parse.add_option('-f', '--force', action='store_true',
1089 help='Bypass check to see if we want to be run. ' 1395 help='Bypass check to see if we want to be run. '
1090 'Should ONLY be used locally or by smart recipes.') 1396 'Should ONLY be used locally or by smart recipes.')
1091 parse.add_option('--revision_mapping', 1397 parse.add_option('--revision_mapping',
1092 help='{"path/to/repo/": "property_name"}') 1398 help='{"path/to/repo/": "property_name"}')
1093 parse.add_option('--revision_mapping_file', 1399 parse.add_option('--revision_mapping_file',
1094 help=('Same as revision_mapping, except its a path to a json' 1400 help=('Same as revision_mapping, except its a path to a json'
1095 ' file containing that format.')) 1401 ' file containing that format.'))
1096 parse.add_option('--revision', action='append', default=[], 1402 parse.add_option('--revision', action='append', default=[],
1097 help='Revision to check out. Can be any form of git ref. ' 1403 help='Revision to check out. Can be an SVN revision number, '
1098 'Can prepend root@<rev> to specify which repository, ' 1404 'git hash, or any form of git ref. Can prepend '
1099 'where root is either a filesystem path or git https ' 1405 'root@<rev> to specify which repository, where root '
1100 'url. To specify Tip of Tree, set rev to HEAD. ') 1406 'is either a filesystem path, git https url, or '
1407 'svn url. To specify Tip of Tree, set rev to HEAD.'
1408 'To specify a git branch and an SVN rev, <rev> can be '
1409 'set to <branch>:<revision>.')
1101 parse.add_option('--output_manifest', action='store_true', 1410 parse.add_option('--output_manifest', action='store_true',
1102 help=('Add manifest json to the json output.')) 1411 help=('Add manifest json to the json output.'))
1103 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], 1412 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0],
1104 help='Hostname of the current machine, ' 1413 help='Hostname of the current machine, '
1105 'used for determining whether or not to activate.') 1414 'used for determining whether or not to activate.')
1106 parse.add_option('--builder_name', help='Name of the builder, ' 1415 parse.add_option('--builder_name', help='Name of the builder, '
1107 'used for determining whether or not to activate.') 1416 'used for determining whether or not to activate.')
1108 parse.add_option('--build_dir', default=os.getcwd()) 1417 parse.add_option('--build_dir', default=os.getcwd())
1109 parse.add_option('--flag_file', default=path.join(os.getcwd(), 1418 parse.add_option('--flag_file', default=path.join(os.getcwd(),
1110 'update.flag')) 1419 'update.flag'))
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1164 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') 1473 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\')
1165 1474
1166 return options, args 1475 return options, args
1167 1476
1168 1477
1169 def prepare(options, git_slns, active): 1478 def prepare(options, git_slns, active):
1170 """Prepares the target folder before we checkout.""" 1479 """Prepares the target folder before we checkout."""
1171 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] 1480 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
1172 # If we're active now, but the flag file doesn't exist (we weren't active 1481 # If we're active now, but the flag file doesn't exist (we weren't active
1173 # last run) or vice versa, blow away all checkouts. 1482 # last run) or vice versa, blow away all checkouts.
1174 if options.clobber or (bool(active) != bool(check_flag(options.flag_file))): 1483 if bool(active) != bool(check_flag(options.flag_file)):
1175 ensure_no_checkout(dir_names) 1484 ensure_no_checkout(dir_names, '*')
1176 if options.output_json: 1485 if options.output_json:
1177 # Make sure we tell recipes that we didn't run if the script exits here. 1486 # Make sure we tell recipes that we didn't run if the script exits here.
1178 emit_json(options.output_json, did_run=active) 1487 emit_json(options.output_json, did_run=active)
1179 emit_flag(options.flag_file) 1488 if active:
1489 if options.clobber:
1490 ensure_no_checkout(dir_names, '*')
1491 else:
1492 ensure_no_checkout(dir_names, '.svn')
1493 emit_flag(options.flag_file)
1494 else:
1495 delete_flag(options.flag_file)
1496 raise Inactive # This is caught in main() and we exit cleanly.
1180 1497
1181 # Do a shallow checkout if the disk is less than 100GB. 1498 # Do a shallow checkout if the disk is less than 100GB.
1182 total_disk_space, free_disk_space = get_total_disk_space() 1499 total_disk_space, free_disk_space = get_total_disk_space()
1183 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) 1500 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024))
1184 used_disk_space_gb = int((total_disk_space - free_disk_space) 1501 used_disk_space_gb = int((total_disk_space - free_disk_space)
1185 / (1024 * 1024 * 1024)) 1502 / (1024 * 1024 * 1024))
1186 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) 1503 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb)
1187 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, 1504 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb,
1188 total_disk_space_gb, 1505 total_disk_space_gb,
1189 percent_used) 1506 percent_used)
1190 if not options.output_json: 1507 if not options.output_json:
1191 print '@@@STEP_TEXT@%s@@@' % step_text 1508 print '@@@STEP_TEXT@%s@@@' % step_text
1192 if not options.shallow: 1509 if not options.shallow:
1193 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD 1510 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD
1194 and not options.no_shallow) 1511 and not options.no_shallow)
1195 1512
1196 # The first solution is where the primary DEPS file resides. 1513 # The first solution is where the primary DEPS file resides.
1197 first_sln = dir_names[0] 1514 first_sln = dir_names[0]
1198 1515
1199 # Split all the revision specifications into a nice dict. 1516 # Split all the revision specifications into a nice dict.
1200 print 'Revisions: %s' % options.revision 1517 print 'Revisions: %s' % options.revision
1201 revisions = parse_revisions(options.revision, first_sln) 1518 revisions = parse_revisions(options.revision, first_sln)
1202 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) 1519 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln])
1203 return revisions, step_text 1520 return revisions, step_text
1204 1521
1205 1522
1206 def checkout(options, git_slns, specs, master, revisions, step_text): 1523 def checkout(options, git_slns, specs, buildspec, master,
1524 svn_root, revisions, step_text):
1207 first_sln = git_slns[0]['name'] 1525 first_sln = git_slns[0]['name']
1208 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] 1526 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
1209 try: 1527 try:
1210 # Outer try is for catching patch failures and exiting gracefully. 1528 # Outer try is for catching patch failures and exiting gracefully.
1211 # Inner try is for catching gclient failures and retrying gracefully. 1529 # Inner try is for catching gclient failures and retrying gracefully.
1212 try: 1530 try:
1213 checkout_parameters = dict( 1531 checkout_parameters = dict(
1214 # First, pass in the base of what we want to check out. 1532 # First, pass in the base of what we want to check out.
1215 solutions=git_slns, 1533 solutions=git_slns,
1216 revisions=revisions, 1534 revisions=revisions,
1217 first_sln=first_sln, 1535 first_sln=first_sln,
1218 1536
1219 # Also, target os variables for gclient. 1537 # Also, target os variables for gclient.
1220 target_os=specs.get('target_os', []), 1538 target_os=specs.get('target_os', []),
1221 target_os_only=specs.get('target_os_only', False), 1539 target_os_only=specs.get('target_os_only', False),
1222 1540
1223 # Then, pass in information about how to patch. 1541 # Then, pass in information about how to patch.
1224 patch_root=options.patch_root, 1542 patch_root=options.patch_root,
1225 issue=options.issue, 1543 issue=options.issue,
1226 patchset=options.patchset, 1544 patchset=options.patchset,
1227 rietveld_server=options.rietveld_server, 1545 rietveld_server=options.rietveld_server,
1228 gerrit_repo=options.gerrit_repo, 1546 gerrit_repo=options.gerrit_repo,
1229 gerrit_ref=options.gerrit_ref, 1547 gerrit_ref=options.gerrit_ref,
1230 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, 1548 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref,
1231 revision_mapping=options.revision_mapping, 1549 revision_mapping=options.revision_mapping,
1232 apply_issue_email_file=options.apply_issue_email_file, 1550 apply_issue_email_file=options.apply_issue_email_file,
1233 apply_issue_key_file=options.apply_issue_key_file, 1551 apply_issue_key_file=options.apply_issue_key_file,
1234 1552
1235 # For official builders. 1553 # For official builders.
1554 buildspec=buildspec,
1236 gyp_env=options.gyp_env, 1555 gyp_env=options.gyp_env,
1237 runhooks=not options.no_runhooks, 1556 runhooks=not options.no_runhooks,
1238 1557
1239 # Finally, extra configurations such as shallowness of the clone. 1558 # Finally, extra configurations such as shallowness of the clone.
1240 shallow=options.shallow, 1559 shallow=options.shallow,
1241 refs=options.refs, 1560 refs=options.refs,
1242 git_cache_dir=options.git_cache_dir, 1561 git_cache_dir=options.git_cache_dir,
1243 gerrit_reset=not options.gerrit_no_reset) 1562 gerrit_reset=not options.gerrit_no_reset)
1244 gclient_output = ensure_checkout(**checkout_parameters) 1563 gclient_output = ensure_checkout(**checkout_parameters)
1245 except GclientSyncFailed: 1564 except GclientSyncFailed:
1246 print 'We failed gclient sync, lets delete the checkout and retry.' 1565 print 'We failed gclient sync, lets delete the checkout and retry.'
1247 ensure_no_checkout(dir_names) 1566 ensure_no_checkout(dir_names, '*')
1248 gclient_output = ensure_checkout(**checkout_parameters) 1567 gclient_output = ensure_checkout(**checkout_parameters)
1249 except PatchFailed as e: 1568 except PatchFailed as e:
1250 if options.output_json: 1569 if options.output_json:
1251 # Tell recipes information such as root, got_revision, etc. 1570 # Tell recipes information such as root, got_revision, etc.
1252 emit_json(options.output_json, 1571 emit_json(options.output_json,
1253 did_run=True, 1572 did_run=True,
1254 root=first_sln, 1573 root=first_sln,
1255 log_lines=[('patch error', e.output),], 1574 log_lines=[('patch error', e.output),],
1256 patch_apply_return_code=e.code, 1575 patch_apply_return_code=e.code,
1257 patch_root=options.patch_root, 1576 patch_root=options.patch_root,
1258 patch_failure=True, 1577 patch_failure=True,
1259 step_text='%s PATCH FAILED' % step_text, 1578 step_text='%s PATCH FAILED' % step_text,
1260 fixed_revisions=revisions) 1579 fixed_revisions=revisions)
1261 else: 1580 else:
1262 # If we're not on recipes, tell annotator about our got_revisions. 1581 # If we're not on recipes, tell annotator about our got_revisions.
1263 emit_log_lines('patch error', e.output) 1582 emit_log_lines('patch error', e.output)
1264 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text 1583 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text
1265 raise 1584 raise
1266 1585
1586 # Revision is an svn revision, unless it's a git master.
1587 use_svn_rev = master not in GIT_MASTERS
1588
1267 # Take care of got_revisions outputs. 1589 # Take care of got_revisions outputs.
1268 revision_mapping = GOT_REVISION_MAPPINGS.get(git_slns[0]['url'], {}) 1590 revision_mapping = dict(GOT_REVISION_MAPPINGS.get(svn_root, {}))
1269 if options.revision_mapping: 1591 if options.revision_mapping:
1270 revision_mapping.update(options.revision_mapping) 1592 revision_mapping.update(options.revision_mapping)
1271 1593
1272 # If the repo is not in the default GOT_REVISION_MAPPINGS and no 1594 # If the repo is not in the default GOT_REVISION_MAPPINGS and no
1273 # revision_mapping were specified on the command line then 1595 # revision_mapping were specified on the command line then
1274 # default to setting 'got_revision' based on the first solution. 1596 # default to setting 'got_revision' based on the first solution.
1275 if not revision_mapping: 1597 if not revision_mapping:
1276 revision_mapping[first_sln] = 'got_revision' 1598 revision_mapping[first_sln] = 'got_revision'
1277 1599
1278 got_revisions = parse_got_revision(gclient_output, revision_mapping) 1600 got_revisions = parse_got_revision(gclient_output, revision_mapping,
1601 use_svn_rev)
1279 1602
1280 if not got_revisions: 1603 if not got_revisions:
1281 # TODO(hinoka): We should probably bail out here, but in the interest 1604 # TODO(hinoka): We should probably bail out here, but in the interest
1282 # of giving mis-configured bots some time to get fixed use a dummy 1605 # of giving mis-configured bots some time to get fixed use a dummy
1283 # revision here. 1606 # revision here.
1284 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } 1607 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' }
1285 #raise Exception('No got_revision(s) found in gclient output') 1608 #raise Exception('No got_revision(s) found in gclient output')
1286 1609
1287 if options.output_json: 1610 if options.output_json:
1288 manifest = create_manifest() if options.output_manifest else None 1611 manifest = create_manifest() if options.output_manifest else None
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1342 # Check if this script should activate or not. 1665 # Check if this script should activate or not.
1343 active = options.force or check_valid_host(master, builder, slave) 1666 active = options.force or check_valid_host(master, builder, slave)
1344 1667
1345 # Print a helpful message to tell developers whats going on with this step. 1668 # Print a helpful message to tell developers whats going on with this step.
1346 print_help_text( 1669 print_help_text(
1347 options.force, options.output_json, active, master, builder, slave) 1670 options.force, options.output_json, active, master, builder, slave)
1348 1671
1349 # Parse, munipulate, and print the gclient solutions. 1672 # Parse, munipulate, and print the gclient solutions.
1350 specs = {} 1673 specs = {}
1351 exec(options.specs, specs) 1674 exec(options.specs, specs)
1352 orig_solutions = specs.get('solutions', []) 1675 svn_solutions = specs.get('solutions', [])
1353 git_slns = modify_solutions(orig_solutions) 1676 git_slns, svn_root, buildspec = solutions_to_git(svn_solutions)
1677 options.revision = maybe_ignore_revision(options.revision, buildspec)
1354 1678
1355 solutions_printer(git_slns) 1679 solutions_printer(git_slns)
1356 1680
1357 try: 1681 try:
1358 # Dun dun dun, the main part of bot_update. 1682 # Dun dun dun, the main part of bot_update.
1359 revisions, step_text = prepare(options, git_slns, active) 1683 revisions, step_text = prepare(options, git_slns, active)
1360 checkout(options, git_slns, specs, master, revisions, step_text) 1684 checkout(options, git_slns, specs, buildspec, master, svn_root, revisions,
1685 step_text)
1361 1686
1687 except Inactive:
1688 # Not active, should count as passing.
1689 pass
1362 except PatchFailed as e: 1690 except PatchFailed as e:
1363 emit_flag(options.flag_file) 1691 emit_flag(options.flag_file)
1364 # Return a specific non-zero exit code for patch failure (because it is 1692 # Return a specific non-zero exit code for patch failure (because it is
1365 # a failure), but make it different than other failures to distinguish 1693 # a failure), but make it different than other failures to distinguish
1366 # between infra failures (independent from patch author), and patch 1694 # between infra failures (independent from patch author), and patch
1367 # failures (that patch author can fix). However, PatchFailure due to 1695 # failures (that patch author can fix). However, PatchFailure due to
1368 # download patch failure is still an infra problem. 1696 # download patch failure is still an infra problem.
1369 if e.code == 3: 1697 if e.code == 3:
1370 # Patch download problem. 1698 # Patch download problem.
1371 return 87 1699 return 87
1372 # Genuine patch problem. 1700 # Genuine patch problem.
1373 return 88 1701 return 88
1374 except Exception: 1702 except Exception:
1375 # Unexpected failure. 1703 # Unexpected failure.
1376 emit_flag(options.flag_file) 1704 emit_flag(options.flag_file)
1377 raise 1705 raise
1378 else: 1706 else:
1379 emit_flag(options.flag_file) 1707 emit_flag(options.flag_file)
1380 1708
1381 1709
1382 if __name__ == '__main__': 1710 if __name__ == '__main__':
1383 sys.exit(main()) 1711 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