OLD | NEW |
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 Loading... |
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') | |
96 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' | 89 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' |
97 | 90 |
98 BUILDSPEC_COMMIT_RE = ( | |
99 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'), | |
100 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'), | |
101 re.compile(r'Auto-converted (\d+\.\d+\.\d+\.\d+) buildspec to git'), | |
102 ) | |
103 | |
104 # Regular expression that matches a single commit footer line. | 91 # Regular expression that matches a single commit footer line. |
105 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') | 92 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') |
106 | 93 |
107 # Footer metadata keys for regular and gsubtreed mirrored commit positions. | 94 # Footer metadata keys for regular and gsubtreed mirrored commit positions. |
108 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' | 95 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' |
109 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' | 96 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' |
110 # Regular expression to parse a commit position | |
111 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}') | |
112 | 97 |
113 # Regular expression to parse gclient's revinfo entries. | 98 # Regular expression to parse gclient's revinfo entries. |
114 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') | 99 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') |
115 | 100 |
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 | 101 |
156 # Copied from scripts/recipes/chromium.py. | 102 # Copied from scripts/recipes/chromium.py. |
157 GOT_REVISION_MAPPINGS = { | 103 GOT_REVISION_MAPPINGS = { |
158 '/chrome/trunk/src': { | 104 CHROMIUM_SRC_URL: { |
159 'src/': 'got_revision', | 105 'src/': 'got_revision', |
160 'src/native_client/': 'got_nacl_revision', | 106 'src/native_client/': 'got_nacl_revision', |
161 'src/tools/swarm_client/': 'got_swarm_client_revision', | 107 'src/tools/swarm_client/': 'got_swarm_client_revision', |
162 'src/tools/swarming_client/': 'got_swarming_client_revision', | 108 'src/tools/swarming_client/': 'got_swarming_client_revision', |
163 'src/third_party/WebKit/': 'got_webkit_revision', | 109 'src/third_party/WebKit/': 'got_webkit_revision', |
164 'src/third_party/webrtc/': 'got_webrtc_revision', | 110 'src/third_party/webrtc/': 'got_webrtc_revision', |
165 'src/v8/': 'got_v8_revision', | 111 'src/v8/': 'got_v8_revision', |
166 } | 112 } |
167 } | 113 } |
168 | 114 |
(...skipping 12 matching lines...) Expand all Loading... |
181 try: | 127 try: |
182 execfile(os.path.join( | 128 execfile(os.path.join( |
183 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), | 129 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), |
184 local_vars) | 130 local_vars) |
185 except Exception: | 131 except Exception: |
186 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. | 132 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. |
187 print 'Warning: unable to read internal configuration file.' | 133 print 'Warning: unable to read internal configuration file.' |
188 print 'If this is an internal bot, this step may be erroneously inactive.' | 134 print 'If this is an internal bot, this step may be erroneously inactive.' |
189 internal_data = local_vars | 135 internal_data = local_vars |
190 | 136 |
191 RECOGNIZED_PATHS = { | |
192 # If SVN path matches key, the entire URL is rewritten to the Git url. | |
193 '/chrome/trunk/src': | |
194 CHROMIUM_SRC_URL, | |
195 '/chrome/trunk/src/tools/cros.DEPS': | |
196 CHROMIUM_GIT_HOST + '/chromium/src/tools/cros.DEPS.git', | |
197 '/chrome-internal/trunk/src-internal': | |
198 'https://chrome-internal.googlesource.com/chrome/src-internal.git', | |
199 } | |
200 RECOGNIZED_PATHS.update(internal_data.get('RECOGNIZED_PATHS', {})) | |
201 | 137 |
202 | 138 |
203 # How many times to try before giving up. | 139 # How many times to try before giving up. |
204 ATTEMPTS = 5 | 140 ATTEMPTS = 5 |
205 | 141 |
206 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') | 142 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') |
207 | 143 |
208 # Find the patch tool. | 144 # Find the patch tool. |
209 if sys.platform.startswith('win'): | 145 if sys.platform.startswith('win'): |
210 if not BUILD_INTERNAL_DIR: | 146 if not BUILD_INTERNAL_DIR: |
(...skipping 18 matching lines...) Expand all Loading... |
229 | 165 |
230 | 166 |
231 class PatchFailed(SubprocessFailed): | 167 class PatchFailed(SubprocessFailed): |
232 pass | 168 pass |
233 | 169 |
234 | 170 |
235 class GclientSyncFailed(SubprocessFailed): | 171 class GclientSyncFailed(SubprocessFailed): |
236 pass | 172 pass |
237 | 173 |
238 | 174 |
239 class SVNRevisionNotFound(Exception): | |
240 pass | |
241 | |
242 | |
243 class InvalidDiff(Exception): | 175 class InvalidDiff(Exception): |
244 pass | 176 pass |
245 | 177 |
246 | 178 |
247 class Inactive(Exception): | |
248 """Not really an exception, just used to exit early cleanly.""" | |
249 pass | |
250 | |
251 | |
252 RETRY = object() | 179 RETRY = object() |
253 OK = object() | 180 OK = object() |
254 FAIL = object() | 181 FAIL = object() |
255 | 182 |
256 | 183 |
257 class PsPrinter(object): | 184 class PsPrinter(object): |
258 def __init__(self, interval=300): | 185 def __init__(self, interval=300): |
259 self.interval = interval | 186 self.interval = interval |
260 self.active = sys.platform.startswith('linux2') | 187 self.active = sys.platform.startswith('linux2') |
261 self.thread = None | 188 self.thread = None |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 | 297 |
371 def get_gclient_spec(solutions, target_os, target_os_only, git_cache_dir): | 298 def get_gclient_spec(solutions, target_os, target_os_only, git_cache_dir): |
372 return GCLIENT_TEMPLATE % { | 299 return GCLIENT_TEMPLATE % { |
373 'solutions': pprint.pformat(solutions, indent=4), | 300 'solutions': pprint.pformat(solutions, indent=4), |
374 'cache_dir': '"%s"' % git_cache_dir, | 301 'cache_dir': '"%s"' % git_cache_dir, |
375 'target_os': ('\ntarget_os=%s' % target_os) if target_os else '', | 302 'target_os': ('\ntarget_os=%s' % target_os) if target_os else '', |
376 'target_os_only': '\ntarget_os_only=%s' % target_os_only | 303 'target_os_only': '\ntarget_os_only=%s' % target_os_only |
377 } | 304 } |
378 | 305 |
379 | 306 |
380 def maybe_ignore_revision(revision, buildspec): | |
381 """Handle builders that don't care what buildbot tells them to build. | |
382 | |
383 This is especially the case with branch builders that build from buildspecs | |
384 and/or trigger off multiple repositories, where the --revision passed in has | |
385 nothing to do with the solution being built. Clearing the revision in this | |
386 case causes bot_update to use HEAD rather that trying to checkout an | |
387 inappropriate version of the solution. | |
388 """ | |
389 if buildspec and buildspec.container == 'branches': | |
390 return [] | |
391 return revision | |
392 | |
393 | |
394 def solutions_printer(solutions): | 307 def solutions_printer(solutions): |
395 """Prints gclient solution to stdout.""" | 308 """Prints gclient solution to stdout.""" |
396 print 'Gclient Solutions' | 309 print 'Gclient Solutions' |
397 print '=================' | 310 print '=================' |
398 for solution in solutions: | 311 for solution in solutions: |
399 name = solution.get('name') | 312 name = solution.get('name') |
400 url = solution.get('url') | 313 url = solution.get('url') |
401 print '%s (%s)' % (name, url) | 314 print '%s (%s)' % (name, url) |
402 if solution.get('deps_file'): | 315 if solution.get('deps_file'): |
403 print ' Dependencies file is %s' % solution['deps_file'] | 316 print ' Dependencies file is %s' % solution['deps_file'] |
(...skipping 14 matching lines...) Expand all Loading... |
418 print ' %s: Ignore' % deps_name | 331 print ' %s: Ignore' % deps_name |
419 for k, v in solution.iteritems(): | 332 for k, v in solution.iteritems(): |
420 # Print out all the keys we don't know about. | 333 # Print out all the keys we don't know about. |
421 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', | 334 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', |
422 'managed']: | 335 'managed']: |
423 continue | 336 continue |
424 print ' %s is %s' % (k, v) | 337 print ' %s is %s' % (k, v) |
425 print | 338 print |
426 | 339 |
427 | 340 |
428 def solutions_to_git(input_solutions): | 341 def modify_solutions(input_solutions): |
429 """Modifies urls in solutions to point at Git repos. | 342 """Modifies urls in solutions to point at Git repos. |
430 | 343 |
431 returns: (git solution, svn root of first solution) tuple. | 344 returns: new solution dictionary |
432 """ | 345 """ |
433 assert input_solutions | 346 assert input_solutions |
434 solutions = copy.deepcopy(input_solutions) | 347 solutions = copy.deepcopy(input_solutions) |
435 first_solution = True | |
436 buildspec = None | |
437 for solution in solutions: | 348 for solution in solutions: |
438 original_url = solution['url'] | 349 original_url = solution['url'] |
439 parsed_url = urlparse.urlparse(original_url) | 350 parsed_url = urlparse.urlparse(original_url) |
440 parsed_path = parsed_url.path | 351 parsed_path = parsed_url.path |
441 | 352 |
442 # Rewrite SVN urls into Git urls. | |
443 buildspec_m = re.match(BUILDSPEC_RE, parsed_path) | |
444 if first_solution and buildspec_m: | |
445 solution['url'] = GIT_BUILDSPEC_PATH | |
446 buildspec = BUILDSPEC_TYPE( | |
447 container=buildspec_m.group(1), | |
448 version=buildspec_m.group(2), | |
449 ) | |
450 solution['deps_file'] = path.join(buildspec.container, buildspec.version, | |
451 'DEPS') | |
452 elif parsed_path in RECOGNIZED_PATHS: | |
453 solution['url'] = RECOGNIZED_PATHS[parsed_path] | |
454 solution['deps_file'] = '.DEPS.git' | |
455 elif parsed_url.scheme == 'https' and 'googlesource' in parsed_url.netloc: | |
456 pass | |
457 else: | |
458 print 'Warning: %s' % ('path %r not recognized' % parsed_path,) | |
459 | |
460 # Strip out deps containing $$V8_REV$$, etc. | |
461 if 'custom_deps' in solution: | |
462 new_custom_deps = {} | |
463 for deps_name, deps_value in solution['custom_deps'].iteritems(): | |
464 if deps_value and '$$' in deps_value: | |
465 print 'Dropping %s:%s from custom deps' % (deps_name, deps_value) | |
466 else: | |
467 new_custom_deps[deps_name] = deps_value | |
468 solution['custom_deps'] = new_custom_deps | |
469 | |
470 if first_solution: | |
471 root = parsed_path | |
472 first_solution = False | |
473 | |
474 solution['managed'] = False | 353 solution['managed'] = False |
475 # We don't want gclient to be using a safesync URL. Instead it should | 354 # We don't want gclient to be using a safesync URL. Instead it should |
476 # using the lkgr/lkcr branch/tags. | 355 # using the lkgr/lkcr branch/tags. |
477 if 'safesync_url' in solution: | 356 if 'safesync_url' in solution: |
478 print 'Removing safesync url %s from %s' % (solution['safesync_url'], | 357 print 'Removing safesync url %s from %s' % (solution['safesync_url'], |
479 parsed_path) | 358 parsed_path) |
480 del solution['safesync_url'] | 359 del solution['safesync_url'] |
481 return solutions, root, buildspec | 360 |
| 361 return solutions |
482 | 362 |
483 | 363 |
484 def remove(target): | 364 def remove(target): |
485 """Remove a target by moving it into build.dead.""" | 365 """Remove a target by moving it into build.dead.""" |
486 dead_folder = path.join(BUILDER_DIR, 'build.dead') | 366 dead_folder = path.join(BUILDER_DIR, 'build.dead') |
487 if not path.exists(dead_folder): | 367 if not path.exists(dead_folder): |
488 os.makedirs(dead_folder) | 368 os.makedirs(dead_folder) |
489 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) | 369 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) |
490 | 370 |
491 | 371 |
492 def ensure_no_checkout(dir_names, scm_dirname): | 372 def ensure_no_checkout(dir_names): |
493 """Ensure that there is no undesired checkout under build/. | 373 """Ensure that there is no undesired checkout under build/.""" |
494 | 374 build_dir = os.getcwd() |
495 If there is an incorrect checkout under build/, then | 375 has_checkout = any(path.exists(path.join(build_dir, dir_name, '.git')) |
496 move build/ to build.dead/ | |
497 This function will check each directory in dir_names. | |
498 | |
499 scm_dirname is expected to be either ['.svn', '.git'] | |
500 """ | |
501 assert scm_dirname in ['.svn', '.git', '*'] | |
502 has_checkout = any(path.exists(path.join(os.getcwd(), dir_name, scm_dirname)) | |
503 for dir_name in dir_names) | 376 for dir_name in dir_names) |
504 | 377 if has_checkout: |
505 if has_checkout or scm_dirname == '*': | |
506 build_dir = os.getcwd() | |
507 prefix = '' | |
508 if scm_dirname != '*': | |
509 prefix = '%s detected in checkout, ' % scm_dirname | |
510 | |
511 for filename in os.listdir(build_dir): | 378 for filename in os.listdir(build_dir): |
512 deletion_target = path.join(build_dir, filename) | 379 deletion_target = path.join(build_dir, filename) |
513 print '%sdeleting %s...' % (prefix, deletion_target), | 380 print '.git detected in checkout, deleting %s...' % deletion_target, |
514 remove(deletion_target) | 381 remove(deletion_target) |
515 print 'done' | 382 print 'done' |
516 | 383 |
517 | 384 |
518 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): | 385 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): |
519 """Should do the same thing as gclient --spec='...'.""" | 386 """Should do the same thing as gclient --spec='...'.""" |
520 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: | 387 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: |
521 f.write(get_gclient_spec( | 388 f.write(get_gclient_spec( |
522 solutions, target_os, target_os_only, git_cache_dir)) | 389 solutions, target_os, target_os_only, git_cache_dir)) |
523 | 390 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 footers[m.group(1)] = m.group(2).strip() | 464 footers[m.group(1)] = m.group(2).strip() |
598 return footers | 465 return footers |
599 | 466 |
600 | 467 |
601 def get_commit_message_footer(message, key): | 468 def get_commit_message_footer(message, key): |
602 """Returns: (str/None) The footer value for 'key', or None if none was found. | 469 """Returns: (str/None) The footer value for 'key', or None if none was found. |
603 """ | 470 """ |
604 return get_commit_message_footer_map(message).get(key) | 471 return get_commit_message_footer_map(message).get(key) |
605 | 472 |
606 | 473 |
607 def get_svn_rev(git_hash, dir_name): | |
608 log = git('log', '-1', git_hash, cwd=dir_name) | |
609 git_svn_id = get_commit_message_footer(log, GIT_SVN_ID_FOOTER_KEY) | |
610 if not git_svn_id: | |
611 return None | |
612 m = GIT_SVN_ID_RE.match(git_svn_id) | |
613 if not m: | |
614 return None | |
615 return int(m.group(2)) | |
616 | |
617 | |
618 def get_git_hash(revision, branch, sln_dir): | |
619 """We want to search for the SVN revision on the git-svn branch. | |
620 | |
621 Note that git will search backwards from origin/master. | |
622 """ | |
623 match = "^%s: [^ ]*@%s " % (GIT_SVN_ID_FOOTER_KEY, revision) | |
624 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch | |
625 cmd = ['log', '-E', '--grep', match, '--format=%H', '--max-count=1', ref] | |
626 result = git(*cmd, cwd=sln_dir).strip() | |
627 if result: | |
628 return result | |
629 raise SVNRevisionNotFound('We can\'t resolve svn r%s into a git hash in %s' % | |
630 (revision, sln_dir)) | |
631 | |
632 | |
633 def emit_log_lines(name, lines): | 474 def emit_log_lines(name, lines): |
634 for line in lines.splitlines(): | 475 for line in lines.splitlines(): |
635 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) | 476 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) |
636 print '@@@STEP_LOG_END@%s@@@' % name | 477 print '@@@STEP_LOG_END@%s@@@' % name |
637 | 478 |
638 | 479 |
639 def emit_properties(properties): | 480 def emit_properties(properties): |
640 for property_name, property_value in sorted(properties.items()): | 481 for property_name, property_value in sorted(properties.items()): |
641 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) | 482 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) |
642 | 483 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 | 518 |
678 | 519 |
679 def force_revision(folder_name, revision): | 520 def force_revision(folder_name, revision): |
680 split_revision = revision.split(':', 1) | 521 split_revision = revision.split(':', 1) |
681 branch = 'master' | 522 branch = 'master' |
682 if len(split_revision) == 2: | 523 if len(split_revision) == 2: |
683 # Support for "branch:revision" syntax. | 524 # Support for "branch:revision" syntax. |
684 branch, revision = split_revision | 525 branch, revision = split_revision |
685 | 526 |
686 if revision and revision.upper() != 'HEAD': | 527 if revision and revision.upper() != 'HEAD': |
687 if revision and revision.isdigit() and len(revision) < 40: | 528 git('checkout', '--force', revision, cwd=folder_name) |
688 # rev_num is really a svn revision number, convert it into a git hash. | |
689 git_ref = get_git_hash(int(revision), branch, folder_name) | |
690 else: | |
691 # rev_num is actually a git hash or ref, we can just use it. | |
692 git_ref = revision | |
693 git('checkout', '--force', git_ref, cwd=folder_name) | |
694 else: | 529 else: |
695 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch | 530 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch |
696 git('checkout', '--force', ref, cwd=folder_name) | 531 git('checkout', '--force', ref, cwd=folder_name) |
697 | 532 |
| 533 |
698 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): | 534 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): |
699 build_dir = os.getcwd() | 535 build_dir = os.getcwd() |
700 # Before we do anything, break all git_cache locks. | 536 # Before we do anything, break all git_cache locks. |
701 if path.isdir(git_cache_dir): | 537 if path.isdir(git_cache_dir): |
702 git('cache', 'unlock', '-vv', '--force', '--all', | 538 git('cache', 'unlock', '-vv', '--force', '--all', |
703 '--cache-dir', git_cache_dir) | 539 '--cache-dir', git_cache_dir) |
704 for item in os.listdir(git_cache_dir): | 540 for item in os.listdir(git_cache_dir): |
705 filename = os.path.join(git_cache_dir, item) | 541 filename = os.path.join(git_cache_dir, item) |
706 if item.endswith('.lock'): | 542 if item.endswith('.lock'): |
707 raise Exception('%s exists after cache unlock' % filename) | 543 raise Exception('%s exists after cache unlock' % filename) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
748 # Exited abnormally, theres probably something wrong. | 584 # Exited abnormally, theres probably something wrong. |
749 # Lets wipe the checkout and try again. | 585 # Lets wipe the checkout and try again. |
750 tries_left -= 1 | 586 tries_left -= 1 |
751 if tries_left > 0: | 587 if tries_left > 0: |
752 print 'Something failed: %s.' % str(e) | 588 print 'Something failed: %s.' % str(e) |
753 print 'waiting 5 seconds and trying again...' | 589 print 'waiting 5 seconds and trying again...' |
754 time.sleep(5) | 590 time.sleep(5) |
755 else: | 591 else: |
756 raise | 592 raise |
757 remove(sln_dir) | 593 remove(sln_dir) |
758 except SVNRevisionNotFound: | |
759 tries_left -= 1 | |
760 if tries_left > 0: | |
761 # If we don't have the correct revision, wait and try again. | |
762 print 'We can\'t find revision %s.' % revision | |
763 print 'The svn to git replicator is probably falling behind.' | |
764 print 'waiting 5 seconds and trying again...' | |
765 time.sleep(5) | |
766 else: | |
767 raise | |
768 | 594 |
769 git('clean', '-dff', cwd=sln_dir) | 595 git('clean', '-dff', cwd=sln_dir) |
770 | 596 |
771 if first_solution: | 597 if first_solution: |
772 git_ref = git('log', '--format=%H', '--max-count=1', | 598 git_ref = git('log', '--format=%H', '--max-count=1', |
773 cwd=sln_dir).strip() | 599 cwd=sln_dir).strip() |
774 first_solution = False | 600 first_solution = False |
775 return git_ref | 601 return git_ref |
776 | 602 |
777 | 603 |
778 def _download(url): | 604 def _download(url): |
779 """Fetch url and return content, with retries for flake.""" | 605 """Fetch url and return content, with retries for flake.""" |
780 for attempt in xrange(ATTEMPTS): | 606 for attempt in xrange(ATTEMPTS): |
781 try: | 607 try: |
782 return urllib2.urlopen(url).read() | 608 return urllib2.urlopen(url).read() |
783 except Exception: | 609 except Exception: |
784 if attempt == ATTEMPTS - 1: | 610 if attempt == ATTEMPTS - 1: |
785 raise | 611 raise |
786 | 612 |
787 | 613 |
788 def parse_diff(diff): | |
789 """Takes a unified diff and returns a list of diffed files and their diffs. | |
790 | |
791 The return format is a list of pairs of: | |
792 (<filename>, <diff contents>) | |
793 <diff contents> is inclusive of the diff line. | |
794 """ | |
795 result = [] | |
796 current_diff = '' | |
797 current_header = None | |
798 for line in diff.splitlines(): | |
799 # "diff" is for git style patches, and "Index: " is for SVN style patches. | |
800 if line.startswith('diff') or line.startswith('Index: '): | |
801 if current_header: | |
802 # If we are in a diff portion, then save the diff. | |
803 result.append((current_header, '%s\n' % current_diff)) | |
804 git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line) | |
805 svn_header_match = re.match(r'Index: (.*)', line) | |
806 | |
807 if git_header_match: | |
808 # First, see if its a git style header. | |
809 from_file = git_header_match.group(1) | |
810 to_file = git_header_match.group(2) | |
811 if from_file != to_file and from_file.startswith('a/'): | |
812 # Sometimes git prepends 'a/' and 'b/' in front of file paths. | |
813 from_file = from_file[2:] | |
814 current_header = from_file | |
815 | |
816 elif svn_header_match: | |
817 # Otherwise, check if its an SVN style header. | |
818 current_header = svn_header_match.group(1) | |
819 | |
820 else: | |
821 # Otherwise... I'm not really sure what to do with this. | |
822 raise InvalidDiff('Can\'t process header: %s\nFull diff:\n%s' % | |
823 (line, diff)) | |
824 | |
825 current_diff = '' | |
826 current_diff += '%s\n' % line | |
827 if current_header: | |
828 # We hit EOF, gotta save the last diff. | |
829 result.append((current_header, current_diff)) | |
830 return result | |
831 | |
832 | |
833 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, | 614 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, |
834 email_file, key_file, whitelist=None, blacklist=None): | 615 email_file, key_file, whitelist=None, blacklist=None): |
835 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') | 616 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') |
836 else 'apply_issue') | 617 else 'apply_issue') |
837 cmd = [apply_issue_bin, | 618 cmd = [apply_issue_bin, |
838 # The patch will be applied on top of this directory. | 619 # The patch will be applied on top of this directory. |
839 '--root_dir', root, | 620 '--root_dir', root, |
840 # Tell apply_issue how to fetch the patch. | 621 # Tell apply_issue how to fetch the patch. |
841 '--issue', issue, | 622 '--issue', issue, |
842 '--server', server, | 623 '--server', server, |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
920 os.remove(flag_file) | 701 os.remove(flag_file) |
921 | 702 |
922 | 703 |
923 def emit_flag(flag_file): | 704 def emit_flag(flag_file): |
924 """Deposit a bot update flag on the system to tell gclient not to run.""" | 705 """Deposit a bot update flag on the system to tell gclient not to run.""" |
925 print 'Emitting flag file at %s' % flag_file | 706 print 'Emitting flag file at %s' % flag_file |
926 with open(flag_file, 'wb') as f: | 707 with open(flag_file, 'wb') as f: |
927 f.write('Success!') | 708 f.write('Success!') |
928 | 709 |
929 | 710 |
930 def get_commit_position_for_git_svn(url, revision): | |
931 """Generates a commit position string for a 'git-svn' URL/revision. | |
932 | |
933 If the 'git-svn' URL maps to a known project, we will construct a commit | |
934 position branch value by applying substitution on the SVN URL. | |
935 """ | |
936 # Identify the base URL so we can strip off trunk/branch name | |
937 project_config = branch = None | |
938 for _, project_config in GIT_SVN_PROJECT_MAP.iteritems(): | |
939 if url.startswith(project_config['svn_url']): | |
940 branch = url[len(project_config['svn_url']):] | |
941 break | |
942 | |
943 if branch: | |
944 # Strip any leading slashes | |
945 branch = branch.lstrip('/') | |
946 | |
947 # Try and map the branch | |
948 for pattern, repl in project_config.get('branch_map', ()): | |
949 nbranch, subn = re.subn(pattern, repl, branch, count=1) | |
950 if subn: | |
951 print 'INFO: Mapped SVN branch to Git branch [%s] => [%s]' % ( | |
952 branch, nbranch) | |
953 branch = nbranch | |
954 break | |
955 else: | |
956 # Use generic 'svn' branch | |
957 print 'INFO: Could not resolve project for SVN URL %r' % (url,) | |
958 branch = 'svn' | |
959 return '%s@{#%s}' % (branch, revision) | |
960 | |
961 | |
962 def get_commit_position(git_path, revision='HEAD'): | 711 def get_commit_position(git_path, revision='HEAD'): |
963 """Dumps the 'git' log for a specific revision and parses out the commit | 712 """Dumps the 'git' log for a specific revision and parses out the commit |
964 position. | 713 position. |
965 | 714 |
966 If a commit position metadata key is found, its value will be returned. | 715 If a commit position metadata key is found, its value will be returned. |
967 | |
968 Otherwise, we will search for a 'git-svn' metadata entry. If one is found, | |
969 we will compose a commit position from it, using its SVN revision value as | |
970 the revision. | |
971 | |
972 If the 'git-svn' URL maps to a known project, we will construct a commit | |
973 position branch value by truncating the URL, mapping 'trunk' to | |
974 "refs/heads/master". Otherwise, we will return the generic branch, 'svn'. | |
975 """ | 716 """ |
| 717 # TODO(iannucci): Use git-footers for this. |
976 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) | 718 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) |
977 footer_map = get_commit_message_footer_map(git_log) | 719 footer_map = get_commit_message_footer_map(git_log) |
978 | 720 |
979 # Search for commit position metadata | 721 # Search for commit position metadata |
980 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or | 722 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or |
981 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) | 723 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) |
982 if value: | 724 if value: |
983 return value | 725 return value |
984 | |
985 # Compose a commit position from 'git-svn' metadata | |
986 value = footer_map.get(GIT_SVN_ID_FOOTER_KEY) | |
987 if value: | |
988 m = GIT_SVN_ID_RE.match(value) | |
989 if not m: | |
990 raise ValueError("Invalid 'git-svn' value: [%s]" % (value,)) | |
991 return get_commit_position_for_git_svn(m.group(1), m.group(2)) | |
992 return None | 726 return None |
993 | 727 |
994 | 728 |
995 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): | 729 def parse_got_revision(gclient_output, got_revision_mapping): |
996 """Translate git gclient revision mapping to build properties. | 730 """Translate git gclient revision mapping to build properties.""" |
997 | |
998 If use_svn_revs is True, then translate git hashes in the revision mapping | |
999 to svn revision numbers. | |
1000 """ | |
1001 properties = {} | 731 properties = {} |
1002 solutions_output = { | 732 solutions_output = { |
1003 # Make sure path always ends with a single slash. | 733 # Make sure path always ends with a single slash. |
1004 '%s/' % path.rstrip('/') : solution_output for path, solution_output | 734 '%s/' % path.rstrip('/') : solution_output for path, solution_output |
1005 in gclient_output['solutions'].iteritems() | 735 in gclient_output['solutions'].iteritems() |
1006 } | 736 } |
1007 for dir_name, property_name in got_revision_mapping.iteritems(): | 737 for dir_name, property_name in got_revision_mapping.iteritems(): |
1008 # Make sure dir_name always ends with a single slash. | 738 # Make sure dir_name always ends with a single slash. |
1009 dir_name = '%s/' % dir_name.rstrip('/') | 739 dir_name = '%s/' % dir_name.rstrip('/') |
1010 if dir_name not in solutions_output: | 740 if dir_name not in solutions_output: |
1011 continue | 741 continue |
1012 solution_output = solutions_output[dir_name] | 742 solution_output = solutions_output[dir_name] |
1013 if solution_output.get('scm') is None: | 743 if solution_output.get('scm') is None: |
1014 # This is an ignored DEPS, so the output got_revision should be 'None'. | 744 # This is an ignored DEPS, so the output got_revision should be 'None'. |
1015 git_revision = revision = commit_position = None | 745 revision = commit_position = None |
1016 else: | 746 else: |
1017 # Since we are using .DEPS.git, everything had better be git. | 747 # Since we are using .DEPS.git, everything had better be git. |
1018 assert solution_output.get('scm') == 'git' | 748 assert solution_output.get('scm') == 'git' |
1019 git_revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() | 749 revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() |
1020 if use_svn_revs: | |
1021 revision = get_svn_rev(git_revision, dir_name) | |
1022 if not revision: | |
1023 revision = git_revision | |
1024 else: | |
1025 revision = git_revision | |
1026 commit_position = get_commit_position(dir_name) | 750 commit_position = get_commit_position(dir_name) |
1027 | 751 |
1028 properties[property_name] = revision | 752 properties[property_name] = revision |
1029 if revision != git_revision: | |
1030 properties['%s_git' % property_name] = git_revision | |
1031 if commit_position: | 753 if commit_position: |
1032 properties['%s_cp' % property_name] = commit_position | 754 properties['%s_cp' % property_name] = commit_position |
1033 | 755 |
1034 return properties | 756 return properties |
1035 | 757 |
1036 | 758 |
1037 def emit_json(out_file, did_run, gclient_output=None, **kwargs): | 759 def emit_json(out_file, did_run, gclient_output=None, **kwargs): |
1038 """Write run information into a JSON file.""" | 760 """Write run information into a JSON file.""" |
1039 output = {} | 761 output = {} |
1040 output.update(gclient_output if gclient_output else {}) | 762 output.update(gclient_output if gclient_output else {}) |
1041 output.update({'did_run': did_run}) | 763 output.update({'did_run': did_run}) |
1042 output.update(kwargs) | 764 output.update(kwargs) |
1043 with open(out_file, 'wb') as f: | 765 with open(out_file, 'wb') as f: |
1044 f.write(json.dumps(output, sort_keys=True)) | 766 f.write(json.dumps(output, sort_keys=True)) |
1045 | 767 |
1046 | 768 |
1047 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): | 769 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): |
1048 """Ensure correct DEPS revisions, ignores solutions.""" | 770 """Ensure correct DEPS revisions, ignores solutions.""" |
1049 for deps_name, deps_data in sorted(deps_url_mapping.items()): | 771 for deps_name, deps_data in sorted(deps_url_mapping.items()): |
1050 if deps_name.strip('/') in solutions: | 772 if deps_name.strip('/') in solutions: |
1051 # This has already been forced to the correct solution by git_checkout(). | 773 # This has already been forced to the correct solution by git_checkout(). |
1052 continue | 774 continue |
1053 revision = get_target_revision(deps_name, deps_data.get('url', None), | 775 revision = get_target_revision(deps_name, deps_data.get('url', None), |
1054 revisions) | 776 revisions) |
1055 if not revision: | 777 if not revision: |
1056 continue | 778 continue |
1057 # TODO(hinoka): Catch SVNRevisionNotFound error maybe? | |
1058 git('fetch', 'origin', cwd=deps_name) | 779 git('fetch', 'origin', cwd=deps_name) |
1059 force_revision(deps_name, revision) | 780 force_revision(deps_name, revision) |
1060 | 781 |
1061 | 782 |
1062 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, | 783 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, |
1063 patch_root, issue, patchset, rietveld_server, | 784 patch_root, issue, patchset, rietveld_server, |
1064 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, | 785 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, |
1065 revision_mapping, apply_issue_email_file, | 786 revision_mapping, apply_issue_email_file, |
1066 apply_issue_key_file, buildspec, gyp_env, shallow, runhooks, | 787 apply_issue_key_file, gyp_env, shallow, runhooks, |
1067 refs, git_cache_dir, gerrit_reset): | 788 refs, git_cache_dir, gerrit_reset): |
1068 # Get a checkout of each solution, without DEPS or hooks. | 789 # Get a checkout of each solution, without DEPS or hooks. |
1069 # Calling git directly because there is no way to run Gclient without | 790 # Calling git directly because there is no way to run Gclient without |
1070 # invoking DEPS. | 791 # invoking DEPS. |
1071 print 'Fetching Git checkout' | 792 print 'Fetching Git checkout' |
1072 | 793 |
1073 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) | 794 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) |
1074 | 795 |
1075 print '===Processing patch solutions===' | 796 print '===Processing patch solutions===' |
1076 already_patched = [] | 797 already_patched = [] |
(...skipping 16 matching lines...) Expand all Loading... |
1093 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, | 814 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, |
1094 gerrit_rebase_patch_ref) | 815 gerrit_rebase_patch_ref) |
1095 applied_gerrit_patch = True | 816 applied_gerrit_patch = True |
1096 | 817 |
1097 # Ensure our build/ directory is set up with the correct .gclient file. | 818 # Ensure our build/ directory is set up with the correct .gclient file. |
1098 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) | 819 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) |
1099 | 820 |
1100 # Let gclient do the DEPS syncing. | 821 # Let gclient do the DEPS syncing. |
1101 # The branch-head refspec is a special case because its possible Chrome | 822 # The branch-head refspec is a special case because its possible Chrome |
1102 # src, which contains the branch-head refspecs, is DEPSed in. | 823 # src, which contains the branch-head refspecs, is DEPSed in. |
1103 gclient_output = gclient_sync(buildspec or BRANCH_HEADS_REFSPEC in refs, | 824 gclient_output = gclient_sync(BRANCH_HEADS_REFSPEC in refs, shallow) |
1104 shallow) | |
1105 | 825 |
1106 # Now that gclient_sync has finished, we should revert any .DEPS.git so that | 826 # Now that gclient_sync has finished, we should revert any .DEPS.git so that |
1107 # presubmit doesn't complain about it being modified. | 827 # presubmit doesn't complain about it being modified. |
1108 if (not buildspec and | 828 if git('ls-files', '.DEPS.git', cwd=first_sln).strip(): |
1109 git('ls-files', '.DEPS.git', cwd=first_sln).strip()): | |
1110 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln) | 829 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln) |
1111 | 830 |
1112 if buildspec and runhooks: | |
1113 # Run gclient runhooks if we're on an official builder. | |
1114 # TODO(hinoka): Remove this when the official builders run their own | |
1115 # runhooks step. | |
1116 gclient_runhooks(gyp_env) | |
1117 | |
1118 # Finally, ensure that all DEPS are pinned to the correct revision. | 831 # Finally, ensure that all DEPS are pinned to the correct revision. |
1119 dir_names = [sln['name'] for sln in solutions] | 832 dir_names = [sln['name'] for sln in solutions] |
1120 ensure_deps_revisions(gclient_output.get('solutions', {}), | 833 ensure_deps_revisions(gclient_output.get('solutions', {}), |
1121 dir_names, revisions) | 834 dir_names, revisions) |
1122 # Apply the rest of the patch here (sans DEPS) | 835 # Apply the rest of the patch here (sans DEPS) |
1123 if issue: | 836 if issue: |
1124 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, | 837 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, |
1125 revision_mapping, git_ref, apply_issue_email_file, | 838 revision_mapping, git_ref, apply_issue_email_file, |
1126 apply_issue_key_file, blacklist=already_patched) | 839 apply_issue_key_file, blacklist=already_patched) |
1127 elif gerrit_ref and not applied_gerrit_patch: | 840 elif gerrit_ref and not applied_gerrit_patch: |
(...skipping 25 matching lines...) Expand all Loading... |
1153 expanded_revisions.extend(revision.split(',')) | 866 expanded_revisions.extend(revision.split(',')) |
1154 for revision in expanded_revisions: | 867 for revision in expanded_revisions: |
1155 split_revision = revision.split('@') | 868 split_revision = revision.split('@') |
1156 if len(split_revision) == 1: | 869 if len(split_revision) == 1: |
1157 # This is just a plain revision, set it as the revision for root. | 870 # This is just a plain revision, set it as the revision for root. |
1158 results[root] = split_revision[0] | 871 results[root] = split_revision[0] |
1159 elif len(split_revision) == 2: | 872 elif len(split_revision) == 2: |
1160 # This is an alt_root@revision argument. | 873 # This is an alt_root@revision argument. |
1161 current_root, current_rev = split_revision | 874 current_root, current_rev = split_revision |
1162 | 875 |
1163 # We want to normalize svn/git urls into .git urls. | |
1164 parsed_root = urlparse.urlparse(current_root) | 876 parsed_root = urlparse.urlparse(current_root) |
1165 if parsed_root.scheme == 'svn': | 877 if parsed_root.scheme in ['http', 'https']: |
1166 if parsed_root.path in RECOGNIZED_PATHS: | 878 # We want to normalize git urls into .git urls. |
1167 normalized_root = RECOGNIZED_PATHS[parsed_root.path] | |
1168 else: | |
1169 print 'WARNING: SVN path %s not recognized, ignoring' % current_root | |
1170 continue | |
1171 elif parsed_root.scheme in ['http', 'https']: | |
1172 normalized_root = 'https://%s/%s' % (parsed_root.netloc, | 879 normalized_root = 'https://%s/%s' % (parsed_root.netloc, |
1173 parsed_root.path) | 880 parsed_root.path) |
1174 if not normalized_root.endswith('.git'): | 881 if not normalized_root.endswith('.git'): |
1175 normalized_root = '%s.git' % normalized_root | 882 normalized_root = '%s.git' % normalized_root |
1176 elif parsed_root.scheme: | 883 elif parsed_root.scheme: |
1177 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme | 884 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme |
1178 continue | 885 continue |
1179 else: | 886 else: |
1180 # This is probably a local path. | 887 # This is probably a local path. |
1181 normalized_root = current_root.strip('/') | 888 normalized_root = current_root.strip('/') |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1214 parse.add_option('--specs', help='Gcilent spec.') | 921 parse.add_option('--specs', help='Gcilent spec.') |
1215 parse.add_option('-f', '--force', action='store_true', | 922 parse.add_option('-f', '--force', action='store_true', |
1216 help='Bypass check to see if we want to be run. ' | 923 help='Bypass check to see if we want to be run. ' |
1217 'Should ONLY be used locally or by smart recipes.') | 924 'Should ONLY be used locally or by smart recipes.') |
1218 parse.add_option('--revision_mapping', | 925 parse.add_option('--revision_mapping', |
1219 help='{"path/to/repo/": "property_name"}') | 926 help='{"path/to/repo/": "property_name"}') |
1220 parse.add_option('--revision_mapping_file', | 927 parse.add_option('--revision_mapping_file', |
1221 help=('Same as revision_mapping, except its a path to a json' | 928 help=('Same as revision_mapping, except its a path to a json' |
1222 ' file containing that format.')) | 929 ' file containing that format.')) |
1223 parse.add_option('--revision', action='append', default=[], | 930 parse.add_option('--revision', action='append', default=[], |
1224 help='Revision to check out. Can be an SVN revision number, ' | 931 help='Revision to check out. Can be any form of git ref. ' |
1225 'git hash, or any form of git ref. Can prepend ' | 932 'Can prepend root@<rev> to specify which repository, ' |
1226 'root@<rev> to specify which repository, where root ' | 933 'where root is either a filesystem path or git https ' |
1227 'is either a filesystem path, git https url, or ' | 934 'url. To specify Tip of Tree, set rev to HEAD. ') |
1228 'svn url. To specify Tip of Tree, set rev to HEAD.' | |
1229 'To specify a git branch and an SVN rev, <rev> can be ' | |
1230 'set to <branch>:<revision>.') | |
1231 parse.add_option('--output_manifest', action='store_true', | 935 parse.add_option('--output_manifest', action='store_true', |
1232 help=('Add manifest json to the json output.')) | 936 help=('Add manifest json to the json output.')) |
1233 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], | 937 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], |
1234 help='Hostname of the current machine, ' | 938 help='Hostname of the current machine, ' |
1235 'used for determining whether or not to activate.') | 939 'used for determining whether or not to activate.') |
1236 parse.add_option('--build_dir', default=os.getcwd()) | 940 parse.add_option('--build_dir', default=os.getcwd()) |
1237 parse.add_option('--flag_file', default=path.join(os.getcwd(), | 941 parse.add_option('--flag_file', default=path.join(os.getcwd(), |
1238 'update.flag')) | 942 'update.flag')) |
1239 parse.add_option('--shallow', action='store_true', | 943 parse.add_option('--shallow', action='store_true', |
1240 help='Use shallow clones for cache repositories.') | 944 help='Use shallow clones for cache repositories.') |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1292 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') | 996 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') |
1293 | 997 |
1294 return options, args | 998 return options, args |
1295 | 999 |
1296 | 1000 |
1297 def prepare(options, git_slns, active): | 1001 def prepare(options, git_slns, active): |
1298 """Prepares the target folder before we checkout.""" | 1002 """Prepares the target folder before we checkout.""" |
1299 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 1003 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
1300 # If we're active now, but the flag file doesn't exist (we weren't active | 1004 # If we're active now, but the flag file doesn't exist (we weren't active |
1301 # last run) or vice versa, blow away all checkouts. | 1005 # last run) or vice versa, blow away all checkouts. |
1302 if bool(active) != bool(check_flag(options.flag_file)): | 1006 if options.clobber or (bool(active) != bool(check_flag(options.flag_file))): |
1303 ensure_no_checkout(dir_names, '*') | 1007 ensure_no_checkout(dir_names) |
1304 if options.output_json: | 1008 if options.output_json: |
1305 # Make sure we tell recipes that we didn't run if the script exits here. | 1009 # Make sure we tell recipes that we didn't run if the script exits here. |
1306 emit_json(options.output_json, did_run=active) | 1010 emit_json(options.output_json, did_run=active) |
1307 if active: | 1011 emit_flag(options.flag_file) |
1308 if options.clobber: | |
1309 ensure_no_checkout(dir_names, '*') | |
1310 else: | |
1311 ensure_no_checkout(dir_names, '.svn') | |
1312 emit_flag(options.flag_file) | |
1313 else: | |
1314 delete_flag(options.flag_file) | |
1315 raise Inactive # This is caught in main() and we exit cleanly. | |
1316 | 1012 |
1317 # Do a shallow checkout if the disk is less than 100GB. | 1013 # Do a shallow checkout if the disk is less than 100GB. |
1318 total_disk_space, free_disk_space = get_total_disk_space() | 1014 total_disk_space, free_disk_space = get_total_disk_space() |
1319 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) | 1015 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) |
1320 used_disk_space_gb = int((total_disk_space - free_disk_space) | 1016 used_disk_space_gb = int((total_disk_space - free_disk_space) |
1321 / (1024 * 1024 * 1024)) | 1017 / (1024 * 1024 * 1024)) |
1322 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) | 1018 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) |
1323 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, | 1019 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, |
1324 total_disk_space_gb, | 1020 total_disk_space_gb, |
1325 percent_used) | 1021 percent_used) |
1326 if not options.output_json: | 1022 if not options.output_json: |
1327 print '@@@STEP_TEXT@%s@@@' % step_text | 1023 print '@@@STEP_TEXT@%s@@@' % step_text |
1328 if not options.shallow: | 1024 if not options.shallow: |
1329 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD | 1025 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD |
1330 and not options.no_shallow) | 1026 and not options.no_shallow) |
1331 | 1027 |
1332 # The first solution is where the primary DEPS file resides. | 1028 # The first solution is where the primary DEPS file resides. |
1333 first_sln = dir_names[0] | 1029 first_sln = dir_names[0] |
1334 | 1030 |
1335 # Split all the revision specifications into a nice dict. | 1031 # Split all the revision specifications into a nice dict. |
1336 print 'Revisions: %s' % options.revision | 1032 print 'Revisions: %s' % options.revision |
1337 revisions = parse_revisions(options.revision, first_sln) | 1033 revisions = parse_revisions(options.revision, first_sln) |
1338 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) | 1034 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) |
1339 return revisions, step_text | 1035 return revisions, step_text |
1340 | 1036 |
1341 | 1037 |
1342 def checkout(options, git_slns, specs, buildspec, | 1038 def checkout(options, git_slns, specs, revisions, step_text): |
1343 svn_root, revisions, step_text): | |
1344 first_sln = git_slns[0]['name'] | 1039 first_sln = git_slns[0]['name'] |
1345 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 1040 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
1346 try: | 1041 try: |
1347 # Outer try is for catching patch failures and exiting gracefully. | 1042 # Outer try is for catching patch failures and exiting gracefully. |
1348 # Inner try is for catching gclient failures and retrying gracefully. | 1043 # Inner try is for catching gclient failures and retrying gracefully. |
1349 try: | 1044 try: |
1350 checkout_parameters = dict( | 1045 checkout_parameters = dict( |
1351 # First, pass in the base of what we want to check out. | 1046 # First, pass in the base of what we want to check out. |
1352 solutions=git_slns, | 1047 solutions=git_slns, |
1353 revisions=revisions, | 1048 revisions=revisions, |
1354 first_sln=first_sln, | 1049 first_sln=first_sln, |
1355 | 1050 |
1356 # Also, target os variables for gclient. | 1051 # Also, target os variables for gclient. |
1357 target_os=specs.get('target_os', []), | 1052 target_os=specs.get('target_os', []), |
1358 target_os_only=specs.get('target_os_only', False), | 1053 target_os_only=specs.get('target_os_only', False), |
1359 | 1054 |
1360 # Then, pass in information about how to patch. | 1055 # Then, pass in information about how to patch. |
1361 patch_root=options.patch_root, | 1056 patch_root=options.patch_root, |
1362 issue=options.issue, | 1057 issue=options.issue, |
1363 patchset=options.patchset, | 1058 patchset=options.patchset, |
1364 rietveld_server=options.rietveld_server, | 1059 rietveld_server=options.rietveld_server, |
1365 gerrit_repo=options.gerrit_repo, | 1060 gerrit_repo=options.gerrit_repo, |
1366 gerrit_ref=options.gerrit_ref, | 1061 gerrit_ref=options.gerrit_ref, |
1367 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, | 1062 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, |
1368 revision_mapping=options.revision_mapping, | 1063 revision_mapping=options.revision_mapping, |
1369 apply_issue_email_file=options.apply_issue_email_file, | 1064 apply_issue_email_file=options.apply_issue_email_file, |
1370 apply_issue_key_file=options.apply_issue_key_file, | 1065 apply_issue_key_file=options.apply_issue_key_file, |
1371 | 1066 |
1372 # For official builders. | 1067 # For official builders. |
1373 buildspec=buildspec, | |
1374 gyp_env=options.gyp_env, | 1068 gyp_env=options.gyp_env, |
1375 runhooks=not options.no_runhooks, | 1069 runhooks=not options.no_runhooks, |
1376 | 1070 |
1377 # Finally, extra configurations such as shallowness of the clone. | 1071 # Finally, extra configurations such as shallowness of the clone. |
1378 shallow=options.shallow, | 1072 shallow=options.shallow, |
1379 refs=options.refs, | 1073 refs=options.refs, |
1380 git_cache_dir=options.git_cache_dir, | 1074 git_cache_dir=options.git_cache_dir, |
1381 gerrit_reset=not options.gerrit_no_reset) | 1075 gerrit_reset=not options.gerrit_no_reset) |
1382 gclient_output = ensure_checkout(**checkout_parameters) | 1076 gclient_output = ensure_checkout(**checkout_parameters) |
1383 except GclientSyncFailed: | 1077 except GclientSyncFailed: |
1384 print 'We failed gclient sync, lets delete the checkout and retry.' | 1078 print 'We failed gclient sync, lets delete the checkout and retry.' |
1385 ensure_no_checkout(dir_names, '*') | 1079 ensure_no_checkout(dir_names) |
1386 gclient_output = ensure_checkout(**checkout_parameters) | 1080 gclient_output = ensure_checkout(**checkout_parameters) |
1387 except PatchFailed as e: | 1081 except PatchFailed as e: |
1388 if options.output_json: | 1082 if options.output_json: |
1389 # Tell recipes information such as root, got_revision, etc. | 1083 # Tell recipes information such as root, got_revision, etc. |
1390 emit_json(options.output_json, | 1084 emit_json(options.output_json, |
1391 did_run=True, | 1085 did_run=True, |
1392 root=first_sln, | 1086 root=first_sln, |
1393 log_lines=[('patch error', e.output),], | 1087 log_lines=[('patch error', e.output),], |
1394 patch_apply_return_code=e.code, | 1088 patch_apply_return_code=e.code, |
1395 patch_root=options.patch_root, | 1089 patch_root=options.patch_root, |
1396 patch_failure=True, | 1090 patch_failure=True, |
1397 step_text='%s PATCH FAILED' % step_text, | 1091 step_text='%s PATCH FAILED' % step_text, |
1398 fixed_revisions=revisions) | 1092 fixed_revisions=revisions) |
1399 else: | 1093 else: |
1400 # If we're not on recipes, tell annotator about our got_revisions. | 1094 # If we're not on recipes, tell annotator about our got_revisions. |
1401 emit_log_lines('patch error', e.output) | 1095 emit_log_lines('patch error', e.output) |
1402 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text | 1096 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text |
1403 raise | 1097 raise |
1404 | 1098 |
1405 use_svn_rev = False | |
1406 | |
1407 # Take care of got_revisions outputs. | 1099 # Take care of got_revisions outputs. |
1408 revision_mapping = dict(GOT_REVISION_MAPPINGS.get(svn_root, {})) | 1100 revision_mapping = GOT_REVISION_MAPPINGS.get(git_slns[0]['url'], {}) |
1409 if options.revision_mapping: | 1101 if options.revision_mapping: |
1410 revision_mapping.update(options.revision_mapping) | 1102 revision_mapping.update(options.revision_mapping) |
1411 | 1103 |
1412 # If the repo is not in the default GOT_REVISION_MAPPINGS and no | 1104 # If the repo is not in the default GOT_REVISION_MAPPINGS and no |
1413 # revision_mapping were specified on the command line then | 1105 # revision_mapping were specified on the command line then |
1414 # default to setting 'got_revision' based on the first solution. | 1106 # default to setting 'got_revision' based on the first solution. |
1415 if not revision_mapping: | 1107 if not revision_mapping: |
1416 revision_mapping[first_sln] = 'got_revision' | 1108 revision_mapping[first_sln] = 'got_revision' |
1417 | 1109 |
1418 got_revisions = parse_got_revision(gclient_output, revision_mapping, | 1110 got_revisions = parse_got_revision(gclient_output, revision_mapping) |
1419 use_svn_rev) | |
1420 | 1111 |
1421 if not got_revisions: | 1112 if not got_revisions: |
1422 # TODO(hinoka): We should probably bail out here, but in the interest | 1113 # TODO(hinoka): We should probably bail out here, but in the interest |
1423 # of giving mis-configured bots some time to get fixed use a dummy | 1114 # of giving mis-configured bots some time to get fixed use a dummy |
1424 # revision here. | 1115 # revision here. |
1425 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } | 1116 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } |
1426 #raise Exception('No got_revision(s) found in gclient output') | 1117 #raise Exception('No got_revision(s) found in gclient output') |
1427 | 1118 |
1428 if options.output_json: | 1119 if options.output_json: |
1429 manifest = create_manifest() if options.output_manifest else None | 1120 manifest = create_manifest() if options.output_manifest else None |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1467 | 1158 |
1468 # Check if this script should activate or not. | 1159 # Check if this script should activate or not. |
1469 active = True | 1160 active = True |
1470 | 1161 |
1471 # Print a helpful message to tell developers whats going on with this step. | 1162 # Print a helpful message to tell developers whats going on with this step. |
1472 print_debug_info() | 1163 print_debug_info() |
1473 | 1164 |
1474 # Parse, munipulate, and print the gclient solutions. | 1165 # Parse, munipulate, and print the gclient solutions. |
1475 specs = {} | 1166 specs = {} |
1476 exec(options.specs, specs) | 1167 exec(options.specs, specs) |
1477 svn_solutions = specs.get('solutions', []) | 1168 orig_solutions = specs.get('solutions', []) |
1478 git_slns, svn_root, buildspec = solutions_to_git(svn_solutions) | 1169 git_slns = modify_solutions(orig_solutions) |
1479 options.revision = maybe_ignore_revision(options.revision, buildspec) | |
1480 | 1170 |
1481 solutions_printer(git_slns) | 1171 solutions_printer(git_slns) |
1482 | 1172 |
1483 try: | 1173 try: |
1484 # Dun dun dun, the main part of bot_update. | 1174 # Dun dun dun, the main part of bot_update. |
1485 revisions, step_text = prepare(options, git_slns, active) | 1175 revisions, step_text = prepare(options, git_slns, active) |
1486 checkout(options, git_slns, specs, buildspec, svn_root, revisions, | 1176 checkout(options, git_slns, specs, revisions, step_text) |
1487 step_text) | |
1488 | 1177 |
1489 except Inactive: | |
1490 # Not active, should count as passing. | |
1491 pass | |
1492 except PatchFailed as e: | 1178 except PatchFailed as e: |
1493 emit_flag(options.flag_file) | 1179 emit_flag(options.flag_file) |
1494 # Return a specific non-zero exit code for patch failure (because it is | 1180 # Return a specific non-zero exit code for patch failure (because it is |
1495 # a failure), but make it different than other failures to distinguish | 1181 # a failure), but make it different than other failures to distinguish |
1496 # between infra failures (independent from patch author), and patch | 1182 # between infra failures (independent from patch author), and patch |
1497 # failures (that patch author can fix). However, PatchFailure due to | 1183 # failures (that patch author can fix). However, PatchFailure due to |
1498 # download patch failure is still an infra problem. | 1184 # download patch failure is still an infra problem. |
1499 if e.code == 3: | 1185 if e.code == 3: |
1500 # Patch download problem. | 1186 # Patch download problem. |
1501 return 87 | 1187 return 87 |
1502 # Genuine patch problem. | 1188 # Genuine patch problem. |
1503 return 88 | 1189 return 88 |
1504 except Exception: | 1190 except Exception: |
1505 # Unexpected failure. | 1191 # Unexpected failure. |
1506 emit_flag(options.flag_file) | 1192 emit_flag(options.flag_file) |
1507 raise | 1193 raise |
1508 else: | 1194 else: |
1509 emit_flag(options.flag_file) | 1195 emit_flag(options.flag_file) |
1510 | 1196 |
1511 | 1197 |
1512 if __name__ == '__main__': | 1198 if __name__ == '__main__': |
1513 sys.exit(main()) | 1199 sys.exit(main()) |
OLD | NEW |