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 10 matching lines...) Expand all Loading... | |
21 import socket | 21 import socket |
22 import subprocess | 22 import subprocess |
23 import sys | 23 import sys |
24 import thread | 24 import thread |
25 import threading | 25 import threading |
26 import time | 26 import time |
27 import urlparse | 27 import urlparse |
28 | 28 |
29 # Imports from within Skia | 29 # Imports from within Skia |
30 # | 30 # |
31 # We need to add the 'tools' directory for svn.py, and the 'gm' directory for | 31 # We need to add the 'gm' directory for gm_json.py . |
32 # gm_json.py . | 32 # Make sure this dir is in the PYTHONPATH, but add it at the *end* |
33 # that directory. | |
34 # Make sure that the 'tools' dir is in the PYTHONPATH, but add it at the *end* | |
35 # so any dirs that are already in the PYTHONPATH will be preferred. | 33 # so any dirs that are already in the PYTHONPATH will be preferred. |
36 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 34 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
37 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) | 35 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) |
38 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) | 36 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) |
39 TOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools') | |
40 if TOOLS_DIRECTORY not in sys.path: | |
41 sys.path.append(TOOLS_DIRECTORY) | |
42 import svn | |
43 if GM_DIRECTORY not in sys.path: | 37 if GM_DIRECTORY not in sys.path: |
44 sys.path.append(GM_DIRECTORY) | 38 sys.path.append(GM_DIRECTORY) |
45 import gm_json | 39 import gm_json |
46 | 40 |
47 # Imports from local dir | 41 # Imports from local dir |
48 # | 42 # |
49 # Note: we import results under a different name, to avoid confusion with the | 43 # Note: we import results under a different name, to avoid confusion with the |
50 # Server.results() property. See discussion at | 44 # Server.results() property. See discussion at |
51 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44 | 45 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44 |
52 import compare_configs | 46 import compare_configs |
53 import compare_to_expectations | 47 import compare_to_expectations |
48 import download_actuals | |
54 import imagepairset | 49 import imagepairset |
55 import results as results_mod | 50 import results as results_mod |
56 | 51 |
57 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') | 52 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') |
58 | 53 |
59 # A simple dictionary of file name extensions to MIME types. The empty string | 54 # A simple dictionary of file name extensions to MIME types. The empty string |
60 # entry is used as the default when no extension was given or if the extension | 55 # entry is used as the default when no extension was given or if the extension |
61 # has no entry in this dictionary. | 56 # has no entry in this dictionary. |
62 MIME_TYPE_MAP = {'': 'application/octet-stream', | 57 MIME_TYPE_MAP = {'': 'application/octet-stream', |
63 'html': 'text/html', | 58 'html': 'text/html', |
64 'css': 'text/css', | 59 'css': 'text/css', |
65 'png': 'image/png', | 60 'png': 'image/png', |
66 'js': 'application/javascript', | 61 'js': 'application/javascript', |
67 'json': 'application/json' | 62 'json': 'application/json' |
68 } | 63 } |
69 | 64 |
70 # Keys that server.py uses to create the toplevel content header. | 65 # Keys that server.py uses to create the toplevel content header. |
71 # NOTE: Keep these in sync with static/constants.js | 66 # NOTE: Keep these in sync with static/constants.js |
72 KEY__EDITS__MODIFICATIONS = 'modifications' | 67 KEY__EDITS__MODIFICATIONS = 'modifications' |
73 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' | 68 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' |
74 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' | 69 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
75 | 70 |
76 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR | 71 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
77 DEFAULT_ACTUALS_REPO_REVISION = 'HEAD' | 72 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
78 DEFAULT_ACTUALS_REPO_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual' | 73 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
79 DEFAULT_PORT = 8888 | 74 DEFAULT_PORT = 8888 |
80 | 75 |
81 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 76 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
82 # out live results (not static files). | 77 # out live results (not static files). |
83 RESULTS_SUBDIR = 'results' | 78 RESULTS_SUBDIR = 'results' |
84 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 79 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
85 # out static files. | 80 # out static files. |
86 STATIC_CONTENTS_SUBDIR = 'static' | 81 STATIC_CONTENTS_SUBDIR = 'static' |
87 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR | 82 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR |
88 GENERATED_HTML_SUBDIR = 'generated-html' | 83 GENERATED_HTML_SUBDIR = 'generated-html' |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 """Returns routable IP address of this host (the IP address of its network | 127 """Returns routable IP address of this host (the IP address of its network |
133 interface that would be used for most traffic, not its localhost | 128 interface that would be used for most traffic, not its localhost |
134 interface). See http://stackoverflow.com/a/166589 """ | 129 interface). See http://stackoverflow.com/a/166589 """ |
135 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | 130 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
136 sock.connect(('8.8.8.8', 80)) | 131 sock.connect(('8.8.8.8', 80)) |
137 host = sock.getsockname()[0] | 132 host = sock.getsockname()[0] |
138 sock.close() | 133 sock.close() |
139 return host | 134 return host |
140 | 135 |
141 | 136 |
142 def _create_svn_checkout(dir_path, repo_url): | |
143 """Creates local checkout of an SVN repository at the specified directory | |
144 path, returning an svn.Svn object referring to the local checkout. | |
145 | |
146 Args: | |
147 dir_path: path to the local checkout; if this directory does not yet exist, | |
148 it will be created and the repo will be checked out into it | |
149 repo_url: URL of SVN repo to check out into dir_path (unless the local | |
150 checkout already exists) | |
151 Returns: an svn.Svn object referring to the local checkout. | |
152 """ | |
153 local_checkout = svn.Svn(dir_path) | |
154 if not os.path.isdir(dir_path): | |
155 os.makedirs(dir_path) | |
156 local_checkout.Checkout(repo_url, '.') | |
157 return local_checkout | |
158 | |
159 | |
160 def _create_index(file_path, config_pairs): | 137 def _create_index(file_path, config_pairs): |
161 """Creates an index file linking to all results available from this server. | 138 """Creates an index file linking to all results available from this server. |
162 | 139 |
163 Prior to https://codereview.chromium.org/215503002 , we had a static | 140 Prior to https://codereview.chromium.org/215503002 , we had a static |
164 index.html within our repo. But now that the results may or may not include | 141 index.html within our repo. But now that the results may or may not include |
165 config comparisons, index.html needs to be generated differently depending | 142 config comparisons, index.html needs to be generated differently depending |
166 on which results are included. | 143 on which results are included. |
167 | 144 |
168 TODO(epoger): Instead of including raw HTML within the Python code, | 145 TODO(epoger): Instead of including raw HTML within the Python code, |
169 consider restoring the index.html file as a template and using django (or | 146 consider restoring the index.html file as a template and using django (or |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
206 file_handle.write('</li>') | 183 file_handle.write('</li>') |
207 file_handle.write('</ul>') | 184 file_handle.write('</ul>') |
208 file_handle.write('</ul></body></html>') | 185 file_handle.write('</ul></body></html>') |
209 | 186 |
210 | 187 |
211 class Server(object): | 188 class Server(object): |
212 """ HTTP server for our HTML rebaseline viewer. """ | 189 """ HTTP server for our HTML rebaseline viewer. """ |
213 | 190 |
214 def __init__(self, | 191 def __init__(self, |
215 actuals_dir=DEFAULT_ACTUALS_DIR, | 192 actuals_dir=DEFAULT_ACTUALS_DIR, |
216 actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION, | 193 json_filename=DEFAULT_JSON_FILENAME, |
217 actuals_repo_url=DEFAULT_ACTUALS_REPO_URL, | 194 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, |
218 port=DEFAULT_PORT, export=False, editable=True, | 195 port=DEFAULT_PORT, export=False, editable=True, |
219 reload_seconds=0, config_pairs=None, builder_regex_list=None): | 196 reload_seconds=0, config_pairs=None, builder_regex_list=None): |
220 """ | 197 """ |
221 Args: | 198 Args: |
222 actuals_dir: directory under which we will check out the latest actual | 199 actuals_dir: directory under which we will check out the latest actual |
223 GM results | 200 GM results |
224 actuals_repo_revision: revision of actual-results.json files to process | 201 json_filename: basename of the JSON summary file to load for each builder |
225 actuals_repo_url: SVN repo to download actual-results.json files from; | 202 gm_summaries_bucket: Google Storage bucket to download json_filename |
226 if None or '', don't fetch new actual-results files at all, | 203 files from; if None or '', don't fetch new actual-results files |
227 just compare to whatever files are already in actuals_dir | 204 at all, just compare to whatever files are already in actuals_dir |
228 port: which TCP port to listen on for HTTP requests | 205 port: which TCP port to listen on for HTTP requests |
229 export: whether to allow HTTP clients on other hosts to access this server | 206 export: whether to allow HTTP clients on other hosts to access this server |
230 editable: whether HTTP clients are allowed to submit new baselines | 207 editable: whether HTTP clients are allowed to submit new baselines |
231 reload_seconds: polling interval with which to check for new results; | 208 reload_seconds: polling interval with which to check for new results; |
232 if 0, don't check for new results at all | 209 if 0, don't check for new results at all |
233 config_pairs: List of (string, string) tuples; for each tuple, compare | 210 config_pairs: List of (string, string) tuples; for each tuple, compare |
234 actual results of these two configs. If None or empty, | 211 actual results of these two configs. If None or empty, |
235 don't compare configs at all. | 212 don't compare configs at all. |
236 builder_regex_list: List of regular expressions specifying which builders | 213 builder_regex_list: List of regular expressions specifying which builders |
237 we will process. If None, process all builders. | 214 we will process. If None, process all builders. |
238 """ | 215 """ |
239 self._actuals_dir = actuals_dir | 216 self._actuals_dir = actuals_dir |
240 self._actuals_repo_revision = actuals_repo_revision | 217 self._json_filename = json_filename |
241 self._actuals_repo_url = actuals_repo_url | 218 self._gm_summaries_bucket = gm_summaries_bucket |
242 self._port = port | 219 self._port = port |
243 self._export = export | 220 self._export = export |
244 self._editable = editable | 221 self._editable = editable |
245 self._reload_seconds = reload_seconds | 222 self._reload_seconds = reload_seconds |
246 self._config_pairs = config_pairs or [] | 223 self._config_pairs = config_pairs or [] |
247 self._builder_regex_list = builder_regex_list | 224 self._builder_regex_list = builder_regex_list |
248 _create_index( | 225 _create_index( |
249 file_path=os.path.join( | 226 file_path=os.path.join( |
250 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, | 227 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, |
251 "index.html"), | 228 "index.html"), |
252 config_pairs=config_pairs) | 229 config_pairs=config_pairs) |
253 # TODO(epoger): Create shareable functions within download_actuals.py that | |
254 # we can use both there and here to download the actual image results. | |
255 if actuals_repo_url: | |
256 self._actuals_repo = _create_svn_checkout( | |
257 dir_path=actuals_dir, repo_url=actuals_repo_url) | |
258 | 230 |
259 # Reentrant lock that must be held whenever updating EITHER of: | 231 # Reentrant lock that must be held whenever updating EITHER of: |
260 # 1. self._results | 232 # 1. self._results |
261 # 2. the expected or actual results on local disk | 233 # 2. the expected or actual results on local disk |
262 self.results_rlock = threading.RLock() | 234 self.results_rlock = threading.RLock() |
263 # self._results will be filled in by calls to update_results() | 235 # self._results will be filled in by calls to update_results() |
264 self._results = None | 236 self._results = None |
265 | 237 |
266 @property | 238 @property |
267 def results(self): | 239 def results(self): |
(...skipping 27 matching lines...) Expand all Loading... | |
295 the same time. | 267 the same time. |
296 | 268 |
297 Args: | 269 Args: |
298 invalidate: if True, invalidate self._results immediately upon entry; | 270 invalidate: if True, invalidate self._results immediately upon entry; |
299 otherwise, we will let readers see those results until we | 271 otherwise, we will let readers see those results until we |
300 replace them | 272 replace them |
301 """ | 273 """ |
302 with self.results_rlock: | 274 with self.results_rlock: |
303 if invalidate: | 275 if invalidate: |
304 self._results = None | 276 self._results = None |
305 if self._actuals_repo_url: | 277 if self._gm_summaries_bucket: |
306 logging.info( | 278 logging.info( |
307 'Updating actual GM results in %s to revision %s from repo %s ...' | 279 'Updating GM result summaries in %s from gm_summaries_bucket %s ...' |
308 % ( | 280 % (self._actuals_dir, self._gm_summaries_bucket)) |
309 self._actuals_dir, self._actuals_repo_revision, | 281 |
310 self._actuals_repo_url)) | 282 # Clean out actuals_dir first, in case some builders have gone away |
311 self._actuals_repo.Update( | 283 # since we last ran. |
312 path='.', revision=self._actuals_repo_revision) | 284 if os.path.isdir(self._actuals_dir): |
285 shutil.rmtree(self._actuals_dir) | |
286 | |
287 # Get the list of builders we care about. | |
288 all_builders = download_actuals.get_builders_list( | |
289 summaries_bucket=self._gm_summaries_bucket) | |
290 if self._builder_regex_list: | |
291 matching_builders = [] | |
292 for builder in all_builders: | |
293 for regex in self._builder_regex_list: | |
294 if re.match(regex, builder): | |
295 matching_builders.append(builder) | |
296 break # go on to the next builder, no need to try more regexes | |
297 else: | |
298 matching_builders = all_builders | |
299 | |
300 # Download the JSON file for each builder we care about. | |
301 # | |
302 # TODO(epoger): When this is a large number of builders, we would be | |
303 # better off downloading them in parallel! | |
304 for builder in matching_builders: | |
305 download_actuals.gcs_download_file( | |
306 source_bucket=self._gm_summaries_bucket, | |
307 source_path=posixpath.join(builder, self._json_filename), | |
308 dest_path=os.path.join(self._actuals_dir, builder, | |
309 self._json_filename), | |
310 create_subdirs_if_needed=True) | |
313 | 311 |
314 # We only update the expectations dir if the server was run with a | 312 # We only update the expectations dir if the server was run with a |
315 # nonzero --reload argument; otherwise, we expect the user to maintain | 313 # nonzero --reload argument; otherwise, we expect the user to maintain |
316 # her own expectations as she sees fit. | 314 # her own expectations as she sees fit. |
317 # | 315 # |
318 # Because the Skia repo is moving from SVN to git, and git does not | 316 # Because the Skia repo is hosted using git, and git does not |
319 # support updating a single directory tree, we have to update the entire | 317 # support updating a single directory tree, we have to update the entire |
320 # repo checkout. | 318 # repo checkout. |
321 # | 319 # |
322 # Because Skia uses depot_tools, we have to update using "gclient sync" | 320 # Because Skia uses depot_tools, we have to update using "gclient sync" |
323 # instead of raw git (or SVN) update. Happily, this will work whether | 321 # instead of raw git commands. |
borenet
2014/06/03 20:27:47
This is not really true, and the below will not ha
epoger
2014/06/03 20:39:04
Yikes.
[Insert "blah blah what is our officially
| |
324 # the checkout was created using git or SVN. | |
325 if self._reload_seconds: | 322 if self._reload_seconds: |
326 logging.info( | 323 logging.info( |
327 'Updating expected GM results in %s by syncing Skia repo ...' % | 324 'Updating expected GM results in %s by syncing Skia repo ...' % |
328 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) | 325 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) |
329 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) | 326 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) |
330 | 327 |
331 self._results = compare_to_expectations.ExpectationComparisons( | 328 self._results = compare_to_expectations.ExpectationComparisons( |
332 actuals_root=self._actuals_dir, | 329 actuals_root=self._actuals_dir, |
333 generated_images_root=os.path.join( | 330 generated_images_root=os.path.join( |
334 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | 331 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
616 def main(): | 613 def main(): |
617 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | 614 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', |
618 datefmt='%m/%d/%Y %H:%M:%S', | 615 datefmt='%m/%d/%Y %H:%M:%S', |
619 level=logging.INFO) | 616 level=logging.INFO) |
620 parser = argparse.ArgumentParser() | 617 parser = argparse.ArgumentParser() |
621 parser.add_argument('--actuals-dir', | 618 parser.add_argument('--actuals-dir', |
622 help=('Directory into which we will check out the latest ' | 619 help=('Directory into which we will check out the latest ' |
623 'actual GM results. If this directory does not ' | 620 'actual GM results. If this directory does not ' |
624 'exist, it will be created. Defaults to %(default)s'), | 621 'exist, it will be created. Defaults to %(default)s'), |
625 default=DEFAULT_ACTUALS_DIR) | 622 default=DEFAULT_ACTUALS_DIR) |
626 parser.add_argument('--actuals-repo', | 623 # TODO(epoger): Before https://codereview.chromium.org/310093003 , |
627 help=('URL of SVN repo to download actual-results.json ' | 624 # when this tool downloaded the JSON summaries from skia-autogen, |
628 'files from. Defaults to %(default)s ; if set to ' | 625 # it had an --actuals-revision the caller could specify to download |
629 'empty string, just compare to actual-results ' | 626 # actual results as of a specific point in time. We should add similar |
630 'already found in ACTUALS_DIR.'), | 627 # functionality when retrieving the summaries from Google Storage. |
631 default=DEFAULT_ACTUALS_REPO_URL) | |
632 parser.add_argument('--actuals-revision', | |
633 help=('revision of actual-results.json files to process. ' | |
634 'Defaults to %(default)s . Beware of setting this ' | |
635 'argument in conjunction with --editable; you ' | |
636 'probably only want to edit results at HEAD.'), | |
637 default=DEFAULT_ACTUALS_REPO_REVISION) | |
638 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', | 628 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', |
639 help=('Only process builders matching these regular ' | 629 help=('Only process builders matching these regular ' |
640 'expressions. If unspecified, process all ' | 630 'expressions. If unspecified, process all ' |
641 'builders.')) | 631 'builders.')) |
642 parser.add_argument('--compare-configs', action='store_true', | 632 parser.add_argument('--compare-configs', action='store_true', |
643 help=('In addition to generating differences between ' | 633 help=('In addition to generating differences between ' |
644 'expectations and actuals, also generate ' | 634 'expectations and actuals, also generate ' |
645 'differences between these config pairs: ' | 635 'differences between these config pairs: ' |
646 + str(CONFIG_PAIRS_TO_COMPARE))) | 636 + str(CONFIG_PAIRS_TO_COMPARE))) |
647 parser.add_argument('--editable', action='store_true', | 637 parser.add_argument('--editable', action='store_true', |
648 help=('Allow HTTP clients to submit new baselines.')) | 638 help=('Allow HTTP clients to submit new baselines.')) |
649 parser.add_argument('--export', action='store_true', | 639 parser.add_argument('--export', action='store_true', |
650 help=('Instead of only allowing access from HTTP clients ' | 640 help=('Instead of only allowing access from HTTP clients ' |
651 'on localhost, allow HTTP clients on other hosts ' | 641 'on localhost, allow HTTP clients on other hosts ' |
652 'to access this server. WARNING: doing so will ' | 642 'to access this server. WARNING: doing so will ' |
653 'allow users on other hosts to modify your ' | 643 'allow users on other hosts to modify your ' |
654 'GM expectations, if combined with --editable.')) | 644 'GM expectations, if combined with --editable.')) |
645 parser.add_argument('--gm-summaries-bucket', | |
646 help=('Google Cloud Storage bucket to download ' | |
647 'JSON_FILENAME files from. ' | |
648 'Defaults to %(default)s ; if set to ' | |
649 'empty string, just compare to actual-results ' | |
650 'already found in ACTUALS_DIR.'), | |
651 default=DEFAULT_GM_SUMMARIES_BUCKET) | |
652 parser.add_argument('--json-filename', | |
653 help=('JSON summary filename to read for each builder; ' | |
654 'defaults to %(default)s.'), | |
655 default=DEFAULT_JSON_FILENAME) | |
655 parser.add_argument('--port', type=int, | 656 parser.add_argument('--port', type=int, |
656 help=('Which TCP port to listen on for HTTP requests; ' | 657 help=('Which TCP port to listen on for HTTP requests; ' |
657 'defaults to %(default)s'), | 658 'defaults to %(default)s'), |
658 default=DEFAULT_PORT) | 659 default=DEFAULT_PORT) |
659 parser.add_argument('--reload', type=int, | 660 parser.add_argument('--reload', type=int, |
660 help=('How often (a period in seconds) to update the ' | 661 help=('How often (a period in seconds) to update the ' |
661 'results. If specified, both expected and actual ' | 662 'results. If specified, both expected and actual ' |
662 'results will be updated by running "gclient sync" ' | 663 'results will be updated by running "gclient sync" ' |
663 'on your Skia checkout as a whole. ' | 664 'on your Skia checkout as a whole. ' |
664 'By default, we do not reload at all, and you ' | 665 'By default, we do not reload at all, and you ' |
665 'must restart the server to pick up new data.'), | 666 'must restart the server to pick up new data.'), |
666 default=0) | 667 default=0) |
667 args = parser.parse_args() | 668 args = parser.parse_args() |
668 if args.compare_configs: | 669 if args.compare_configs: |
669 config_pairs = CONFIG_PAIRS_TO_COMPARE | 670 config_pairs = CONFIG_PAIRS_TO_COMPARE |
670 else: | 671 else: |
671 config_pairs = None | 672 config_pairs = None |
672 | 673 |
673 global _SERVER | 674 global _SERVER |
674 _SERVER = Server(actuals_dir=args.actuals_dir, | 675 _SERVER = Server(actuals_dir=args.actuals_dir, |
675 actuals_repo_revision=args.actuals_revision, | 676 json_filename=args.json_filename, |
676 actuals_repo_url=args.actuals_repo, | 677 gm_summaries_bucket=args.gm_summaries_bucket, |
677 port=args.port, export=args.export, editable=args.editable, | 678 port=args.port, export=args.export, editable=args.editable, |
678 reload_seconds=args.reload, config_pairs=config_pairs, | 679 reload_seconds=args.reload, config_pairs=config_pairs, |
679 builder_regex_list=args.builders) | 680 builder_regex_list=args.builders) |
680 _SERVER.run() | 681 _SERVER.run() |
681 | 682 |
682 | 683 |
683 if __name__ == '__main__': | 684 if __name__ == '__main__': |
684 main() | 685 main() |
OLD | NEW |