OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 """ | 3 """ |
4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
5 | 5 |
6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
7 found in the LICENSE file. | 7 found in the LICENSE file. |
8 | 8 |
9 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
10 """ | 10 """ |
(...skipping 22 matching lines...) Expand all Loading... | |
33 import gm_json | 33 import gm_json |
34 | 34 |
35 # Imports from local dir | 35 # Imports from local dir |
36 # | 36 # |
37 # pylint: disable=C0301 | 37 # pylint: disable=C0301 |
38 # Note: we import results under a different name, to avoid confusion with the | 38 # Note: we import results under a different name, to avoid confusion with the |
39 # Server.results() property. See discussion at | 39 # Server.results() property. See discussion at |
40 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44 | 40 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44 |
41 # pylint: enable=C0301 | 41 # pylint: enable=C0301 |
42 import compare_configs | 42 import compare_configs |
43 import compare_rendered_pictures | |
43 import compare_to_expectations | 44 import compare_to_expectations |
44 import download_actuals | 45 import download_actuals |
45 import imagediffdb | 46 import imagediffdb |
46 import imagepairset | 47 import imagepairset |
47 import results as results_mod | 48 import results as results_mod |
48 | 49 |
49 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') | 50 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') |
50 | 51 |
51 # A simple dictionary of file name extensions to MIME types. The empty string | 52 # A simple dictionary of file name extensions to MIME types. The empty string |
52 # entry is used as the default when no extension was given or if the extension | 53 # entry is used as the default when no extension was given or if the extension |
(...skipping 13 matching lines...) Expand all Loading... | |
66 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' | 67 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
67 | 68 |
68 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR | 69 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
69 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET | 70 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
70 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME | 71 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
71 DEFAULT_PORT = 8888 | 72 DEFAULT_PORT = 8888 |
72 | 73 |
73 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 74 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
74 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) | 75 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) |
75 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 76 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
76 # out live results (not static files). | 77 # out image diff data from within the precomputed _SERVER.results . |
77 RESULTS_SUBDIR = 'results' | 78 PRECOMPUTED_RESULTS_SUBDIR = 'results' |
79 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | |
80 # out live-generated image diff data. | |
81 LIVE_RESULTS_SUBDIR = 'live-results' | |
78 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 82 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
79 # out static files. | 83 # out static files. |
80 STATIC_CONTENTS_SUBDIR = 'static' | 84 STATIC_CONTENTS_SUBDIR = 'static' |
81 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR | 85 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR |
82 GENERATED_HTML_SUBDIR = 'generated-html' | 86 GENERATED_HTML_SUBDIR = 'generated-html' |
83 GENERATED_IMAGES_SUBDIR = 'generated-images' | 87 GENERATED_IMAGES_SUBDIR = 'generated-images' |
84 GENERATED_JSON_SUBDIR = 'generated-json' | 88 GENERATED_JSON_SUBDIR = 'generated-json' |
85 | 89 |
86 # How often (in seconds) clients should reload while waiting for initial | 90 # How often (in seconds) clients should reload while waiting for initial |
87 # results to load. | 91 # results to load. |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
158 '<!DOCTYPE html><html>' | 162 '<!DOCTYPE html><html>' |
159 '<head><title>rebaseline_server</title></head>' | 163 '<head><title>rebaseline_server</title></head>' |
160 '<body><ul>') | 164 '<body><ul>') |
161 if SUMMARY_TYPES: | 165 if SUMMARY_TYPES: |
162 file_handle.write('<li>Expectations vs Actuals</li><ul>') | 166 file_handle.write('<li>Expectations vs Actuals</li><ul>') |
163 for summary_type in SUMMARY_TYPES: | 167 for summary_type in SUMMARY_TYPES: |
164 file_handle.write( | 168 file_handle.write( |
165 '<li><a href="/{static_subdir}/view.html#/view.html?' | 169 '<li><a href="/{static_subdir}/view.html#/view.html?' |
166 'resultsToLoad=/{results_subdir}/{summary_type}">' | 170 'resultsToLoad=/{results_subdir}/{summary_type}">' |
167 '{summary_type}</a></li>'.format( | 171 '{summary_type}</a></li>'.format( |
168 results_subdir=RESULTS_SUBDIR, | 172 results_subdir=PRECOMPUTED_RESULTS_SUBDIR, |
169 static_subdir=STATIC_CONTENTS_SUBDIR, | 173 static_subdir=STATIC_CONTENTS_SUBDIR, |
170 summary_type=summary_type)) | 174 summary_type=summary_type)) |
171 file_handle.write('</ul>') | 175 file_handle.write('</ul>') |
172 if config_pairs: | 176 if config_pairs: |
173 file_handle.write('<li>Comparing configs within actual results</li><ul>') | 177 file_handle.write('<li>Comparing configs within actual results</li><ul>') |
174 for config_pair in config_pairs: | 178 for config_pair in config_pairs: |
175 file_handle.write('<li>%s vs %s:' % config_pair) | 179 file_handle.write('<li>%s vs %s:' % config_pair) |
176 for summary_type in SUMMARY_TYPES: | 180 for summary_type in SUMMARY_TYPES: |
177 file_handle.write( | 181 file_handle.write( |
178 ' <a href="/%s/view.html#/view.html?' | 182 ' <a href="/%s/view.html#/view.html?' |
179 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( | 183 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( |
180 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, | 184 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, |
181 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], | 185 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], |
182 summary_type, summary_type)) | 186 summary_type, summary_type)) |
183 file_handle.write('</li>') | 187 file_handle.write('</li>') |
184 file_handle.write('</ul>') | 188 file_handle.write('</ul>') |
185 file_handle.write('</ul></body></html>') | 189 file_handle.write('</ul></body></html>') |
186 | 190 |
187 | 191 |
188 class Server(object): | 192 class Server(object): |
189 """ HTTP server for our HTML rebaseline viewer. """ | 193 """ HTTP server for our HTML rebaseline viewer. """ |
190 | 194 |
191 def __init__(self, | 195 def __init__(self, |
192 actuals_dir=DEFAULT_ACTUALS_DIR, | 196 actuals_dir=DEFAULT_ACTUALS_DIR, |
193 json_filename=DEFAULT_JSON_FILENAME, | 197 json_filename=DEFAULT_JSON_FILENAME, |
194 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, | 198 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, |
195 port=DEFAULT_PORT, export=False, editable=True, | 199 port=DEFAULT_PORT, export=False, editable=True, |
196 reload_seconds=0, config_pairs=None, builder_regex_list=None): | 200 reload_seconds=0, config_pairs=None, builder_regex_list=None, |
201 boto_file_path=None): | |
197 """ | 202 """ |
198 Args: | 203 Args: |
199 actuals_dir: directory under which we will check out the latest actual | 204 actuals_dir: directory under which we will check out the latest actual |
200 GM results | 205 GM results |
201 json_filename: basename of the JSON summary file to load for each builder | 206 json_filename: basename of the JSON summary file to load for each builder |
202 gm_summaries_bucket: Google Storage bucket to download json_filename | 207 gm_summaries_bucket: Google Storage bucket to download json_filename |
203 files from; if None or '', don't fetch new actual-results files | 208 files from; if None or '', don't fetch new actual-results files |
204 at all, just compare to whatever files are already in actuals_dir | 209 at all, just compare to whatever files are already in actuals_dir |
205 port: which TCP port to listen on for HTTP requests | 210 port: which TCP port to listen on for HTTP requests |
206 export: whether to allow HTTP clients on other hosts to access this server | 211 export: whether to allow HTTP clients on other hosts to access this server |
207 editable: whether HTTP clients are allowed to submit new baselines | 212 editable: whether HTTP clients are allowed to submit new baselines |
208 reload_seconds: polling interval with which to check for new results; | 213 reload_seconds: polling interval with which to check for new results; |
209 if 0, don't check for new results at all | 214 if 0, don't check for new results at all |
210 config_pairs: List of (string, string) tuples; for each tuple, compare | 215 config_pairs: List of (string, string) tuples; for each tuple, compare |
211 actual results of these two configs. If None or empty, | 216 actual results of these two configs. If None or empty, |
212 don't compare configs at all. | 217 don't compare configs at all. |
213 builder_regex_list: List of regular expressions specifying which builders | 218 builder_regex_list: List of regular expressions specifying which builders |
214 we will process. If None, process all builders. | 219 we will process. If None, process all builders. |
220 boto_file_path: Path to .boto file giving us credentials to access | |
221 Google Storage buckets; if None, we will only be able to access | |
222 public GS buckets. | |
215 """ | 223 """ |
216 self._actuals_dir = actuals_dir | 224 self._actuals_dir = actuals_dir |
217 self._json_filename = json_filename | 225 self._json_filename = json_filename |
218 self._gm_summaries_bucket = gm_summaries_bucket | 226 self._gm_summaries_bucket = gm_summaries_bucket |
219 self._port = port | 227 self._port = port |
220 self._export = export | 228 self._export = export |
221 self._editable = editable | 229 self._editable = editable |
222 self._reload_seconds = reload_seconds | 230 self._reload_seconds = reload_seconds |
223 self._config_pairs = config_pairs or [] | 231 self._config_pairs = config_pairs or [] |
224 self._builder_regex_list = builder_regex_list | 232 self._builder_regex_list = builder_regex_list |
225 self._gs = gs_utils.GSUtils() | 233 |
234 if boto_file_path: | |
235 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) | |
236 else: | |
237 self._gs = gs_utils.GSUtils() | |
238 | |
226 _create_index( | 239 _create_index( |
227 file_path=os.path.join( | 240 file_path=os.path.join( |
228 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, | 241 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, |
229 "index.html"), | 242 "index.html"), |
230 config_pairs=config_pairs) | 243 config_pairs=config_pairs) |
231 | 244 |
232 # Reentrant lock that must be held whenever updating EITHER of: | 245 # Reentrant lock that must be held whenever updating EITHER of: |
233 # 1. self._results | 246 # 1. self._results |
234 # 2. the expected or actual results on local disk | 247 # 2. the expected or actual results on local disk |
235 self.results_rlock = threading.RLock() | 248 self.results_rlock = threading.RLock() |
236 | 249 |
237 # These will be filled in by calls to update_results() | 250 # Create a single ImageDiffDB instance that is used by all our differs. |
251 self._image_diff_db = imagediffdb.ImageDiffDB( | |
252 gs=self._gs, | |
253 storage_root=os.path.join( | |
254 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | |
255 GENERATED_IMAGES_SUBDIR)) | |
256 | |
257 # This will be filled in by calls to update_results() | |
238 self._results = None | 258 self._results = None |
239 self._image_diff_db = None | |
240 | 259 |
241 @property | 260 @property |
242 def results(self): | 261 def results(self): |
243 """ Returns the most recently generated results, or None if we don't have | 262 """ Returns the most recently generated results, or None if we don't have |
244 any valid results (update_results() has not completed yet). """ | 263 any valid results (update_results() has not completed yet). """ |
245 return self._results | 264 return self._results |
246 | 265 |
247 @property | 266 @property |
248 def is_exported(self): | 267 def is_exported(self): |
249 """ Returns true iff HTTP clients on other hosts are allowed to access | 268 """ Returns true iff HTTP clients on other hosts are allowed to access |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
336 # updated expectations into a temp directory, and leaving the rest of | 355 # updated expectations into a temp directory, and leaving the rest of |
337 # the checkout alone. This could be done using "git show", or by | 356 # the checkout alone. This could be done using "git show", or by |
338 # downloading individual expectation JSON files from | 357 # downloading individual expectation JSON files from |
339 # skia.googlesource.com . | 358 # skia.googlesource.com . |
340 if self._reload_seconds: | 359 if self._reload_seconds: |
341 logging.info( | 360 logging.info( |
342 'Updating expected GM results in %s by syncing Skia repo ...' % | 361 'Updating expected GM results in %s by syncing Skia repo ...' % |
343 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) | 362 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) |
344 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) | 363 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) |
345 | 364 |
346 if not self._image_diff_db: | |
347 self._image_diff_db = imagediffdb.ImageDiffDB( | |
348 storage_root=os.path.join( | |
349 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | |
350 GENERATED_IMAGES_SUBDIR)) | |
351 | |
352 self._results = compare_to_expectations.ExpectationComparisons( | 365 self._results = compare_to_expectations.ExpectationComparisons( |
353 image_diff_db=self._image_diff_db, | 366 image_diff_db=self._image_diff_db, |
354 actuals_root=self._actuals_dir, | 367 actuals_root=self._actuals_dir, |
355 diff_base_url=posixpath.join( | 368 diff_base_url=posixpath.join( |
356 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), | 369 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), |
357 builder_regex_list=self._builder_regex_list) | 370 builder_regex_list=self._builder_regex_list) |
358 | 371 |
359 json_dir = os.path.join( | 372 json_dir = os.path.join( |
360 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) | 373 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) |
361 if not os.path.isdir(json_dir): | 374 if not os.path.isdir(json_dir): |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
436 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) | 449 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) |
437 return | 450 return |
438 | 451 |
439 # All requests must be of this form: | 452 # All requests must be of this form: |
440 # /dispatcher/remainder | 453 # /dispatcher/remainder |
441 # where 'dispatcher' indicates which do_GET_* dispatcher to run | 454 # where 'dispatcher' indicates which do_GET_* dispatcher to run |
442 # and 'remainder' is the remaining path sent to that dispatcher. | 455 # and 'remainder' is the remaining path sent to that dispatcher. |
443 normpath = posixpath.normpath(self.path) | 456 normpath = posixpath.normpath(self.path) |
444 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() | 457 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() |
445 dispatchers = { | 458 dispatchers = { |
446 RESULTS_SUBDIR: self.do_GET_results, | 459 PRECOMPUTED_RESULTS_SUBDIR: self.do_GET_precomputed_results, |
460 LIVE_RESULTS_SUBDIR: self.do_GET_live_results, | |
447 STATIC_CONTENTS_SUBDIR: self.do_GET_static, | 461 STATIC_CONTENTS_SUBDIR: self.do_GET_static, |
448 } | 462 } |
449 dispatcher = dispatchers[dispatcher_name] | 463 dispatcher = dispatchers[dispatcher_name] |
450 dispatcher(remainder) | 464 dispatcher(remainder) |
451 except: | 465 except: |
452 self.send_error(404) | 466 self.send_error(404) |
453 raise | 467 raise |
454 | 468 |
455 def do_GET_results(self, results_type): | 469 def do_GET_precomputed_results(self, results_type): |
epoger
2014/07/31 15:18:31
For now, leaving this one in place to fetch the la
| |
456 """ Handle a GET request for GM results. | 470 """ Handle a GET request for part of the precomputed _SERVER.results object. |
457 | 471 |
458 Args: | 472 Args: |
459 results_type: string indicating which set of results to return; | 473 results_type: string indicating which set of results to return; |
460 must be one of the results_mod.RESULTS_* constants | 474 must be one of the results_mod.RESULTS_* constants |
461 """ | 475 """ |
462 logging.debug('do_GET_results: sending results of type "%s"' % results_type) | 476 logging.debug('do_GET_precomputed_results: sending results of type "%s"' % |
477 results_type) | |
463 # Since we must make multiple calls to the ExpectationComparisons object, | 478 # Since we must make multiple calls to the ExpectationComparisons object, |
464 # grab a reference to it in case it is updated to point at a new | 479 # grab a reference to it in case it is updated to point at a new |
465 # ExpectationComparisons object within another thread. | 480 # ExpectationComparisons object within another thread. |
466 # | 481 # |
467 # TODO(epoger): Rather than using a global variable for the handler | 482 # TODO(epoger): Rather than using a global variable for the handler |
468 # to refer to the Server object, make Server a subclass of | 483 # to refer to the Server object, make Server a subclass of |
469 # HTTPServer, and then it could be available to the handler via | 484 # HTTPServer, and then it could be available to the handler via |
470 # the handler's .server instance variable. | 485 # the handler's .server instance variable. |
471 results_obj = _SERVER.results | 486 results_obj = _SERVER.results |
472 if results_obj: | 487 if results_obj: |
473 response_dict = results_obj.get_packaged_results_of_type( | 488 response_dict = results_obj.get_packaged_results_of_type( |
474 results_type=results_type, reload_seconds=_SERVER.reload_seconds, | 489 results_type=results_type, reload_seconds=_SERVER.reload_seconds, |
475 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) | 490 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) |
476 else: | 491 else: |
477 now = int(time.time()) | 492 now = int(time.time()) |
478 response_dict = { | 493 response_dict = { |
479 imagepairset.KEY__ROOT__HEADER: { | 494 imagepairset.KEY__ROOT__HEADER: { |
480 results_mod.KEY__HEADER__SCHEMA_VERSION: ( | 495 results_mod.KEY__HEADER__SCHEMA_VERSION: ( |
481 results_mod.VALUE__HEADER__SCHEMA_VERSION), | 496 results_mod.VALUE__HEADER__SCHEMA_VERSION), |
482 results_mod.KEY__HEADER__IS_STILL_LOADING: True, | 497 results_mod.KEY__HEADER__IS_STILL_LOADING: True, |
483 results_mod.KEY__HEADER__TIME_UPDATED: now, | 498 results_mod.KEY__HEADER__TIME_UPDATED: now, |
484 results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( | 499 results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( |
485 now + RELOAD_INTERVAL_UNTIL_READY), | 500 now + RELOAD_INTERVAL_UNTIL_READY), |
486 }, | 501 }, |
487 } | 502 } |
488 self.send_json_dict(response_dict) | 503 self.send_json_dict(response_dict) |
489 | 504 |
505 def do_GET_live_results(self, url_remainder): | |
506 """ Handle a GET request for live-generated image diff data. | |
507 | |
508 Args: | |
509 url_remainder: string indicating which image diffs to generate | |
510 """ | |
511 # EPOGER: move these definitions to the top of the file? | |
512 PARAM_ACTUALS_DIR = 'actualsDir' | |
513 PARAM_EXPECTATIONS_DIR = 'expectationsDir' | |
514 | |
515 logging.debug('do_GET_live_results: url_remainder="%s"' % url_remainder) | |
516 param_dict = urlparse.parse_qs(url_remainder) | |
517 results_obj = compare_rendered_pictures.RenderedPicturesComparisons( | |
518 actuals_dirs=param_dict[PARAM_ACTUALS_DIR], | |
epoger
2014/07/31 15:18:31
The live results are nice and flexible, because th
| |
519 expectations_dirs=param_dict[PARAM_EXPECTATIONS_DIR], | |
520 image_diff_db=_SERVER._image_diff_db, | |
521 diff_base_url='/static/generated-images') | |
522 self.send_json_dict(results_obj.get_packaged_results_of_type( | |
523 results_mod.KEY__HEADER__RESULTS_ALL)) | |
524 | |
490 def do_GET_static(self, path): | 525 def do_GET_static(self, path): |
491 """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . | 526 """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . |
492 Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a | 527 Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a |
493 filesystem sibling of this script. | 528 filesystem sibling of this script. |
494 | 529 |
495 Args: | 530 Args: |
496 path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve | 531 path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve |
497 """ | 532 """ |
498 # Strip arguments ('?resultsToLoad=all') from the path | 533 # Strip arguments ('?resultsToLoad=all') from the path |
499 path = urlparse.urlparse(path).path | 534 path = urlparse.urlparse(path).path |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
636 def main(): | 671 def main(): |
637 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | 672 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', |
638 datefmt='%m/%d/%Y %H:%M:%S', | 673 datefmt='%m/%d/%Y %H:%M:%S', |
639 level=logging.INFO) | 674 level=logging.INFO) |
640 parser = argparse.ArgumentParser() | 675 parser = argparse.ArgumentParser() |
641 parser.add_argument('--actuals-dir', | 676 parser.add_argument('--actuals-dir', |
642 help=('Directory into which we will check out the latest ' | 677 help=('Directory into which we will check out the latest ' |
643 'actual GM results. If this directory does not ' | 678 'actual GM results. If this directory does not ' |
644 'exist, it will be created. Defaults to %(default)s'), | 679 'exist, it will be created. Defaults to %(default)s'), |
645 default=DEFAULT_ACTUALS_DIR) | 680 default=DEFAULT_ACTUALS_DIR) |
681 parser.add_argument('--boto', | |
682 help=('Path to .boto file giving us credentials to access ' | |
683 'Google Storage buckets. If not specified, we will ' | |
684 'only be able to access public GS buckets (and thus ' | |
685 'won\'t be able to download SKP images).'), | |
686 default='') | |
646 # TODO(epoger): Before https://codereview.chromium.org/310093003 , | 687 # TODO(epoger): Before https://codereview.chromium.org/310093003 , |
647 # when this tool downloaded the JSON summaries from skia-autogen, | 688 # when this tool downloaded the JSON summaries from skia-autogen, |
648 # it had an --actuals-revision the caller could specify to download | 689 # it had an --actuals-revision the caller could specify to download |
649 # actual results as of a specific point in time. We should add similar | 690 # actual results as of a specific point in time. We should add similar |
650 # functionality when retrieving the summaries from Google Storage. | 691 # functionality when retrieving the summaries from Google Storage. |
651 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', | 692 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', |
652 help=('Only process builders matching these regular ' | 693 help=('Only process builders matching these regular ' |
653 'expressions. If unspecified, process all ' | 694 'expressions. If unspecified, process all ' |
654 'builders.')) | 695 'builders.')) |
655 parser.add_argument('--compare-configs', action='store_true', | 696 parser.add_argument('--compare-configs', action='store_true', |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
693 config_pairs = CONFIG_PAIRS_TO_COMPARE | 734 config_pairs = CONFIG_PAIRS_TO_COMPARE |
694 else: | 735 else: |
695 config_pairs = None | 736 config_pairs = None |
696 | 737 |
697 global _SERVER | 738 global _SERVER |
698 _SERVER = Server(actuals_dir=args.actuals_dir, | 739 _SERVER = Server(actuals_dir=args.actuals_dir, |
699 json_filename=args.json_filename, | 740 json_filename=args.json_filename, |
700 gm_summaries_bucket=args.gm_summaries_bucket, | 741 gm_summaries_bucket=args.gm_summaries_bucket, |
701 port=args.port, export=args.export, editable=args.editable, | 742 port=args.port, export=args.export, editable=args.editable, |
702 reload_seconds=args.reload, config_pairs=config_pairs, | 743 reload_seconds=args.reload, config_pairs=config_pairs, |
703 builder_regex_list=args.builders) | 744 builder_regex_list=args.builders, boto_file_path=args.boto) |
704 _SERVER.run() | 745 _SERVER.run() |
705 | 746 |
706 | 747 |
707 if __name__ == '__main__': | 748 if __name__ == '__main__': |
708 main() | 749 main() |
OLD | NEW |