Add basic PolyGerrit UI
This basic variant of the UI shows the CRDs and provides links. But it
does not yet color them red if cycles are detected.
Depends-On: I783a00df00d386292ac458e925079219bb6392c7
Change-Id: I019d3b35de2933abecb5caef0613a02904c8eb68
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.eslintignore
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..6d9ae7c
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,166 @@
+{
+ "extends": ["eslint:recommended", "google"],
+ "parserOptions": {
+ "ecmaVersion": 8,
+ "sourceType": "module"
+ },
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "globals": {
+ "__dirname": false,
+ "app": false,
+ "page": false,
+ "Polymer": false,
+ "process": false,
+ "require": false,
+ "Gerrit": false,
+ "Promise": false,
+ "assert": false,
+ "test": false,
+ "flushAsynchronousOperations": false
+ },
+ "rules": {
+ "arrow-parens": ["error", "as-needed"],
+ "block-spacing": ["error", "always"],
+ "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
+ "camelcase": "off",
+ "comma-dangle": ["error", {
+ "arrays": "always-multiline",
+ "objects": "always-multiline",
+ "imports": "always-multiline",
+ "exports": "always-multiline",
+ "functions": "never"
+ }],
+ "eol-last": "off",
+ "indent": ["error", 2, {
+ "MemberExpression": 2,
+ "FunctionDeclaration": {"body": 1, "parameters": 2},
+ "FunctionExpression": {"body": 1, "parameters": 2},
+ "CallExpression": {"arguments": 2 },
+ "ArrayExpression": 1,
+ "ObjectExpression": 1,
+ "SwitchCase": 1
+ }],
+ "keyword-spacing": ["error", { "after": true, "before": true }],
+ "lines-between-class-members": ["error", "always"],
+ "max-len": [
+ "error",
+ 80,
+ 2,
+ {
+ "ignoreComments": true,
+ "ignorePattern": "^import .*;$"
+ }
+ ],
+ "new-cap": ["error", { "capIsNewExceptions": ["Polymer", "LegacyElementMixin", "GestureEventListeners", "LegacyDataMixin"] }],
+ "no-console": "off",
+ "no-multiple-empty-lines": [ "error", { "max": 1 } ],
+ "no-prototype-builtins": "off",
+ "no-redeclare": "off",
+ "no-restricted-syntax": [
+ "error",
+ {
+ "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']",
+ "message": "Remove test.only."
+ },
+ {
+ "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']",
+ "message": "Remove suite.only."
+ }
+ ],
+ "no-undef": "off",
+ "no-useless-escape": "off",
+ "no-var": "error",
+ "object-shorthand": ["error", "always"],
+ "padding-line-between-statements": [
+ "error",
+ {
+ "blankLine": "always",
+ "prev": "class",
+ "next": "*"
+ },
+ {
+ "blankLine": "always",
+ "prev": "*",
+ "next": "class"
+ }
+ ],
+ "prefer-arrow-callback": "error",
+ "prefer-const": "error",
+ "prefer-spread": "error",
+ "quote-props": ["error", "consistent-as-needed"],
+ "semi": [2, "always"],
+ "template-curly-spacing": "error",
+ "valid-jsdoc": "off",
+
+ "require-jsdoc": 0,
+ "valid-jsdoc": 0,
+ "jsdoc/check-alignment": 2,
+ "jsdoc/check-examples": 0,
+ "jsdoc/check-indentation": 0,
+ "jsdoc/check-param-names": 0,
+ "jsdoc/check-syntax": 0,
+ "jsdoc/check-tag-names": 0,
+ "jsdoc/check-types": 0,
+ "jsdoc/implements-on-classes": 2,
+ "jsdoc/match-description": 0,
+ "jsdoc/newline-after-description": 2,
+ "jsdoc/no-types": 0,
+ "jsdoc/no-undefined-types": 0,
+ "jsdoc/require-description": 0,
+ "jsdoc/require-description-complete-sentence": 0,
+ "jsdoc/require-example": 0,
+ "jsdoc/require-hyphen-before-param-description": 0,
+ "jsdoc/require-jsdoc": 0,
+ "jsdoc/require-param": 0,
+ "jsdoc/require-param-description": 0,
+ "jsdoc/require-param-name": 2,
+ "jsdoc/require-param-type": 2,
+ "jsdoc/require-returns": 0,
+ "jsdoc/require-returns-check": 0,
+ "jsdoc/require-returns-description": 0,
+ "jsdoc/require-returns-type": 2,
+ "jsdoc/valid-types": 2,
+ "jsdoc/require-file-overview": ["error", {
+ "tags": {
+ "license": {
+ "mustExist": true,
+ "preventDuplicates": true
+ }
+ }
+ }],
+ "import/named": 2,
+ "import/no-unresolved": 2,
+ "import/no-self-import": 2,
+ // The no-cycle rule is slow, because it doesn't cache dependencies.
+ // Disable it.
+ "import/no-cycle": 0,
+ "import/no-useless-path-segments": 2,
+ "import/no-unused-modules": 2,
+ "import/no-default-export": 2
+ },
+ "plugins": [
+ "html",
+ "jsdoc",
+ "import"
+ ],
+ "settings": {
+ "html/report-bad-indent": "error"
+ },
+ "overrides": [
+ {
+ "files": ["*_html.js", "*-styles.js", "externs.js"],
+ "rules": {
+ "max-len": "off"
+ }
+ },
+ {
+ "files": ["*.html"],
+ "rules": {
+ "jsdoc/require-file-overview": "off"
+ }
+ }
+ ]
+}
diff --git a/BUILD b/BUILD
index 5368f19..5f1c689 100644
--- a/BUILD
+++ b/BUILD
@@ -1,11 +1,15 @@
load("@rules_java//java:defs.bzl", "java_library")
+load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
load("//tools/bzl:junit.bzl", "junit_tests")
+load("//tools/js:eslint.bzl", "eslint")
load(
"//tools/bzl:plugin.bzl",
"PLUGIN_DEPS",
"PLUGIN_TEST_DEPS",
"gerrit_plugin",
)
+load("//tools/bzl:genrule2.bzl", "genrule2")
+load("//tools/bzl:js.bzl", "polygerrit_plugin")
gerrit_plugin(
name = "zuul",
@@ -14,7 +18,9 @@
manifest_entries = [
"Gerrit-PluginName: zuul",
"Gerrit-Module: com.googlesource.gerrit.plugins.zuul.Module",
+ "Gerrit-HttpModule: com.googlesource.gerrit.plugins.zuul.HttpModule",
],
+ resource_jars = [":gr-zuul-static"],
)
junit_tests(
@@ -36,3 +42,54 @@
],
)
+genrule2(
+ name = "gr-zuul-static",
+ srcs = [":gr-zuul"],
+ outs = ["gr-zuul-static.jar"],
+ cmd = " && ".join([
+ "mkdir $$TMP/static",
+ "cp $(locations :gr-zuul) $$TMP/static",
+ "cd $$TMP",
+ "zip -Drq $$ROOT/$@ -g .",
+ ]),
+)
+
+polygerrit_plugin(
+ name = "gr-zuul",
+ app = "zuul-bundle.js",
+ plugin_name = "zuul",
+)
+
+rollup_bundle(
+ name = "zuul-bundle",
+ srcs = glob(["gr-zuul/*.js"]),
+ entry_point = "gr-zuul/plugin.js",
+ rollup_bin = "//tools/node_tools:rollup-bin",
+ sourcemap = "hidden",
+ format = "iife",
+ deps = [
+ "@tools_npm//rollup-plugin-node-resolve",
+ ],
+)
+
+# Define the eslinter for the plugin
+# The eslint macro creates 2 rules: lint_test and lint_bin
+eslint(
+ name = "lint",
+ srcs = glob([
+ "gr-zuul/**/*.js",
+ ]),
+ config = ".eslintrc.json",
+ data = [],
+ extensions = [
+ ".js",
+ ],
+ ignore = ".eslintignore",
+ plugins = [
+ "@npm//eslint-config-google",
+ "@npm//eslint-plugin-html",
+ "@npm//eslint-plugin-import",
+ "@npm//eslint-plugin-jsdoc",
+ ],
+)
+
diff --git a/gr-zuul/gr-zuul.js b/gr-zuul/gr-zuul.js
new file mode 100644
index 0000000..9bcd761
--- /dev/null
+++ b/gr-zuul/gr-zuul.js
@@ -0,0 +1,58 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {htmlTemplate} from './gr-zuul_html.js';
+
+class GrZuul extends Polymer.Element {
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-zuul'; }
+
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ static get properties() {
+ return {
+ change: {
+ type: Object,
+ observer: '_onChangeChanged',
+ },
+ _crd: {
+ type: Object,
+ value: {},
+ },
+ _crd_loaded: {
+ type: Boolean,
+ value: false,
+ },
+ };
+ }
+
+ _onChangeChanged() {
+ this._crd_loaded = false;
+ const url = '/changes/' + this.change.id + '/revisions/current/crd';
+ return this.plugin.restApi().send('GET', url).then(crd => {
+ this._crd = crd;
+ this._crd_loaded = true;
+ });
+ }
+
+ _computeDependencyUrl(changeId) {
+ return Gerrit.Nav.getUrlForSearchQuery(changeId);
+ }
+}
+
+customElements.define(GrZuul.is, GrZuul);
diff --git a/gr-zuul/gr-zuul_html.js b/gr-zuul/gr-zuul_html.js
new file mode 100644
index 0000000..a5328e2
--- /dev/null
+++ b/gr-zuul/gr-zuul_html.js
@@ -0,0 +1,88 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const htmlTemplate = Polymer.html`
+ <style include="shared-styles">
+ section.related-changes-section {
+ margin-bottom: 1.4em; /* Same as line height for collapse purposes */
+ display: block;
+ }
+ div.foo {
+ margin-bottom: 1.4em; /* Same as line height for collapse purposes */
+ }
+ a {
+ display: block;
+ }
+ .changeContainer,
+ a {
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .changeContainer {
+ display: flex;
+ }
+ .changeContainer.thisChange:before {
+ content: '➔';
+ width: 1.2em;
+ }
+ h4,
+ section div {
+ display: flex;
+ }
+ h4:before,
+ section div:before {
+ content: ' ';
+ flex-shrink: 0;
+ width: 1.2em;
+ }
+ </style>
+ <template is="dom-if" if="[[_crd_loaded]]">
+ <template is="dom-if" if="[[_crd.depends_on.length]]">
+ <section class="related-changes-section">
+ <h4>Depends on</h4>
+ <template is="dom-repeat" items="[[_crd.depends_on]]">
+ <div class="changeContainer zuulDependencyContainer">
+ <a
+ href$="[[_computeDependencyUrl(item)]]"
+ title$="[[item]]"
+ >
+ [[item]]
+ </a>
+ </div>
+ </template>
+ </section>
+ </template>
+ <template is="dom-if" if="[[_crd.needed_by.length]]">
+ <section class="related-changes-section">
+ <h4>Needed by</h4>
+ <template is="dom-repeat" items="[[_crd.needed_by]]">
+ <div class="changeContainer zuulDependencyContainer">
+ <a
+ href$="[[_computeDependencyUrl(item)]]"
+ title$="[[item]]"
+ >
+ [[item]]
+ </a>
+ </div>
+ </template>
+ </section>
+ </template>
+ </template>
+`;
+
diff --git a/gr-zuul/plugin.js b/gr-zuul/plugin.js
new file mode 100644
index 0000000..475f053
--- /dev/null
+++ b/gr-zuul/plugin.js
@@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import './gr-zuul.js';
+
+Gerrit.install(plugin => {
+ plugin.registerCustomComponent(
+ 'related-changes-section', 'gr-zuul', {slot: 'bottom'});
+});
diff --git a/src/main/java/com/googlesource/gerrit/plugins/zuul/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/zuul/HttpModule.java
new file mode 100644
index 0000000..bc6db6b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/zuul/HttpModule.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.zuul;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.JavaScriptPlugin;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
+import com.google.inject.servlet.ServletModule;
+
+public class HttpModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ DynamicSet.bind(binder(), WebUiPlugin.class).toInstance(new JavaScriptPlugin("zuul.html"));
+ }
+}