Merge "Use <> operator where possible"
diff --git a/contrib/mitm-ui/README.md b/contrib/mitm-ui/README.md
new file mode 100644
index 0000000..c8df490
--- /dev/null
+++ b/contrib/mitm-ui/README.md
@@ -0,0 +1,47 @@
+# Scripts for PolyGerrit local development against prod using MitmProxy.
+
+## Installation (OSX)
+
+1. Install Docker from http://docker.com
+2. Start the proxy and create a new proxied browser instance
+ ```
+ cd ~/gerrit
+ ~/mitm-gerrit/mitm-serve-app-dev.sh
+ ```
+3. Install MITM certificates
+ - Open http://mitm.it in the proxied browser window
+ - Follow the instructions to install MITM certs
+
+## Usage
+
+### Add or replace a single plugin containing static content
+
+To develop unminified plugin that loads multiple files, use this.
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ ~/mitm-gerrit/mitm-single-plugin.sh ./path/to/static/plugin.html
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. plugin.html and ./path/to/static/* will be served
+
+### Add or replace a minified plugin for *.googlesource.com
+
+This flow assumes no additional .html/.js are needed, i.e. the plugin is a single file.
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ ~/mitm-gerrit/mitm-plugins.sh ./path/to/plugin.html,./maybe/one/more.js
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. plugin.html and more.js are served
+
+### Serve uncompiled PolyGerrit
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ cd ~/gerrit
+ ~/mitm-gerrit/mitm-serve-app-dev.sh
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. Instead of prod UI (gr-app.html, gr-app.js), local source files will be served
diff --git a/contrib/mitm-ui/add-header.py b/contrib/mitm-ui/add-header.py
new file mode 100644
index 0000000..f9b2b12
--- /dev/null
+++ b/contrib/mitm-ui/add-header.py
@@ -0,0 +1,5 @@
+# mitmdump -s add-header.py
+def response(flow):
+ if flow.request.host == 'gerrit-review.googlesource.com' and flow.request.path == "/c/92000?1":
+ #flow.response.headers['any'] = '<meta.rdf>; rel=meta'
+ flow.response.headers['Link'] = '</changes/98000/detail?O=11640c>;rel="preload";crossorigin;'
diff --git a/contrib/mitm-ui/dev-chrome.sh b/contrib/mitm-ui/dev-chrome.sh
new file mode 100755
index 0000000..adcb296
--- /dev/null
+++ b/contrib/mitm-ui/dev-chrome.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [[ "$OSTYPE" != "darwin"* ]]; then
+ echo Only works on OSX.
+ exit 1
+fi
+
+/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir=${HOME}/devchrome --proxy-server="127.0.0.1:8888"
diff --git a/contrib/mitm-ui/force-version.py b/contrib/mitm-ui/force-version.py
new file mode 100644
index 0000000..a69c885
--- /dev/null
+++ b/contrib/mitm-ui/force-version.py
@@ -0,0 +1,22 @@
+# mitmdump -q -p 8888 -s "force-version.py --version $1"
+# Request URL is not changed, only the response context
+from mitmproxy import http
+import argparse
+import re
+
+class Server:
+ def __init__(self, version):
+ self.version = version
+
+ def request(self, flow: http.HTTPFlow) -> None:
+ if "gr-app." in flow.request.pretty_url:
+ flow.request.url = re.sub(
+ r"polygerrit_ui/([\d.]+)/elements",
+ "polygerrit_ui/" + self.version + "/elements",
+ flow.request.url)
+
+def start():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--version", type=str, help="Rapid release version, e.g. 432.0")
+ args = parser.parse_args()
+ return Server(args.version)
diff --git a/contrib/mitm-ui/mitm-docker.sh b/contrib/mitm-ui/mitm-docker.sh
new file mode 100755
index 0000000..77f209e
--- /dev/null
+++ b/contrib/mitm-ui/mitm-docker.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+extra_volume='/tmp:/tmp'
+
+POSITIONAL=()
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+ -v|--volume)
+ extra_volume="$2"
+ shift # past argument
+ shift # past value
+ ;;
+ *) # unknown option
+ POSITIONAL+=("$1") # save it in an array for later
+ shift # past argument
+ ;;
+esac
+done
+set -- "${POSITIONAL[@]}" # restore positional parameters
+
+if [[ -z "$1" ]]; then
+ echo This is a runner for higher-level scripts, e.g. mitm-serve-app-dev.sh
+ echo Alternatively, pass mitmproxy script from the same dir as a parameter, e.g. serve-app-dev.py
+ exit 1
+fi
+
+gerrit_dir=$(pwd)
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+CMD="${mitm_dir}/$1"
+
+docker run --rm -it \
+ -v ~/.mitmproxy:/home/mitmproxy/.mitmproxy \
+ -v ${mitm_dir}:${mitm_dir} \
+ -v ${gerrit_dir}:${gerrit_dir} \
+ -v ${extra_volume} \
+ -p 8888:8888 \
+ mitmproxy/mitmproxy:2.0.2 \
+ mitmdump -q -p 8888 -s "${CMD}"
diff --git a/contrib/mitm-ui/mitm-plugins.sh b/contrib/mitm-ui/mitm-plugins.sh
new file mode 100755
index 0000000..992ef07
--- /dev/null
+++ b/contrib/mitm-ui/mitm-plugins.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+if [[ -z "$1" ]]; then
+ echo This script injects plugins for *.googlesource.com.
+ echo Provide plugin paths, comma-separated, as a parameter.
+ echo This script assumes files do not have dependencies, i.e. minified.
+ exit 1
+fi
+
+realpath() {
+ [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
+}
+
+join () {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
+
+plugins=$1
+plugin_paths=()
+for plugin in $(echo ${plugins} | sed "s/,/ /g")
+do
+ plugin_paths+=($(realpath ${plugin}))
+done
+
+absolute_plugin_paths=$(join , "${plugin_paths[@]}")
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+${mitm_dir}/dev-chrome.sh &
+
+${mitm_dir}/mitm-docker.sh "serve-app-dev.py --plugins ${absolute_plugin_paths} --strip_assets"
diff --git a/contrib/mitm-ui/mitm-serve-app-dev.sh b/contrib/mitm-ui/mitm-serve-app-dev.sh
new file mode 100755
index 0000000..4fa8958
--- /dev/null
+++ b/contrib/mitm-ui/mitm-serve-app-dev.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+workspace="./WORKSPACE"
+if [[ ! -f ${workspace} ]] || [[ ! $(head -n 1 ${workspace}) == *"gerrit"* ]]; then
+ echo Please change to cloned Gerrit repo from https://gerrit.googlesource.com/gerrit/
+ exit 1
+fi
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+${mitm_dir}/dev-chrome.sh &
+
+${mitm_dir}/mitm-docker.sh "serve-app-dev.py --app $(pwd)/polygerrit-ui/app/"
diff --git a/contrib/mitm-ui/mitm-single-plugin.sh b/contrib/mitm-ui/mitm-single-plugin.sh
new file mode 100755
index 0000000..4acae7f
--- /dev/null
+++ b/contrib/mitm-ui/mitm-single-plugin.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [[ -z "$1" ]]; then
+ echo This script serves one plugin with the rest of static content.
+ echo Provide path to index plugin file, e.g. buildbucket.html for buildbucket plugin
+ exit 1
+fi
+
+realpath() {
+ OURPWD=$PWD
+ cd "$(dirname "$1")"
+ LINK=$(basename "$1")
+ while [ -L "$LINK" ]; do
+ LINK=$(readlink "$LINK")
+ cd "$(dirname "$LINK")"
+ LINK="$(basename "$1")"
+ done
+ REAL_DIR=`pwd -P`
+ RESULT=$REAL_DIR/$LINK
+ cd "$OURPWD"
+ echo "$RESULT"
+}
+
+plugin=$(realpath $1)
+plugin_root=$(dirname ${plugin})
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+${mitm_dir}/dev-chrome.sh &
+
+${mitm_dir}/mitm-docker.sh -v ${plugin_root}:${plugin_root} "serve-app-dev.py --plugins ${plugin} --strip_assets --plugin_root ${plugin_root}"
diff --git a/contrib/mitm-ui/serve-app-dev.py b/contrib/mitm-ui/serve-app-dev.py
new file mode 100644
index 0000000..bd054e5
--- /dev/null
+++ b/contrib/mitm-ui/serve-app-dev.py
@@ -0,0 +1,139 @@
+# 1. install and setup mitmproxy v2.0.2: https://mitmproxy.readthedocs.io/en/v2.0.2/install.html
+# (In case of python versions trouble, use https://www.anaconda.com/)
+# 2. mitmdump -q -s -p 8888 \
+# "serve-app-dev.py --app /path/to/polygerrit-ui/app/"
+# 3. start Chrome with --proxy-server="127.0.0.1:8888" --user-data-dir=/tmp/devchrome
+# 4. open, say, gerrit-review.googlesource.com. Or chromium-review.googlesource.com. Any.
+# 5. uncompiled source files are served and you can log in, too.
+# 6. enjoy!
+#
+# P.S. For replacing plugins, use --plugins or --plugin_root
+#
+# --plugin takes comma-separated list of plugins to add or replace.
+#
+# Example: Adding a new plugin to the server response:
+# --plugins ~/gerrit-testsite/plugins/myplugin.html
+#
+# Example: Replace all matching plugins with local versions:
+# --plugins ~/gerrit-testsite/plugins/
+# Following files will be served if they exist for /plugins/tricium/static/tricium.html:
+# ~/gerrit-testsite/plugins/tricium.html
+# ~/gerrit-testsite/plugins/tricium/static/tricium.html
+#
+# --assets takes assets bundle.html, expecting rest of the assets files to be in the same folder
+#
+# Example:
+# --assets ~/gerrit-testsite/assets/a3be19f.html
+#
+
+from mitmproxy import http
+from mitmproxy.script import concurrent
+import re
+import argparse
+import os.path
+import json
+
+class Server:
+ def __init__(self, devpath, plugins, pluginroot, assets, strip_assets):
+ if devpath:
+ print("Serving app from " + devpath)
+ if pluginroot:
+ print("Serving plugins from " + pluginroot)
+ if assets:
+ self.assets_root, self.assets_file = os.path.split(assets)
+ print("Assets: using " + self.assets_file + " from " + self.assets_root)
+ else:
+ self.assets_root = None
+ if plugins:
+ self.plugins = {path.split("/")[-1:][0]: path for path in map(expandpath, plugins.split(","))}
+ for filename, path in self.plugins.items():
+ print("Serving " + filename + " from " + path)
+ else:
+ self.plugins = {}
+ self.devpath = devpath
+ self.pluginroot = pluginroot
+ self.strip_assets = strip_assets
+
+ def readfile(self, path):
+ with open(path, 'rb') as contentfile:
+ return contentfile.read()
+
+@concurrent
+def response(flow: http.HTTPFlow) -> None:
+ if server.strip_assets:
+ assets_bundle = 'googlesource.com/polygerrit_assets'
+ assets_pos = flow.response.text.find(assets_bundle)
+ if assets_pos != -1:
+ t = flow.response.text
+ flow.response.text = t[:t.rfind('<', 0, assets_pos)] + t[t.find('>', assets_pos) + 1:]
+ return
+
+ if server.assets_root:
+ marker = 'webcomponents-lite.js"></script>'
+ pos = flow.response.text.find(marker)
+ if pos != -1:
+ pos += len(marker)
+ flow.response.text = ''.join([
+ flow.response.text[:pos],
+ '<link rel="import" href="/gerrit_assets/123.0/' + server.assets_file + '">',
+ flow.response.text[pos:]
+ ])
+
+ assets_prefix = "/gerrit_assets/123.0/"
+ if flow.request.path.startswith(assets_prefix):
+ assets_file = flow.request.path[len(assets_prefix):]
+ flow.response.content = server.readfile(server.assets_root + '/' + assets_file)
+ flow.response.status_code = 200
+ if assets_file.endswith('.js'):
+ flow.response.headers['Content-type'] = 'text/javascript'
+ return
+ m = re.match(".+polygerrit_ui/\d+\.\d+/(.+)", flow.request.path)
+ pluginmatch = re.match("^/plugins/(.+)", flow.request.path)
+ localfile = ""
+ if flow.request.path == "/config/server/info":
+ config = json.loads(flow.response.content[5:].decode('utf8'))
+ for filename, path in server.plugins.items():
+ pluginname = filename.split(".")[0]
+ payload = config["plugin"]["js_resource_paths" if filename.endswith(".js") else "html_resource_paths"]
+ if list(filter(lambda url: filename in url, payload)):
+ continue
+ payload.append("plugins/" + pluginname + "/static/" + filename)
+ flow.response.content = str.encode(")]}'\n" + json.dumps(config))
+ if m is not None:
+ filepath = m.groups()[0]
+ localfile = server.devpath + filepath
+ elif pluginmatch is not None:
+ pluginfile = flow.request.path_components[-1]
+ if server.plugins and pluginfile in server.plugins:
+ if os.path.isfile(server.plugins[pluginfile]):
+ localfile = server.plugins[pluginfile]
+ else:
+ print("Can't find file " + server.plugins[pluginfile] + " for " + flow.request.path)
+ elif server.pluginroot:
+ pluginurl = pluginmatch.groups()[0]
+ if os.path.isfile(server.pluginroot + pluginfile):
+ localfile = server.pluginroot + pluginfile
+ elif os.path.isfile(server.pluginroot + pluginurl):
+ localfile = server.pluginroot + pluginurl
+ if localfile and os.path.isfile(localfile):
+ if pluginmatch is not None:
+ print("Serving " + flow.request.path + " from " + localfile)
+ flow.response.content = server.readfile(localfile)
+ flow.response.status_code = 200
+ if localfile.endswith('.js'):
+ flow.response.headers['Content-type'] = 'text/javascript'
+
+def expandpath(path):
+ return os.path.realpath(os.path.expanduser(path))
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--app", type=str, default="", help="Path to /polygerrit-ui/app/")
+parser.add_argument("--plugins", type=str, default="", help="Comma-separated list of plugin files to add/replace")
+parser.add_argument("--plugin_root", type=str, default="", help="Path containing individual plugin files to replace")
+parser.add_argument("--assets", type=str, default="", help="Path containing assets file to import.")
+parser.add_argument("--strip_assets", action="store_true", help="Strip plugin bundles from the response.")
+args = parser.parse_args()
+server = Server(expandpath(args.app) + '/',
+ args.plugins, expandpath(args.plugin_root) + '/',
+ args.assets and expandpath(args.assets),
+ args.strip_assets)
diff --git a/contrib/mitm-ui/serve-app-locally.py b/contrib/mitm-ui/serve-app-locally.py
new file mode 100644
index 0000000..636c684
--- /dev/null
+++ b/contrib/mitm-ui/serve-app-locally.py
@@ -0,0 +1,46 @@
+# bazel build polygerrit-ui/app:gr-app
+# mitmdump -s "serve-app-locally.py ~/gerrit/bazel-bin/polygerrit-ui/app"
+from mitmproxy import http
+import argparse
+import os
+import zipfile
+
+class Server:
+ def __init__(self, bundle):
+ self.bundle = bundle
+ self.bundlemtime = 0
+ self.files = {
+ 'polygerrit_ui/elements/gr-app.js': '',
+ 'polygerrit_ui/elements/gr-app.html': '',
+ 'polygerrit_ui/styles/main.css': '',
+ }
+ self.read_files()
+
+ def read_files(self):
+ if not os.path.isfile(self.bundle):
+ print("bundle not found!")
+ return
+ mtime = os.stat(self.bundle).st_mtime
+ if mtime <= self.bundlemtime:
+ return
+ self.bundlemtime = mtime
+ with zipfile.ZipFile(self.bundle) as z:
+ for fname in self.files:
+ print('Reading new content for ' + fname)
+ with z.open(fname, 'r') as content_file:
+ self.files[fname] = content_file.read()
+
+ def response(self, flow: http.HTTPFlow) -> None:
+ self.read_files()
+ for name in self.files:
+ if name.rsplit('/', 1)[1] in flow.request.pretty_url:
+ flow.response.content = self.files[name]
+
+def expandpath(path):
+ return os.path.expanduser(path)
+
+def start():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("bundle", type=str)
+ args = parser.parse_args()
+ return Server(expandpath(args.bundle))
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 1853c53..e74e1a2 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -526,6 +526,8 @@
(tagName, traceId) -> addMessage(tagName + ": " + traceId))) {
traceContext.addTag(RequestId.Type.RECEIVE_ID, new RequestId(project.getNameKey().get()));
+ logger.atFinest().log("Calling user: %s", user.getLoggableName());
+
// Log the push options here, rather than in parsePushOptions(), so that they are included
// into the trace if tracing is enabled.
logger.atFine().log("push options: %s", receivePack.getPushOptions());
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index e0c7c37..54c283d 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -441,28 +441,32 @@
test('installing preloaded plugin', () => {
let plugin;
- window.ASSETS_PATH = 'http://blips.com/chitz/';
+ window.ASSETS_PATH = 'http://blips.com/chitz';
Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
assert.strictEqual(plugin.getPluginName(), 'foo');
assert.strictEqual(plugin.url('/some/thing.html'),
- 'http://blips.com/plugins/foo/some/thing.html');
+ 'http://blips.com/chitz/plugins/foo/some/thing.html');
delete window.ASSETS_PATH;
});
suite('test plugin with base url', () => {
+ let baseUrlPlugin;
+
setup(() => {
sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns('/r');
Gerrit._setPluginsCount(1);
- Gerrit.install(p => { plugin = p; }, '0.1',
- 'http://test.com/r/plugins/testplugin/static/test.js');
+ Gerrit.install(p => { baseUrlPlugin = p; }, '0.1',
+ 'http://test.com/r/plugins/baseurlplugin/static/test.js');
});
test('url', () => {
- assert.notEqual(plugin.url(), 'http://test.com/plugins/testplugin/');
- assert.equal(plugin.url(), 'http://test.com/r/plugins/testplugin/');
- assert.equal(plugin.url('/static/test.js'),
- 'http://test.com/r/plugins/testplugin/static/test.js');
+ assert.notEqual(baseUrlPlugin.url(),
+ 'http://test.com/plugins/baseurlplugin/');
+ assert.equal(baseUrlPlugin.url(),
+ 'http://test.com/r/plugins/baseurlplugin/');
+ assert.equal(baseUrlPlugin.url('/static/test.js'),
+ 'http://test.com/r/plugins/baseurlplugin/static/test.js');
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 36a428d..8bf4a2d 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -217,9 +217,14 @@
};
Plugin.prototype.url = function(opt_path) {
- const base = Gerrit.BaseUrlBehavior.getBaseUrl();
- return this._url.origin + base + '/plugins/' +
- this._name + (opt_path || '/');
+ const relPath = '/plugins/' + this._name + (opt_path || '/');
+ if (window.location.origin === this._url.origin) {
+ // Plugin loaded from the same origin as gr-app, getBaseUrl in effect.
+ return this._url.origin + Gerrit.BaseUrlBehavior.getBaseUrl() + relPath;
+ } else {
+ // Plugin loaded from assets bundle, expect assets placed along with it.
+ return this._url.href.split('/plugins/' + this._name)[0] + relPath;
+ }
};
Plugin.prototype.screenUrl = function(opt_screenName) {