Index: gm/rebaseline_server/server.py |
diff --git a/gm/rebaseline_server/server.py b/gm/rebaseline_server/server.py |
index 0079ec55fffd0438756a4e7fc94b32f0c99490bb..06807796704a5322af1a8427fc432ed0947f66fe 100755 |
--- a/gm/rebaseline_server/server.py |
+++ b/gm/rebaseline_server/server.py |
@@ -20,14 +20,28 @@ import re |
import shutil |
import socket |
import subprocess |
+import sys |
import thread |
import threading |
import time |
import urlparse |
# Imports from within Skia |
-import fix_pythonpath # must do this first |
-from pyutils import gs_utils |
+# |
+# We need to add the 'tools' directory for svn.py, and the 'gm' directory for |
+# gm_json.py . |
+# that directory. |
+# Make sure that the 'tools' dir is in the PYTHONPATH, but add it at the *end* |
+# so any dirs that are already in the PYTHONPATH will be preferred. |
+PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
+GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) |
+TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) |
+TOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools') |
+if TOOLS_DIRECTORY not in sys.path: |
+ sys.path.append(TOOLS_DIRECTORY) |
+import svn |
+if GM_DIRECTORY not in sys.path: |
+ sys.path.append(GM_DIRECTORY) |
import gm_json |
# Imports from local dir |
@@ -37,7 +51,6 @@ import gm_json |
# https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.py#newcode44 |
import compare_configs |
import compare_to_expectations |
-import download_actuals |
import imagepairset |
import results as results_mod |
@@ -61,12 +74,10 @@ KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' |
KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
-DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
-DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
+DEFAULT_ACTUALS_REPO_REVISION = 'HEAD' |
+DEFAULT_ACTUALS_REPO_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual' |
DEFAULT_PORT = 8888 |
-PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
-TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) |
# Directory, relative to PARENT_DIRECTORY, within which the server will serve |
# out live results (not static files). |
RESULTS_SUBDIR = 'results' |
@@ -128,6 +139,24 @@ def _get_routable_ip_address(): |
return host |
+def _create_svn_checkout(dir_path, repo_url): |
+ """Creates local checkout of an SVN repository at the specified directory |
+ path, returning an svn.Svn object referring to the local checkout. |
+ |
+ Args: |
+ dir_path: path to the local checkout; if this directory does not yet exist, |
+ it will be created and the repo will be checked out into it |
+ repo_url: URL of SVN repo to check out into dir_path (unless the local |
+ checkout already exists) |
+ Returns: an svn.Svn object referring to the local checkout. |
+ """ |
+ local_checkout = svn.Svn(dir_path) |
+ if not os.path.isdir(dir_path): |
+ os.makedirs(dir_path) |
+ local_checkout.Checkout(repo_url, '.') |
+ return local_checkout |
+ |
+ |
def _create_index(file_path, config_pairs): |
"""Creates an index file linking to all results available from this server. |
@@ -184,18 +213,18 @@ class Server(object): |
def __init__(self, |
actuals_dir=DEFAULT_ACTUALS_DIR, |
- json_filename=DEFAULT_JSON_FILENAME, |
- gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, |
+ actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION, |
+ actuals_repo_url=DEFAULT_ACTUALS_REPO_URL, |
port=DEFAULT_PORT, export=False, editable=True, |
reload_seconds=0, config_pairs=None, builder_regex_list=None): |
""" |
Args: |
actuals_dir: directory under which we will check out the latest actual |
GM results |
- json_filename: basename of the JSON summary file to load for each builder |
- gm_summaries_bucket: Google Storage bucket to download json_filename |
- files from; if None or '', don't fetch new actual-results files |
- at all, just compare to whatever files are already in actuals_dir |
+ actuals_repo_revision: revision of actual-results.json files to process |
+ actuals_repo_url: SVN repo to download actual-results.json files from; |
+ if None or '', don't fetch new actual-results files at all, |
+ just compare to whatever files are already in actuals_dir |
port: which TCP port to listen on for HTTP requests |
export: whether to allow HTTP clients on other hosts to access this server |
editable: whether HTTP clients are allowed to submit new baselines |
@@ -208,8 +237,8 @@ class Server(object): |
we will process. If None, process all builders. |
""" |
self._actuals_dir = actuals_dir |
- self._json_filename = json_filename |
- self._gm_summaries_bucket = gm_summaries_bucket |
+ self._actuals_repo_revision = actuals_repo_revision |
+ self._actuals_repo_url = actuals_repo_url |
self._port = port |
self._export = export |
self._editable = editable |
@@ -221,6 +250,11 @@ class Server(object): |
PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, |
"index.html"), |
config_pairs=config_pairs) |
+ # TODO(epoger): Create shareable functions within download_actuals.py that |
+ # we can use both there and here to download the actual image results. |
+ if actuals_repo_url: |
+ self._actuals_repo = _create_svn_checkout( |
+ dir_path=actuals_dir, repo_url=actuals_repo_url) |
# Reentrant lock that must be held whenever updating EITHER of: |
# 1. self._results |
@@ -268,66 +302,26 @@ class Server(object): |
with self.results_rlock: |
if invalidate: |
self._results = None |
- if self._gm_summaries_bucket: |
+ if self._actuals_repo_url: |
logging.info( |
- 'Updating GM result summaries in %s from gm_summaries_bucket %s ...' |
- % (self._actuals_dir, self._gm_summaries_bucket)) |
- |
- # Clean out actuals_dir first, in case some builders have gone away |
- # since we last ran. |
- if os.path.isdir(self._actuals_dir): |
- shutil.rmtree(self._actuals_dir) |
- |
- # Get the list of builders we care about. |
- all_builders = download_actuals.get_builders_list( |
- summaries_bucket=self._gm_summaries_bucket) |
- if self._builder_regex_list: |
- matching_builders = [] |
- for builder in all_builders: |
- for regex in self._builder_regex_list: |
- if re.match(regex, builder): |
- matching_builders.append(builder) |
- break # go on to the next builder, no need to try more regexes |
- else: |
- matching_builders = all_builders |
- |
- # Download the JSON file for each builder we care about. |
- # |
- # TODO(epoger): When this is a large number of builders, we would be |
- # better off downloading them in parallel! |
- for builder in matching_builders: |
- gs_utils.download_file( |
- source_bucket=self._gm_summaries_bucket, |
- source_path=posixpath.join(builder, self._json_filename), |
- dest_path=os.path.join(self._actuals_dir, builder, |
- self._json_filename), |
- create_subdirs_if_needed=True) |
+ 'Updating actual GM results in %s to revision %s from repo %s ...' |
+ % ( |
+ self._actuals_dir, self._actuals_repo_revision, |
+ self._actuals_repo_url)) |
+ self._actuals_repo.Update( |
+ path='.', revision=self._actuals_repo_revision) |
# We only update the expectations dir if the server was run with a |
# nonzero --reload argument; otherwise, we expect the user to maintain |
# her own expectations as she sees fit. |
# |
- # Because the Skia repo is hosted using git, and git does not |
+ # Because the Skia repo is moving from SVN to git, and git does not |
# support updating a single directory tree, we have to update the entire |
# repo checkout. |
# |
# Because Skia uses depot_tools, we have to update using "gclient sync" |
- # instead of raw git commands. |
- # |
- # TODO(epoger): Fetch latest expectations in some other way. |
- # Eric points out that our official documentation recommends an |
- # unmanaged Skia checkout, so "gclient sync" will not bring down updated |
- # expectations from origin/master-- you'd have to do a "git pull" of |
- # some sort instead. |
- # However, the live rebaseline_server at |
- # http://skia-tree-status.appspot.com/redirect/rebaseline-server (which |
- # is probably the only user of the --reload flag!) uses a managed |
- # checkout, so "gclient sync" works in that case. |
- # Probably the best idea is to avoid all of this nonsense by fetching |
- # updated expectations into a temp directory, and leaving the rest of |
- # the checkout alone. This could be done using "git show", or by |
- # downloading individual expectation JSON files from |
- # skia.googlesource.com . |
+ # instead of raw git (or SVN) update. Happily, this will work whether |
+ # the checkout was created using git or SVN. |
if self._reload_seconds: |
logging.info( |
'Updating expected GM results in %s by syncing Skia repo ...' % |
@@ -629,11 +623,18 @@ def main(): |
'actual GM results. If this directory does not ' |
'exist, it will be created. Defaults to %(default)s'), |
default=DEFAULT_ACTUALS_DIR) |
- # TODO(epoger): Before https://codereview.chromium.org/310093003 , |
- # when this tool downloaded the JSON summaries from skia-autogen, |
- # it had an --actuals-revision the caller could specify to download |
- # actual results as of a specific point in time. We should add similar |
- # functionality when retrieving the summaries from Google Storage. |
+ parser.add_argument('--actuals-repo', |
+ help=('URL of SVN repo to download actual-results.json ' |
+ 'files from. Defaults to %(default)s ; if set to ' |
+ 'empty string, just compare to actual-results ' |
+ 'already found in ACTUALS_DIR.'), |
+ default=DEFAULT_ACTUALS_REPO_URL) |
+ parser.add_argument('--actuals-revision', |
+ help=('revision of actual-results.json files to process. ' |
+ 'Defaults to %(default)s . Beware of setting this ' |
+ 'argument in conjunction with --editable; you ' |
+ 'probably only want to edit results at HEAD.'), |
+ default=DEFAULT_ACTUALS_REPO_REVISION) |
parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', |
help=('Only process builders matching these regular ' |
'expressions. If unspecified, process all ' |
@@ -651,17 +652,6 @@ def main(): |
'to access this server. WARNING: doing so will ' |
'allow users on other hosts to modify your ' |
'GM expectations, if combined with --editable.')) |
- parser.add_argument('--gm-summaries-bucket', |
- help=('Google Cloud Storage bucket to download ' |
- 'JSON_FILENAME files from. ' |
- 'Defaults to %(default)s ; if set to ' |
- 'empty string, just compare to actual-results ' |
- 'already found in ACTUALS_DIR.'), |
- default=DEFAULT_GM_SUMMARIES_BUCKET) |
- parser.add_argument('--json-filename', |
- help=('JSON summary filename to read for each builder; ' |
- 'defaults to %(default)s.'), |
- default=DEFAULT_JSON_FILENAME) |
parser.add_argument('--port', type=int, |
help=('Which TCP port to listen on for HTTP requests; ' |
'defaults to %(default)s'), |
@@ -682,8 +672,8 @@ def main(): |
global _SERVER |
_SERVER = Server(actuals_dir=args.actuals_dir, |
- json_filename=args.json_filename, |
- gm_summaries_bucket=args.gm_summaries_bucket, |
+ actuals_repo_revision=args.actuals_revision, |
+ actuals_repo_url=args.actuals_repo, |
port=args.port, export=args.export, editable=args.editable, |
reload_seconds=args.reload, config_pairs=config_pairs, |
builder_regex_list=args.builders) |