OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 """Snapshot Build Bisect Tool | 6 """Snapshot Build Bisect Tool |
7 | 7 |
8 This script bisects a snapshot archive using binary search. It starts at | 8 This script bisects a snapshot archive using binary search. It starts at |
9 a bad revision (it will try to guess HEAD) and asks for a last known-good | 9 a bad revision (it will try to guess HEAD) and asks for a last known-good |
10 revision. It will then binary search across this revision range by downloading, | 10 revision. It will then binary search across this revision range by downloading, |
11 unzipping, and opening Chromium for you. After testing the specific revision, | 11 unzipping, and opening Chromium for you. After testing the specific revision, |
12 it will ask you whether it is good or bad before continuing the search. | 12 it will ask you whether it is good or bad before continuing the search. |
13 """ | 13 """ |
14 | 14 |
15 # The base URL for stored build archives. | 15 # The base URL for stored build archives. |
16 CHROMIUM_BASE_URL = ('http://commondatastorage.googleapis.com' | 16 CHROMIUM_BASE_URL = ('http://commondatastorage.googleapis.com' |
17 '/chromium-browser-snapshots') | 17 '/chromium-browser-snapshots') |
18 WEBKIT_BASE_URL = ('http://commondatastorage.googleapis.com' | 18 WEBKIT_BASE_URL = ('http://commondatastorage.googleapis.com' |
19 '/chromium-webkit-snapshots') | 19 '/chromium-webkit-snapshots') |
20 ASAN_BASE_URL = ('http://commondatastorage.googleapis.com' | 20 ASAN_BASE_URL = ('http://commondatastorage.googleapis.com' |
21 '/chromium-browser-asan') | 21 '/chromium-browser-asan') |
22 | 22 |
23 # The base URL for official builds. | 23 # The base URL for official builds. |
24 OFFICIAL_BASE_URL = 'http://master.chrome.corp.google.com/official_builds' | 24 OFFICIAL_BASE_URL = ('http://commondatastorage.googleapis.com/' |
DaleCurtis
2014/08/21 00:18:55
Construct the official base_url using the bucket_n
pshenoy
2014/08/21 16:27:23
Done.
| |
25 'chrome-unsigned/desktop-W15K3Y') | |
26 # GS bucket name. | |
27 GS_BUCKET_NAME = 'chrome-unsigned/desktop-W15K3Y' | |
28 | |
29 # Base URL for downloading official builds. | |
30 GOOGLE_APIS_URL = 'commondatastorage.googleapis.com' | |
25 | 31 |
26 # URL template for viewing changelogs between revisions. | 32 # URL template for viewing changelogs between revisions. |
27 CHANGELOG_URL = ('http://build.chromium.org' | 33 CHANGELOG_URL = ('http://build.chromium.org' |
28 '/f/chromium/perf/dashboard/ui/changelog.html' | 34 '/f/chromium/perf/dashboard/ui/changelog.html' |
29 '?url=/trunk/src&range=%d%%3A%d') | 35 '?url=/trunk/src&range=%d%%3A%d') |
30 | 36 |
31 # URL template for viewing changelogs between official versions. | 37 # URL template for viewing changelogs between official versions. |
32 OFFICIAL_CHANGELOG_URL = ('http://omahaproxy.appspot.com/changelog' | 38 OFFICIAL_CHANGELOG_URL = ('http://omahaproxy.appspot.com/changelog' |
33 '?old_version=%s&new_version=%s') | 39 '?old_version=%s&new_version=%s') |
34 | 40 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
66 BLINK_SEARCH_PATTERN = ( | 72 BLINK_SEARCH_PATTERN = ( |
67 r'.*git-svn-id: svn://svn.chromium.org/blink/trunk@(\d+) ') | 73 r'.*git-svn-id: svn://svn.chromium.org/blink/trunk@(\d+) ') |
68 | 74 |
69 SEARCH_PATTERN = { | 75 SEARCH_PATTERN = { |
70 'chromium': CHROMIUM_SEARCH_PATTERN, | 76 'chromium': CHROMIUM_SEARCH_PATTERN, |
71 'blink': BLINK_SEARCH_PATTERN, | 77 'blink': BLINK_SEARCH_PATTERN, |
72 } | 78 } |
73 | 79 |
74 ############################################################################### | 80 ############################################################################### |
75 | 81 |
82 import httplib | |
76 import json | 83 import json |
77 import optparse | 84 import optparse |
78 import os | 85 import os |
79 import re | 86 import re |
80 import shlex | 87 import shlex |
81 import shutil | 88 import shutil |
82 import subprocess | 89 import subprocess |
83 import sys | 90 import sys |
84 import tempfile | 91 import tempfile |
85 import threading | 92 import threading |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 self._archive_extract_dir = 'chrome-mac' | 139 self._archive_extract_dir = 'chrome-mac' |
133 elif self.platform == 'win': | 140 elif self.platform == 'win': |
134 self.archive_name = 'chrome-win32.zip' | 141 self.archive_name = 'chrome-win32.zip' |
135 self._archive_extract_dir = 'chrome-win32' | 142 self._archive_extract_dir = 'chrome-win32' |
136 self._binary_name = 'chrome.exe' | 143 self._binary_name = 'chrome.exe' |
137 else: | 144 else: |
138 raise Exception('Invalid platform: %s' % self.platform) | 145 raise Exception('Invalid platform: %s' % self.platform) |
139 | 146 |
140 if is_official: | 147 if is_official: |
141 if self.platform == 'linux': | 148 if self.platform == 'linux': |
142 self._listing_platform_dir = 'precise32bit/' | 149 self._listing_platform_dir = 'precise32/' |
143 self.archive_name = 'chrome-precise32bit.zip' | 150 self.archive_name = 'chrome-precise32.zip' |
144 self._archive_extract_dir = 'chrome-precise32bit' | 151 self._archive_extract_dir = 'chrome-precise32' |
145 elif self.platform == 'linux64': | 152 elif self.platform == 'linux64': |
146 self._listing_platform_dir = 'precise64bit/' | 153 self._listing_platform_dir = 'precise64/' |
147 self.archive_name = 'chrome-precise64bit.zip' | 154 self.archive_name = 'chrome-precise64.zip' |
148 self._archive_extract_dir = 'chrome-precise64bit' | 155 self._archive_extract_dir = 'chrome-precise64' |
149 elif self.platform == 'mac': | 156 elif self.platform == 'mac': |
150 self._listing_platform_dir = 'mac/' | 157 self._listing_platform_dir = 'mac/' |
151 self._binary_name = 'Google Chrome.app/Contents/MacOS/Google Chrome' | 158 self._binary_name = 'Google Chrome.app/Contents/MacOS/Google Chrome' |
152 elif self.platform == 'win': | 159 elif self.platform == 'win': |
153 self._listing_platform_dir = 'win/' | 160 self._listing_platform_dir = 'win/' |
154 else: | 161 else: |
155 if self.platform in ('linux', 'linux64', 'linux-arm'): | 162 if self.platform in ('linux', 'linux64', 'linux-arm'): |
156 self.archive_name = 'chrome-linux.zip' | 163 self.archive_name = 'chrome-linux.zip' |
157 self._archive_extract_dir = 'chrome-linux' | 164 self._archive_extract_dir = 'chrome-linux' |
158 if self.platform == 'linux': | 165 if self.platform == 'linux': |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
386 self.good_revision) | 393 self.good_revision) |
387 self.bad_revision = FixChromiumRevForBlink(revlist, | 394 self.bad_revision = FixChromiumRevForBlink(revlist, |
388 revlist_all, | 395 revlist_all, |
389 self, | 396 self, |
390 self.bad_revision) | 397 self.bad_revision) |
391 return revlist | 398 return revlist |
392 | 399 |
393 def GetOfficialBuildsList(self): | 400 def GetOfficialBuildsList(self): |
394 """Gets the list of official build numbers between self.good_revision and | 401 """Gets the list of official build numbers between self.good_revision and |
395 self.bad_revision.""" | 402 self.bad_revision.""" |
403 | |
404 def CheckDepotToolsInPath(): | |
405 delimiter = ';' if sys.platform.startswith('win') else ':' | |
406 path_list = os.environ['PATH'].split(delimiter) | |
407 for path in path_list: | |
408 if path.find('depot_tools') != -1: | |
409 return path | |
410 return None | |
411 | |
412 def RunGsutilCommand(args): | |
413 gsutil_path = CheckDepotToolsInPath() | |
414 if gsutil_path is None: | |
415 print ('Follow the instructions in this document ' | |
416 'http://dev.chromium.org/developers/how-tos/install-depot-tools' | |
417 ' to install depot_tools and then try again.') | |
418 sys.exit(1) | |
419 gsutil_path = os.path.join(gsutil_path, 'third_party', 'gsutil', 'gsutil') | |
420 gsutil = subprocess.Popen([sys.executable, gsutil_path] + args, | |
421 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | |
422 env=None) | |
423 stdout, stderr = gsutil.communicate() | |
424 if gsutil.returncode: | |
425 if re.findall(r'status[ |=]40[1|3]', stderr): | |
426 print ('Follow these steps to configure your credentials and try' | |
427 ' running the bisect-builds.py again.:\n' | |
428 ' 1. Run "python %s config" and follow its instructions.\n' | |
429 ' 2. If you have a @google.com account, use that account.\n' | |
430 ' 3. For the project-id, just enter 0.' % gsutil_path) | |
431 sys.exit(1) | |
432 else: | |
433 raise Exception('Error running the gsutil command') | |
434 return stdout | |
435 | |
436 def GsutilList(bucket): | |
437 query = 'gs://%s/' % bucket | |
438 stdout = RunGsutilCommand(['ls', query]) | |
439 return [url[len(query):].strip('/') for url in stdout.splitlines()] | |
440 | |
396 # Download the revlist and filter for just the range between good and bad. | 441 # Download the revlist and filter for just the range between good and bad. |
397 minrev = min(self.good_revision, self.bad_revision) | 442 minrev = min(self.good_revision, self.bad_revision) |
398 maxrev = max(self.good_revision, self.bad_revision) | 443 maxrev = max(self.good_revision, self.bad_revision) |
399 handle = urllib.urlopen(OFFICIAL_BASE_URL) | 444 build_numbers = GsutilList(GS_BUCKET_NAME) |
400 dirindex = handle.read() | 445 revision_re = re.compile(r'(\d\d\.\d\.\d{4}\.\d+)') |
401 handle.close() | 446 build_numbers = filter(lambda b: revision_re.search(b), build_numbers) |
402 build_numbers = re.findall(r'<a href="([0-9][0-9].*)/">', dirindex) | |
403 final_list = [] | 447 final_list = [] |
404 i = 0 | 448 i = 0 |
405 parsed_build_numbers = [LooseVersion(x) for x in build_numbers] | 449 parsed_build_numbers = [LooseVersion(x) for x in build_numbers] |
450 connection = httplib.HTTPConnection(GOOGLE_APIS_URL) | |
406 for build_number in sorted(parsed_build_numbers): | 451 for build_number in sorted(parsed_build_numbers): |
407 path = (OFFICIAL_BASE_URL + '/' + str(build_number) + '/' + | 452 if build_number > maxrev: |
453 break | |
454 if build_number < minrev: | |
455 continue | |
456 path = ('/' + GS_BUCKET_NAME + '/' + str(build_number) + '/' + | |
408 self._listing_platform_dir + self.archive_name) | 457 self._listing_platform_dir + self.archive_name) |
409 i = i + 1 | 458 i = i + 1 |
410 try: | 459 connection.request('HEAD', path) |
411 connection = urllib.urlopen(path) | 460 response = connection.getresponse() |
412 connection.close() | 461 if response.status == 200: |
413 if build_number > maxrev: | 462 final_list.append(str(build_number)) |
414 break | 463 response.read() |
415 if build_number >= minrev: | 464 connection.close() |
416 final_list.append(str(build_number)) | |
417 except urllib.HTTPError: | |
418 pass | |
419 return final_list | 465 return final_list |
420 | 466 |
421 def UnzipFilenameToDir(filename, directory): | 467 def UnzipFilenameToDir(filename, directory): |
422 """Unzip |filename| to |directory|.""" | 468 """Unzip |filename| to |directory|.""" |
423 cwd = os.getcwd() | 469 cwd = os.getcwd() |
424 if not os.path.isabs(filename): | 470 if not os.path.isabs(filename): |
425 filename = os.path.join(cwd, filename) | 471 filename = os.path.join(cwd, filename) |
426 zf = zipfile.ZipFile(filename) | 472 zf = zipfile.ZipFile(filename) |
427 # Make base. | 473 # Make base. |
428 if not os.path.isdir(directory): | 474 if not os.path.isdir(directory): |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 """ | 696 """ |
651 | 697 |
652 if not profile: | 698 if not profile: |
653 profile = 'profile' | 699 profile = 'profile' |
654 | 700 |
655 good_rev = context.good_revision | 701 good_rev = context.good_revision |
656 bad_rev = context.bad_revision | 702 bad_rev = context.bad_revision |
657 cwd = os.getcwd() | 703 cwd = os.getcwd() |
658 | 704 |
659 print 'Downloading list of known revisions...', | 705 print 'Downloading list of known revisions...', |
660 if not context.use_local_repo: | 706 if not context.use_local_repo and not context.is_official: |
661 print '(use --use-local-repo for speed if you have a local checkout)' | 707 print '(use --use-local-repo for speed if you have a local checkout)' |
662 else: | 708 else: |
663 print | 709 print |
664 _GetDownloadPath = lambda rev: os.path.join(cwd, | 710 _GetDownloadPath = lambda rev: os.path.join(cwd, |
665 '%s-%s' % (str(rev), context.archive_name)) | 711 '%s-%s' % (str(rev), context.archive_name)) |
666 if context.is_official: | 712 if context.is_official: |
667 revlist = context.GetOfficialBuildsList() | 713 revlist = context.GetOfficialBuildsList() |
668 else: | 714 else: |
669 revlist = context.GetRevList() | 715 revlist = context.GetRevList() |
670 | 716 |
(...skipping 417 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1088 | 1134 |
1089 print 'CHANGELOG URL:' | 1135 print 'CHANGELOG URL:' |
1090 if opts.official_builds: | 1136 if opts.official_builds: |
1091 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 1137 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
1092 else: | 1138 else: |
1093 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 1139 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
1094 | 1140 |
1095 | 1141 |
1096 if __name__ == '__main__': | 1142 if __name__ == '__main__': |
1097 sys.exit(main()) | 1143 sys.exit(main()) |
OLD | NEW |