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

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

Issue 2280213002: 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 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
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. 89 # Official builds use buildspecs, so this is a special case.
90 BUILDSPEC_TYPE = collections.namedtuple('buildspec', 90 BUILDSPEC_TYPE = collections.namedtuple('buildspec',
91 ('container', 'version')) 91 ('container', 'version'))
92 BUILDSPEC_RE = (r'^/chrome-internal/trunk/tools/buildspec/' 92 BUILDSPEC_RE = (r'^/chrome/tools/buildspec/\+/master/'
hinoka 2016/08/26 21:59:54 If this doesn't work / isn't invoked to begin with
agable 2016/08/29 19:09:58 Deleted a bunch of buildspec code now.
93 '(build|branches|releases)/(.+)$') 93 '(build|branches|releases)/(.+)$')
94 GIT_BUILDSPEC_PATH = ('https://chrome-internal.googlesource.com/chrome/tools/' 94 GIT_BUILDSPEC_PATH = ('https://chrome-internal.googlesource.com/chrome/tools/'
95 'buildspec') 95 'buildspec')
96 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' 96 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*'
97 97
98 BUILDSPEC_COMMIT_RE = ( 98 BUILDSPEC_COMMIT_RE = (
99 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'), 99 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'),
100 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'), 100 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'),
101 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'),
102 ) 102 )
103 103
104 # Regular expression that matches a single commit footer line. 104 # Regular expression that matches a single commit footer line.
105 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') 105 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)')
106 106
107 # Footer metadata keys for regular and gsubtreed mirrored commit positions. 107 # Footer metadata keys for regular and gsubtreed mirrored commit positions.
108 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' 108 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position'
109 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' 109 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position'
110 # Regular expression to parse a commit position 110 # Regular expression to parse a commit position
111 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}') 111 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}')
112 112
113 # Regular expression to parse gclient's revinfo entries. 113 # Regular expression to parse gclient's revinfo entries.
114 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') 114 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$')
115 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')
155 116
156 # Copied from scripts/recipes/chromium.py. 117 # Copied from scripts/recipes/chromium.py.
157 GOT_REVISION_MAPPINGS = { 118 GOT_REVISION_MAPPINGS = {
158 '/chrome/trunk/src': { 119 CHROMIUM_SRC_URL: {
159 'src/': 'got_revision', 120 'src/': 'got_revision',
160 'src/native_client/': 'got_nacl_revision', 121 'src/native_client/': 'got_nacl_revision',
161 'src/tools/swarm_client/': 'got_swarm_client_revision', 122 'src/tools/swarm_client/': 'got_swarm_client_revision',
162 'src/tools/swarming_client/': 'got_swarming_client_revision', 123 'src/tools/swarming_client/': 'got_swarming_client_revision',
163 'src/third_party/WebKit/': 'got_webkit_revision', 124 'src/third_party/WebKit/': 'got_webkit_revision',
164 'src/third_party/webrtc/': 'got_webrtc_revision', 125 'src/third_party/webrtc/': 'got_webrtc_revision',
165 'src/v8/': 'got_v8_revision', 126 'src/v8/': 'got_v8_revision',
166 } 127 }
167 } 128 }
168 129
169 130
170 BOT_UPDATE_MESSAGE = """ 131 BOT_UPDATE_MESSAGE = """
171 What is the "Bot Update" step? 132 What is the "Bot Update" step?
172 ============================== 133 ==============================
173 134
174 This step ensures that the source checkout on the bot (e.g. Chromium's src/ and 135 This step ensures that the source checkout on the bot (e.g. Chromium's src/ and
175 its dependencies) is checked out in a consistent state. This means that all of 136 its dependencies) is checked out in a consistent state. This means that all of
176 the necessary repositories are checked out, no extra repositories are checked 137 the necessary repositories are checked out, no extra repositories are checked
177 out, and no locally modified files are present. 138 out, and no locally modified files are present.
178 139
179 These actions used to be taken care of by the "gclient revert" and "update" 140 These actions used to be taken care of by the "gclient revert" and "update"
180 steps. However, those steps are known to be buggy and occasionally flaky. This 141 steps. However, those steps are known to be buggy and occasionally flaky. This
181 step has two main advantages over them: 142 step has two main advantages over them:
182 * it only operates in Git, so the logic can be clearer and cleaner; and 143 * it only operates in Git, so the logic can be clearer and cleaner; and
183 * it is a slave-side script, so its behavior can be modified without 144 * it is a slave-side script, so its behavior can be modified without
184 restarting the master. 145 restarting the master.
185 146
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
198 Debugging information: 147 Debugging information:
199 (master/builder/slave may be unspecified on recipes) 148 (master/builder/slave may be unspecified on recipes)
200 master: %(master)s 149 master: %(master)s
201 builder: %(builder)s 150 builder: %(builder)s
202 slave: %(slave)s 151 slave: %(slave)s
203 forced by recipes: %(recipe)s 152 forced by recipes: %(recipe)s
204 CURRENT_DIR: %(CURRENT_DIR)s 153 CURRENT_DIR: %(CURRENT_DIR)s
205 BUILDER_DIR: %(BUILDER_DIR)s 154 BUILDER_DIR: %(BUILDER_DIR)s
206 SLAVE_DIR: %(SLAVE_DIR)s 155 SLAVE_DIR: %(SLAVE_DIR)s
207 THIS_DIR: %(THIS_DIR)s 156 THIS_DIR: %(THIS_DIR)s
(...skipping 29 matching lines...) Expand all
237 try: 186 try:
238 execfile(os.path.join( 187 execfile(os.path.join(
239 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), 188 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'),
240 local_vars) 189 local_vars)
241 except Exception: 190 except Exception:
242 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. 191 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place.
243 print 'Warning: unable to read internal configuration file.' 192 print 'Warning: unable to read internal configuration file.'
244 print 'If this is an internal bot, this step may be erroneously inactive.' 193 print 'If this is an internal bot, this step may be erroneously inactive.'
245 internal_data = local_vars 194 internal_data = local_vars
246 195
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', {}))
257 196
258 ENABLED_MASTERS = [ 197 ENABLED_MASTERS = [
259 'bot_update.always_on', 198 'bot_update.always_on',
260 'chromium.android', 199 'chromium.android',
261 'chromium.angle', 200 'chromium.angle',
262 'chromium.chrome', 201 'chromium.chrome',
263 'chromium.chromedriver', 202 'chromium.chromedriver',
264 'chromium.chromiumos', 203 'chromium.chromiumos',
265 'chromium', 204 'chromium',
266 'chromium.fyi', 205 'chromium.fyi',
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 269
331 # Disabled filters get run AFTER enabled filters, so for example if a builder 270 # Disabled filters get run AFTER enabled filters, so for example if a builder
332 # config is enabled, but a bot on that builder is disabled, that bot will 271 # config is enabled, but a bot on that builder is disabled, that bot will
333 # be disabled. 272 # be disabled.
334 DISABLED_BUILDERS = {} 273 DISABLED_BUILDERS = {}
335 DISABLED_BUILDERS.update(internal_data.get('DISABLED_BUILDERS', {})) 274 DISABLED_BUILDERS.update(internal_data.get('DISABLED_BUILDERS', {}))
336 275
337 DISABLED_SLAVES = {} 276 DISABLED_SLAVES = {}
338 DISABLED_SLAVES.update(internal_data.get('DISABLED_SLAVES', {})) 277 DISABLED_SLAVES.update(internal_data.get('DISABLED_SLAVES', {}))
339 278
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
351 # How many times to try before giving up. 279 # How many times to try before giving up.
352 ATTEMPTS = 5 280 ATTEMPTS = 5
353 281
354 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') 282 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py')
355 283
356 # Find the patch tool. 284 # Find the patch tool.
357 if sys.platform.startswith('win'): 285 if sys.platform.startswith('win'):
358 if not BUILD_INTERNAL_DIR: 286 if not BUILD_INTERNAL_DIR:
359 print 'Warning: could not find patch tool because there is no ' 287 print 'Warning: could not find patch tool because there is no '
360 print 'build_internal present.' 288 print 'build_internal present.'
(...skipping 16 matching lines...) Expand all
377 305
378 306
379 class PatchFailed(SubprocessFailed): 307 class PatchFailed(SubprocessFailed):
380 pass 308 pass
381 309
382 310
383 class GclientSyncFailed(SubprocessFailed): 311 class GclientSyncFailed(SubprocessFailed):
384 pass 312 pass
385 313
386 314
387 class SVNRevisionNotFound(Exception):
388 pass
389
390
391 class InvalidDiff(Exception): 315 class InvalidDiff(Exception):
392 pass 316 pass
393 317
394 318
395 class Inactive(Exception): 319 class Inactive(Exception):
396 """Not really an exception, just used to exit early cleanly.""" 320 """Not really an exception, just used to exit early cleanly."""
397 pass 321 pass
398 322
399 323
400 RETRY = object() 324 RETRY = object()
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
594 print ' %s: Ignore' % deps_name 518 print ' %s: Ignore' % deps_name
595 for k, v in solution.iteritems(): 519 for k, v in solution.iteritems():
596 # Print out all the keys we don't know about. 520 # Print out all the keys we don't know about.
597 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', 521 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps',
598 'managed']: 522 'managed']:
599 continue 523 continue
600 print ' %s is %s' % (k, v) 524 print ' %s is %s' % (k, v)
601 print 525 print
602 526
603 527
604 def solutions_to_git(input_solutions): 528 def modify_solutions(input_solutions):
605 """Modifies urls in solutions to point at Git repos. 529 """Modifies urls in solutions to point at Git repos.
606 530
607 returns: (git solution, svn root of first solution) tuple. 531 returns: (git solution, buildspec) tuple.
608 """ 532 """
609 assert input_solutions 533 assert input_solutions
610 solutions = copy.deepcopy(input_solutions) 534 solutions = copy.deepcopy(input_solutions)
611 first_solution = True 535 first_solution = True
612 buildspec = None 536 buildspec = None
613 for solution in solutions: 537 for solution in solutions:
614 original_url = solution['url'] 538 original_url = solution['url']
615 parsed_url = urlparse.urlparse(original_url) 539 parsed_url = urlparse.urlparse(original_url)
616 parsed_path = parsed_url.path 540 parsed_path = parsed_url.path
617 541
618 # Rewrite SVN urls into Git urls. 542 # Rewrite SVN urls into Git urls.
619 buildspec_m = re.match(BUILDSPEC_RE, parsed_path) 543 buildspec_m = re.match(BUILDSPEC_RE, parsed_path)
620 if first_solution and buildspec_m: 544 if first_solution and buildspec_m:
621 solution['url'] = GIT_BUILDSPEC_PATH 545 solution['url'] = GIT_BUILDSPEC_PATH
622 buildspec = BUILDSPEC_TYPE( 546 buildspec = BUILDSPEC_TYPE(
623 container=buildspec_m.group(1), 547 container=buildspec_m.group(1),
624 version=buildspec_m.group(2), 548 version=buildspec_m.group(2),
625 ) 549 )
626 solution['deps_file'] = path.join(buildspec.container, buildspec.version, 550 solution['deps_file'] = path.join(
627 'DEPS') 551 buildspec.container, buildspec.version, '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: 552 elif parsed_url.scheme == 'https' and 'googlesource' in parsed_url.netloc:
632 pass 553 pass
633 else: 554 else:
634 print 'Warning: %s' % ('path %r not recognized' % parsed_path,) 555 print 'Warning: %s' % ('path %r not recognized' % parsed_path,)
635 556
636 # Strip out deps containing $$V8_REV$$, etc. 557 # Strip out deps containing $$V8_REV$$, etc.
637 if 'custom_deps' in solution: 558 if 'custom_deps' in solution:
638 new_custom_deps = {} 559 new_custom_deps = {}
639 for deps_name, deps_value in solution['custom_deps'].iteritems(): 560 for deps_name, deps_value in solution['custom_deps'].iteritems():
640 if deps_value and '$$' in deps_value: 561 if deps_value and '$$' in deps_value:
641 print 'Dropping %s:%s from custom deps' % (deps_name, deps_value) 562 print 'Dropping %s:%s from custom deps' % (deps_name, deps_value)
642 else: 563 else:
643 new_custom_deps[deps_name] = deps_value 564 new_custom_deps[deps_name] = deps_value
644 solution['custom_deps'] = new_custom_deps 565 solution['custom_deps'] = new_custom_deps
645 566
646 if first_solution:
647 root = parsed_path
648 first_solution = False
649
650 solution['managed'] = False 567 solution['managed'] = False
651 # We don't want gclient to be using a safesync URL. Instead it should 568 # We don't want gclient to be using a safesync URL. Instead it should
652 # using the lkgr/lkcr branch/tags. 569 # using the lkgr/lkcr branch/tags.
653 if 'safesync_url' in solution: 570 if 'safesync_url' in solution:
654 print 'Removing safesync url %s from %s' % (solution['safesync_url'], 571 print 'Removing safesync url %s from %s' % (solution['safesync_url'],
655 parsed_path) 572 parsed_path)
656 del solution['safesync_url'] 573 del solution['safesync_url']
657 return solutions, root, buildspec 574 first_solution = False
575
576 return solutions, buildspec
658 577
659 578
660 def remove(target): 579 def remove(target):
661 """Remove a target by moving it into build.dead.""" 580 """Remove a target by moving it into build.dead."""
662 dead_folder = path.join(BUILDER_DIR, 'build.dead') 581 dead_folder = path.join(BUILDER_DIR, 'build.dead')
663 if not path.exists(dead_folder): 582 if not path.exists(dead_folder):
664 os.makedirs(dead_folder) 583 os.makedirs(dead_folder)
665 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) 584 os.rename(target, path.join(dead_folder, uuid.uuid4().hex))
666 585
667 586
668 def ensure_no_checkout(dir_names, scm_dirname): 587 def ensure_no_checkout(dir_names):
669 """Ensure that there is no undesired checkout under build/. 588 """Ensure that there is no undesired checkout under build/."""
670 589 has_checkout = any(path.exists(path.join(os.getcwd(), 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))
679 for dir_name in dir_names) 590 for dir_name in dir_names)
680 591
681 if has_checkout or scm_dirname == '*': 592 if has_checkout:
682 build_dir = os.getcwd() 593 build_dir = os.getcwd()
683 prefix = ''
684 if scm_dirname != '*':
685 prefix = '%s detected in checkout, ' % scm_dirname
686
687 for filename in os.listdir(build_dir): 594 for filename in os.listdir(build_dir):
688 deletion_target = path.join(build_dir, filename) 595 deletion_target = path.join(build_dir, filename)
689 print '%sdeleting %s...' % (prefix, deletion_target), 596 print '.git detected in checkout, deleting %s...' % deletion_target,
690 remove(deletion_target) 597 remove(deletion_target)
691 print 'done' 598 print 'done'
692 599
693 600
694 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): 601 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir):
695 """Should do the same thing as gclient --spec='...'.""" 602 """Should do the same thing as gclient --spec='...'."""
696 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: 603 with codecs.open('.gclient', mode='w', encoding='utf-8') as f:
697 f.write(get_gclient_spec( 604 f.write(get_gclient_spec(
698 solutions, target_os, target_os_only, git_cache_dir)) 605 solutions, target_os, target_os_only, git_cache_dir))
699 606
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 footers[m.group(1)] = m.group(2).strip() 680 footers[m.group(1)] = m.group(2).strip()
774 return footers 681 return footers
775 682
776 683
777 def get_commit_message_footer(message, key): 684 def get_commit_message_footer(message, key):
778 """Returns: (str/None) The footer value for 'key', or None if none was found. 685 """Returns: (str/None) The footer value for 'key', or None if none was found.
779 """ 686 """
780 return get_commit_message_footer_map(message).get(key) 687 return get_commit_message_footer_map(message).get(key)
781 688
782 689
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
809 def emit_log_lines(name, lines): 690 def emit_log_lines(name, lines):
810 for line in lines.splitlines(): 691 for line in lines.splitlines():
811 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) 692 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line)
812 print '@@@STEP_LOG_END@%s@@@' % name 693 print '@@@STEP_LOG_END@%s@@@' % name
813 694
814 695
815 def emit_properties(properties): 696 def emit_properties(properties):
816 for property_name, property_value in sorted(properties.items()): 697 for property_name, property_value in sorted(properties.items()):
817 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) 698 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value)
818 699
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
853 734
854 735
855 def force_revision(folder_name, revision): 736 def force_revision(folder_name, revision):
856 split_revision = revision.split(':', 1) 737 split_revision = revision.split(':', 1)
857 branch = 'master' 738 branch = 'master'
858 if len(split_revision) == 2: 739 if len(split_revision) == 2:
859 # Support for "branch:revision" syntax. 740 # Support for "branch:revision" syntax.
860 branch, revision = split_revision 741 branch, revision = split_revision
861 742
862 if revision and revision.upper() != 'HEAD': 743 if revision and revision.upper() != 'HEAD':
863 if revision and revision.isdigit() and len(revision) < 40: 744 git('checkout', '--force', revision, cwd=folder_name)
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)
870 else: 745 else:
871 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch 746 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch
872 git('checkout', '--force', ref, cwd=folder_name) 747 git('checkout', '--force', ref, cwd=folder_name)
873 748
749
874 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): 750 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir):
875 build_dir = os.getcwd() 751 build_dir = os.getcwd()
876 # Before we do anything, break all git_cache locks. 752 # Before we do anything, break all git_cache locks.
877 if path.isdir(git_cache_dir): 753 if path.isdir(git_cache_dir):
878 git('cache', 'unlock', '-vv', '--force', '--all', 754 git('cache', 'unlock', '-vv', '--force', '--all',
879 '--cache-dir', git_cache_dir) 755 '--cache-dir', git_cache_dir)
880 for item in os.listdir(git_cache_dir): 756 for item in os.listdir(git_cache_dir):
881 filename = os.path.join(git_cache_dir, item) 757 filename = os.path.join(git_cache_dir, item)
882 if item.endswith('.lock'): 758 if item.endswith('.lock'):
883 raise Exception('%s exists after cache unlock' % filename) 759 raise Exception('%s exists after cache unlock' % filename)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
924 # Exited abnormally, theres probably something wrong. 800 # Exited abnormally, theres probably something wrong.
925 # Lets wipe the checkout and try again. 801 # Lets wipe the checkout and try again.
926 tries_left -= 1 802 tries_left -= 1
927 if tries_left > 0: 803 if tries_left > 0:
928 print 'Something failed: %s.' % str(e) 804 print 'Something failed: %s.' % str(e)
929 print 'waiting 5 seconds and trying again...' 805 print 'waiting 5 seconds and trying again...'
930 time.sleep(5) 806 time.sleep(5)
931 else: 807 else:
932 raise 808 raise
933 remove(sln_dir) 809 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
944 810
945 git('clean', '-dff', cwd=sln_dir) 811 git('clean', '-dff', cwd=sln_dir)
946 812
947 if first_solution: 813 if first_solution:
948 git_ref = git('log', '--format=%H', '--max-count=1', 814 git_ref = git('log', '--format=%H', '--max-count=1',
949 cwd=sln_dir).strip() 815 cwd=sln_dir).strip()
950 first_solution = False 816 first_solution = False
951 return git_ref 817 return git_ref
952 818
953 819
954 def _download(url): 820 def _download(url):
955 """Fetch url and return content, with retries for flake.""" 821 """Fetch url and return content, with retries for flake."""
956 for attempt in xrange(ATTEMPTS): 822 for attempt in xrange(ATTEMPTS):
957 try: 823 try:
958 return urllib2.urlopen(url).read() 824 return urllib2.urlopen(url).read()
959 except Exception: 825 except Exception:
960 if attempt == ATTEMPTS - 1: 826 if attempt == ATTEMPTS - 1:
961 raise 827 raise
962 828
963 829
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
1009 def get_svn_patch(patch_url):
1010 """Fetch patch from patch_url, return list of (filename, diff)"""
1011 svn_exe = 'svn.bat' if sys.platform.startswith('win') else 'svn'
1012 patch_data = call(svn_exe, 'cat', patch_url)
1013 return parse_diff(patch_data)
1014
1015
1016 def apply_svn_patch(patch_root, patches, whitelist=None, blacklist=None):
1017 """Expects a list of (filename, diff), applies it on top of patch_root."""
1018 if whitelist:
1019 patches = [(name, diff) for name, diff in patches if name in whitelist]
1020 elif blacklist:
1021 patches = [(name, diff) for name, diff in patches if name not in blacklist]
1022 diffs = [diff for _, diff in patches]
1023 patch = ''.join(diffs)
1024
1025 if patch:
1026 print '===Patching files==='
1027 for filename, _ in patches:
1028 print 'Patching %s' % filename
1029 try:
1030 call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward',
1031 stdin_data=patch, cwd=patch_root, tries=1)
1032 for filename, _ in patches:
1033 full_filename = path.abspath(path.join(patch_root, filename))
1034 git('add', full_filename, cwd=path.dirname(full_filename))
1035 except SubprocessFailed as e:
1036 raise PatchFailed(e.message, e.code, e.output)
1037
1038 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, 830 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision,
1039 email_file, key_file, whitelist=None, blacklist=None): 831 email_file, key_file, whitelist=None, blacklist=None):
1040 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') 832 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win')
1041 else 'apply_issue') 833 else 'apply_issue')
1042 cmd = [apply_issue_bin, 834 cmd = [apply_issue_bin,
1043 # The patch will be applied on top of this directory. 835 # The patch will be applied on top of this directory.
1044 '--root_dir', root, 836 '--root_dir', root,
1045 # Tell apply_issue how to fetch the patch. 837 # Tell apply_issue how to fetch the patch.
1046 '--issue', issue, 838 '--issue', issue,
1047 '--server', server, 839 '--server', server,
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1123 os.remove(flag_file) 915 os.remove(flag_file)
1124 916
1125 917
1126 def emit_flag(flag_file): 918 def emit_flag(flag_file):
1127 """Deposit a bot update flag on the system to tell gclient not to run.""" 919 """Deposit a bot update flag on the system to tell gclient not to run."""
1128 print 'Emitting flag file at %s' % flag_file 920 print 'Emitting flag file at %s' % flag_file
1129 with open(flag_file, 'wb') as f: 921 with open(flag_file, 'wb') as f:
1130 f.write('Success!') 922 f.write('Success!')
1131 923
1132 924
1133 def get_commit_position_for_git_svn(url, revision):
1134 """Generates a commit position string for a 'git-svn' URL/revision.
1135
1136 If the 'git-svn' URL maps to a known project, we will construct a commit
1137 position branch value by applying substitution on the SVN URL.
1138 """
1139 # Identify the base URL so we can strip off trunk/branch name
1140 project_config = branch = None
1141 for _, project_config in GIT_SVN_PROJECT_MAP.iteritems():
1142 if url.startswith(project_config['svn_url']):
1143 branch = url[len(project_config['svn_url']):]
1144 break
1145
1146 if branch:
1147 # Strip any leading slashes
1148 branch = branch.lstrip('/')
1149
1150 # Try and map the branch
1151 for pattern, repl in project_config.get('branch_map', ()):
1152 nbranch, subn = re.subn(pattern, repl, branch, count=1)
1153 if subn:
1154 print 'INFO: Mapped SVN branch to Git branch [%s] => [%s]' % (
1155 branch, nbranch)
1156 branch = nbranch
1157 break
1158 else:
1159 # Use generic 'svn' branch
1160 print 'INFO: Could not resolve project for SVN URL %r' % (url,)
1161 branch = 'svn'
1162 return '%s@{#%s}' % (branch, revision)
1163
1164
1165 def get_commit_position(git_path, revision='HEAD'): 925 def get_commit_position(git_path, revision='HEAD'):
1166 """Dumps the 'git' log for a specific revision and parses out the commit 926 """Dumps the 'git' log for a specific revision and parses out the commit
1167 position. 927 position.
1168 928
1169 If a commit position metadata key is found, its value will be returned. 929 If a commit position metadata key is found, its value will be returned.
1170
1171 Otherwise, we will search for a 'git-svn' metadata entry. If one is found,
1172 we will compose a commit position from it, using its SVN revision value as
1173 the revision.
1174
1175 If the 'git-svn' URL maps to a known project, we will construct a commit
1176 position branch value by truncating the URL, mapping 'trunk' to
1177 "refs/heads/master". Otherwise, we will return the generic branch, 'svn'.
1178 """ 930 """
1179 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) 931 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path)
1180 footer_map = get_commit_message_footer_map(git_log) 932 footer_map = get_commit_message_footer_map(git_log)
1181 933
1182 # Search for commit position metadata 934 # Search for commit position metadata
1183 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or 935 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or
1184 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) 936 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY))
1185 if value: 937 if value:
1186 return value 938 return value
1187
1188 # Compose a commit position from 'git-svn' metadata
1189 value = footer_map.get(GIT_SVN_ID_FOOTER_KEY)
1190 if value:
1191 m = GIT_SVN_ID_RE.match(value)
1192 if not m:
1193 raise ValueError("Invalid 'git-svn' value: [%s]" % (value,))
1194 return get_commit_position_for_git_svn(m.group(1), m.group(2))
1195 return None 939 return None
1196 940
1197 941
1198 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): 942 def parse_got_revision(gclient_output, got_revision_mapping):
1199 """Translate git gclient revision mapping to build properties. 943 """Translate git gclient revision mapping to build properties."""
1200
1201 If use_svn_revs is True, then translate git hashes in the revision mapping
1202 to svn revision numbers.
1203 """
1204 properties = {} 944 properties = {}
1205 solutions_output = { 945 solutions_output = {
1206 # Make sure path always ends with a single slash. 946 # Make sure path always ends with a single slash.
1207 '%s/' % path.rstrip('/') : solution_output for path, solution_output 947 '%s/' % path.rstrip('/') : solution_output for path, solution_output
1208 in gclient_output['solutions'].iteritems() 948 in gclient_output['solutions'].iteritems()
1209 } 949 }
1210 for dir_name, property_name in got_revision_mapping.iteritems(): 950 for dir_name, property_name in got_revision_mapping.iteritems():
1211 # Make sure dir_name always ends with a single slash. 951 # Make sure dir_name always ends with a single slash.
1212 dir_name = '%s/' % dir_name.rstrip('/') 952 dir_name = '%s/' % dir_name.rstrip('/')
1213 if dir_name not in solutions_output: 953 if dir_name not in solutions_output:
1214 continue 954 continue
1215 solution_output = solutions_output[dir_name] 955 solution_output = solutions_output[dir_name]
1216 if solution_output.get('scm') is None: 956 if solution_output.get('scm') is None:
1217 # This is an ignored DEPS, so the output got_revision should be 'None'. 957 # This is an ignored DEPS, so the output got_revision should be 'None'.
1218 git_revision = revision = commit_position = None 958 git_revision = revision = commit_position = None
1219 else: 959 else:
1220 # Since we are using .DEPS.git, everything had better be git. 960 # Since we are using .DEPS.git, everything had better be git.
1221 assert solution_output.get('scm') == 'git' 961 assert solution_output.get('scm') == 'git'
1222 git_revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() 962 revision = git('rev-parse', 'HEAD', cwd=dir_name).strip()
1223 if use_svn_revs:
1224 revision = get_svn_rev(git_revision, dir_name)
1225 if not revision:
1226 revision = git_revision
1227 else:
1228 revision = git_revision
1229 commit_position = get_commit_position(dir_name) 963 commit_position = get_commit_position(dir_name)
1230 964
1231 properties[property_name] = revision 965 properties[property_name] = revision
1232 if revision != git_revision: 966 if revision != git_revision:
1233 properties['%s_git' % property_name] = git_revision 967 properties['%s_git' % property_name] = git_revision
1234 if commit_position: 968 if commit_position:
1235 properties['%s_cp' % property_name] = commit_position 969 properties['%s_cp' % property_name] = commit_position
1236 970
1237 return properties 971 return properties
1238 972
(...skipping 11 matching lines...) Expand all
1250 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): 984 def ensure_deps_revisions(deps_url_mapping, solutions, revisions):
1251 """Ensure correct DEPS revisions, ignores solutions.""" 985 """Ensure correct DEPS revisions, ignores solutions."""
1252 for deps_name, deps_data in sorted(deps_url_mapping.items()): 986 for deps_name, deps_data in sorted(deps_url_mapping.items()):
1253 if deps_name.strip('/') in solutions: 987 if deps_name.strip('/') in solutions:
1254 # This has already been forced to the correct solution by git_checkout(). 988 # This has already been forced to the correct solution by git_checkout().
1255 continue 989 continue
1256 revision = get_target_revision(deps_name, deps_data.get('url', None), 990 revision = get_target_revision(deps_name, deps_data.get('url', None),
1257 revisions) 991 revisions)
1258 if not revision: 992 if not revision:
1259 continue 993 continue
1260 # TODO(hinoka): Catch SVNRevisionNotFound error maybe?
1261 git('fetch', 'origin', cwd=deps_name) 994 git('fetch', 'origin', cwd=deps_name)
1262 force_revision(deps_name, revision) 995 force_revision(deps_name, revision)
1263 996
1264 997
1265 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, 998 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only,
1266 patch_root, issue, patchset, patch_url, rietveld_server, 999 patch_root, issue, patchset, rietveld_server,
1267 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, 1000 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref,
1268 revision_mapping, apply_issue_email_file, 1001 revision_mapping, apply_issue_email_file,
1269 apply_issue_key_file, buildspec, gyp_env, shallow, runhooks, 1002 apply_issue_key_file, buildspec, gyp_env, shallow, runhooks,
1270 refs, git_cache_dir, gerrit_reset): 1003 refs, git_cache_dir, gerrit_reset):
1271 # Get a checkout of each solution, without DEPS or hooks. 1004 # Get a checkout of each solution, without DEPS or hooks.
1272 # Calling git directly because there is no way to run Gclient without 1005 # Calling git directly because there is no way to run Gclient without
1273 # invoking DEPS. 1006 # invoking DEPS.
1274 print 'Fetching Git checkout' 1007 print 'Fetching Git checkout'
1275 1008
1276 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) 1009 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir)
1277 1010
1278 patches = None 1011 patches = None
1279 if patch_url:
1280 patches = get_svn_patch(patch_url)
1281 1012
1282 print '===Processing patch solutions===' 1013 print '===Processing patch solutions==='
1283 already_patched = [] 1014 already_patched = []
1284 patch_root = patch_root or '' 1015 patch_root = patch_root or ''
1285 print 'Patch root is %r' % patch_root 1016 print 'Patch root is %r' % patch_root
1286 for solution in solutions: 1017 for solution in solutions:
1287 print 'Processing solution %r' % solution['name'] 1018 print 'Processing solution %r' % solution['name']
1288 if (patch_root == solution['name'] or 1019 if (patch_root == solution['name'] or
1289 solution['name'].startswith(patch_root + '/')): 1020 solution['name'].startswith(patch_root + '/')):
1290 relative_root = solution['name'][len(patch_root) + 1:] 1021 relative_root = solution['name'][len(patch_root) + 1:]
1291 target = '/'.join([relative_root, 'DEPS']).lstrip('/') 1022 target = '/'.join([relative_root, 'DEPS']).lstrip('/')
1292 print ' relative root is %r, target is %r' % (relative_root, target) 1023 print ' relative root is %r, target is %r' % (relative_root, target)
1293 if patches: 1024 if issue:
1294 apply_svn_patch(patch_root, patches, whitelist=[target])
1295 already_patched.append(target)
1296 elif issue:
1297 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, 1025 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server,
1298 revision_mapping, git_ref, apply_issue_email_file, 1026 revision_mapping, git_ref, apply_issue_email_file,
1299 apply_issue_key_file, whitelist=[target]) 1027 apply_issue_key_file, whitelist=[target])
1300 already_patched.append(target) 1028 already_patched.append(target)
1301 elif gerrit_ref and gerrit_rebase_patch_ref: 1029 elif gerrit_ref and gerrit_rebase_patch_ref:
1302 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, 1030 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset,
1303 True) 1031 True)
1304 1032
1305 # Ensure our build/ directory is set up with the correct .gclient file. 1033 # Ensure our build/ directory is set up with the correct .gclient file.
1306 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) 1034 gclient_configure(solutions, target_os, target_os_only, git_cache_dir)
(...skipping 14 matching lines...) Expand all
1321 # Run gclient runhooks if we're on an official builder. 1049 # Run gclient runhooks if we're on an official builder.
1322 # TODO(hinoka): Remove this when the official builders run their own 1050 # TODO(hinoka): Remove this when the official builders run their own
1323 # runhooks step. 1051 # runhooks step.
1324 gclient_runhooks(gyp_env) 1052 gclient_runhooks(gyp_env)
1325 1053
1326 # Finally, ensure that all DEPS are pinned to the correct revision. 1054 # Finally, ensure that all DEPS are pinned to the correct revision.
1327 dir_names = [sln['name'] for sln in solutions] 1055 dir_names = [sln['name'] for sln in solutions]
1328 ensure_deps_revisions(gclient_output.get('solutions', {}), 1056 ensure_deps_revisions(gclient_output.get('solutions', {}),
1329 dir_names, revisions) 1057 dir_names, revisions)
1330 # Apply the rest of the patch here (sans DEPS) 1058 # Apply the rest of the patch here (sans DEPS)
1331 if patches: 1059 if issue:
1332 apply_svn_patch(patch_root, patches, blacklist=already_patched)
1333 elif issue:
1334 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, 1060 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server,
1335 revision_mapping, git_ref, apply_issue_email_file, 1061 revision_mapping, git_ref, apply_issue_email_file,
1336 apply_issue_key_file, blacklist=already_patched) 1062 apply_issue_key_file, blacklist=already_patched)
1337 elif gerrit_ref and not gerrit_rebase_patch_ref: 1063 elif gerrit_ref and not gerrit_rebase_patch_ref:
1338 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, False) 1064 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, False)
1339 1065
1340 # Reset the deps_file point in the solutions so that hooks get run properly. 1066 # Reset the deps_file point in the solutions so that hooks get run properly.
1341 for sln in solutions: 1067 for sln in solutions:
1342 sln['deps_file'] = sln.get('deps_file', 'DEPS').replace('.DEPS.git', 'DEPS') 1068 sln['deps_file'] = sln.get('deps_file', 'DEPS').replace('.DEPS.git', 'DEPS')
1343 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) 1069 gclient_configure(solutions, target_os, target_os_only, git_cache_dir)
(...skipping 15 matching lines...) Expand all
1359 expanded_revisions.extend(revision.split(',')) 1085 expanded_revisions.extend(revision.split(','))
1360 for revision in expanded_revisions: 1086 for revision in expanded_revisions:
1361 split_revision = revision.split('@') 1087 split_revision = revision.split('@')
1362 if len(split_revision) == 1: 1088 if len(split_revision) == 1:
1363 # This is just a plain revision, set it as the revision for root. 1089 # This is just a plain revision, set it as the revision for root.
1364 results[root] = split_revision[0] 1090 results[root] = split_revision[0]
1365 elif len(split_revision) == 2: 1091 elif len(split_revision) == 2:
1366 # This is an alt_root@revision argument. 1092 # This is an alt_root@revision argument.
1367 current_root, current_rev = split_revision 1093 current_root, current_rev = split_revision
1368 1094
1369 # We want to normalize svn/git urls into .git urls.
1370 parsed_root = urlparse.urlparse(current_root) 1095 parsed_root = urlparse.urlparse(current_root)
1371 if parsed_root.scheme == 'svn': 1096 if parsed_root.scheme in ['http', 'https']:
1372 if parsed_root.path in RECOGNIZED_PATHS: 1097 # We want to normalize git urls into .git urls.
1373 normalized_root = RECOGNIZED_PATHS[parsed_root.path]
1374 else:
1375 print 'WARNING: SVN path %s not recognized, ignoring' % current_root
1376 continue
1377 elif parsed_root.scheme in ['http', 'https']:
1378 normalized_root = 'https://%s/%s' % (parsed_root.netloc, 1098 normalized_root = 'https://%s/%s' % (parsed_root.netloc,
1379 parsed_root.path) 1099 parsed_root.path)
1380 if not normalized_root.endswith('.git'): 1100 if not normalized_root.endswith('.git'):
1381 normalized_root = '%s.git' % normalized_root 1101 normalized_root = '%s.git' % normalized_root
1382 elif parsed_root.scheme: 1102 elif parsed_root.scheme:
1383 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme 1103 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme
1384 continue 1104 continue
1385 else: 1105 else:
1386 # This is probably a local path. 1106 # This is probably a local path.
1387 normalized_root = current_root.strip('/') 1107 normalized_root = current_root.strip('/')
1388 1108
1389 results[normalized_root] = current_rev 1109 results[normalized_root] = current_rev
1390 else: 1110 else:
1391 print ('WARNING: %r is not recognized as a valid revision specification,' 1111 print ('WARNING: %r is not recognized as a valid revision specification,'
1392 'skipping' % revision) 1112 'skipping' % revision)
1393 return results 1113 return results
1394 1114
1395 1115
1396 def parse_args(): 1116 def parse_args():
1397 parse = optparse.OptionParser() 1117 parse = optparse.OptionParser()
1398 1118
1399 parse.add_option('--issue', help='Issue number to patch from.') 1119 parse.add_option('--issue', help='Issue number to patch from.')
1400 parse.add_option('--patchset', 1120 parse.add_option('--patchset',
1401 help='Patchset from issue to patch from, if applicable.') 1121 help='Patchset from issue to patch from, if applicable.')
1402 parse.add_option('--apply_issue_email_file', 1122 parse.add_option('--apply_issue_email_file',
1403 help='--email-file option passthrough for apply_patch.py.') 1123 help='--email-file option passthrough for apply_patch.py.')
1404 parse.add_option('--apply_issue_key_file', 1124 parse.add_option('--apply_issue_key_file',
1405 help='--private-key-file option passthrough for ' 1125 help='--private-key-file option passthrough for '
1406 'apply_patch.py.') 1126 'apply_patch.py.')
1407 parse.add_option('--patch_url', help='Optional URL to SVN patch.') 1127 parse.add_option('--patch_url', help='DEPRECATED')
1408 parse.add_option('--root', dest='patch_root', 1128 parse.add_option('--root', dest='patch_root',
1409 help='DEPRECATED: Use --patch_root.') 1129 help='DEPRECATED: Use --patch_root.')
1410 parse.add_option('--patch_root', help='Directory to patch on top of.') 1130 parse.add_option('--patch_root', help='Directory to patch on top of.')
1411 parse.add_option('--rietveld_server', 1131 parse.add_option('--rietveld_server',
1412 default='codereview.chromium.org', 1132 default='codereview.chromium.org',
1413 help='Rietveld server.') 1133 help='Rietveld server.')
1414 parse.add_option('--gerrit_repo', 1134 parse.add_option('--gerrit_repo',
1415 help='Gerrit repository to pull the ref from.') 1135 help='Gerrit repository to pull the ref from.')
1416 parse.add_option('--gerrit_ref', help='Gerrit ref to apply.') 1136 parse.add_option('--gerrit_ref', help='Gerrit ref to apply.')
1417 parse.add_option('--gerrit_rebase_patch_ref', action='store_true', 1137 parse.add_option('--gerrit_rebase_patch_ref', action='store_true',
1418 help='Rebase Gerrit patch ref after of checking it out.') 1138 help='Rebase Gerrit patch ref after of checking it out.')
1419 parse.add_option('--gerrit_no_reset', action='store_true', 1139 parse.add_option('--gerrit_no_reset', action='store_true',
1420 help='Bypass calling reset after applying a gerrit ref.') 1140 help='Bypass calling reset after applying a gerrit ref.')
1421 parse.add_option('--specs', help='Gcilent spec.') 1141 parse.add_option('--specs', help='Gcilent spec.')
1422 parse.add_option('--master', 1142 parse.add_option('--master',
1423 help='Master name. If specified and it is not in ' 1143 help='Master name. If specified and it is not in '
1424 'bot_update\'s whitelist, bot_update will be noop.') 1144 'bot_update\'s whitelist, bot_update will be noop.')
1425 parse.add_option('-f', '--force', action='store_true', 1145 parse.add_option('-f', '--force', action='store_true',
1426 help='Bypass check to see if we want to be run. ' 1146 help='Bypass check to see if we want to be run. '
1427 'Should ONLY be used locally or by smart recipes.') 1147 'Should ONLY be used locally or by smart recipes.')
1428 parse.add_option('--revision_mapping', 1148 parse.add_option('--revision_mapping',
1429 help='{"path/to/repo/": "property_name"}') 1149 help='{"path/to/repo/": "property_name"}')
1430 parse.add_option('--revision_mapping_file', 1150 parse.add_option('--revision_mapping_file',
1431 help=('Same as revision_mapping, except its a path to a json' 1151 help=('Same as revision_mapping, except its a path to a json'
1432 ' file containing that format.')) 1152 ' file containing that format.'))
1433 parse.add_option('--revision', action='append', default=[], 1153 parse.add_option('--revision', action='append', default=[],
1434 help='Revision to check out. Can be an SVN revision number, ' 1154 help='Revision to check out. Can be any form of git ref. '
1435 'git hash, or any form of git ref. Can prepend ' 1155 'Can prepend root@<rev> to specify which repository, '
1436 'root@<rev> to specify which repository, where root ' 1156 'where root is either a filesystem path or git https '
1437 'is either a filesystem path, git https url, or ' 1157 'url. To specify Tip of Tree, set rev to HEAD. ')
1438 'svn url. To specify Tip of Tree, set rev to HEAD.'
1439 'To specify a git branch and an SVN rev, <rev> can be '
1440 'set to <branch>:<revision>.')
1441 parse.add_option('--output_manifest', action='store_true', 1158 parse.add_option('--output_manifest', action='store_true',
1442 help=('Add manifest json to the json output.')) 1159 help=('Add manifest json to the json output.'))
1443 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], 1160 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0],
1444 help='Hostname of the current machine, ' 1161 help='Hostname of the current machine, '
1445 'used for determining whether or not to activate.') 1162 'used for determining whether or not to activate.')
1446 parse.add_option('--builder_name', help='Name of the builder, ' 1163 parse.add_option('--builder_name', help='Name of the builder, '
1447 'used for determining whether or not to activate.') 1164 'used for determining whether or not to activate.')
1448 parse.add_option('--build_dir', default=os.getcwd()) 1165 parse.add_option('--build_dir', default=os.getcwd())
1449 parse.add_option('--flag_file', default=path.join(os.getcwd(), 1166 parse.add_option('--flag_file', default=path.join(os.getcwd(),
1450 'update.flag')) 1167 'update.flag'))
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1504 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') 1221 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\')
1505 1222
1506 return options, args 1223 return options, args
1507 1224
1508 1225
1509 def prepare(options, git_slns, active): 1226 def prepare(options, git_slns, active):
1510 """Prepares the target folder before we checkout.""" 1227 """Prepares the target folder before we checkout."""
1511 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] 1228 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
1512 # If we're active now, but the flag file doesn't exist (we weren't active 1229 # If we're active now, but the flag file doesn't exist (we weren't active
1513 # last run) or vice versa, blow away all checkouts. 1230 # last run) or vice versa, blow away all checkouts.
1514 if bool(active) != bool(check_flag(options.flag_file)): 1231 if options.clobber or (bool(active) != bool(check_flag(options.flag_file))):
1515 ensure_no_checkout(dir_names, '*') 1232 ensure_no_checkout(dir_names)
1516 if options.output_json: 1233 if options.output_json:
1517 # Make sure we tell recipes that we didn't run if the script exits here. 1234 # Make sure we tell recipes that we didn't run if the script exits here.
1518 emit_json(options.output_json, did_run=active) 1235 emit_json(options.output_json, did_run=active)
1519 if active: 1236 emit_flag(options.flag_file)
1520 if options.clobber:
1521 ensure_no_checkout(dir_names, '*')
1522 else:
1523 ensure_no_checkout(dir_names, '.svn')
1524 emit_flag(options.flag_file)
1525 else:
1526 delete_flag(options.flag_file)
1527 raise Inactive # This is caught in main() and we exit cleanly.
1528 1237
1529 # Do a shallow checkout if the disk is less than 100GB. 1238 # Do a shallow checkout if the disk is less than 100GB.
1530 total_disk_space, free_disk_space = get_total_disk_space() 1239 total_disk_space, free_disk_space = get_total_disk_space()
1531 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) 1240 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024))
1532 used_disk_space_gb = int((total_disk_space - free_disk_space) 1241 used_disk_space_gb = int((total_disk_space - free_disk_space)
1533 / (1024 * 1024 * 1024)) 1242 / (1024 * 1024 * 1024))
1534 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) 1243 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb)
1535 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, 1244 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb,
1536 total_disk_space_gb, 1245 total_disk_space_gb,
1537 percent_used) 1246 percent_used)
1538 if not options.output_json: 1247 if not options.output_json:
1539 print '@@@STEP_TEXT@%s@@@' % step_text 1248 print '@@@STEP_TEXT@%s@@@' % step_text
1540 if not options.shallow: 1249 if not options.shallow:
1541 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD 1250 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD
1542 and not options.no_shallow) 1251 and not options.no_shallow)
1543 1252
1544 # The first solution is where the primary DEPS file resides. 1253 # The first solution is where the primary DEPS file resides.
1545 first_sln = dir_names[0] 1254 first_sln = dir_names[0]
1546 1255
1547 # Split all the revision specifications into a nice dict. 1256 # Split all the revision specifications into a nice dict.
1548 print 'Revisions: %s' % options.revision 1257 print 'Revisions: %s' % options.revision
1549 revisions = parse_revisions(options.revision, first_sln) 1258 revisions = parse_revisions(options.revision, first_sln)
1550 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) 1259 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln])
1551 return revisions, step_text 1260 return revisions, step_text
1552 1261
1553 1262
1554 def checkout(options, git_slns, specs, buildspec, master, 1263 def checkout(options, git_slns, specs, buildspec, master, revisions, step_text):
1555 svn_root, revisions, step_text):
1556 first_sln = git_slns[0]['name'] 1264 first_sln = git_slns[0]['name']
1557 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] 1265 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln]
1558 try: 1266 try:
1559 # Outer try is for catching patch failures and exiting gracefully. 1267 # Outer try is for catching patch failures and exiting gracefully.
1560 # Inner try is for catching gclient failures and retrying gracefully. 1268 # Inner try is for catching gclient failures and retrying gracefully.
1561 try: 1269 try:
1562 checkout_parameters = dict( 1270 checkout_parameters = dict(
1563 # First, pass in the base of what we want to check out. 1271 # First, pass in the base of what we want to check out.
1564 solutions=git_slns, 1272 solutions=git_slns,
1565 revisions=revisions, 1273 revisions=revisions,
1566 first_sln=first_sln, 1274 first_sln=first_sln,
1567 1275
1568 # Also, target os variables for gclient. 1276 # Also, target os variables for gclient.
1569 target_os=specs.get('target_os', []), 1277 target_os=specs.get('target_os', []),
1570 target_os_only=specs.get('target_os_only', False), 1278 target_os_only=specs.get('target_os_only', False),
1571 1279
1572 # Then, pass in information about how to patch. 1280 # Then, pass in information about how to patch.
1573 patch_root=options.patch_root, 1281 patch_root=options.patch_root,
1574 issue=options.issue, 1282 issue=options.issue,
1575 patchset=options.patchset, 1283 patchset=options.patchset,
1576 patch_url=options.patch_url,
1577 rietveld_server=options.rietveld_server, 1284 rietveld_server=options.rietveld_server,
1578 gerrit_repo=options.gerrit_repo, 1285 gerrit_repo=options.gerrit_repo,
1579 gerrit_ref=options.gerrit_ref, 1286 gerrit_ref=options.gerrit_ref,
1580 gerrit_rebase_patch_ref=options.gerrit_rebase_patch_ref, 1287 gerrit_rebase_patch_ref=options.gerrit_rebase_patch_ref,
1581 revision_mapping=options.revision_mapping, 1288 revision_mapping=options.revision_mapping,
1582 apply_issue_email_file=options.apply_issue_email_file, 1289 apply_issue_email_file=options.apply_issue_email_file,
1583 apply_issue_key_file=options.apply_issue_key_file, 1290 apply_issue_key_file=options.apply_issue_key_file,
1584 1291
1585 # For official builders. 1292 # For official builders.
1586 buildspec=buildspec, 1293 buildspec=buildspec,
1587 gyp_env=options.gyp_env, 1294 gyp_env=options.gyp_env,
1588 runhooks=not options.no_runhooks, 1295 runhooks=not options.no_runhooks,
1589 1296
1590 # Finally, extra configurations such as shallowness of the clone. 1297 # Finally, extra configurations such as shallowness of the clone.
1591 shallow=options.shallow, 1298 shallow=options.shallow,
1592 refs=options.refs, 1299 refs=options.refs,
1593 git_cache_dir=options.git_cache_dir, 1300 git_cache_dir=options.git_cache_dir,
1594 gerrit_reset=not options.gerrit_no_reset) 1301 gerrit_reset=not options.gerrit_no_reset)
1595 gclient_output = ensure_checkout(**checkout_parameters) 1302 gclient_output = ensure_checkout(**checkout_parameters)
1596 except GclientSyncFailed: 1303 except GclientSyncFailed:
1597 print 'We failed gclient sync, lets delete the checkout and retry.' 1304 print 'We failed gclient sync, lets delete the checkout and retry.'
1598 ensure_no_checkout(dir_names, '*') 1305 ensure_no_checkout(dir_names)
1599 gclient_output = ensure_checkout(**checkout_parameters) 1306 gclient_output = ensure_checkout(**checkout_parameters)
1600 except PatchFailed as e: 1307 except PatchFailed as e:
1601 if options.output_json: 1308 if options.output_json:
1602 # Tell recipes information such as root, got_revision, etc. 1309 # Tell recipes information such as root, got_revision, etc.
1603 emit_json(options.output_json, 1310 emit_json(options.output_json,
1604 did_run=True, 1311 did_run=True,
1605 root=first_sln, 1312 root=first_sln,
1606 log_lines=[('patch error', e.output),], 1313 log_lines=[('patch error', e.output),],
1607 patch_apply_return_code=e.code, 1314 patch_apply_return_code=e.code,
1608 patch_root=options.patch_root, 1315 patch_root=options.patch_root,
1609 patch_failure=True, 1316 patch_failure=True,
1610 step_text='%s PATCH FAILED' % step_text, 1317 step_text='%s PATCH FAILED' % step_text,
1611 fixed_revisions=revisions) 1318 fixed_revisions=revisions)
1612 else: 1319 else:
1613 # If we're not on recipes, tell annotator about our got_revisions. 1320 # If we're not on recipes, tell annotator about our got_revisions.
1614 emit_log_lines('patch error', e.output) 1321 emit_log_lines('patch error', e.output)
1615 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text 1322 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text
1616 raise 1323 raise
1617 1324
1618 # Revision is an svn revision, unless it's a git master.
1619 use_svn_rev = master not in GIT_MASTERS
1620
1621 # Take care of got_revisions outputs. 1325 # Take care of got_revisions outputs.
1622 revision_mapping = dict(GOT_REVISION_MAPPINGS.get(svn_root, {})) 1326 revision_mapping = GOT_REVISION_MAPPINGS.get(git_slns[0]['url'], {})
1623 if options.revision_mapping: 1327 if options.revision_mapping:
1624 revision_mapping.update(options.revision_mapping) 1328 revision_mapping.update(options.revision_mapping)
1625 1329
1626 # If the repo is not in the default GOT_REVISION_MAPPINGS and no 1330 # If the repo is not in the default GOT_REVISION_MAPPINGS and no
1627 # revision_mapping were specified on the command line then 1331 # revision_mapping were specified on the command line then
1628 # default to setting 'got_revision' based on the first solution. 1332 # default to setting 'got_revision' based on the first solution.
1629 if not revision_mapping: 1333 if not revision_mapping:
1630 revision_mapping[first_sln] = 'got_revision' 1334 revision_mapping[first_sln] = 'got_revision'
1631 1335
1632 got_revisions = parse_got_revision(gclient_output, revision_mapping, 1336 got_revisions = parse_got_revision(gclient_output, revision_mapping)
1633 use_svn_rev)
1634 1337
1635 if not got_revisions: 1338 if not got_revisions:
1636 # TODO(hinoka): We should probably bail out here, but in the interest 1339 # TODO(hinoka): We should probably bail out here, but in the interest
1637 # of giving mis-configured bots some time to get fixed use a dummy 1340 # of giving mis-configured bots some time to get fixed use a dummy
1638 # revision here. 1341 # revision here.
1639 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } 1342 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' }
1640 #raise Exception('No got_revision(s) found in gclient output') 1343 #raise Exception('No got_revision(s) found in gclient output')
1641 1344
1642 if options.output_json: 1345 if options.output_json:
1643 manifest = create_manifest() if options.output_manifest else None 1346 manifest = create_manifest() if options.output_manifest else None
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1698 # Check if this script should activate or not. 1401 # Check if this script should activate or not.
1699 active = options.force or check_valid_host(master, builder, slave) 1402 active = options.force or check_valid_host(master, builder, slave)
1700 1403
1701 # Print a helpful message to tell developers whats going on with this step. 1404 # Print a helpful message to tell developers whats going on with this step.
1702 print_help_text( 1405 print_help_text(
1703 options.force, options.output_json, active, master, builder, slave) 1406 options.force, options.output_json, active, master, builder, slave)
1704 1407
1705 # Parse, munipulate, and print the gclient solutions. 1408 # Parse, munipulate, and print the gclient solutions.
1706 specs = {} 1409 specs = {}
1707 exec(options.specs, specs) 1410 exec(options.specs, specs)
1708 svn_solutions = specs.get('solutions', []) 1411 orig_solutions = specs.get('solutions', [])
1709 git_slns, svn_root, buildspec = solutions_to_git(svn_solutions) 1412 git_slns, buildspec = modify_solutions(orig_solutions)
1710 options.revision = maybe_ignore_revision(options.revision, buildspec) 1413 options.revision = maybe_ignore_revision(options.revision, buildspec)
1711 1414
1712 solutions_printer(git_slns) 1415 solutions_printer(git_slns)
1713 1416
1714 try: 1417 try:
1715 # Dun dun dun, the main part of bot_update. 1418 # Dun dun dun, the main part of bot_update.
1716 revisions, step_text = prepare(options, git_slns, active) 1419 revisions, step_text = prepare(options, git_slns, active)
1717 checkout(options, git_slns, specs, buildspec, master, svn_root, revisions, 1420 checkout(options, git_slns, specs, buildspec, master, revisions, step_text)
1718 step_text)
1719 1421
1720 except Inactive: 1422 except Inactive:
hinoka 2016/08/26 21:59:54 I don't think "Inactive" is referenced anymore aft
agable 2016/08/29 19:09:58 Right, deleted.
1721 # Not active, should count as passing. 1423 # Not active, should count as passing.
1722 pass 1424 pass
1723 except PatchFailed as e: 1425 except PatchFailed as e:
1724 emit_flag(options.flag_file) 1426 emit_flag(options.flag_file)
1725 # Return a specific non-zero exit code for patch failure (because it is 1427 # Return a specific non-zero exit code for patch failure (because it is
1726 # a failure), but make it different than other failures to distinguish 1428 # a failure), but make it different than other failures to distinguish
1727 # between infra failures (independent from patch author), and patch 1429 # between infra failures (independent from patch author), and patch
1728 # failures (that patch author can fix). However, PatchFailure due to 1430 # failures (that patch author can fix). However, PatchFailure due to
1729 # download patch failure is still an infra problem. 1431 # download patch failure is still an infra problem.
1730 if e.code == 3: 1432 if e.code == 3:
1731 # Patch download problem. 1433 # Patch download problem.
1732 return 87 1434 return 87
1733 # Genuine patch problem. 1435 # Genuine patch problem.
1734 return 88 1436 return 88
1735 except Exception: 1437 except Exception:
1736 # Unexpected failure. 1438 # Unexpected failure.
1737 emit_flag(options.flag_file) 1439 emit_flag(options.flag_file)
1738 raise 1440 raise
1739 else: 1441 else:
1740 emit_flag(options.flag_file) 1442 emit_flag(options.flag_file)
1741 1443
1742 1444
1743 if __name__ == '__main__': 1445 if __name__ == '__main__':
1744 sys.exit(main()) 1446 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