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

Side by Side Diff: tools/bisect-builds.py

Issue 28313002: Add Flash binary path option to bisect script. (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 1 month 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 (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,
(...skipping 11 matching lines...) Expand all
22 # Changelogs URL. 22 # Changelogs URL.
23 CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ 23 CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \
24 'perf/dashboard/ui/changelog.html?' \ 24 'perf/dashboard/ui/changelog.html?' \
25 'url=/trunk/src&range=%d%%3A%d' 25 'url=/trunk/src&range=%d%%3A%d'
26 26
27 # Official Changelogs URL. 27 # Official Changelogs URL.
28 OFFICIAL_CHANGELOG_URL = 'http://omahaproxy.appspot.com/'\ 28 OFFICIAL_CHANGELOG_URL = 'http://omahaproxy.appspot.com/'\
29 'changelog?old_version=%s&new_version=%s' 29 'changelog?old_version=%s&new_version=%s'
30 30
31 # DEPS file URL. 31 # DEPS file URL.
32 DEPS_FILE= 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d' 32 DEPS_FILE = 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d'
33 # Blink Changelogs URL. 33 # Blink Changelogs URL.
34 BLINK_CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ 34 BLINK_CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \
35 'perf/dashboard/ui/changelog_blink.html?' \ 35 'perf/dashboard/ui/changelog_blink.html?' \
36 'url=/trunk&range=%d%%3A%d' 36 'url=/trunk&range=%d%%3A%d'
37 37
38 DONE_MESSAGE_GOOD_MIN = 'You are probably looking for a change made after %s ' \ 38 DONE_MESSAGE_GOOD_MIN = 'You are probably looking for a change made after %s ' \
39 '(known good), but no later than %s (first known bad).' 39 '(known good), but no later than %s (first known bad).'
40 DONE_MESSAGE_GOOD_MAX = 'You are probably looking for a change made after %s ' \ 40 DONE_MESSAGE_GOOD_MAX = 'You are probably looking for a change made after %s ' \
41 '(known bad), but no later than %s (first known good).' 41 '(known bad), but no later than %s (first known good).'
42 42
43 ############################################################################### 43 ###############################################################################
44 44
45 import json 45 import json
46 import math
47 import optparse 46 import optparse
48 import os 47 import os
49 import pipes
50 import re 48 import re
51 import shlex 49 import shlex
52 import shutil 50 import shutil
53 import subprocess 51 import subprocess
54 import sys 52 import sys
55 import tempfile 53 import tempfile
56 import threading 54 import threading
57 import urllib 55 import urllib
58 from distutils.version import LooseVersion 56 from distutils.version import LooseVersion
59 from xml.etree import ElementTree 57 from xml.etree import ElementTree
60 import zipfile 58 import zipfile
61 59
62 60
63 class PathContext(object): 61 class PathContext(object):
64 """A PathContext is used to carry the information used to construct URLs and 62 """A PathContext is used to carry the information used to construct URLs and
65 paths when dealing with the storage server and archives.""" 63 paths when dealing with the storage server and archives."""
66 def __init__(self, base_url, platform, good_revision, bad_revision, 64 def __init__(self, base_url, platform, good_revision, bad_revision,
67 is_official, is_aura): 65 is_official, is_aura, flash_path = None):
68 super(PathContext, self).__init__() 66 super(PathContext, self).__init__()
69 # Store off the input parameters. 67 # Store off the input parameters.
70 self.base_url = base_url 68 self.base_url = base_url
71 self.platform = platform # What's passed in to the '-a/--archive' option. 69 self.platform = platform # What's passed in to the '-a/--archive' option.
72 self.good_revision = good_revision 70 self.good_revision = good_revision
73 self.bad_revision = bad_revision 71 self.bad_revision = bad_revision
74 self.is_official = is_official 72 self.is_official = is_official
75 self.is_aura = is_aura 73 self.is_aura = is_aura
74 self.flash_path = flash_path
76 75
77 # The name of the ZIP file in a revision directory on the server. 76 # The name of the ZIP file in a revision directory on the server.
78 self.archive_name = None 77 self.archive_name = None
79 78
80 # Set some internal members: 79 # Set some internal members:
81 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'. 80 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'.
82 # _archive_extract_dir = Uncompressed directory in the archive_name file. 81 # _archive_extract_dir = Uncompressed directory in the archive_name file.
83 # _binary_name = The name of the executable to run. 82 # _binary_name = The name of the executable to run.
84 if self.platform in ('linux', 'linux64', 'linux-arm'): 83 if self.platform in ('linux', 'linux64', 'linux-arm'):
85 self._binary_name = 'chrome' 84 self._binary_name = 'chrome'
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 # If we are bisecting only official builds (without --aura), 279 # If we are bisecting only official builds (without --aura),
281 # we can not include builds which ends with '.1' or '.2' since 280 # we can not include builds which ends with '.1' or '.2' since
282 # they have different folder hierarchy inside. 281 # they have different folder hierarchy inside.
283 elif (not self.IsAuraBuild(str(build_number)) and 282 elif (not self.IsAuraBuild(str(build_number)) and
284 not self.IsASANBuild(str(build_number))): 283 not self.IsASANBuild(str(build_number))):
285 final_list.append(str(build_number)) 284 final_list.append(str(build_number))
286 except urllib.HTTPError, e: 285 except urllib.HTTPError, e:
287 pass 286 pass
288 return final_list 287 return final_list
289 288
290 def UnzipFilenameToDir(filename, dir): 289 def UnzipFilenameToDir(filename, directory):
291 """Unzip |filename| to directory |dir|.""" 290 """Unzip |filename| to |directory|."""
292 cwd = os.getcwd() 291 cwd = os.getcwd()
293 if not os.path.isabs(filename): 292 if not os.path.isabs(filename):
294 filename = os.path.join(cwd, filename) 293 filename = os.path.join(cwd, filename)
295 zf = zipfile.ZipFile(filename) 294 zf = zipfile.ZipFile(filename)
296 # Make base. 295 # Make base.
297 if not os.path.isdir(dir): 296 if not os.path.isdir(directory):
298 os.mkdir(dir) 297 os.mkdir(directory)
299 os.chdir(dir) 298 os.chdir(directory)
300 # Extract files. 299 # Extract files.
301 for info in zf.infolist(): 300 for info in zf.infolist():
302 name = info.filename 301 name = info.filename
303 if name.endswith('/'): # dir 302 if name.endswith('/'): # dir
304 if not os.path.isdir(name): 303 if not os.path.isdir(name):
305 os.makedirs(name) 304 os.makedirs(name)
306 else: # file 305 else: # file
307 dir = os.path.dirname(name) 306 directory = os.path.dirname(name)
308 if not os.path.isdir(dir): 307 if not os.path.isdir(directory):
309 os.makedirs(dir) 308 os.makedirs(directory)
310 out = open(name, 'wb') 309 out = open(name, 'wb')
311 out.write(zf.read(name)) 310 out.write(zf.read(name))
312 out.close() 311 out.close()
313 # Set permissions. Permission info in external_attr is shifted 16 bits. 312 # Set permissions. Permission info in external_attr is shifted 16 bits.
314 os.chmod(name, info.external_attr >> 16L) 313 os.chmod(name, info.external_attr >> 16L)
315 os.chdir(cwd) 314 os.chdir(cwd)
316 315
317 316
318 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None): 317 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None):
319 """Downloads and unzips revision |rev|. 318 """Downloads and unzips revision |rev|.
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 print "Trying revision %s..." % str(revision) 354 print "Trying revision %s..." % str(revision)
356 355
357 # Create a temp directory and unzip the revision into it. 356 # Create a temp directory and unzip the revision into it.
358 cwd = os.getcwd() 357 cwd = os.getcwd()
359 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') 358 tempdir = tempfile.mkdtemp(prefix='bisect_tmp')
360 UnzipFilenameToDir(zipfile, tempdir) 359 UnzipFilenameToDir(zipfile, tempdir)
361 os.chdir(tempdir) 360 os.chdir(tempdir)
362 361
363 # Run the build as many times as specified. 362 # Run the build as many times as specified.
364 testargs = ['--user-data-dir=%s' % profile] + args 363 testargs = ['--user-data-dir=%s' % profile] + args
364 if context.flash_path:
365 testargs.append('--flag-switches-begin')
Robert Sesek 2013/11/08 18:35:44 Why do you need --flag-switches-begin?
ilja 2013/11/08 19:38:39 I will try this on Windows again, I thought it nee
365 # The sandbox must be run as root on Official Chrome, so bypass it. 366 # The sandbox must be run as root on Official Chrome, so bypass it.
366 if context.is_official and context.platform.startswith('linux'): 367 if (context.is_official and context.platform.startswith('linux') or
368 context.flash_path):
Robert Sesek 2013/11/08 18:35:44 Is this the intended condition? Can you add anothe
367 testargs.append('--no-sandbox') 369 testargs.append('--no-sandbox')
370 if context.flash_path:
371 testargs.append('--ppapi-flash-path=%s' % context.flash_path)
372 # We have to pass a large enough Flash version, which currently needs not
373 # be correct. Instead of requiring the user of the script to figure out and
374 # pass the correct version we just spoof it.
375 testargs.append('--ppapi-flash-version=99.9.999.999')
enne (OOO) 2013/11/07 23:05:23 Is this impossible to do in Chrome?
ilja 2013/11/08 06:07:39 Yes. It actually is a command line parameter that
376 testargs.append('--flag-switches-end')
368 377
369 runcommand = [] 378 runcommand = []
370 for token in shlex.split(command): 379 for token in shlex.split(command):
371 if token == "%a": 380 if token == "%a":
372 runcommand.extend(testargs) 381 runcommand.extend(testargs)
373 else: 382 else:
374 runcommand.append( \ 383 runcommand.append( \
375 token.replace('%p', context.GetLaunchPath()) \ 384 token.replace('%p', context.GetLaunchPath()) \
376 .replace('%s', ' '.join(testargs))) 385 .replace('%s', ' '.join(testargs)))
377 386
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 def Bisect(base_url, 454 def Bisect(base_url,
446 platform, 455 platform,
447 official_builds, 456 official_builds,
448 is_aura, 457 is_aura,
449 good_rev=0, 458 good_rev=0,
450 bad_rev=0, 459 bad_rev=0,
451 num_runs=1, 460 num_runs=1,
452 command="%p %a", 461 command="%p %a",
453 try_args=(), 462 try_args=(),
454 profile=None, 463 profile=None,
464 flash_path=None,
455 evaluate=AskIsGoodBuild): 465 evaluate=AskIsGoodBuild):
456 """Given known good and known bad revisions, run a binary search on all 466 """Given known good and known bad revisions, run a binary search on all
457 archived revisions to determine the last known good revision. 467 archived revisions to determine the last known good revision.
458 468
459 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). 469 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.).
460 @param official_builds Specify build type (Chromium or Official build). 470 @param official_builds Specify build type (Chromium or Official build).
461 @param good_rev Number/tag of the known good revision. 471 @param good_rev Number/tag of the known good revision.
462 @param bad_rev Number/tag of the known bad revision. 472 @param bad_rev Number/tag of the known bad revision.
463 @param num_runs Number of times to run each build for asking good/bad. 473 @param num_runs Number of times to run each build for asking good/bad.
464 @param try_args A tuple of arguments to pass to the test application. 474 @param try_args A tuple of arguments to pass to the test application.
(...skipping 13 matching lines...) Expand all
478 is run on rev 75. 488 is run on rev 75.
479 489
480 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test 490 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test
481 is run on rev 25. 491 is run on rev 25.
482 """ 492 """
483 493
484 if not profile: 494 if not profile:
485 profile = 'profile' 495 profile = 'profile'
486 496
487 context = PathContext(base_url, platform, good_rev, bad_rev, 497 context = PathContext(base_url, platform, good_rev, bad_rev,
488 official_builds, is_aura) 498 official_builds, is_aura, flash_path)
489 cwd = os.getcwd() 499 cwd = os.getcwd()
490 500
491 print "Downloading list of known revisions..." 501 print "Downloading list of known revisions..."
492 _GetDownloadPath = lambda rev: os.path.join(cwd, 502 _GetDownloadPath = lambda rev: os.path.join(cwd,
493 '%s-%s' % (str(rev), context.archive_name)) 503 '%s-%s' % (str(rev), context.archive_name))
494 if official_builds: 504 if official_builds:
495 revlist = context.GetOfficialBuildsList() 505 revlist = context.GetOfficialBuildsList()
496 else: 506 else:
497 revlist = context.GetRevList() 507 revlist = context.GetRevList()
498 508
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 stderr = None 557 stderr = None
548 try: 558 try:
549 (status, stdout, stderr) = RunRevision(context, 559 (status, stdout, stderr) = RunRevision(context,
550 rev, 560 rev,
551 fetch.zipfile, 561 fetch.zipfile,
552 profile, 562 profile,
553 num_runs, 563 num_runs,
554 command, 564 command,
555 try_args) 565 try_args)
556 except Exception, e: 566 except Exception, e:
557 print >>sys.stderr, e 567 print >> sys.stderr, e
558 568
559 # Call the evaluate function to see if the current revision is good or bad. 569 # Call the evaluate function to see if the current revision is good or bad.
560 # On that basis, kill one of the background downloads and complete the 570 # On that basis, kill one of the background downloads and complete the
561 # other, as described in the comments above. 571 # other, as described in the comments above.
562 try: 572 try:
563 answer = evaluate(rev, official_builds, status, stdout, stderr) 573 answer = evaluate(rev, official_builds, status, stdout, stderr)
564 if answer == 'g' and good_rev < bad_rev or \ 574 if answer == 'g' and good_rev < bad_rev or \
565 answer == 'b' and bad_rev < good_rev: 575 answer == 'b' and bad_rev < good_rev:
566 fetch.Stop() 576 fetch.Stop()
567 minrev = pivot 577 minrev = pivot
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 help = 'The buildbot archive to bisect [%s].' % 724 help = 'The buildbot archive to bisect [%s].' %
715 '|'.join(choices)) 725 '|'.join(choices))
716 parser.add_option('-o', action="store_true", dest='official_builds', 726 parser.add_option('-o', action="store_true", dest='official_builds',
717 help = 'Bisect across official ' + 727 help = 'Bisect across official ' +
718 'Chrome builds (internal only) instead of ' + 728 'Chrome builds (internal only) instead of ' +
719 'Chromium archives.') 729 'Chromium archives.')
720 parser.add_option('-b', '--bad', type = 'str', 730 parser.add_option('-b', '--bad', type = 'str',
721 help = 'A bad revision to start bisection. ' + 731 help = 'A bad revision to start bisection. ' +
722 'May be earlier or later than the good revision. ' + 732 'May be earlier or later than the good revision. ' +
723 'Default is HEAD.') 733 'Default is HEAD.')
734 parser.add_option('-f', '--flash_path', type = 'str',
735 help = 'Absolute path to a recent Adobe Pepper Flash ' +
736 'binary to be used in this bisection (e.g. ' +
737 'on Windows C:\...\pepflashplayer.dll and on Linux ' +
738 '/opt/google/chrome/PepperFlash/libpepflashplayer.so).')
724 parser.add_option('-g', '--good', type = 'str', 739 parser.add_option('-g', '--good', type = 'str',
725 help = 'A good revision to start bisection. ' + 740 help = 'A good revision to start bisection. ' +
726 'May be earlier or later than the bad revision. ' + 741 'May be earlier or later than the bad revision. ' +
727 'Default is 0.') 742 'Default is 0.')
728 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', 743 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str',
729 help = 'Profile to use; this will not reset every run. ' + 744 help = 'Profile to use; this will not reset every run. ' +
730 'Defaults to a clean profile.', default = 'profile') 745 'Defaults to a clean profile.', default = 'profile')
731 parser.add_option('-t', '--times', type = 'int', 746 parser.add_option('-t', '--times', type = 'int',
732 help = 'Number of times to run each build before asking ' + 747 help = 'Number of times to run each build before asking ' +
733 'if it\'s good or bad. Temporary profiles are reused.', 748 'if it\'s good or bad. Temporary profiles are reused.',
734 default = 1) 749 default = 1)
735 parser.add_option('-c', '--command', type = 'str', 750 parser.add_option('-c', '--command', type = 'str',
736 help = 'Command to execute. %p and %a refer to Chrome ' + 751 help = 'Command to execute. %p and %a refer to Chrome ' +
737 'executable and specified extra arguments respectively. ' + 752 'executable and specified extra arguments respectively. ' +
738 'Use %s to specify all extra arguments as one string. ' + 753 'Use %s to specify all extra arguments as one string. ' +
739 'Defaults to "%p %a". Note that any extra paths ' + 754 'Defaults to "%p %a". Note that any extra paths ' +
740 'specified should be absolute.', 755 'specified should be absolute.',
741 default = '%p %a'); 756 default = '%p %a')
742 parser.add_option('-l', '--blink', action='store_true', 757 parser.add_option('-l', '--blink', action='store_true',
743 help = 'Use Blink bisect instead of Chromium. ') 758 help = 'Use Blink bisect instead of Chromium. ')
744 parser.add_option('--aura', 759 parser.add_option('--aura',
745 dest='aura', 760 dest='aura',
746 action='store_true', 761 action='store_true',
747 default=False, 762 default=False,
748 help='Allow the script to bisect aura builds') 763 help='Allow the script to bisect aura builds')
749 764
750 (opts, args) = parser.parse_args() 765 (opts, args) = parser.parse_args()
751 766
752 if opts.archive is None: 767 if opts.archive is None:
753 print 'Error: missing required parameter: --archive' 768 print 'Error: missing required parameter: --archive'
754 print 769 print
755 parser.print_help() 770 parser.print_help()
756 return 1 771 return 1
757 772
758 if opts.aura: 773 if opts.aura:
759 if opts.archive != 'win' or not opts.official_builds: 774 if opts.archive != 'win' or not opts.official_builds:
760 print 'Error: Aura is supported only on Windows platform '\ 775 print 'Error: Aura is supported only on Windows platform '\
761 'and official builds.' 776 'and official builds.'
762 return 1 777 return 1
763 778
764 if opts.blink: 779 if opts.blink:
765 base_url = WEBKIT_BASE_URL 780 base_url = WEBKIT_BASE_URL
766 else: 781 else:
767 base_url = CHROMIUM_BASE_URL 782 base_url = CHROMIUM_BASE_URL
768 783
769 # Create the context. Initialize 0 for the revisions as they are set below. 784 # Create the context. Initialize 0 for the revisions as they are set below.
770 context = PathContext(base_url, opts.archive, 0, 0, 785 context = PathContext(base_url, opts.archive, 0, 0,
771 opts.official_builds, opts.aura) 786 opts.official_builds, opts.aura, None)
772 # Pick a starting point, try to get HEAD for this. 787 # Pick a starting point, try to get HEAD for this.
773 if opts.bad: 788 if opts.bad:
774 bad_rev = opts.bad 789 bad_rev = opts.bad
775 else: 790 else:
776 bad_rev = '999.0.0.0' 791 bad_rev = '999.0.0.0'
777 if not opts.official_builds: 792 if not opts.official_builds:
778 bad_rev = GetChromiumRevision(context.GetLastChangeURL()) 793 bad_rev = GetChromiumRevision(context.GetLastChangeURL())
779 794
780 # Find out when we were good. 795 # Find out when we were good.
781 if opts.good: 796 if opts.good:
782 good_rev = opts.good 797 good_rev = opts.good
783 else: 798 else:
784 good_rev = '0.0.0.0' if opts.official_builds else 0 799 good_rev = '0.0.0.0' if opts.official_builds else 0
785 800
801 if opts.flash_path:
802 flash_path = opts.flash_path
803 msg = 'Could not find Flash binary at %s' % flash_path
804 assert os.path.exists(flash_path), msg
805
786 if opts.official_builds: 806 if opts.official_builds:
787 good_rev = LooseVersion(good_rev) 807 good_rev = LooseVersion(good_rev)
788 bad_rev = LooseVersion(bad_rev) 808 bad_rev = LooseVersion(bad_rev)
789 else: 809 else:
790 good_rev = int(good_rev) 810 good_rev = int(good_rev)
791 bad_rev = int(bad_rev) 811 bad_rev = int(bad_rev)
792 812
793 if opts.times < 1: 813 if opts.times < 1:
794 print('Number of times to run (%d) must be greater than or equal to 1.' % 814 print('Number of times to run (%d) must be greater than or equal to 1.' %
795 opts.times) 815 opts.times)
796 parser.print_help() 816 parser.print_help()
797 return 1 817 return 1
798 818
799 (min_chromium_rev, max_chromium_rev) = Bisect( 819 (min_chromium_rev, max_chromium_rev) = Bisect(
800 base_url, opts.archive, opts.official_builds, opts.aura, good_rev, 820 base_url, opts.archive, opts.official_builds, opts.aura, good_rev,
801 bad_rev, opts.times, opts.command, args, opts.profile) 821 bad_rev, opts.times, opts.command, args, opts.profile, opts.flash_path)
802 822
803 # Get corresponding blink revisions. 823 # Get corresponding blink revisions.
804 try: 824 try:
805 min_blink_rev = GetBlinkRevisionForChromiumRevision(context, 825 min_blink_rev = GetBlinkRevisionForChromiumRevision(context,
806 min_chromium_rev) 826 min_chromium_rev)
807 max_blink_rev = GetBlinkRevisionForChromiumRevision(context, 827 max_blink_rev = GetBlinkRevisionForChromiumRevision(context,
808 max_chromium_rev) 828 max_chromium_rev)
809 except Exception, e: 829 except Exception, e:
810 # Silently ignore the failure. 830 # Silently ignore the failure.
811 min_blink_rev, max_blink_rev = 0, 0 831 min_blink_rev, max_blink_rev = 0, 0
(...skipping 21 matching lines...) Expand all
833 "you might also want to do a Blink bisect.") 853 "you might also want to do a Blink bisect.")
834 854
835 print 'CHANGELOG URL:' 855 print 'CHANGELOG URL:'
836 if opts.official_builds: 856 if opts.official_builds:
837 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) 857 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev)
838 else: 858 else:
839 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) 859 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev)
840 860
841 if __name__ == '__main__': 861 if __name__ == '__main__':
842 sys.exit(main()) 862 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