diff options
109 files changed, 1269 insertions, 579 deletions
@@ -2,6 +2,10 @@ package(default_visibility = ["//visibility:public"]) load("//tools/bzl:genrule2.bzl", "genrule2") load("//tools/bzl:pkg_war.bzl", "pkg_war") +load( + "@bazel_tools//tools/jdk:default_java_toolchain.bzl", + "default_java_toolchain", +) config_setting( name = "java9", @@ -10,6 +14,43 @@ config_setting( }, ) +config_setting( + name = "java10", + values = { + "java_toolchain": ":toolchain_vanilla", + }, +) + +# TODO(davido): Switch to consuming it from @bazel_tool//tools/jdk:absolute_javabase +# when new Bazel version is released with this change included: +# https://github.com/bazelbuild/bazel/issues/6012 +# https://github.com/bazelbuild/bazel/commit/0173bdbf7bdd1874379d4dd3eb70d5321e0f1816 +# As the interim use a hack that works around it by putting the variable reference +# behind a select +config_setting( + name = "use_absolute_javabase", + values = {"define": "USE_ABSOLUTE_JAVABASE=true"}, +) + +java_runtime( + name = "absolute_javabase", + java_home = select({ + ":use_absolute_javabase": "$(ABSOLUTE_JAVABASE)", + "//conditions:default": "", + }), + visibility = ["//visibility:public"], +) + +# TODO(davido): Switch to consuming it from @bazel_tool//tools/jdk:toolchain_vanilla +# when my change is included in released Bazel version: +# https://github.com/bazelbuild/bazel/commit/0bef68e054eccecd690e5d9f46db8a0c4b2d887a +default_java_toolchain( + name = "toolchain_vanilla", + forcibly_disable_header_compilation = True, + javabuilder = ["@bazel_tools//tools/jdk:VanillaJavaBuilder_deploy.jar"], + jvm_opts = [], +) + genrule( name = "gen_version", outs = ["version.txt"], diff --git a/Documentation/cmd-close-connection.txt b/Documentation/cmd-close-connection.txt index 973441e294..c161541e41 100644 --- a/Documentation/cmd-close-connection.txt +++ b/Documentation/cmd-close-connection.txt @@ -1,7 +1,7 @@ = gerrit close-connection == NAME -gerrit close-connection - Close the specified SSH connection +gerrit close-connection - Close the specified SSH connection. == SYNOPSIS [verse] diff --git a/Documentation/cmd-create-account.txt b/Documentation/cmd-create-account.txt index 62bd0aa328..617191fb6d 100644 --- a/Documentation/cmd-create-account.txt +++ b/Documentation/cmd-create-account.txt @@ -69,7 +69,7 @@ Create a new batch/role access user account called `watcher` in the 'Non-Interactive Users' group. ---- - $ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit create-account --group "'Non-Interactive Users'" --ssh-key - watcher +$ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit create-account --group "'Non-Interactive Users'" --ssh-key - watcher ---- GERRIT diff --git a/Documentation/cmd-create-branch.txt b/Documentation/cmd-create-branch.txt index 336af56d11..2060917d8c 100644 --- a/Documentation/cmd-create-branch.txt +++ b/Documentation/cmd-create-branch.txt @@ -1,7 +1,7 @@ = gerrit create-branch == NAME -gerrit create-branch - Create a new branch +gerrit create-branch - Create a new branch. == SYNOPSIS [verse] @@ -40,7 +40,7 @@ Create a new branch called 'newbranch' from the 'master' branch of the project 'myproject'. ---- - $ ssh -p 29418 review.example.com gerrit create-branch myproject newbranch master +$ ssh -p 29418 review.example.com gerrit create-branch myproject newbranch master ---- GERRIT diff --git a/Documentation/cmd-create-group.txt b/Documentation/cmd-create-group.txt index 7f1f463dea..2ba611c0ea 100644 --- a/Documentation/cmd-create-group.txt +++ b/Documentation/cmd-create-group.txt @@ -68,14 +68,14 @@ Create a new account group called `gerritdev` with two initial members `developer1` and `developer2`. The group should be owned by itself: ---- - $ ssh -p 29418 user@review.example.com gerrit create-group --member developer1 --member developer2 gerritdev +$ ssh -p 29418 user@review.example.com gerrit create-group --member developer1 --member developer2 gerritdev ---- Create a new account group called `Foo` owned by the `Foo-admin` group. Put `developer1` as the initial member and include group description: ---- - $ ssh -p 29418 user@review.example.com gerrit create-group --owner Foo-admin --member developer1 --description "'Foo description'" Foo +$ ssh -p 29418 user@review.example.com gerrit create-group --owner Foo-admin --member developer1 --description "'Foo description'" Foo ---- Note that it is necessary to quote the description twice. The local diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt index e8c3857b60..026d7b1f5a 100644 --- a/Documentation/cmd-create-project.txt +++ b/Documentation/cmd-create-project.txt @@ -1,7 +1,7 @@ = gerrit create-project == NAME -gerrit create-project - Create a new hosted project +gerrit create-project - Create a new hosted project. == SYNOPSIS [verse] @@ -61,15 +61,17 @@ This command is intended to be used in scripts. --owner:: -o:: Identifier of the group(s) which will initially own this repository. ++ +-- +This can be: - This can be: - - * the UUID of the group - * the legacy numeric ID of the group - * the name of the group if it is unique - - The specified group(s) must already be defined within Gerrit. - Several groups can be specified on the command line. +* the UUID of the group +* the legacy numeric ID of the group +* the name of the group if it is unique +-- ++ +The specified group(s) must already be defined within Gerrit. +Several groups can be specified on the command line. + Defaults to what is specified by `repository.*.ownerGroup` in gerrit.config. @@ -180,13 +182,13 @@ Common unit suffixes of 'k', 'm', or 'g' are supported. Create a new project called `tools/gerrit`: ---- - $ ssh -p 29418 review.example.com gerrit create-project tools/gerrit.git +$ ssh -p 29418 review.example.com gerrit create-project tools/gerrit.git ---- Create a new project with a description: ---- - $ ssh -p 29418 review.example.com gerrit create-project tool.git --description "'Tools used by build system'" +$ ssh -p 29418 review.example.com gerrit create-project tool.git --description "'Tools used by build system'" ---- Note that it is necessary to quote the description twice. The local @@ -199,7 +201,7 @@ If the replication plugin is installed, the plugin will attempt to perform remote repository creation by a Bourne shell script: ---- - mkdir -p '/base/project.git' && cd '/base/project.git' && git init --bare && git update-ref HEAD refs/heads/master +mkdir -p '/base/project.git' && cd '/base/project.git' && git init --bare && git update-ref HEAD refs/heads/master ---- For this to work successfully the remote system must be able to run diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt index 9ba4808a1d..55d9083178 100644 --- a/Documentation/cmd-flush-caches.txt +++ b/Documentation/cmd-flush-caches.txt @@ -1,7 +1,7 @@ = gerrit flush-caches == NAME -gerrit flush-caches - Flush some/all server caches from memory +gerrit flush-caches - Flush some/all server caches from memory. == SYNOPSIS [verse] @@ -58,40 +58,40 @@ This command is intended to be used in scripts. List caches available for flushing: ---- - $ ssh -p 29418 review.example.com gerrit flush-caches --list - accounts - diff - groups - ldap_groups - openid - projects - sshkeys - web_sessions +$ ssh -p 29418 review.example.com gerrit flush-caches --list +accounts +diff +groups +ldap_groups +openid +projects +sshkeys +web_sessions ---- Flush all caches known to the server, forcing them to recompute: ---- - $ ssh -p 29418 review.example.com gerrit flush-caches --all +$ ssh -p 29418 review.example.com gerrit flush-caches --all ---- or ---- - $ ssh -p 29418 review.example.com gerrit flush-caches +$ ssh -p 29418 review.example.com gerrit flush-caches ---- Flush only the "sshkeys" cache, after manually editing an SSH key for a user: ---- - $ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys +$ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys ---- Flush "web_sessions", forcing all users to sign-in again: ---- - $ ssh -p 29418 review.example.com gerrit flush-caches --cache web_sessions +$ ssh -p 29418 review.example.com gerrit flush-caches --cache web_sessions ---- == SEE ALSO diff --git a/Documentation/cmd-gc.txt b/Documentation/cmd-gc.txt index 1d1cc002b9..390dce18d0 100644 --- a/Documentation/cmd-gc.txt +++ b/Documentation/cmd-gc.txt @@ -1,7 +1,7 @@ = gerrit gc == NAME -gerrit gc - Run the Git garbage collection +gerrit gc - Run the Git garbage collection. == SYNOPSIS [verse] @@ -54,19 +54,19 @@ This command is intended to be used in scripts. Run the Git garbage collection for the projects 'myProject' and 'yourProject': ---- - $ ssh -p 29418 review.example.com gerrit gc myProject yourProject - collecting garbage for "myProject": - ... - done. - - collecting garbage for "yourProject": - ... - done. +$ ssh -p 29418 review.example.com gerrit gc myProject yourProject +collecting garbage for "myProject": +... +done. + +collecting garbage for "yourProject": +... +done. ---- Run the Git garbage collection for all projects: ---- - $ ssh -p 29418 review.example.com gerrit gc --all +$ ssh -p 29418 review.example.com gerrit gc --all ---- GERRIT diff --git a/Documentation/cmd-gsql.txt b/Documentation/cmd-gsql.txt index d2eb7833c4..7f2aaf7b78 100644 --- a/Documentation/cmd-gsql.txt +++ b/Documentation/cmd-gsql.txt @@ -1,7 +1,7 @@ = gerrit gsql == NAME -gerrit gsql - Administrative interface to active database +gerrit gsql - Administrative interface to active database. == SYNOPSIS [verse] @@ -42,18 +42,18 @@ JSON_SINGLE. To manually correct a user's SSH user name: ---- - $ ssh -p 29418 review.example.com gerrit gsql - Welcome to Gerrit Code Review v2.0.25 - (PostgreSQL 8.3.8) +$ ssh -p 29418 review.example.com gerrit gsql +Welcome to Gerrit Code Review v2.0.25 +(PostgreSQL 8.3.8) - Type '\h' for help. Type '\r' to clear the buffer. +Type '\h' for help. Type '\r' to clear the buffer. - gerrit> update accounts set ssh_user_name = 'alice' where account_id=1; - UPDATE 1; 1 ms - gerrit> \q - Bye +gerrit> update accounts set ssh_user_name = 'alice' where account_id=1; +UPDATE 1; 1 ms +gerrit> \q +Bye - $ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys --cache accounts +$ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys --cache accounts ---- GERRIT diff --git a/Documentation/cmd-hook-commit-msg.txt b/Documentation/cmd-hook-commit-msg.txt index ffdd5da05a..49d5c173be 100644 --- a/Documentation/cmd-hook-commit-msg.txt +++ b/Documentation/cmd-hook-commit-msg.txt @@ -20,24 +20,24 @@ After the hook has been installed in the user's local Git repository for a project, the hook will modify a commit message such as: ---- - Improve foo widget by attaching a bar. +Improve foo widget by attaching a bar. - We want a bar, because it improves the foo by providing more - wizbangery to the dowhatimeanery. +We want a bar, because it improves the foo by providing more +wizbangery to the dowhatimeanery. - Signed-off-by: A. U. Thor <author@example.com> +Signed-off-by: A. U. Thor <author@example.com> ---- by inserting a new `Change-Id: ` line in the footer: ---- - Improve foo widget by attaching a bar. +Improve foo widget by attaching a bar. - We want a bar, because it improves the foo by providing more - wizbangery to the dowhatimeanery. +We want a bar, because it improves the foo by providing more +wizbangery to the dowhatimeanery. - Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b - Signed-off-by: A. U. Thor <author@example.com> +Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b +Signed-off-by: A. U. Thor <author@example.com> ---- The hook implementation is reasonably intelligent at inserting the @@ -57,31 +57,29 @@ to `false` in the git config. == OBTAINING - To obtain the `commit-msg` script use `scp`, `wget` or `curl` to download it to your local system from your Gerrit server. You can use either of the below commands: ---- - $ scp -p -P 29418 <your username>@<your Gerrit review server>:hooks/commit-msg <local path to your git>/.git/hooks/ +$ scp -p -P 29418 <your username>@<your Gerrit review server>:hooks/commit-msg <local path to your git>/.git/hooks/ - $ curl -Lo <local path to your git>/.git/hooks/commit-msg <your Gerrit http URL>/tools/hooks/commit-msg +$ curl -Lo <local path to your git>/.git/hooks/commit-msg <your Gerrit http URL>/tools/hooks/commit-msg ---- A specific example of this might look something like this: -.Example ---- - $ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg ~/duhproject/.git/hooks/ +$ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg ~/duhproject/.git/hooks/ - $ curl -Lo ~/duhproject/.git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg +$ curl -Lo ~/duhproject/.git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg ---- Make sure the hook file is executable: ---- - $ chmod u+x ~/duhproject/.git/hooks/commit-msg +$ chmod u+x ~/duhproject/.git/hooks/commit-msg ---- == SEE ALSO diff --git a/Documentation/cmd-index-activate.txt b/Documentation/cmd-index-activate.txt index 4428d12708..0bbf308494 100644 --- a/Documentation/cmd-index-activate.txt +++ b/Documentation/cmd-index-activate.txt @@ -1,7 +1,7 @@ = gerrit index activate == NAME -gerrit index activate - Activate the latest index version available +gerrit index activate - Activate the latest index version available. == SYNOPSIS [verse] @@ -37,7 +37,7 @@ This command is intended to be used in scripts. Activate the latest change index: ---- - $ ssh -p 29418 review.example.com gerrit index activate changes +$ ssh -p 29418 review.example.com gerrit index activate changes ---- GERRIT diff --git a/Documentation/cmd-index-changes-in-project.txt b/Documentation/cmd-index-changes-in-project.txt index ec1626f29e..b2282fc939 100644 --- a/Documentation/cmd-index-changes-in-project.txt +++ b/Documentation/cmd-index-changes-in-project.txt @@ -26,7 +26,7 @@ This command is intended to be used in scripts. Index all changes in projects MyProject and NiceProject. ---- - $ ssh -p 29418 user@review.example.com gerrit index changes-in-project MyProject NiceProject +$ ssh -p 29418 user@review.example.com gerrit index changes-in-project MyProject NiceProject ---- GERRIT diff --git a/Documentation/cmd-index-changes.txt b/Documentation/cmd-index-changes.txt index d38c51ad4a..0ee7aabd26 100644 --- a/Documentation/cmd-index-changes.txt +++ b/Documentation/cmd-index-changes.txt @@ -30,7 +30,7 @@ This command is intended to be used in scripts. Index changes with legacy ID numbers 1 and 2. ---- - $ ssh -p 29418 user@review.example.com gerrit index changes 1 2 +$ ssh -p 29418 user@review.example.com gerrit index changes 1 2 ---- GERRIT diff --git a/Documentation/cmd-index-start.txt b/Documentation/cmd-index-start.txt index 5a002f397d..d1a02b362f 100644 --- a/Documentation/cmd-index-start.txt +++ b/Documentation/cmd-index-start.txt @@ -1,7 +1,7 @@ = gerrit index start == NAME -gerrit index start - Start the online indexer +gerrit index start - Start the online indexer. == SYNOPSIS [verse] @@ -44,7 +44,7 @@ This command is intended to be used in scripts. Start the online indexer for the 'changes' index: ---- - $ ssh -p 29418 review.example.com gerrit index start changes +$ ssh -p 29418 review.example.com gerrit index start changes ---- GERRIT diff --git a/Documentation/cmd-kill.txt b/Documentation/cmd-kill.txt index ac8e8028a3..db5093d221 100644 --- a/Documentation/cmd-kill.txt +++ b/Documentation/cmd-kill.txt @@ -1,7 +1,7 @@ = kill == NAME -kill - Cancel or abort a background task +kill - Cancel or abort a background task. == SYNOPSIS [verse] diff --git a/Documentation/cmd-logging-ls-level.txt b/Documentation/cmd-logging-ls-level.txt index ee015bb0de..fb1fb33efd 100644 --- a/Documentation/cmd-logging-ls-level.txt +++ b/Documentation/cmd-logging-ls-level.txt @@ -1,9 +1,9 @@ = gerrit logging ls-level == NAME -gerrit logging ls-level - view the logging level +gerrit logging ls-level - view the logging level. -gerrit logging ls - view the logging level +gerrit logging ls - view the logging level. == SYNOPSIS [verse] @@ -27,13 +27,12 @@ Caller must have the ADMINISTRATE_SERVER capability. View the logging level of the loggers in the package com.google: ---- - $ssh -p 29418 review.example.com gerrit logging ls-level \ - com.google. +$ssh -p 29418 review.example.com gerrit logging ls-level com.google. ---- View the logging level of every logger ---- - $ssh -p 29418 review.example.com gerrit logging ls-level +$ssh -p 29418 review.example.com gerrit logging ls-level ---- GERRIT diff --git a/Documentation/cmd-logging-set-level.txt b/Documentation/cmd-logging-set-level.txt index 5baa9687c4..d7fc69e1bc 100644 --- a/Documentation/cmd-logging-set-level.txt +++ b/Documentation/cmd-logging-set-level.txt @@ -1,9 +1,9 @@ = gerrit logging set-level == NAME -gerrit logging set-level - set the logging level +gerrit logging set-level - set the logging level. -gerrit logging set - set the logging level +gerrit logging set - set the logging level. == SYNOPSIS [verse] @@ -34,14 +34,12 @@ Caller must have the ADMINISTRATE_SERVER capability. Change the logging level of the loggers in the package com.google to DEBUG. ---- - $ssh -p 29418 review.example.com gerrit logging set-level \ - debug com.google. +$ssh -p 29418 review.example.com gerrit logging set-level debug com.google. ---- Reset the logging level of every logger to what they were at deployment time. ---- - $ssh -p 29418 review.example.com gerrit logging set-level \ - reset +$ssh -p 29418 review.example.com gerrit logging set-level reset ---- GERRIT diff --git a/Documentation/cmd-ls-groups.txt b/Documentation/cmd-ls-groups.txt index 6d4bdc5374..8a4845c263 100644 --- a/Documentation/cmd-ls-groups.txt +++ b/Documentation/cmd-ls-groups.txt @@ -1,7 +1,7 @@ = gerrit ls-groups == NAME -gerrit ls-groups - List groups visible to caller +gerrit ls-groups - List groups visible to caller. == SYNOPSIS [verse] @@ -88,53 +88,53 @@ nonexistent group, the owner group name field will read `n/a`. List visible groups: ---- - $ ssh -p 29418 review.example.com gerrit ls-groups - Administrators - Anonymous Users - MyProject_Committers - Project Owners - Registered Users +$ ssh -p 29418 review.example.com gerrit ls-groups +Administrators +Anonymous Users +MyProject_Committers +Project Owners +Registered Users ---- List all groups for which any permission is set for the project "MyProject": ---- - $ ssh -p 29418 review.example.com gerrit ls-groups --project MyProject - MyProject_Committers - Project Owners - Registered Users +$ ssh -p 29418 review.example.com gerrit ls-groups --project MyProject +MyProject_Committers +Project Owners +Registered Users ---- List all groups which are owned by the calling user: ---- - $ ssh -p 29418 review.example.com gerrit ls-groups --owned - MyProject_Committers - MyProject_Verifiers +$ ssh -p 29418 review.example.com gerrit ls-groups --owned +MyProject_Committers +MyProject_Verifiers ---- Check if the calling user owns the group `MyProject_Committers`. If `MyProject_Committers` is returned the calling user owns this group. If the result is empty, the calling user doesn't own the group. ---- - $ ssh -p 29418 review.example.com gerrit ls-groups --owned -q MyProject_Committers - MyProject_Committers +$ ssh -p 29418 review.example.com gerrit ls-groups --owned -q MyProject_Committers +MyProject_Committers ---- Extract the UUID of the 'Administrators' group: ---- - $ ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $2}' - ad463411db3eec4e1efb0d73f55183c1db2fd82a +$ ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $2}' +ad463411db3eec4e1efb0d73f55183c1db2fd82a ---- Extract and expand the multi-line description of the 'Administrators' group: ---- - $ printf "$(ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $3}')\n" - This is a - multi-line - description. +$ printf "$(ssh -p 29418 review.example.com gerrit ls-groups -v | awk '-F\t' '$1 == "Administrators" {print $3}')\n" +This is a +multi-line +description. ---- GERRIT diff --git a/Documentation/cmd-ls-members.txt b/Documentation/cmd-ls-members.txt index 273451ba04..8f8857c5bb 100644 --- a/Documentation/cmd-ls-members.txt +++ b/Documentation/cmd-ls-members.txt @@ -1,7 +1,7 @@ = gerrit ls-members == NAME -gerrit ls-members - Show members of a given group +gerrit ls-members - Show members of a given group. == SYNOPSIS [verse] @@ -40,17 +40,17 @@ the output. List members of the Administrators group: ---- - $ ssh -p 29418 review.example.com gerrit ls-members Administrators - id username full name email - 100000 jim Jim Bob somebody@example.com - 100001 johnny John Smith n/a - 100002 mrnoname n/a someoneelse@example.com +$ ssh -p 29418 review.example.com gerrit ls-members Administrators +id username full name email +100000 jim Jim Bob somebody@example.com +100001 johnny John Smith n/a +100002 mrnoname n/a someoneelse@example.com ---- List members of a non-existent group: ---- - $ ssh -p 29418 review.example.com gerrit ls-members BadlySpelledGroup - Group not found or not visible +$ ssh -p 29418 review.example.com gerrit ls-members BadlySpelledGroup +Group not found or not visible ---- GERRIT diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt index 486ca44aab..3bb8e4f597 100644 --- a/Documentation/cmd-ls-projects.txt +++ b/Documentation/cmd-ls-projects.txt @@ -1,7 +1,7 @@ = gerrit ls-projects == NAME -gerrit ls-projects - List projects visible to caller +gerrit ls-projects - List projects visible to caller. == SYNOPSIS [verse] @@ -115,28 +115,28 @@ by the client in the request headers. List visible projects: ---- - $ ssh -p 29418 review.example.com gerrit ls-projects - platform/manifest - tools/gerrit - tools/gwtorm - - $ curl http://review.example.com/projects/ - platform/manifest - tools/gerrit - tools/gwtorm - - $ curl http://review.example.com/projects/tools/ - tools/gerrit - tools/gwtorm +$ ssh -p 29418 review.example.com gerrit ls-projects +platform/manifest +tools/gerrit +tools/gwtorm + +$ curl http://review.example.com/projects/ +platform/manifest +tools/gerrit +tools/gwtorm + +$ curl http://review.example.com/projects/tools/ +tools/gerrit +tools/gwtorm ---- Clone any project visible to the user: ---- - for p in `ssh -p 29418 review.example.com gerrit ls-projects` - do - mkdir -p `dirname "$p"` - git clone --bare "ssh://review.example.com:29418/$p.git" "$p.git" - done +for p in `ssh -p 29418 review.example.com gerrit ls-projects` +do + mkdir -p `dirname "$p"` + git clone --bare "ssh://review.example.com:29418/$p.git" "$p.git" +done ---- == SEE ALSO diff --git a/Documentation/cmd-ls-user-refs.txt b/Documentation/cmd-ls-user-refs.txt index 1a87fc9507..cba7d1bdef 100644 --- a/Documentation/cmd-ls-user-refs.txt +++ b/Documentation/cmd-ls-user-refs.txt @@ -1,7 +1,7 @@ = gerrit ls-user-refs == NAME -gerrit ls-user-refs - List refs visible to a specific user +gerrit ls-user-refs - List refs visible to a specific user. == SYNOPSIS [verse] @@ -42,7 +42,7 @@ Administrators List visible refs for the user "mr.developer" in project "gerrit" ---- - $ ssh -p 29418 review.example.com gerrit ls-user-refs -p gerrit -u mr.developer +$ ssh -p 29418 review.example.com gerrit ls-user-refs -p gerrit -u mr.developer ---- GERRIT diff --git a/Documentation/cmd-plugin-enable.txt b/Documentation/cmd-plugin-enable.txt index 9b5273630c..955267e88c 100644 --- a/Documentation/cmd-plugin-enable.txt +++ b/Documentation/cmd-plugin-enable.txt @@ -32,7 +32,7 @@ This command is intended to be used in scripts. Enable a plugin: ---- - ssh -p 29418 localhost gerrit plugin enable my-plugin +ssh -p 29418 localhost gerrit plugin enable my-plugin ---- GERRIT diff --git a/Documentation/concept-changes.txt b/Documentation/concept-changes.txt index 7320a508f6..1d275b419a 100644 --- a/Documentation/concept-changes.txt +++ b/Documentation/concept-changes.txt @@ -55,7 +55,7 @@ are not required to review it. |An optional topic. |Strategy -|The <<submit-strategy>> for the change. +|The <<submit-strategies,submit strategy>> for the change. |Code Review |Displays the Code Review status for the change. @@ -84,10 +84,10 @@ listed next to the change message. These related changes are grouped together in several categories, including: * Relation Chain. These changes are related by parent-child relationships, - regardless of <<topics>>. + regardless of <<topic,topic>>. * Merge Conflicts. These are changes in which there is a merge conflict with the current change. -* Submitted Together. These are changes that share the same <<topics>>. +* Submitted Together. These are changes that share the same <<topic,topic>>. An arrow indicates the change you are currently viewing. diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 734397e877..9f0bf8650e 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -690,7 +690,7 @@ H2 uses memory to cache its database content. The parameter `h2CacheSize` allows to limit the memory used by H2 and thus prevent out-of-memory caused by the H2 database using too much memory. + -See <<database.h2.cachesize,database.h2.cachesize>> for a detailed discussion. +See <<database.h2.cacheSize,database.h2.cacheSize>> for a detailed discussion. + Default is unset, using up to half of the available memory. + @@ -3684,6 +3684,9 @@ allow the owner to circumvent certain enforced rules (like important BLOCK rules). + Default is false. ++ +This value supports configuration reloads: +link:cmd-reload-config.html[reload-config] [[receive.checkReferencedObjectsAreReachable]]receive.checkReferencedObjectsAreReachable:: + diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt index 6331581fc7..0ecc82075c 100644 --- a/Documentation/dev-bazel.txt +++ b/Documentation/dev-bazel.txt @@ -6,7 +6,7 @@ To build Gerrit from source, you need: * A Linux or macOS system (Windows is not supported at this time) -* A JDK for Java 8 +* A JDK for Java 8|9|10 * Python 2 or 3 * Node.js * link:https://www.bazel.io/versions/master/docs/install.html[Bazel] @@ -14,6 +14,30 @@ To build Gerrit from source, you need: * zip, unzip * gcc +[[Java 10 support]] +Java 10 is supported through vanilla java toolchain +link:https://docs.bazel.build/versions/master/toolchains.html[Bazel option]. +To build Gerrit with Java 10, specify vanilla java toolchain and provide +path to Java 10 home: + +``` + $ bazel build --host_javabase=:absolute_javabase \ + --define=ABSOLUTE_JAVABASE=<path-to-java-10> \ + --define=USE_ABSOLUTE_JAVABASE=true \ + --host_java_toolchain=//:toolchain_vanilla \ + --java_toolchain=//:toolchain_vanilla \ + :release +``` + +Note that the following options must be added to `container.javaOptions` +in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 10: + +``` +[container] + javaOptions = --add-modules java.activation + javaOptions = --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED +``` + [[Java 9 support]] Java 9 is supported through alternative java toolchain link:https://docs.bazel.build/versions/master/toolchains.html[Bazel option]. diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt index 19d3b41fc4..2647b359cf 100644 --- a/Documentation/metrics.txt +++ b/Documentation/metrics.txt @@ -138,6 +138,13 @@ failed by table. * `notedb/read_all_external_ids_latency`: Latency for reading all external ID's from NoteDb. +=== Permissions + +* `permissions/project_state/computation_latency`: Latency to compute current access +sections on a project by traversing it's parents. +* `permissions/permission_collection/filter_latency`: Latency to filter access sections +by user and ref. + === Reviewer Suggestion * `reviewer_suggestion/query_accounts`: Latency for querying accounts for diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index b517d3c0d4..2a9dcee018 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt @@ -3570,6 +3570,12 @@ Whether the usage of Change-Ids is required for the project (`TRUE`, `FALSE`, `INHERIT`). This property is deprecated and will be removed in a future release. +|`enable_signed_push` |`INHERIT` if not set| +Whether signed push validation is enabled on the project (`TRUE`, +`FALSE`, `INHERIT`). +|`require_signed_push` |`INHERIT` if not set| +Whether signed push validation is required on the project (`TRUE`, +`FALSE`, `INHERIT`). |`max_object_size_limit` |optional| Max allowed Git object size for this project. Common unit suffixes of 'k', 'm', or 'g' are supported. @@ -3707,8 +3713,6 @@ The path to the `GerritSiteHeader.html` file. The path to the `GerritSiteFooter.html` file. |============================= ----- - GERRIT ------ Part of link:index.html[Gerrit Code Review] diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt index 41cb380908..7c904f5332 100644 --- a/Documentation/user-search.txt +++ b/Documentation/user-search.txt @@ -166,7 +166,7 @@ Changes occurring in 'PROJECT' or in one of the child projects of 'PROJECT'. [[repository]] -repository:'REPOSITORY':: +repository:'REPOSITORY', repo:'REPOSITORY':: + Changes occurring in 'REPOSITORY'. If 'REPOSITORY' starts with `^` it matches repository names by regular expression. The @@ -174,12 +174,12 @@ link:http://www.brics.dk/automaton/[dk.brics.automaton library] is used for evaluation of such patterns. [[repositories]] -repositories:'PREFIX':: +repositories:'PREFIX', repos:'PREFIX':: + Changes occurring in repositories starting with 'PREFIX'. [[parentrepository]] -parentrepository:'REPOSITORY':: +parentrepository:'REPOSITORY', parentrepo:'REPOSITORY':: + Changes occurring in 'REPOSITORY' or in one of the child repositories of 'REPOSITORY'. @@ -25,6 +25,7 @@ http_archive( # https://github.com/google/closure-compiler/blob/master/contrib/externs/polymer-1.0.js http_file( name = "polymer_closure", + downloaded_file_path = "polymer_closure.js", sha256 = "5a589bdba674e1fec7188e9251c8624ebf2d4d969beb6635f9148f420d1e08b1", urls = ["https://raw.githubusercontent.com/google/closure-compiler/775609aad61e14aef289ebec4bfc09ad88877f9e/contrib/externs/polymer-1.0.js"], ) @@ -107,24 +108,24 @@ maven_jar( sha1 = "83cd2cd674a217ade95a4bb83a8a14f351f48bd0", ) -GUICE_VERS = "4.2.0" +GUICE_VERS = "4.2.1" maven_jar( name = "guice-library", artifact = "com.google.inject:guice:" + GUICE_VERS, - sha1 = "25e1f4c1d528a1cffabcca0d432f634f3132f6c8", + sha1 = "f77dfd89318fe3ff293bafceaa75fbf66e4e4b10", ) maven_jar( name = "guice-assistedinject", artifact = "com.google.inject.extensions:guice-assistedinject:" + GUICE_VERS, - sha1 = "e7270305960ad7db56f7e30cb9df6be9ff1cfb45", + sha1 = "d327e4aee7c96f08cd657c17da231a1f4a8999ac", ) maven_jar( name = "guice-servlet", artifact = "com.google.inject.extensions:guice-servlet:" + GUICE_VERS, - sha1 = "f57581625c36c148f088d9f52a568d5bdf12c61d", + sha1 = "3927e462f923b0c672fdb045c5645bca4beab5c0", ) maven_jar( @@ -329,8 +330,8 @@ maven_jar( maven_jar( name = "args4j-intern", - artifact = "args4j:args4j:2.0.29", - sha1 = "55ca4ddc4e906ffbaec043113b36bb410a3d909e", + artifact = "args4j:args4j:2.33", + sha1 = "bd87a75374a6d6523de82fef51fc3cfe9baf9fc9", ) maven_jar( @@ -636,36 +637,36 @@ maven_jar( sha1 = "05b6f921f1810bdf90e25471968f741f87168b64", ) -LUCENE_VERS = "5.5.4" +LUCENE_VERS = "6.6.5" maven_jar( name = "lucene-core", artifact = "org.apache.lucene:lucene-core:" + LUCENE_VERS, - sha1 = "ab9c77e75cf142aa6e284b310c8395617bd9b19b", + sha1 = "2983f80b1037e098209657b0ca9176827892d0c0", ) maven_jar( name = "lucene-analyzers-common", artifact = "org.apache.lucene:lucene-analyzers-common:" + LUCENE_VERS, - sha1 = "08ce9d34c8124c80e176e8332ee947480bbb9576", + sha1 = "6094f91071d90570b7f5f8ce481d5de7d2d2e9d5", ) maven_jar( name = "backward-codecs", artifact = "org.apache.lucene:lucene-backward-codecs:" + LUCENE_VERS, - sha1 = "a933f42e758c54c43083398127ea7342b54d8212", + sha1 = "460a19e8d1aa7d31e9614cf528a6cb508c9e823d", ) maven_jar( name = "lucene-misc", artifact = "org.apache.lucene:lucene-misc:" + LUCENE_VERS, - sha1 = "a74388857f73614e528ae44d742c60187cb55a5a", + sha1 = "ce3a1b7b6a92b9af30791356a4bd46d1cea6cc1e", ) maven_jar( name = "lucene-queryparser", artifact = "org.apache.lucene:lucene-queryparser:" + LUCENE_VERS, - sha1 = "8a06fad4675473d98d93b61fea529e3f464bf69e", + sha1 = "2db9ca0086a4b8e0b9bc9f08a9b420303168e37c", ) maven_jar( @@ -777,13 +778,10 @@ maven_jar( sha1 = "d0c46320fbc07be3a24eb13a56cee4e3d38e0c75", ) -# TODO(davido): Remove exlusion of file system provider, when this issue is fixed: -# https://issues.apache.org/jira/browse/SSHD-736 maven_jar( name = "sshd", - artifact = "org.apache.sshd:sshd-core:1.7.0", - exclude = ["META-INF/services/java.nio.file.spi.FileSystemProvider"], - sha1 = "2e8b14f6d841b098e46bf407b6fdccab4c19fa41", + artifact = "org.apache.sshd:sshd-core:2.0.0", + sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b", ) maven_jar( @@ -794,8 +792,14 @@ maven_jar( maven_jar( name = "mina-core", - artifact = "org.apache.mina:mina-core:2.0.16", - sha1 = "f720f17643eaa7b0fec07c1d7f6272972c02bba4", + artifact = "org.apache.mina:mina-core:2.0.17", + sha1 = "7e10ec974760436d931f3e58be507d1957bcc8db", +) + +maven_jar( + name = "sshd-mina", + artifact = "org.apache.sshd:sshd-mina:2.0.0", + sha1 = "50f2669312494f6c1996d8bd0d266c1fca7be6f6", ) maven_jar( @@ -1063,14 +1067,14 @@ maven_jar( maven_jar( name = "asciidoctor", - artifact = "org.asciidoctor:asciidoctorj:1.5.6", - sha1 = "bb757d4b8b0f8438ce2ed781f6688cc6c01d9237", + artifact = "org.asciidoctor:asciidoctorj:1.5.7", + sha1 = "8e8c1d8fc6144405700dd8df3b177f2801ac5987", ) maven_jar( name = "jruby", - artifact = "org.jruby:jruby-complete:9.1.13.0", - sha1 = "8903bf42272062e87a7cbc1d98919e0729a9939f", + artifact = "org.jruby:jruby-complete:9.1.17.0", + sha1 = "76716d529710fc03d1d429b43e3cedd4419f78d4", ) maven_jar( diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java index 69d603fa4a..030ec2a528 100644 --- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java +++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java @@ -502,6 +502,8 @@ public abstract class AbstractDaemonTest { in.useSignedOffBy = ann.useSignedOffBy(); in.useContentMerge = ann.useContentMerge(); in.rejectEmptyCommit = ann.rejectEmptyCommit(); + in.enableSignedPush = ann.enableSignedPush(); + in.requireSignedPush = ann.requireSignedPush(); } else { // Defaults should match TestProjectConfig, omitting nullable values. in.createEmptyCommit = true; @@ -714,7 +716,7 @@ public abstract class AbstractDaemonTest { throws Exception { assertThat(topic).isNotEmpty(); return createCommitAndPush( - repo, "refs/for/master/" + name(topic), commitMsg, fileName, content); + repo, "refs/for/master%topic=" + name(topic), commitMsg, fileName, content); } protected PushOneCommit.Result createChange(String subject, String fileName, String content) @@ -733,7 +735,7 @@ public abstract class AbstractDaemonTest { String topic) throws Exception { PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content); - return push.to("refs/for/" + branch + "/" + name(topic)); + return push.to("refs/for/" + branch + "%topic=" + name(topic)); } protected BranchApi createBranch(Branch.NameKey branch) throws Exception { diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java index 83a3874d37..b23b6e65e6 100644 --- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java +++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java @@ -44,16 +44,12 @@ import com.google.gwtorm.server.OrmRuntimeException; import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Inject; import com.google.inject.Key; -import com.google.inject.Provides; import com.google.inject.ProvisionException; -import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.util.Providers; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.sshd.common.keyprovider.KeyPairProvider; -import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; import org.eclipse.jgit.lib.Config; class InMemoryTestingDatabaseModule extends LifecycleModule { @@ -107,23 +103,8 @@ class InMemoryTestingDatabaseModule extends LifecycleModule { install(new SchemaModule()); bind(SchemaVersion.class).to(SchemaVersion.C); - } - - @Provides - @Singleton - KeyPairProvider createHostKey() { - return getHostKeys(); - } - private static SimpleGeneratorHostKeyProvider keys; - - private static synchronized KeyPairProvider getHostKeys() { - if (keys == null) { - keys = new SimpleGeneratorHostKeyProvider(); - keys.setAlgorithm("RSA"); - keys.loadKeys(); - } - return keys; + install(new SshdModule()); } static class CreateDatabase implements LifecycleListener { diff --git a/java/com/google/gerrit/acceptance/SshdModule.java b/java/com/google/gerrit/acceptance/SshdModule.java new file mode 100644 index 0000000000..185d6e2a3d --- /dev/null +++ b/java/com/google/gerrit/acceptance/SshdModule.java @@ -0,0 +1,41 @@ +// Copyright (C) 2018 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.google.gerrit.acceptance; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; + +public class SshdModule extends AbstractModule { + + @Provides + @Singleton + KeyPairProvider createHostKey() { + return getHostKeys(); + } + + private static SimpleGeneratorHostKeyProvider keys; + + private static synchronized KeyPairProvider getHostKeys() { + if (keys == null) { + keys = new SimpleGeneratorHostKeyProvider(); + keys.setAlgorithm("RSA"); + keys.loadKeys(); + } + return keys; + } +} diff --git a/java/com/google/gerrit/acceptance/TestProjectInput.java b/java/com/google/gerrit/acceptance/TestProjectInput.java index eada6434c2..0a3686b543 100644 --- a/java/com/google/gerrit/acceptance/TestProjectInput.java +++ b/java/com/google/gerrit/acceptance/TestProjectInput.java @@ -47,6 +47,10 @@ public @interface TestProjectInput { InheritableBoolean rejectEmptyCommit() default InheritableBoolean.INHERIT; + InheritableBoolean enableSignedPush() default InheritableBoolean.INHERIT; + + InheritableBoolean requireSignedPush() default InheritableBoolean.INHERIT; + // Fields specific to acceptance test behavior. /** Username to use for initial clone, passed to {@link AccountCreator}. */ diff --git a/java/com/google/gerrit/asciidoctor/DocIndexer.java b/java/com/google/gerrit/asciidoctor/DocIndexer.java index ef3ea3f241..951781157a 100644 --- a/java/com/google/gerrit/asciidoctor/DocIndexer.java +++ b/java/com/google/gerrit/asciidoctor/DocIndexer.java @@ -33,8 +33,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.util.CharArraySet; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.StringField; diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java index 2c1c93a1ba..fec7137094 100644 --- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java +++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java @@ -26,6 +26,7 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.flogger.FluentLogger; import com.google.common.io.CharStreams; +import com.google.gerrit.common.Nullable; import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; import com.google.gerrit.elasticsearch.builders.QueryBuilder; import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; @@ -160,7 +161,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { @Override public void delete(K id) throws IOException { String uri = getURI(type, BULK); - Response response = postRequest(getDeleteActions(id), uri, getRefreshParam()); + Response response = postRequest(uri, getDeleteActions(id), getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( @@ -172,10 +173,10 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { public void deleteAll() throws IOException { // Delete the index, if it exists. String endpoint = indexName + client.adapter().indicesExistParam(); - Response response = client.get().performRequest(new Request("HEAD", endpoint)); + Response response = performRequest("HEAD", endpoint); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { - response = client.get().performRequest(new Request("DELETE", indexName)); + response = performRequest("DELETE", indexName); statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( @@ -185,7 +186,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { // Recreate the index. String indexCreationFields = concatJsonString(getSettings(), getMappings()); - response = performRequest("PUT", indexCreationFields, indexName, Collections.emptyMap()); + response = performRequest("PUT", indexName, indexCreationFields); statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { String error = String.format("Failed to create index %s: %s", indexName, statusCode); @@ -297,20 +298,36 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { return encodedIndexName + "/" + encodedType + "/" + request; } - protected Response postRequest(Object payload, String uri, Map<String, String> params) + protected Response postRequest(String uri, Object payload) throws IOException { + return performRequest("POST", uri, payload); + } + + protected Response postRequest(String uri, Object payload, Map<String, String> params) throws IOException { - return performRequest("POST", payload, uri, params); + return performRequest("POST", uri, payload, params); } private String concatJsonString(String target, String addition) { return target.substring(0, target.length() - 1) + "," + addition.substring(1); } + private Response performRequest(String method, String uri) throws IOException { + return performRequest(method, uri, null); + } + + private Response performRequest(String method, String uri, @Nullable Object payload) + throws IOException { + return performRequest(method, uri, payload, Collections.emptyMap()); + } + private Response performRequest( - String method, Object payload, String uri, Map<String, String> params) throws IOException { - Request request = new Request(method, uri); - String payloadStr = payload instanceof String ? (String) payload : payload.toString(); - request.setEntity(new NStringEntity(payloadStr, ContentType.APPLICATION_JSON)); + String method, String uri, @Nullable Object payload, Map<String, String> params) + throws IOException { + Request request = new Request(method, uri.startsWith("/") ? uri : "/" + uri); + if (payload != null) { + String payloadStr = payload instanceof String ? (String) payload : payload.toString(); + request.setEntity(new NStringEntity(payloadStr, ContentType.APPLICATION_JSON)); + } for (Map.Entry<String, String> entry : params.entrySet()) { request.addParameter(entry.getKey(), entry.getValue()); } diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD index 31ede79f16..8d23051517 100644 --- a/java/com/google/gerrit/elasticsearch/BUILD +++ b/java/com/google/gerrit/elasticsearch/BUILD @@ -3,6 +3,7 @@ java_library( srcs = glob(["**/*.java"]), visibility = ["//visibility:public"], deps = [ + "//java/com/google/gerrit/common:annotations", "//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/index", "//java/com/google/gerrit/index:query_exception", diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java index d18af423f5..1b69b6d295 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java @@ -79,7 +79,7 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun .add(new UpdateRequest<>(schema, as)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java index f6af79f228..d7c8b00039 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java @@ -138,7 +138,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData> } String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java index bf6b962304..f694a05482 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java @@ -77,7 +77,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I .add(new UpdateRequest<>(schema, group)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java index 623f62c439..8510559791 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java @@ -77,7 +77,7 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P .add(new UpdateRequest<>(schema, projectState)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java index 8cb69e0179..05fd7a7afc 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java +++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java @@ -33,7 +33,7 @@ public class ElasticQueryAdapter { ElasticQueryAdapter(ElasticVersion version) { this.ignoreUnmapped = version == ElasticVersion.V2_4; this.usePostV5Type = version.isV6(); - this.versionDiscoveryUrl = version.isV6() ? "%s*" : "%s*/_aliases"; + this.versionDiscoveryUrl = version.isV6() ? "/%s*" : "/%s*/_aliases"; switch (version) { case V5_6: diff --git a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java index 337f2ca882..e9839b7978 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java +++ b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java @@ -106,7 +106,7 @@ class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListen private ElasticVersion getVersion() throws ElasticException { try { - Response response = client.performRequest(new Request("GET", "")); + Response response = client.performRequest(new Request("GET", "/")); StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() != HttpStatus.SC_OK) { throw new FailedToGetVersion(statusLine); diff --git a/java/com/google/gerrit/extensions/api/projects/ProjectInput.java b/java/com/google/gerrit/extensions/api/projects/ProjectInput.java index b7079ae1ee..e61d316a0a 100644 --- a/java/com/google/gerrit/extensions/api/projects/ProjectInput.java +++ b/java/com/google/gerrit/extensions/api/projects/ProjectInput.java @@ -34,6 +34,8 @@ public class ProjectInput { public InheritableBoolean requireChangeId; public InheritableBoolean createNewChangeForAllNotInTarget; public InheritableBoolean rejectEmptyCommit; + public InheritableBoolean enableSignedPush; + public InheritableBoolean requireSignedPush; public String maxObjectSizeLimit; public Map<String, Map<String, ConfigValue>> pluginConfigValues; } diff --git a/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java b/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java index 07b5adb000..6229041067 100644 --- a/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java +++ b/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java @@ -30,8 +30,12 @@ public class ProjectSchemaDefinitions extends SchemaDefinitions<ProjectData> { ProjectField.NAME_PART, ProjectField.ANCESTOR_NAME); + @Deprecated static final Schema<ProjectData> V2 = schema(V1, ProjectField.STATE, ProjectField.REF_STATE); + // Bump Lucene version requires reindexing + static final Schema<ProjectData> V3 = schema(V2); + public static final ProjectSchemaDefinitions INSTANCE = new ProjectSchemaDefinitions(); private ProjectSchemaDefinitions() { diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java index dc293cd791..40acf80744 100644 --- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java +++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java @@ -64,15 +64,14 @@ import java.util.function.Function; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.Store; -import org.apache.lucene.document.IntField; -import org.apache.lucene.document.LongField; +import org.apache.lucene.document.LegacyIntField; +import org.apache.lucene.document.LegacyLongField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TrackingIndexWriter; import org.apache.lucene.search.ControlledRealTimeReopenThread; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -86,6 +85,7 @@ import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.Directory; /** Basic Lucene index implementation. */ +@SuppressWarnings("deprecation") public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @@ -98,7 +98,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { private final Directory dir; private final String name; private final ListeningExecutorService writerThread; - private final TrackingIndexWriter writer; + private final IndexWriter writer; private final ReferenceManager<IndexSearcher> searcherManager; private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread; private final Set<NrtFuture> notDoneNrtFutures; @@ -118,17 +118,16 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { this.dir = dir; this.name = name; String index = Joiner.on('_').skipNulls().join(name, subIndex); - IndexWriter delegateWriter; long commitPeriod = writerConfig.getCommitWithinMs(); if (commitPeriod < 0) { - delegateWriter = new AutoCommitWriter(dir, writerConfig.getLuceneConfig()); + writer = new AutoCommitWriter(dir, writerConfig.getLuceneConfig()); } else if (commitPeriod == 0) { - delegateWriter = new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), true); + writer = new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), true); } else { final AutoCommitWriter autoCommitWriter = new AutoCommitWriter(dir, writerConfig.getLuceneConfig()); - delegateWriter = autoCommitWriter; + writer = autoCommitWriter; autoCommitExecutor = new LoggingContextAwareScheduledExecutorService( @@ -165,8 +164,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { commitPeriod, MILLISECONDS); } - writer = new TrackingIndexWriter(delegateWriter); - searcherManager = new WrappableSearcherManager(writer.getIndexWriter(), true, searcherFactory); + searcherManager = new WrappableSearcherManager(writer, true, searcherFactory); notDoneNrtFutures = Sets.newConcurrentHashSet(); @@ -251,7 +249,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { } try { - writer.getIndexWriter().close(); + writer.close(); } catch (AlreadyClosedException e) { // Ignore. } catch (IOException e) { @@ -294,7 +292,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { writer.deleteAll(); } - public TrackingIndexWriter getWriter() { + public IndexWriter getWriter() { return writer; } @@ -325,15 +323,15 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> { if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) { for (Object value : values.getValues()) { - doc.add(new IntField(name, (Integer) value, store)); + doc.add(new LegacyIntField(name, (Integer) value, store)); } } else if (type == FieldType.LONG) { for (Object value : values.getValues()) { - doc.add(new LongField(name, (Long) value, store)); + doc.add(new LegacyLongField(name, (Long) value, store)); } } else if (type == FieldType.TIMESTAMP) { for (Object value : values.getValues()) { - doc.add(new LongField(name, ((Timestamp) value).getTime(), store)); + doc.add(new LegacyLongField(name, ((Timestamp) value).getTime(), store)); } } else if (type == FieldType.EXACT || type == FieldType.PREFIX) { for (Object value : values.getValues()) { diff --git a/java/com/google/gerrit/lucene/AutoCommitWriter.java b/java/com/google/gerrit/lucene/AutoCommitWriter.java index 7a418aa8ca..2cc7563130 100644 --- a/java/com/google/gerrit/lucene/AutoCommitWriter.java +++ b/java/com/google/gerrit/lucene/AutoCommitWriter.java @@ -47,58 +47,64 @@ public class AutoCommitWriter extends IndexWriter { } @Override - public void addDocument(Iterable<? extends IndexableField> doc) throws IOException { - super.addDocument(doc); + public long addDocument(Iterable<? extends IndexableField> doc) throws IOException { + long ret = super.addDocument(doc); autoFlush(); + return ret; } @Override - public void addDocuments(Iterable<? extends Iterable<? extends IndexableField>> docs) + public long addDocuments(Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException { - super.addDocuments(docs); + long ret = super.addDocuments(docs); autoFlush(); + return ret; } @Override - public void updateDocuments( + public long updateDocuments( Term delTerm, Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException { - super.updateDocuments(delTerm, docs); + long ret = super.updateDocuments(delTerm, docs); autoFlush(); + return ret; } @Override - public void deleteDocuments(Term... term) throws IOException { - super.deleteDocuments(term); + public long deleteDocuments(Term... term) throws IOException { + long ret = super.deleteDocuments(term); autoFlush(); + return ret; } @Override - public synchronized boolean tryDeleteDocument(IndexReader readerIn, int docID) - throws IOException { - boolean ret = super.tryDeleteDocument(readerIn, docID); - if (ret) { + public synchronized long tryDeleteDocument(IndexReader readerIn, int docID) throws IOException { + long ret = super.tryDeleteDocument(readerIn, docID); + if (ret != -1) { autoFlush(); } return ret; } @Override - public void deleteDocuments(Query... queries) throws IOException { - super.deleteDocuments(queries); + public long deleteDocuments(Query... queries) throws IOException { + long ret = super.deleteDocuments(queries); autoFlush(); + return ret; } @Override - public void updateDocument(Term term, Iterable<? extends IndexableField> doc) throws IOException { - super.updateDocument(term, doc); + public long updateDocument(Term term, Iterable<? extends IndexableField> doc) throws IOException { + long ret = super.updateDocument(term, doc); autoFlush(); + return ret; } @Override - public void deleteAll() throws IOException { - super.deleteAll(); + public long deleteAll() throws IOException { + long ret = super.deleteAll(); autoFlush(); + return ret; } void manualFlush() throws IOException { diff --git a/java/com/google/gerrit/lucene/GerritIndexWriterConfig.java b/java/com/google/gerrit/lucene/GerritIndexWriterConfig.java index ada3220d49..75e03e32b5 100644 --- a/java/com/google/gerrit/lucene/GerritIndexWriterConfig.java +++ b/java/com/google/gerrit/lucene/GerritIndexWriterConfig.java @@ -19,8 +19,8 @@ import static java.util.concurrent.TimeUnit.MINUTES; import com.google.common.collect.ImmutableMap; import com.google.gerrit.server.config.ConfigUtil; +import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.util.CharArraySet; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.eclipse.jgit.lib.Config; diff --git a/java/com/google/gerrit/lucene/QueryBuilder.java b/java/com/google/gerrit/lucene/QueryBuilder.java index 6aab7c74b7..ce5ba9859a 100644 --- a/java/com/google/gerrit/lucene/QueryBuilder.java +++ b/java/com/google/gerrit/lucene/QueryBuilder.java @@ -38,19 +38,20 @@ import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.LegacyNumericRangeQuery; import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRefBuilder; -import org.apache.lucene.util.NumericUtils; +import org.apache.lucene.util.LegacyNumericUtils; +@SuppressWarnings("deprecation") public class QueryBuilder<V> { static Term intTerm(String name, int value) { BytesRefBuilder builder = new BytesRefBuilder(); - NumericUtils.intToPrefixCoded(value, 0, builder); + LegacyNumericUtils.intToPrefixCoded(value, 0, builder); return new Term(name, builder.get()); } @@ -180,7 +181,8 @@ public class QueryBuilder<V> { // Just fall back to a standard integer query. return new TermQuery(intTerm(p.getField().getName(), minimum)); } - return NumericRangeQuery.newIntRange(r.getField().getName(), minimum, maximum, true, true); + return LegacyNumericRangeQuery.newIntRange( + r.getField().getName(), minimum, maximum, true, true); } throw new QueryParseException("not an integer range: " + p); } @@ -188,7 +190,7 @@ public class QueryBuilder<V> { private Query timestampQuery(IndexPredicate<V> p) throws QueryParseException { if (p instanceof TimestampRangePredicate) { TimestampRangePredicate<V> r = (TimestampRangePredicate<V>) p; - return NumericRangeQuery.newLongRange( + return LegacyNumericRangeQuery.newLongRange( r.getField().getName(), r.getMinTimestamp().getTime(), r.getMaxTimestamp().getTime(), @@ -200,7 +202,7 @@ public class QueryBuilder<V> { private Query notTimestamp(TimestampRangePredicate<V> r) throws QueryParseException { if (r.getMinTimestamp().getTime() == 0) { - return NumericRangeQuery.newLongRange( + return LegacyNumericRangeQuery.newLongRange( r.getField().getName(), r.getMaxTimestamp().getTime(), null, true, true); } throw new QueryParseException("cannot negate: " + r); diff --git a/java/com/google/gerrit/lucene/WrappableSearcherManager.java b/java/com/google/gerrit/lucene/WrappableSearcherManager.java index f9ecac38cb..ba8d7dab11 100644 --- a/java/com/google/gerrit/lucene/WrappableSearcherManager.java +++ b/java/com/google/gerrit/lucene/WrappableSearcherManager.java @@ -81,11 +81,17 @@ final class WrappableSearcherManager extends ReferenceManager<IndexSearcher> { WrappableSearcherManager( IndexWriter writer, boolean applyAllDeletes, SearcherFactory searcherFactory) throws IOException { + // TODO(davido): Make it configurable + // If true, new deletes will be written down to index files instead of carried over from writer + // to reader directly in heap + boolean writeAllDeletes = false; if (searcherFactory == null) { searcherFactory = new SearcherFactory(); } this.searcherFactory = searcherFactory; - current = getSearcher(searcherFactory, DirectoryReader.open(writer, applyAllDeletes)); + current = + getSearcher( + searcherFactory, DirectoryReader.open(writer, applyAllDeletes, writeAllDeletes)); } /** diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java index fa6cd6c61a..29974e9941 100644 --- a/java/com/google/gerrit/server/StarredChangesUtil.java +++ b/java/com/google/gerrit/server/StarredChangesUtil.java @@ -42,6 +42,8 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.index.change.ChangeField; import com.google.gerrit.server.index.change.ChangeIndexer; +import com.google.gerrit.server.logging.TraceContext; +import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.InternalChangeQuery; @@ -376,20 +378,20 @@ public class StarredChangesUtil { } public static StarRef readLabels(Repository repo, String refName) throws IOException { - logger.atFine().log("Read star labels from %s", refName); - - Ref ref = repo.exactRef(refName); - if (ref == null) { - return StarRef.MISSING; - } + try (TraceTimer traceTimer = TraceContext.newTimer("Read star labels from %s", refName)) { + Ref ref = repo.exactRef(refName); + if (ref == null) { + return StarRef.MISSING; + } - try (ObjectReader reader = repo.newObjectReader()) { - ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB); - return StarRef.create( - ref, - Splitter.on(CharMatcher.whitespace()) - .omitEmptyStrings() - .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8))); + try (ObjectReader reader = repo.newObjectReader()) { + ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB); + return StarRef.create( + ref, + Splitter.on(CharMatcher.whitespace()) + .omitEmptyStrings() + .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8))); + } } } @@ -450,8 +452,9 @@ public class StarredChangesUtil { private void updateLabels( Repository repo, String refName, ObjectId oldObjectId, Collection<String> labels) throws IOException, OrmException, InvalidLabelsException { - logger.atFine().log("Update star labels in %s (labels=%s)", refName, labels); - try (RevWalk rw = new RevWalk(repo)) { + try (TraceTimer traceTimer = + TraceContext.newTimer("Update star labels in %s (labels=%s)", refName, labels); + RevWalk rw = new RevWalk(repo)) { RefUpdate u = repo.updateRef(refName); u.setExpectedOldObjectId(oldObjectId); u.setForceUpdate(true); @@ -488,31 +491,32 @@ public class StarredChangesUtil { return; } - logger.atFine().log("Delete star labels in %s", refName); - RefUpdate u = repo.updateRef(refName); - u.setForceUpdate(true); - u.setExpectedOldObjectId(oldObjectId); - u.setRefLogIdent(serverIdent.get()); - u.setRefLogMessage("Unstar change", true); - RefUpdate.Result result = u.delete(); - switch (result) { - case FORCED: - gitRefUpdated.fire(allUsers, u, null); - return; - case NEW: - case NO_CHANGE: - case FAST_FORWARD: - case IO_FAILURE: - case LOCK_FAILURE: - case NOT_ATTEMPTED: - case REJECTED: - case REJECTED_CURRENT_BRANCH: - case RENAMED: - case REJECTED_MISSING_OBJECT: - case REJECTED_OTHER_REASON: - default: - throw new OrmException( - String.format("Delete star ref %s failed: %s", refName, result.name())); + try (TraceTimer traceTimer = TraceContext.newTimer("Delete star labels in %s", refName)) { + RefUpdate u = repo.updateRef(refName); + u.setForceUpdate(true); + u.setExpectedOldObjectId(oldObjectId); + u.setRefLogIdent(serverIdent.get()); + u.setRefLogMessage("Unstar change", true); + RefUpdate.Result result = u.delete(); + switch (result) { + case FORCED: + gitRefUpdated.fire(allUsers, u, null); + return; + case NEW: + case NO_CHANGE: + case FAST_FORWARD: + case IO_FAILURE: + case LOCK_FAILURE: + case NOT_ATTEMPTED: + case REJECTED: + case REJECTED_CURRENT_BRANCH: + case RENAMED: + case REJECTED_MISSING_OBJECT: + case REJECTED_OTHER_REASON: + default: + throw new OrmException( + String.format("Delete star ref %s failed: %s", refName, result.name())); + } } } } diff --git a/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java b/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java index 9bd45330aa..66d65552c8 100644 --- a/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java +++ b/java/com/google/gerrit/server/config/ConfigUpdatedEvent.java @@ -66,6 +66,10 @@ public class ConfigUpdatedEvent { return createUpdate(entries, UpdateResult.APPLIED); } + public Update reject(ConfigKey entry) { + return reject(Collections.singleton(entry)); + } + public Update reject(Set<ConfigKey> entries) { return createUpdate(entries, UpdateResult.REJECTED); } diff --git a/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java b/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java index c606919d29..66cb3804a4 100644 --- a/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java +++ b/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java @@ -15,12 +15,12 @@ package com.google.gerrit.server.documentation; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import com.google.common.flogger.FluentLogger; import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; @@ -85,9 +85,9 @@ public class QueryDocumentationExecutor { // and skipped paging. Maybe add paging later. TopDocs results = searcher.search(query, Integer.MAX_VALUE); ScoreDoc[] hits = results.scoreDocs; - int totalHits = results.totalHits; + long totalHits = results.totalHits; - List<DocResult> out = Lists.newArrayListWithCapacity(totalHits); + List<DocResult> out = new ArrayList<>(); for (int i = 0; i < totalHits; i++) { DocResult result = new DocResult(); Document doc = searcher.doc(hits[i].doc); diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java index eb62d547aa..b9ab6fda46 100644 --- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java +++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java @@ -110,14 +110,9 @@ public class AsyncReceiveCommits implements PreReceiveHook { final MultiProgressMonitor progress; private final Collection<ReceiveCommand> commands; - private final ReceiveCommits receiveCommits; private Worker(Collection<ReceiveCommand> commands) { this.commands = commands; - receiveCommits = - factory.create( - projectState, user, receivePack, allRefsWatcher, extraReviewers, messageSender); - receiveCommits.init(); progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes"); } @@ -173,7 +168,7 @@ public class AsyncReceiveCommits implements PreReceiveHook { } } - private final ReceiveCommits.Factory factory; + private final ReceiveCommits receiveCommits; private final PermissionBackend.ForProject perm; private final ReceivePack receivePack; private final ExecutorService executor; @@ -184,8 +179,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { private final ProjectState projectState; private final IdentifiedUser user; private final Repository repo; - private final MessageSender messageSender; - private final SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers; private final AllRefsWatcher allRefsWatcher; @Inject @@ -206,7 +199,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { @Assisted @Nullable MessageSender messageSender, @Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers) throws PermissionBackendException { - this.factory = factory; this.executor = executor; this.scopePropagator = scopePropagator; this.receiveConfig = receiveConfig; @@ -215,8 +207,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { this.projectState = projectState; this.user = user; this.repo = repo; - this.messageSender = messageSender; - this.extraReviewers = extraReviewers; Project.NameKey projectName = projectState.getNameKey(); receivePack = new ReceivePack(repo); @@ -251,6 +241,11 @@ public class AsyncReceiveCommits implements PreReceiveHook { advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName)); advHooks.add(new HackPushNegotiateHook()); receivePack.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks)); + + receiveCommits = + factory.create( + projectState, user, receivePack, allRefsWatcher, extraReviewers, messageSender); + receiveCommits.init(); } /** Determine if the user can upload commits. */ @@ -275,6 +270,11 @@ public class AsyncReceiveCommits implements PreReceiveHook { @Override public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { + if (commands.stream().anyMatch(c -> c.getResult() != Result.NOT_ATTEMPTED)) { + // Stop processing when command was already processed by previously invoked + // pre-receive hooks + return; + } Worker w = new Worker(commands); try { w.progress.waitFor( diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java index ee751d7541..7566b5593c 100644 --- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java +++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java @@ -1325,6 +1325,7 @@ class ReceiveCommits { static class MagicBranchInput { private static final Splitter COMMAS = Splitter.on(',').omitEmptyStrings(); + boolean deprecatedTopicSeen; final ReceiveCommand cmd; final LabelTypes labelTypes; final NotesMigration notesMigration; @@ -1482,6 +1483,7 @@ class ReceiveCommits { ReceiveCommand cmd, LabelTypes labelTypes, NotesMigration notesMigration) { + this.deprecatedTopicSeen = false; this.cmd = cmd; this.draft = cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE); this.publish = cmd.getRefName().startsWith(MagicBranch.NEW_PUBLISH_CHANGE); @@ -1545,8 +1547,7 @@ class ReceiveCommits { // We accept refs/for/BRANCHNAME/TOPIC. Since we don't know // for sure where the branch ends and the topic starts, look - // backward for a split that works. This behavior has not been - // documented and should probably be deprecated. + // backward for a split that works. This behavior is deprecated. String head = readHEAD(repo); int split = ref.length(); for (; ; ) { @@ -1562,6 +1563,7 @@ class ReceiveCommits { } if (split < ref.length()) { topic = Strings.emptyToNull(ref.substring(split + 1)); + deprecatedTopicSeen = true; } return ref.substring(0, split); } @@ -1767,6 +1769,13 @@ class ReceiveCommits { return; } + if (magicBranch.deprecatedTopicSeen) { + messages.add( + new ValidationMessage( + "WARNING: deprecated topic syntax. Use %topic=TOPIC instead", false)); + logger.atInfo().log("deprecated topic push seen for project %s", project.getName()); + } + if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) { this.magicBranch = magicBranch; } diff --git a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java index 3e702f2bfd..6b7fe62322 100644 --- a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java +++ b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java @@ -42,8 +42,12 @@ public class AccountSchemaDefinitions extends SchemaDefinitions<AccountState> { @Deprecated static final Schema<AccountState> V7 = schema(V6, AccountField.PREFERRED_EMAIL_EXACT); + @Deprecated static final Schema<AccountState> V8 = schema(V7, AccountField.NAME_PART_NO_SECONDARY_EMAIL); + // Bump Lucene version requires reindexing + static final Schema<AccountState> V9 = schema(V8); + public static final String NAME = "accounts"; public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions(); diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java index 5e7e4ddc6f..2000cd176d 100644 --- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java +++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java @@ -96,7 +96,10 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> { // Rename of star label 'mute' to 'reviewed' requires reindexing @Deprecated static final Schema<ChangeData> V48 = schema(V47); - static final Schema<ChangeData> V49 = schema(V48); + @Deprecated static final Schema<ChangeData> V49 = schema(V48); + + // Bump Lucene version requires reindexing + static final Schema<ChangeData> V50 = schema(V49); public static final String NAME = "changes"; public static final ChangeSchemaDefinitions INSTANCE = new ChangeSchemaDefinitions(); diff --git a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java index 912524fe3c..c175434e60 100644 --- a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java +++ b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java @@ -37,7 +37,10 @@ public class GroupSchemaDefinitions extends SchemaDefinitions<InternalGroup> { @Deprecated static final Schema<InternalGroup> V4 = schema(V3, GroupField.MEMBER, GroupField.SUBGROUP); - static final Schema<InternalGroup> V5 = schema(V4, GroupField.REF_STATE); + @Deprecated static final Schema<InternalGroup> V5 = schema(V4, GroupField.REF_STATE); + + // Bump Lucene version requires reindexing + static final Schema<InternalGroup> V6 = schema(V5); public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions(); diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java index 977baa585d..d68874039b 100644 --- a/java/com/google/gerrit/server/logging/TraceContext.java +++ b/java/com/google/gerrit/server/logging/TraceContext.java @@ -192,6 +192,21 @@ public class TraceContext implements AutoCloseable { return new TraceTimer(format, arg1, arg2); } + /** + * Opens a new timer that logs the time for an operation if request tracing is enabled. + * + * <p>If request tracing is not enabled this is a no-op. + * + * @param format the message format string + * @param arg1 first argument for the message + * @param arg2 second argument for the message + * @param arg3 third argument for the message + * @return the trace timer + */ + public static TraceTimer newTimer(String format, Object arg1, Object arg2, Object arg3) { + return new TraceTimer(format, arg1, arg2, arg3); + } + public static class TraceTimer implements AutoCloseable { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @@ -210,6 +225,11 @@ public class TraceContext implements AutoCloseable { this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, elapsedMs)); } + private TraceTimer( + String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) { + this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, elapsedMs)); + } + private TraceTimer(Consumer<Long> logFn) { this.logFn = logFn; this.stopwatch = Stopwatch.createStarted(); diff --git a/java/com/google/gerrit/server/permissions/PermissionCollection.java b/java/com/google/gerrit/server/permissions/PermissionCollection.java index 81e8d24250..b419698415 100644 --- a/java/com/google/gerrit/server/permissions/PermissionCollection.java +++ b/java/com/google/gerrit/server/permissions/PermissionCollection.java @@ -26,6 +26,10 @@ import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule.Action; +import com.google.gerrit.metrics.Description; +import com.google.gerrit.metrics.Description.Units; +import com.google.gerrit.metrics.MetricMaker; +import com.google.gerrit.metrics.Timer0; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; @@ -54,10 +58,18 @@ public class PermissionCollection { @Singleton public static class Factory { private final SectionSortCache sorter; + // TODO(hiesel): Remove this once we got production data + private final Timer0 filterLatency; @Inject - Factory(SectionSortCache sorter) { + Factory(SectionSortCache sorter, MetricMaker metricMaker) { this.sorter = sorter; + this.filterLatency = + metricMaker.newTimer( + "permissions/permission_collection/filter_latency", + new Description("Latency for access filter computations in PermissionCollection") + .setCumulative() + .setUnit(Units.NANOSECONDS)); } /** @@ -117,41 +129,43 @@ public class PermissionCollection { */ PermissionCollection filter( Iterable<SectionMatcher> matcherList, String ref, CurrentUser user) { - if (isRE(ref)) { - ref = RefPattern.shortestExample(ref); - } else if (ref.endsWith("/*")) { - ref = ref.substring(0, ref.length() - 1); - } + try (Timer0.Context ignored = filterLatency.start()) { + if (isRE(ref)) { + ref = RefPattern.shortestExample(ref); + } else if (ref.endsWith("/*")) { + ref = ref.substring(0, ref.length() - 1); + } - // LinkedHashMap to maintain input ordering. - Map<AccessSection, Project.NameKey> sectionToProject = new LinkedHashMap<>(); - boolean perUser = filterRefMatchingSections(matcherList, ref, user, sectionToProject); - List<AccessSection> sections = Lists.newArrayList(sectionToProject.keySet()); + // LinkedHashMap to maintain input ordering. + Map<AccessSection, Project.NameKey> sectionToProject = new LinkedHashMap<>(); + boolean perUser = filterRefMatchingSections(matcherList, ref, user, sectionToProject); + List<AccessSection> sections = Lists.newArrayList(sectionToProject.keySet()); - // Sort by ref pattern specificity. For equally specific patterns, the sections from the - // project closer to the current one come first. - sorter.sort(ref, sections); + // Sort by ref pattern specificity. For equally specific patterns, the sections from the + // project closer to the current one come first. + sorter.sort(ref, sections); - // For block permissions, we want a different order: first, we want to go from parent to - // child. - List<Map.Entry<AccessSection, Project.NameKey>> accessDescending = - Lists.reverse(Lists.newArrayList(sectionToProject.entrySet())); + // For block permissions, we want a different order: first, we want to go from parent to + // child. + List<Map.Entry<AccessSection, Project.NameKey>> accessDescending = + Lists.reverse(Lists.newArrayList(sectionToProject.entrySet())); - Map<Project.NameKey, List<AccessSection>> accessByProject = - accessDescending - .stream() - .collect( - Collectors.groupingBy( - Map.Entry::getValue, - LinkedHashMap::new, - mapping(Map.Entry::getKey, toList()))); - // Within each project, sort by ref specificity. - for (List<AccessSection> secs : accessByProject.values()) { - sorter.sort(ref, secs); - } + Map<Project.NameKey, List<AccessSection>> accessByProject = + accessDescending + .stream() + .collect( + Collectors.groupingBy( + Map.Entry::getValue, + LinkedHashMap::new, + mapping(Map.Entry::getKey, toList()))); + // Within each project, sort by ref specificity. + for (List<AccessSection> secs : accessByProject.values()) { + sorter.sort(ref, secs); + } - return new PermissionCollection( - Lists.newArrayList(accessByProject.values()), sections, perUser); + return new PermissionCollection( + Lists.newArrayList(accessByProject.values()), sections, perUser); + } } } diff --git a/java/com/google/gerrit/server/project/CreateProjectArgs.java b/java/com/google/gerrit/server/project/CreateProjectArgs.java index e4623b227f..a68bd84860 100644 --- a/java/com/google/gerrit/server/project/CreateProjectArgs.java +++ b/java/com/google/gerrit/server/project/CreateProjectArgs.java @@ -35,6 +35,8 @@ public class CreateProjectArgs { public InheritableBoolean newChangeForAllNotInTarget; public InheritableBoolean changeIdRequired; public InheritableBoolean rejectEmptyCommit; + public InheritableBoolean enableSignedPush; + public InheritableBoolean requireSignedPush; public boolean createEmptyCommit; public String maxObjectSizeLimit; @@ -44,6 +46,8 @@ public class CreateProjectArgs { contentMerge = InheritableBoolean.INHERIT; changeIdRequired = InheritableBoolean.INHERIT; newChangeForAllNotInTarget = InheritableBoolean.INHERIT; + enableSignedPush = InheritableBoolean.INHERIT; + requireSignedPush = InheritableBoolean.INHERIT; submitType = SubmitType.MERGE_IF_NECESSARY; } diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java index a9b19d9f19..8355625550 100644 --- a/java/com/google/gerrit/server/project/ProjectState.java +++ b/java/com/google/gerrit/server/project/ProjectState.java @@ -36,6 +36,11 @@ import com.google.gerrit.extensions.api.projects.ThemeInfo; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.index.project.ProjectData; +import com.google.gerrit.metrics.Description; +import com.google.gerrit.metrics.Description.Units; +import com.google.gerrit.metrics.Field; +import com.google.gerrit.metrics.MetricMaker; +import com.google.gerrit.metrics.Timer1; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.BooleanProjectConfig; import com.google.gerrit.reviewdb.client.Branch; @@ -92,6 +97,9 @@ public class ProjectState { private final long globalMaxObjectSizeLimit; private final boolean inheritProjectMaxObjectSizeLimit; + // TODO(hiesel): Remove this once we got production data + private final Timer1<String> computationLatency; + /** Last system time the configuration's revision was examined. */ private volatile long lastCheckGeneration; @@ -119,6 +127,7 @@ public class ProjectState { List<CommentLinkInfo> commentLinks, CapabilityCollection.Factory limitsFactory, TransferConfig transferConfig, + MetricMaker metricMaker, @Assisted ProjectConfig config) { this.sitePaths = sitePaths; this.projectCache = projectCache; @@ -136,6 +145,14 @@ public class ProjectState { this.globalMaxObjectSizeLimit = transferConfig.getMaxObjectSizeLimit(); this.inheritProjectMaxObjectSizeLimit = transferConfig.getInheritProjectMaxObjectSizeLimit(); + this.computationLatency = + metricMaker.newTimer( + "permissions/project_state/computation_latency", + new Description("Latency for access computations in ProjectState") + .setCumulative() + .setUnit(Units.NANOSECONDS), + Field.ofString("method")); + if (isAllProjects && !Permission.canBeOnAllProjects(AccessSection.ALL, Permission.OWNER)) { localOwners = Collections.emptySet(); } else { @@ -354,15 +371,20 @@ public class ProjectState { * cached. Callers should try to cache this result per-request as much as possible. */ public List<SectionMatcher> getAllSections() { - if (isAllProjects) { - return getLocalAccessSections(); - } + try (Timer1.Context ignored = computationLatency.start("getAllSections")) { + if (isAllProjects) { + return getLocalAccessSections(); + } - List<SectionMatcher> all = new ArrayList<>(); - for (ProjectState s : tree()) { - all.addAll(s.getLocalAccessSections()); + List<SectionMatcher> all = new ArrayList<>(); + Iterable<ProjectState> tree = tree(); + try (Timer1.Context ignored2 = computationLatency.start("getAllSections-parsing-only")) { + for (ProjectState s : tree) { + all.addAll(s.getLocalAccessSections()); + } + } + return all; } - return all; } /** diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index 566786984b..3db72ef43a 100644 --- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java @@ -673,6 +673,21 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> { } @Operator + public Predicate<ChangeData> repo(String name) { + return project(name); + } + + @Operator + public Predicate<ChangeData> repos(String name) { + return projects(name); + } + + @Operator + public Predicate<ChangeData> parentrepo(String name) { + return parentproject(name); + } + + @Operator public Predicate<ChangeData> branch(String name) { if (name.startsWith("^")) { return ref("^" + RefNames.fullName(name.substring(1))); diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java index 66e9f90cd3..d6071d5e93 100644 --- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java +++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java @@ -157,9 +157,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> { info.gerrit = getGerritInfo(); info.noteDbEnabled = toBoolean(isNoteDbEnabled()); info.plugin = getPluginInfo(); - if (Files.exists(sitePaths.site_theme)) { - info.defaultTheme = "/static/" + SitePaths.THEME_FILENAME; - } + info.defaultTheme = getDefaultTheme(); info.sshd = getSshdInfo(); info.suggest = getSuggestInfo(); @@ -342,6 +340,22 @@ public class GetServerInfo implements RestReadView<ConfigResource> { return info; } + private static final String DEFAULT_THEME = "/static/" + SitePaths.THEME_FILENAME; + + private String getDefaultTheme() { + if (config.getString("theme", null, "enableDefault") == null) { + // If not explicitly enabled or disabled, check for the existence of the theme file. + return Files.exists(sitePaths.site_theme) ? DEFAULT_THEME : null; + } + if (config.getBoolean("theme", null, "enableDefault", true)) { + // Return non-null theme path without checking for file existence. Even if the file doesn't + // exist under the site path, it may be served from a CDN (in which case it's up to the admin + // to also pass a proper asset path to the index Soy template). + return DEFAULT_THEME; + } + return null; + } + private Map<String, String> getUrlAliasesInfo() { Map<String, String> urlAliases = new HashMap<>(); for (String subsection : config.getSubsections(URL_ALIAS)) { diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java index 271848b9de..030402e18d 100644 --- a/java/com/google/gerrit/server/restapi/project/CreateProject.java +++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java @@ -203,6 +203,10 @@ public class CreateProject MoreObjects.firstNonNull(input.requireChangeId, InheritableBoolean.INHERIT); args.rejectEmptyCommit = MoreObjects.firstNonNull(input.rejectEmptyCommit, InheritableBoolean.INHERIT); + args.enableSignedPush = + MoreObjects.firstNonNull(input.enableSignedPush, InheritableBoolean.INHERIT); + args.requireSignedPush = + MoreObjects.firstNonNull(input.requireSignedPush, InheritableBoolean.INHERIT); try { args.maxObjectSizeLimit = ProjectConfig.validMaxObjectSizeLimit(input.maxObjectSizeLimit); } catch (ConfigInvalidException e) { @@ -297,6 +301,8 @@ public class CreateProject newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, args.changeIdRequired); newProject.setBooleanConfig(BooleanProjectConfig.REJECT_EMPTY_COMMIT, args.rejectEmptyCommit); newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit); + newProject.setBooleanConfig(BooleanProjectConfig.ENABLE_SIGNED_PUSH, args.enableSignedPush); + newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_SIGNED_PUSH, args.requireSignedPush); if (args.newParent != null) { newProject.setParentName(args.newParent); } diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java index 04977877dd..8c8ab495f4 100644 --- a/java/com/google/gerrit/server/restapi/project/Module.java +++ b/java/com/google/gerrit/server/restapi/project/Module.java @@ -23,7 +23,9 @@ import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND; import static com.google.gerrit.server.project.TagResource.TAG_KIND; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.RestApiModule; +import com.google.gerrit.server.config.GerritConfigListener; import com.google.gerrit.server.project.RefValidationHelper; import com.google.gerrit.server.restapi.change.CherryPickCommit; @@ -41,6 +43,8 @@ public class Module extends RestApiModule { DynamicMap.mapOf(binder(), COMMIT_KIND); DynamicMap.mapOf(binder(), TAG_KIND); + DynamicSet.bind(binder(), GerritConfigListener.class).to(SetParent.class); + create(PROJECT_KIND).to(CreateProject.class); put(PROJECT_KIND).to(PutProject.class); get(PROJECT_KIND).to(GetProject.class); diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java index 8e1ba6bf34..6abf102d65 100644 --- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java +++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java @@ -15,6 +15,7 @@ package com.google.gerrit.server.restapi.project; import com.google.common.collect.ListMultimap; +import com.google.common.flogger.FluentLogger; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AuthException; @@ -46,6 +47,8 @@ import org.eclipse.jgit.lib.Constants; @Singleton public class ProjectsCollection implements RestCollection<TopLevelResource, ProjectResource>, NeedsParams { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private final DynamicMap<RestView<ProjectResource>> views; private final Provider<ListProjects> list; private final Provider<QueryProjects> queryProjects; @@ -143,6 +146,8 @@ public class ProjectsCollection return null; } + logger.atFine().log("Project %s has state %s", nameKey, state.getProject().getState()); + if (checkAccess) { // Hidden projects(permitsRead = false) should only be accessible by the project owners. // WRITE_CONFIG is checked here because it's only allowed to project owners (ACCESS may also diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java index b38619beb4..d4e67b1f7e 100644 --- a/java/com/google/gerrit/server/restapi/project/SetParent.java +++ b/java/com/google/gerrit/server/restapi/project/SetParent.java @@ -30,6 +30,9 @@ import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.config.ConfigKey; +import com.google.gerrit.server.config.ConfigUpdatedEvent; +import com.google.gerrit.server.config.GerritConfigListener; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.git.meta.MetaDataUpdate; import com.google.gerrit.server.permissions.GlobalPermission; @@ -43,18 +46,21 @@ import com.google.gerrit.server.project.ProjectState; import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.IOException; +import java.util.Collections; +import java.util.List; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Config; @Singleton -public class SetParent implements RestModifyView<ProjectResource, ParentInput> { +public class SetParent + implements RestModifyView<ProjectResource, ParentInput>, GerritConfigListener { private final ProjectCache cache; private final PermissionBackend permissionBackend; private final MetaDataUpdate.Server updateFactory; private final AllProjectsName allProjects; private final AllUsersName allUsers; - private final boolean allowProjectOwnersToChangeParent; + private volatile boolean allowProjectOwnersToChangeParent; @Inject SetParent( @@ -164,4 +170,20 @@ public class SetParent implements RestModifyView<ProjectResource, ParentInput> { } } } + + @Override + public List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event) { + ConfigKey receiveSetParent = ConfigKey.create("receive", "allowProjectOwnersToChangeParent"); + if (!event.isValueUpdated(receiveSetParent)) { + return Collections.emptyList(); + } + try { + boolean enabled = + event.getNewConfig().getBoolean("receive", "allowProjectOwnersToChangeParent", false); + this.allowProjectOwnersToChangeParent = enabled; + return Collections.singletonList(event.accept(receiveSetParent)); + } catch (IllegalArgumentException iae) { + return Collections.singletonList(event.reject(receiveSetParent)); + } + } } diff --git a/java/com/google/gerrit/sshd/AliasCommand.java b/java/com/google/gerrit/sshd/AliasCommand.java index cb61651475..567cf00735 100644 --- a/java/com/google/gerrit/sshd/AliasCommand.java +++ b/java/com/google/gerrit/sshd/AliasCommand.java @@ -26,8 +26,8 @@ import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; +import org.apache.sshd.server.command.Command; /** Command that executes some other command. */ public class AliasCommand extends BaseCommand { diff --git a/java/com/google/gerrit/sshd/AliasCommandProvider.java b/java/com/google/gerrit/sshd/AliasCommandProvider.java index 085b6d6aaa..58e255920a 100644 --- a/java/com/google/gerrit/sshd/AliasCommandProvider.java +++ b/java/com/google/gerrit/sshd/AliasCommandProvider.java @@ -17,7 +17,7 @@ package com.google.gerrit.sshd; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.inject.Inject; import com.google.inject.Provider; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; /** Resolves an alias to another command. */ public class AliasCommandProvider implements Provider<Command> { diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java index 942a571325..1d19a8a3ff 100644 --- a/java/com/google/gerrit/sshd/BaseCommand.java +++ b/java/com/google/gerrit/sshd/BaseCommand.java @@ -54,9 +54,9 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.atomic.AtomicReference; import org.apache.sshd.common.SshException; -import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; +import org.apache.sshd.server.command.Command; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.Option; diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java index 1fdf7d8203..3fb2ed411b 100644 --- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java +++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java @@ -39,11 +39,11 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import org.apache.sshd.server.Command; -import org.apache.sshd.server.CommandFactory; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; +import org.apache.sshd.server.command.Command; +import org.apache.sshd.server.command.CommandFactory; import org.apache.sshd.server.session.ServerSession; import org.eclipse.jgit.lib.Config; diff --git a/java/com/google/gerrit/sshd/CommandModule.java b/java/com/google/gerrit/sshd/CommandModule.java index 93aab0bb45..ac0705679e 100644 --- a/java/com/google/gerrit/sshd/CommandModule.java +++ b/java/com/google/gerrit/sshd/CommandModule.java @@ -16,7 +16,7 @@ package com.google.gerrit.sshd; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.inject.binder.LinkedBindingBuilder; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; /** Module to register commands in the SSH daemon. */ public abstract class CommandModule extends LifecycleModule { diff --git a/java/com/google/gerrit/sshd/CommandProvider.java b/java/com/google/gerrit/sshd/CommandProvider.java index 61c36cbe53..cf2e84c44b 100644 --- a/java/com/google/gerrit/sshd/CommandProvider.java +++ b/java/com/google/gerrit/sshd/CommandProvider.java @@ -15,7 +15,7 @@ package com.google.gerrit.sshd; import com.google.inject.Provider; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; final class CommandProvider { diff --git a/java/com/google/gerrit/sshd/Commands.java b/java/com/google/gerrit/sshd/Commands.java index 43d2c5033a..b6d3401955 100644 --- a/java/com/google/gerrit/sshd/Commands.java +++ b/java/com/google/gerrit/sshd/Commands.java @@ -17,7 +17,7 @@ package com.google.gerrit.sshd; import com.google.auto.value.AutoAnnotation; import com.google.inject.Key; import java.lang.annotation.Annotation; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; /** Utilities to support {@link CommandName} construction. */ public class Commands { diff --git a/java/com/google/gerrit/sshd/DispatchCommand.java b/java/com/google/gerrit/sshd/DispatchCommand.java index 490dd525af..4c9ca91471 100644 --- a/java/com/google/gerrit/sshd/DispatchCommand.java +++ b/java/com/google/gerrit/sshd/DispatchCommand.java @@ -31,8 +31,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; -import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; +import org.apache.sshd.server.command.Command; import org.kohsuke.args4j.Argument; /** Command that dispatches to a subcommand from its command table. */ diff --git a/java/com/google/gerrit/sshd/DispatchCommandProvider.java b/java/com/google/gerrit/sshd/DispatchCommandProvider.java index c782d2fa4f..7ff7224b9a 100644 --- a/java/com/google/gerrit/sshd/DispatchCommandProvider.java +++ b/java/com/google/gerrit/sshd/DispatchCommandProvider.java @@ -24,7 +24,7 @@ import com.google.inject.TypeLiteral; import java.lang.annotation.Annotation; import java.util.List; import java.util.concurrent.ConcurrentMap; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; /** Creates DispatchCommand using commands registered by {@link CommandModule}. */ public class DispatchCommandProvider implements Provider<DispatchCommand> { diff --git a/java/com/google/gerrit/sshd/NoShell.java b/java/com/google/gerrit/sshd/NoShell.java index ba0dcbaaa6..02355547f0 100644 --- a/java/com/google/gerrit/sshd/NoShell.java +++ b/java/com/google/gerrit/sshd/NoShell.java @@ -30,10 +30,10 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import org.apache.sshd.common.Factory; -import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; +import org.apache.sshd.server.command.Command; import org.apache.sshd.server.session.ServerSession; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.util.SystemReader; diff --git a/java/com/google/gerrit/sshd/PluginCommandModule.java b/java/com/google/gerrit/sshd/PluginCommandModule.java index b0116e4013..984c8a6aa3 100644 --- a/java/com/google/gerrit/sshd/PluginCommandModule.java +++ b/java/com/google/gerrit/sshd/PluginCommandModule.java @@ -18,7 +18,7 @@ import com.google.common.base.Preconditions; import com.google.gerrit.extensions.annotations.PluginName; import com.google.inject.Inject; import com.google.inject.binder.LinkedBindingBuilder; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; public abstract class PluginCommandModule extends CommandModule { private CommandName command; diff --git a/java/com/google/gerrit/sshd/SingleCommandPluginModule.java b/java/com/google/gerrit/sshd/SingleCommandPluginModule.java index 079661ad02..5b602f48e9 100644 --- a/java/com/google/gerrit/sshd/SingleCommandPluginModule.java +++ b/java/com/google/gerrit/sshd/SingleCommandPluginModule.java @@ -18,7 +18,7 @@ import com.google.common.base.Preconditions; import com.google.gerrit.extensions.annotations.PluginName; import com.google.inject.Inject; import com.google.inject.binder.LinkedBindingBuilder; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; /** * Binds one SSH command to the plugin name itself. diff --git a/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java b/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java index 0fdde816ce..830dba7755 100644 --- a/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java +++ b/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java @@ -28,7 +28,7 @@ import com.google.inject.TypeLiteral; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; class SshAutoRegisterModuleGenerator extends AbstractModule implements ModuleGenerator { private final Map<String, Class<Command>> commands = new HashMap<>(); diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java index 688c5730ab..ef356f1687 100644 --- a/java/com/google/gerrit/sshd/SshDaemon.java +++ b/java/com/google/gerrit/sshd/SshDaemon.java @@ -93,8 +93,6 @@ import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.common.util.security.SecurityUtils; -import org.apache.sshd.server.Command; -import org.apache.sshd.server.CommandFactory; import org.apache.sshd.server.ServerBuilder; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.auth.UserAuth; @@ -102,6 +100,8 @@ import org.apache.sshd.server.auth.gss.GSSAuthenticator; import org.apache.sshd.server.auth.gss.UserAuthGSSFactory; import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; import org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory; +import org.apache.sshd.server.command.Command; +import org.apache.sshd.server.command.CommandFactory; import org.apache.sshd.server.forward.ForwardingFilter; import org.apache.sshd.server.global.CancelTcpipForwardHandler; import org.apache.sshd.server.global.KeepAliveHandler; diff --git a/java/com/google/gerrit/sshd/SshLog.java b/java/com/google/gerrit/sshd/SshLog.java index bd8a3ee974..0e34889f08 100644 --- a/java/com/google/gerrit/sshd/SshLog.java +++ b/java/com/google/gerrit/sshd/SshLog.java @@ -323,16 +323,20 @@ class SshLog implements LifecycleListener, GerritConfigListener { if (!event.isValueUpdated(sshdRequestLog)) { return Collections.emptyList(); } - - boolean enabled = event.getNewConfig().getBoolean("sshd", "requestLog", true); boolean stateUpdated; - if (enabled) { - stateUpdated = enableLogging(); - } else { - stateUpdated = disableLogging(); + try { + boolean enabled = event.getNewConfig().getBoolean("sshd", "requestLog", true); + + if (enabled) { + stateUpdated = enableLogging(); + } else { + stateUpdated = disableLogging(); + } + return stateUpdated + ? Collections.singletonList(event.accept(sshdRequestLog)) + : Collections.emptyList(); + } catch (IllegalArgumentException iae) { + return Collections.singletonList(event.reject(sshdRequestLog)); } - return stateUpdated - ? Collections.singletonList(event.accept(sshdRequestLog)) - : Collections.emptyList(); } } diff --git a/java/com/google/gerrit/sshd/SshModule.java b/java/com/google/gerrit/sshd/SshModule.java index f047017d6a..acdc9582ed 100644 --- a/java/com/google/gerrit/sshd/SshModule.java +++ b/java/com/google/gerrit/sshd/SshModule.java @@ -45,9 +45,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledThreadPoolExecutor; -import org.apache.sshd.server.CommandFactory; import org.apache.sshd.server.auth.gss.GSSAuthenticator; import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; +import org.apache.sshd.server.command.CommandFactory; import org.eclipse.jgit.lib.Config; /** Configures standard dependencies for {@link SshDaemon}. */ diff --git a/java/com/google/gerrit/sshd/SshPluginStarterCallback.java b/java/com/google/gerrit/sshd/SshPluginStarterCallback.java index e9a095f661..6e8590c888 100644 --- a/java/com/google/gerrit/sshd/SshPluginStarterCallback.java +++ b/java/com/google/gerrit/sshd/SshPluginStarterCallback.java @@ -24,7 +24,7 @@ import com.google.inject.Inject; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.Singleton; -import org.apache.sshd.server.Command; +import org.apache.sshd.server.command.Command; @Singleton class SshPluginStarterCallback implements StartPluginListener, ReloadPluginListener { diff --git a/java/com/google/gerrit/sshd/SuExec.java b/java/com/google/gerrit/sshd/SuExec.java index 54371c1119..7053a0d5f4 100644 --- a/java/com/google/gerrit/sshd/SuExec.java +++ b/java/com/google/gerrit/sshd/SuExec.java @@ -34,8 +34,8 @@ import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; +import org.apache.sshd.server.command.Command; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; diff --git a/java/com/google/gerrit/sshd/commands/CloseConnection.java b/java/com/google/gerrit/sshd/commands/CloseConnection.java index a38461d7ca..60a878a62c 100644 --- a/java/com/google/gerrit/sshd/commands/CloseConnection.java +++ b/java/com/google/gerrit/sshd/commands/CloseConnection.java @@ -53,7 +53,7 @@ final class CloseConnection extends SshCommand { required = true, metaVar = "SESSION_ID", usage = "List of SSH session IDs to be closed") - private final List<String> sessionIds = new ArrayList<>(); + private List<String> sessionIds = new ArrayList<>(); @Option(name = "--wait", usage = "wait for connection to close before exiting") private boolean wait; diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java index b74fc16ed0..8891deef08 100644 --- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java +++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java @@ -231,6 +231,15 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest { } @Test + @GerritConfig(name = "receive.enableSignedPush", value = "true") + @TestProjectInput( + enableSignedPush = InheritableBoolean.TRUE, + requireSignedPush = InheritableBoolean.TRUE) + public void nonSignedPushRejectedWhenSignPushRequired() throws Exception { + pushTo("refs/for/master").assertErrorStatus("push cert error"); + } + + @Test public void pushInitialCommitForRefsMetaConfigBranch() throws Exception { // delete refs/meta/config try (Repository repo = repoManager.openRepository(project); @@ -405,6 +414,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest { PushOneCommit.Result r = pushTo("refs/for/master/" + topic); r.assertOkStatus(); r.assertChange(Change.Status.NEW, topic); + r.assertMessage("deprecated topic syntax"); // specify topic as option r = pushTo("refs/for/master%topic=" + topic); diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java index 31eee9f74c..a3f9f93923 100644 --- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java +++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java @@ -43,6 +43,7 @@ import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.errors.InvalidNameException; import com.google.gerrit.extensions.api.projects.CommentLinkInfo; +import com.google.gerrit.metrics.MetricMaker; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.server.ReviewDb; @@ -204,6 +205,7 @@ public class RefControlTest { @Inject private ThreadLocalRequestContext requestContext; @Inject private DefaultRefFilter.Factory refFilterFactory; @Inject private TransferConfig transferConfig; + @Inject private MetricMaker metricMaker; @Before public void setUp() throws Exception { @@ -291,7 +293,7 @@ public class RefControlTest { Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c = CacheBuilder.newBuilder().build(); - sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c)); + sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c), metricMaker); parent = new ProjectConfig(parentKey); parent.load(newRepository(parentKey)); @@ -972,6 +974,7 @@ public class RefControlTest { commentLinks, capabilityCollectionFactory, transferConfig, + metricMaker, pc)); return repo; } diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java index 41f60bbe34..b9973e906c 100644 --- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java +++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java @@ -201,7 +201,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests { protected static final String DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY = "has:draft"; protected static final String DASHBOARD_ASSIGNED_QUERY = - "assignee:${user} (-is:wip OR " + "owner:self OR assignee:self)"; + "assignee:${user} (-is:wip OR " + "owner:self OR assignee:self) is:open -is:ignored"; protected static final String DASHBOARD_WORK_IN_PROGRESS_QUERY = "is:open owner:${user} is:wip"; protected static final String DASHBOARD_OUTGOING_QUERY = "is:open owner:${user} -is:wip -is:ignored"; @@ -841,6 +841,43 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests { } @Test + public void byRepo() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("repo:foo"); + assertQuery("repo:repo"); + assertQuery("repo:repo1", change1); + assertQuery("repo:repo2", change2); + } + + @Test + public void byParentRepo() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2", "repo1"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("parentrepo:repo1", change2, change1); + assertQuery("parentrepo:repo2", change2); + } + + @Test + public void byRepoPrefix() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("repos:foo"); + assertQuery("repos:repo1", change1); + assertQuery("repos:repo2", change2); + assertQuery("repos:repo", change2, change1); + } + + @Test public void byBranchAndRef() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = insert(repo, newChangeForBranch(repo, "master")); @@ -2746,6 +2783,14 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests { .create(repo); // Create changes that should not be returned by query. + new DashboardChangeState(user.getAccountId()).assignTo(user.getAccountId()).abandon(); + new DashboardChangeState(user.getAccountId()) + .assignTo(user.getAccountId()) + .ignoreBy(user.getAccountId()); + new DashboardChangeState(user.getAccountId()) + .assignTo(user.getAccountId()) + .mergeBy(user.getAccountId()); + assertDashboardQuery("self", DASHBOARD_ASSIGNED_QUERY, selfOpenWip, otherOpenWip); // Viewing another user's dashboard. diff --git a/lib/mina/BUILD b/lib/mina/BUILD index 8595bb52c1..6ee7e41657 100644 --- a/lib/mina/BUILD +++ b/lib/mina/BUILD @@ -4,6 +4,7 @@ java_library( visibility = ["//visibility:public"], exports = [ ":eddsa", + "@sshd-mina//jar", "@sshd//jar", ], runtime_deps = [":core"], diff --git a/lib/polymer_externs/BUILD b/lib/polymer_externs/BUILD index ae8f9c0ffe..2f1bdbded2 100644 --- a/lib/polymer_externs/BUILD +++ b/lib/polymer_externs/BUILD @@ -18,16 +18,9 @@ package( load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") -genrule( - name = "polymer_closure_renamed", - srcs = ["@polymer_closure//file"], - outs = ["polymer_closure_renamed.js"], - cmd = "cp $< $@", -) - closure_js_library( name = "polymer_closure", - srcs = [":polymer_closure_renamed"], + srcs = ["@polymer_closure//file"], data = ["//lib:LICENSE-Apache2.0"], no_closure_library = True, ) diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html index b18e8cd3f1..af320e2b49 100644 --- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html +++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html @@ -20,6 +20,9 @@ limitations under the License. <link rel="import" href="../../../styles/shared-styles.html"> <link rel="import" href="../../change-list/gr-change-list/gr-change-list.html"> <link rel="import" href="../../core/gr-reporting/gr-reporting.html"> +<link rel="import" href="../../shared/gr-button/gr-button.html"> +<link rel="import" href="../../shared/gr-dialog/gr-dialog.html"> +<link rel="import" href="../../shared/gr-overlay/gr-overlay.html"> <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> <link rel="import" href="../gr-user-header/gr-user-header.html"> @@ -37,18 +40,43 @@ limitations under the License. gr-change-list { width: 100%; } - .hide { - display: none; - } gr-user-header { border-bottom: 1px solid var(--border-color); } + .banner { + align-items: center; + background-color: var(--comment-background-color); + border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + padding: .25em var(--default-horizontal-margin); + } + .banner gr-button { + --gr-button: { + color: var(--primary-text-color); + } + } + .hide { + display: none; + } @media only screen and (max-width: 50em) { .loading { padding: 0 var(--default-horizontal-margin); } } </style> + <div class$="banner [[_computeBannerClass(_showDraftsBanner)]]"> + <div> + You have draft comments on closed changes. + <a href$="[[_computeDraftsLink(_showDraftsBanner)]]" target="_blank">(view all)</a> + </div> + <div> + <gr-button + class="delete" + link + on-tap="_handleOpenDeleteDialog">Delete All</gr-button> + </div> + </div> <div class="loading" hidden$="[[!_loading]]">Loading...</div> <div hidden$="[[_loading]]" hidden> <gr-user-header @@ -64,6 +92,21 @@ limitations under the License. on-toggle-star="_handleToggleStar" on-toggle-reviewed="_handleToggleReviewed"></gr-change-list> </div> + <gr-overlay id="confirmDeleteOverlay" with-backdrop> + <gr-dialog + id="confirmDeleteDialog" + confirm-label="Delete" + on-confirm="_handleConfirmDelete" + on-cancel="_closeConfirmDeleteOverlay"> + <div class="header" slot="header"> + Delete comments + </div> + <div class="main" slot="main"> + Are you sure you want to delete all your draft comments in closed changes? This action + cannot be undone. + </div> + </gr-dialog> + </gr-overlay> <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> <gr-reporting id="reporting"></gr-reporting> </template> diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js index 72ef0e58ba..8aa7084ba4 100644 --- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js +++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js @@ -18,67 +18,6 @@ 'use strict'; const PROJECT_PLACEHOLDER_PATTERN = /\$\{project\}/g; - const USER_PLACEHOLDER_PATTERN = /\$\{user\}/g; - - // NOTE: These queries are tested in Java. Any changes made to definitions - // here require corresponding changes to: - // javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java - const DEFAULT_SECTIONS = [ - { - // Changes with unpublished draft comments. This section is omitted when - // viewing other users, so we don't need to filter anything out. - name: 'Has draft comments', - query: 'has:draft', - selfOnly: true, - hideIfEmpty: true, - suffixForDashboard: 'limit:10', - }, - { - // Changes that are assigned to the viewed user. - name: 'Assigned reviews', - query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self)', - hideIfEmpty: true, - }, - { - // WIP open changes owned by viewing user. This section is omitted when - // viewing other users, so we don't need to filter anything out. - name: 'Work in progress', - query: 'is:open owner:${user} is:wip', - selfOnly: true, - hideIfEmpty: true, - }, - { - // Non-WIP open changes owned by viewed user. Filter out changes ignored - // by the viewing user. - name: 'Outgoing reviews', - query: 'is:open owner:${user} -is:wip -is:ignored', - }, - { - // Non-WIP open changes not owned by the viewed user, that the viewed user - // is associated with (as either a reviewer or the assignee). Changes - // ignored by the viewing user are filtered out. - name: 'Incoming reviews', - query: 'is:open -owner:${user} -is:wip -is:ignored ' + - '(reviewer:${user} OR assignee:${user})', - }, - { - // Open changes the viewed user is CCed on. Changes ignored by the viewing - // user are filtered out. - name: 'CCed on', - query: 'is:open -is:ignored cc:${user}', - }, - { - name: 'Recently closed', - // Closed changes where viewed user is owner, reviewer, or assignee. - // Changes ignored by the viewing user are filtered out, and so are WIP - // changes not owned by the viewing user (the one instance of - // 'owner:self' is intentional and implements this logic). - query: 'is:closed -is:ignored (-is:wip OR owner:self) ' + - '(owner:${user} OR reviewer:${user} OR assignee:${user} ' + - 'OR cc:${user})', - suffixForDashboard: '-age:4w limit:10', - }, - ]; Polymer({ is: 'gr-dashboard-view', @@ -104,10 +43,6 @@ }, _results: Array, - _sectionMetadata: { - type: Array, - value() { return DEFAULT_SECTIONS; }, - }, /** * For showing a "loading..." string during ajax requests. @@ -116,6 +51,11 @@ type: Boolean, value: true, }, + + _showDraftsBanner: { + type: Boolean, + value: false, + }, }, observers: [ @@ -174,16 +114,6 @@ }); }, - _getUserDashboard(user, sections, title) { - sections = sections - .filter(section => (user === 'self' || !section.selfOnly)) - .map(section => Object.assign({}, section, { - name: section.name, - query: section.query.replace(USER_PLACEHOLDER_PATTERN, user), - })); - return Promise.resolve({title, sections}); - }, - _computeTitle(user) { if (!user || user === 'self') { return 'My Reviews'; @@ -221,13 +151,14 @@ const {project, dashboard, title, user, sections} = this.params; const dashboardPromise = project ? this._getProjectDashboard(project, dashboard) : - this._getUserDashboard( - user || 'self', - sections || DEFAULT_SECTIONS, - title || this._computeTitle(user)); + Promise.resolve(Gerrit.Nav.getUserDashboard( + user, + sections, + title || this._computeTitle(user))); return dashboardPromise.then(this._fetchDashboardChanges.bind(this)) .then(() => { + this._maybeShowDraftsBanner(); this.$.reporting.dashboardDisplayed(); }).catch(err => { console.warn(err); @@ -274,5 +205,48 @@ this.$.restAPI.saveChangeReviewed(e.detail.change._number, e.detail.reviewed); }, + + /** + * Banner is shown if a user is on their own dashboard and they have draft + * comments on closed changes. + */ + _maybeShowDraftsBanner() { + this._showDraftsBanner = false; + if (!(this.params.user === 'self')) { return; } + + const draftSection = this._results + .find(section => section.query === 'has:draft'); + if (!draftSection || !draftSection.results.length) { return; } + + const closedChanges = draftSection.results + .filter(change => !this.changeIsOpen(change.status)); + if (!closedChanges.length) { return; } + + this._showDraftsBanner = true; + }, + + _computeBannerClass(show) { + return show ? '' : 'hide'; + }, + + _handleOpenDeleteDialog() { + this.$.confirmDeleteOverlay.open(); + }, + + _handleConfirmDelete() { + this.$.confirmDeleteDialog.disabled = true; + return this.$.restAPI.deleteDraftComments('-is:open').then(() => { + this._closeConfirmDeleteOverlay(); + this._reload(); + }); + }, + + _closeConfirmDeleteOverlay() { + this.$.confirmDeleteOverlay.close(); + }, + + _computeDraftsLink() { + return Gerrit.Nav.getUrlForSearchQuery('has:draft -is:open'); + }, }); })(); diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html index cac262718d..aebd452f5e 100644 --- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html +++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html @@ -63,6 +63,85 @@ limitations under the License. sandbox.restore(); }); + suite('drafts banner functionality', () => { + suite('_maybeShowDraftsBanner', () => { + test('not dashboard/self', () => { + element.params = {user: 'notself'}; + element._maybeShowDraftsBanner(); + assert.isFalse(element._showDraftsBanner); + }); + + test('no drafts at all', () => { + element.params = {user: 'self'}; + element._results = []; + element._maybeShowDraftsBanner(); + assert.isFalse(element._showDraftsBanner); + }); + + test('no drafts on open changes', () => { + element.params = {user: 'self'}; + element._results = [{query: 'has:draft', results: [{status: '_'}]}]; + sandbox.stub(element, 'changeIsOpen').returns(true); + element._maybeShowDraftsBanner(); + assert.isFalse(element._showDraftsBanner); + }); + + test('no drafts on open changes', () => { + element.params = {user: 'self'}; + element._results = [{query: 'has:draft', results: [{status: '_'}]}]; + sandbox.stub(element, 'changeIsOpen').returns(false); + element._maybeShowDraftsBanner(); + assert.isTrue(element._showDraftsBanner); + }); + }); + + test('_showDraftsBanner', () => { + element._showDraftsBanner = false; + flushAsynchronousOperations(); + assert.isTrue(isHidden(element.$$('.banner'))); + + element._showDraftsBanner = true; + flushAsynchronousOperations(); + assert.isFalse(isHidden(element.$$('.banner'))); + }); + + test('delete tap opens dialog', () => { + sandbox.stub(element, '_handleOpenDeleteDialog'); + element._showDraftsBanner = true; + flushAsynchronousOperations(); + + MockInteractions.tap(element.$$('.banner .delete')); + assert.isTrue(element._handleOpenDeleteDialog.called); + }); + + test('delete comments flow', async () => { + sandbox.spy(element, '_handleConfirmDelete'); + sandbox.stub(element, '_reload'); + + // Set up control over timing of when RPC resolves. + let deleteDraftCommentsPromiseResolver; + const deleteDraftCommentsPromise = new Promise(resolve => { + deleteDraftCommentsPromiseResolver = resolve; + }); + sandbox.stub(element.$.restAPI, 'deleteDraftComments') + .returns(deleteDraftCommentsPromise); + + // Open confirmation dialog and tap confirm button. + await element.$.confirmDeleteOverlay.open(); + MockInteractions.tap(element.$.confirmDeleteDialog.$.confirm); + flushAsynchronousOperations(); + assert.isTrue(element.$.restAPI.deleteDraftComments + .calledWithExactly('-is:open')); + assert.isTrue(element.$.confirmDeleteDialog.disabled); + assert.equal(element._reload.callCount, 0); + + // Verify state after RPC resolves. + deleteDraftCommentsPromiseResolver([]); + await deleteDraftCommentsPromise; + assert.equal(element._reload.callCount, 1); + }); + }); + test('_computeTitle', () => { assert.equal(element._computeTitle('self'), 'My Reviews'); assert.equal(element._computeTitle('not self'), 'Dashboard for not self'); @@ -189,59 +268,6 @@ limitations under the License. }); }); - suite('_getUserDashboard', () => { - const sections = [ - {name: 'section 1', query: 'query 1'}, - {name: 'section 2', query: 'query 2 for ${user}'}, - {name: 'section 3', query: 'self only query', selfOnly: true}, - {name: 'section 4', query: 'query 4', suffixForDashboard: 'suffix'}, - ]; - - test('dashboard for self', () => { - return element._getUserDashboard('self', sections, 'title') - .then(dashboard => { - assert.deepEqual( - dashboard, - { - title: 'title', - sections: [ - {name: 'section 1', query: 'query 1'}, - {name: 'section 2', query: 'query 2 for self'}, - { - name: 'section 3', - query: 'self only query', - selfOnly: true, - }, { - name: 'section 4', - query: 'query 4', - suffixForDashboard: 'suffix', - }, - ], - }); - }); - }); - - test('dashboard for other user', () => { - return element._getUserDashboard('user', sections, 'title') - .then(dashboard => { - assert.deepEqual( - dashboard, - { - title: 'title', - sections: [ - {name: 'section 1', query: 'query 1'}, - {name: 'section 2', query: 'query 2 for user'}, - { - name: 'section 4', - query: 'query 4', - suffixForDashboard: 'suffix', - }, - ], - }); - }); - }); - }); - test('hideIfEmpty sections', () => { const sections = [ {name: 'test1', query: 'test1', hideIfEmpty: true}, diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js index 6af94a1c25..60479e8100 100644 --- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js +++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js @@ -241,6 +241,10 @@ ]; }, }, + disableEdit: { + type: Boolean, + value: false, + }, _hasKnownChainState: { type: Boolean, value: false, @@ -397,7 +401,7 @@ '_actionsChanged(actions.*, revisionActions.*, _additionalActions.*)', '_changeChanged(change)', '_editStatusChanged(editMode, editPatchsetLoaded, ' + - 'editBasedOnCurrentPatchSet, actions.*, change.*)', + 'editBasedOnCurrentPatchSet, disableEdit, actions.*, change.*)', ], listeners: { @@ -575,7 +579,15 @@ }, _editStatusChanged(editMode, editPatchsetLoaded, - editBasedOnCurrentPatchSet) { + editBasedOnCurrentPatchSet, disableEdit) { + if (disableEdit) { + this._deleteAndNotify('publishEdit'); + this._deleteAndNotify('rebaseEdit'); + this._deleteAndNotify('deleteEdit'); + this._deleteAndNotify('stopEdit'); + this._deleteAndNotify('edit'); + return; + } if (editPatchsetLoaded) { // Only show actions that mutate an edit if an actual edit patch set // is loaded. diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html index 91b39deede..89d4f1f883 100644 --- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html +++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html @@ -439,6 +439,20 @@ limitations under the License. }); suite('change edits', () => { + test('disableEdit', () => { + element.set('editMode', false); + element.set('editPatchsetLoaded', false); + element.change = {status: 'NEW'}; + element.set('disableEdit', true); + flushAsynchronousOperations(); + + assert.isNotOk(element.$$('gr-button[data-action-key="publishEdit"]')); + assert.isNotOk(element.$$('gr-button[data-action-key="rebaseEdit"]')); + assert.isNotOk(element.$$('gr-button[data-action-key="deleteEdit"]')); + assert.isNotOk(element.$$('gr-button[data-action-key="edit"]')); + assert.isNotOk(element.$$('gr-button[data-action-key="stopEdit"]')); + }); + test('shows confirm dialog for delete edit', () => { element.set('editMode', true); element.set('editPatchsetLoaded', true); diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html index b056233d96..5b36221b86 100644 --- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html +++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html @@ -136,43 +136,42 @@ limitations under the License. suite('label updates', () => { let plugin; - let labelChangeStub; - setup(done => { + setup(() => { Gerrit.install(p => plugin = p, '0.1', new URL('test/plugin.html?' + Math.random(), window.location.href).toString()); sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); Gerrit._setPluginsPending([]); element = createElement(); - - labelChangeStub = sandbox.stub(); - plugin.changeMetadata().onLabelsChanged(labelChangeStub); - flush(done); }); test('labels changed callback', done => { - assert.equal(labelChangeStub.callCount, 1); - assert.isTrue(labelChangeStub.calledWithExactly(labels)); - assert.equal(labelChangeStub.args[0][0]['CI'].all.length, 2); - element.set(['change', 'labels'], { - CI: { - all: [ - {value: 1, name: 'user 2', _account_id: 1}, - ], - values: { - ' 0': 'Don\'t submit as-is', - '+1': 'No score', - '+2': 'Looks good to me', - }, - }, - }); - // Wait for fake rest API response. - flush(() => { - assert.equal(labelChangeStub.callCount, 2); - assert.equal(labelChangeStub.args[1][0]['CI'].all.length, 1); - done(); + let callCount = 0; + const labelChangeSpy = sandbox.spy(arg => { + callCount++; + if (callCount === 1) { + assert.deepEqual(arg, labels); + assert.equal(arg.CI.all.length, 2); + element.set(['change', 'labels'], { + CI: { + all: [ + {value: 1, name: 'user 2', _account_id: 1}, + ], + values: { + ' 0': 'Don\'t submit as-is', + '+1': 'No score', + '+2': 'Looks good to me', + }, + }, + }); + } else if (callCount === 2) { + assert.equal(arg.CI.all.length, 1); + done(); + } }); + + plugin.changeMetadata().onLabelsChanged(labelChangeSpy); }); }); }); diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html index 461bfc402a..8885a6f933 100644 --- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html +++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html @@ -394,8 +394,10 @@ limitations under the License. <span class="headerSubject">[[_change.subject]]</span> </div><!-- end headerTitle --> <div class="commitActions" hidden$="[[!_loggedIn]]"> - <gr-change-actions id="actions" + <gr-change-actions + id="actions" change="[[_change]]" + disable-edit="[[disableEdit]]" has-parent="[[hasParent]]" actions="[[_change.actions]]" revision-actions="[[_currentRevisionActions]]" diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js index ebd72ad9da..4d54161419 100644 --- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js +++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js @@ -102,6 +102,10 @@ type: Object, value() { return document.body; }, }, + disableEdit: { + type: Boolean, + value: false, + }, _commentThreads: Array, /** @type {?} */ _serverConfig: { diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html index 89b354842e..9822860669 100644 --- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html +++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html @@ -112,12 +112,14 @@ limitations under the License. margin: 5px 4px; text-decoration: none; } + .invisible, .settingsButton, gr-account-dropdown { display: none; } :host([loading]) .accountContainer, - :host([logged-in]) .loginButton { + :host([logged-in]) .loginButton, + :host([logged-in]) .registerButton { display: none; } :host([logged-in]) .settingsButton, @@ -135,7 +137,7 @@ limitations under the License. text-overflow: ellipsis; white-space: nowrap; } - .loginButton { + .loginButton, .registerButton { color: var(--header-text-color); padding: .5em 1em; } @@ -194,6 +196,13 @@ limitations under the License. class="hideOnMobile" name="header-browse-source"></gr-endpoint-decorator> <div class="accountContainer" id="accountContainer"> + <div class$="[[_computeIsInvisible(_registerURL)]]"> + <a + class="registerButton" + href$="[[_registerURL]]"> + [[_registerText]] + </a> + </div> <a class="loginButton" href$="[[_loginURL]]">Sign in</a> <a class="settingsButton" diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js index dad7b7c1c4..738d29e21d 100644 --- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js +++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js @@ -62,6 +62,13 @@ }, ]; + // Set of authentication methods that can provide custom registration page. + const AUTH_TYPES_WITH_REGISTER_URL = new Set([ + 'LDAP', + 'LDAP_BIND', + 'CUSTOM_EXTENSION', + ]); + Polymer({ is: 'gr-main-header', @@ -102,7 +109,7 @@ _links: { type: Array, computed: '_computeLinks(_defaultLinks, _userLinks, _adminLinks, ' + - '_docBaseUrl)', + '_topMenus, _docBaseUrl)', }, _loginURL: { type: String, @@ -112,6 +119,18 @@ type: Array, value() { return []; }, }, + _topMenus: { + type: Array, + value() { return []; }, + }, + _registerText: { + type: String, + value: 'Sign up', + }, + _registerURL: { + type: String, + value: null, + }, }, behaviors: [ @@ -159,7 +178,7 @@ return '//' + window.location.host + this.getBaseUrl() + path; }, - _computeLinks(defaultLinks, userLinks, adminLinks, docBaseUrl) { + _computeLinks(defaultLinks, userLinks, adminLinks, topMenus, docBaseUrl) { const links = defaultLinks.slice(); if (userLinks && userLinks.length > 0) { links.push({ @@ -179,6 +198,12 @@ title: 'Browse', links: adminLinks, }); + for (const m of topMenus) { + links.push({ + title: m.name, + links: m.items.map(this._fixCustomMenuItem), + }); + } return links; }, @@ -203,6 +228,7 @@ this.loading = true; const promises = [ this.$.restAPI.getAccount(), + this.$.restAPI.getTopMenus(), Gerrit.awaitPluginsLoaded(), ]; @@ -211,6 +237,7 @@ this._account = account; this.loggedIn = !!account; this.loading = false; + this._topMenus = result[1]; return this.getAdminLinks(account, this.$.restAPI.getAccountCapabilities.bind(this.$.restAPI), @@ -223,7 +250,10 @@ _loadConfig() { this.$.restAPI.getConfig() - .then(config => this.getDocsBaseUrl(config, this.$.restAPI)) + .then(config => { + this._retrieveRegisterURL(config); + return this.getDocsBaseUrl(config, this.$.restAPI); + }) .then(docBaseUrl => { this._docBaseUrl = docBaseUrl; }); }, @@ -232,11 +262,24 @@ this.$.restAPI.getPreferences().then(prefs => { this._userLinks = - prefs.my.map(this._fixMyMenuItem).filter(this._isSupportedLink); + prefs.my.map(this._fixCustomMenuItem).filter(this._isSupportedLink); }); }, - _fixMyMenuItem(linkObj) { + _retrieveRegisterURL(config) { + if (AUTH_TYPES_WITH_REGISTER_URL.has(config.auth.auth_type)) { + this._registerURL = config.auth.register_url; + if (config.auth.register_text) { + this._registerText = config.auth.register_text; + } + } + }, + + _computeIsInvisible(registerURL) { + return registerURL ? '' : 'invisible'; + }, + + _fixCustomMenuItem(linkObj) { // Normalize all urls to PolyGerrit style. if (linkObj.url.startsWith('#')) { linkObj.url = linkObj.url.slice(1); @@ -251,7 +294,7 @@ // so we'll just disable it altogether for now. delete linkObj.target; - // Becasue the "my menu" links may be arbitrary URLs, we don't know + // Because the user provided links may be arbitrary URLs, we don't know // whether they correspond to any client routes. Mark all such links as // external. linkObj.external = true; diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html index 30e8e1fc79..b6e64ec51a 100644 --- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html +++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html @@ -63,6 +63,8 @@ limitations under the License. 'none'); assert.notEqual(getComputedStyle(element.$$('.loginButton')).display, 'none'); + assert.notEqual(getComputedStyle(element.$$('.registerButton')).display, + 'none'); assert.equal(getComputedStyle(element.$$('gr-account-dropdown')).display, 'none'); assert.equal(getComputedStyle(element.$$('.settingsButton')).display, @@ -70,6 +72,8 @@ limitations under the License. element.loggedIn = true; assert.equal(getComputedStyle(element.$$('.loginButton')).display, 'none'); + assert.equal(getComputedStyle(element.$$('.registerButton')).display, + 'none'); assert.notEqual(getComputedStyle(element.$$('gr-account-dropdown')) .display, 'none'); @@ -81,7 +85,7 @@ limitations under the License. assert.deepEqual([ {url: 'https://awesometown.com/#hashyhash'}, {url: 'url', target: '_blank'}, - ].map(element._fixMyMenuItem), [ + ].map(element._fixCustomMenuItem), [ {url: 'https://awesometown.com/#hashyhash', external: true}, {url: 'url', external: true}, ]); @@ -98,7 +102,6 @@ limitations under the License. ]); }); - test('user links', () => { const defaultLinks = [{ title: 'Faves', @@ -117,13 +120,13 @@ limitations under the License. }]; // When no admin links are passed, it should use the default. - assert.deepEqual(element._computeLinks(defaultLinks, [], adminLinks), + assert.deepEqual(element._computeLinks(defaultLinks, [], adminLinks, []), defaultLinks.concat({ title: 'Browse', links: adminLinks, })); assert.deepEqual( - element._computeLinks(defaultLinks, userLinks, adminLinks), + element._computeLinks(defaultLinks, userLinks, adminLinks, []), defaultLinks.concat([ { title: 'Your', @@ -160,5 +163,61 @@ limitations under the License. url: 'base/index.html', }]); }); + + test('top menus', () => { + const adminLinks = [{ + name: 'Repos', + url: '/repos', + }]; + const topMenus = [{ + name: 'Plugins', + items: [{ + name: 'Manage', + target: '_blank', + url: 'https://gerrit/plugins/plugin-manager/static/index.html', + }], + }]; + assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{ + title: 'Browse', + links: adminLinks, + }, + { + title: 'Plugins', + links: [{ + name: 'Manage', + external: true, + url: 'https://gerrit/plugins/plugin-manager/static/index.html', + }], + }]); + }); + + test('register URL', () => { + const config = { + auth: { + auth_type: 'LDAP', + register_url: 'https//gerrit.example.com/register', + }, + }; + element._retrieveRegisterURL(config); + assert.equal(element._registerURL, config.auth.register_url); + assert.equal(element._registerText, 'Sign up'); + + config.auth.register_text = 'Create account'; + element._retrieveRegisterURL(config); + assert.equal(element._registerURL, config.auth.register_url); + assert.equal(element._registerText, config.auth.register_text); + }); + + test('register URL ignored for wrong auth type', () => { + const config = { + auth: { + auth_type: 'OPENID', + register_url: 'https//gerrit.example.com/register', + }, + }; + element._retrieveRegisterURL(config); + assert.equal(element._registerURL, null); + assert.equal(element._registerText, 'Sign up'); + }); }); </script> diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html index c5e32ccade..05cda9b241 100644 --- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html +++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html @@ -92,6 +92,69 @@ limitations under the License. const EDIT_PATCHNUM = 'edit'; const PARENT_PATCHNUM = 'PARENT'; + const USER_PLACEHOLDER_PATTERN = /\$\{user\}/g; + + // NOTE: These queries are tested in Java. Any changes made to definitions + // here require corresponding changes to: + // javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java + const DEFAULT_SECTIONS = [ + { + // Changes with unpublished draft comments. This section is omitted when + // viewing other users, so we don't need to filter anything out. + name: 'Has draft comments', + query: 'has:draft', + selfOnly: true, + hideIfEmpty: true, + suffixForDashboard: 'limit:10', + }, + { + // Changes that are assigned to the viewed user. + name: 'Assigned reviews', + query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self) ' + + 'is:open -is:ignored', + hideIfEmpty: true, + }, + { + // WIP open changes owned by viewing user. This section is omitted when + // viewing other users, so we don't need to filter anything out. + name: 'Work in progress', + query: 'is:open owner:${user} is:wip', + selfOnly: true, + hideIfEmpty: true, + }, + { + // Non-WIP open changes owned by viewed user. Filter out changes ignored + // by the viewing user. + name: 'Outgoing reviews', + query: 'is:open owner:${user} -is:wip -is:ignored', + }, + { + // Non-WIP open changes not owned by the viewed user, that the viewed user + // is associated with (as either a reviewer or the assignee). Changes + // ignored by the viewing user are filtered out. + name: 'Incoming reviews', + query: 'is:open -owner:${user} -is:wip -is:ignored ' + + '(reviewer:${user} OR assignee:${user})', + }, + { + // Open changes the viewed user is CCed on. Changes ignored by the viewing + // user are filtered out. + name: 'CCed on', + query: 'is:open -is:ignored cc:${user}', + }, + { + name: 'Recently closed', + // Closed changes where viewed user is owner, reviewer, or assignee. + // Changes ignored by the viewing user are filtered out, and so are WIP + // changes not owned by the viewing user (the one instance of + // 'owner:self' is intentional and implements this logic). + query: 'is:closed -is:ignored (-is:wip OR owner:self) ' + + '(owner:${user} OR reviewer:${user} OR assignee:${user} ' + + 'OR cc:${user})', + suffixForDashboard: '-age:4w limit:10', + }, + ]; + window.Gerrit.Nav = { View: { @@ -645,6 +708,17 @@ limitations under the License. } return [].concat(this._generateWeblinks(params)); }, + + getUserDashboard(user = 'self', sections = DEFAULT_SECTIONS, + title = '') { + sections = sections + .filter(section => (user === 'self' || !section.selfOnly)) + .map(section => Object.assign({}, section, { + name: section.name, + query: section.query.replace(USER_PLACEHOLDER_PATTERN, user), + })); + return {title, sections}; + }, }; })(window); </script> diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html index 61d1100360..2f7233812e 100644 --- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html +++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html @@ -29,5 +29,56 @@ limitations under the License. assert.throw(() => Gerrit.Nav.getUrlForChange('123', undefined, 12)); assert.throw(() => Gerrit.Nav.getUrlForDiff('123', 'x.c', undefined, 12)); }); + + suite('_getUserDashboard', () => { + const sections = [ + {name: 'section 1', query: 'query 1'}, + {name: 'section 2', query: 'query 2 for ${user}'}, + {name: 'section 3', query: 'self only query', selfOnly: true}, + {name: 'section 4', query: 'query 4', suffixForDashboard: 'suffix'}, + ]; + + test('dashboard for self', () => { + const dashboard = + Gerrit.Nav.getUserDashboard('self', sections, 'title'); + assert.deepEqual( + dashboard, + { + title: 'title', + sections: [ + {name: 'section 1', query: 'query 1'}, + {name: 'section 2', query: 'query 2 for self'}, + { + name: 'section 3', + query: 'self only query', + selfOnly: true, + }, { + name: 'section 4', + query: 'query 4', + suffixForDashboard: 'suffix', + }, + ], + }); + }); + + test('dashboard for other user', () => { + const dashboard = + Gerrit.Nav.getUserDashboard('user', sections, 'title'); + assert.deepEqual( + dashboard, + { + title: 'title', + sections: [ + {name: 'section 1', query: 'query 1'}, + {name: 'section 2', query: 'query 2 for user'}, + { + name: 'section 4', + query: 'query 4', + suffixForDashboard: 'suffix', + }, + ], + }); + }); + }); }); </script> diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html index f527aa34b0..58e3125def 100644 --- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html +++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html @@ -102,7 +102,7 @@ limitations under the License. class="dropdown-trigger" id="trigger" down-arrow="[[downArrow]]" on-tap="_dropdownTriggerTapHandler"> - <content></content> + <slot></slot> </gr-button> <iron-dropdown id="dropdown" vertical-align="top" diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js index c97c4c77b4..0b90cdf1b6 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js @@ -2640,6 +2640,14 @@ }); }, + getTopMenus(opt_errFn) { + return this._fetchJSON({ + url: '/config/server/top-menus', + errFn: opt_errFn, + reportUrlAsIs: true, + }); + }, + setAssignee(changeNum, assignee) { return this._getChangeURLAndSend({ changeNum, @@ -2925,5 +2933,13 @@ reportEndpointAsIs: true, }); }, + + deleteDraftComments(query) { + return this._send({ + method: 'POST', + url: '/accounts/self/drafts:delete', + body: {query}, + }); + }, }); })(); diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html index 8ade9ba34e..6037a88f53 100644 --- a/polygerrit-ui/app/styles/themes/dark-theme.html +++ b/polygerrit-ui/app/styles/themes/dark-theme.html @@ -5,7 +5,7 @@ --view-background-color: #212121; --border-color: #555555; --table-header-background-color: #353637; - --table-subheader-background-color: rgb(23, 27, 31); + --table-subheader-background-color: rgb(19, 20, 22); --header-background-color: #5487E5; --header-text-color: var(--primary-text-color); --deemphasized-text-color: #9a9a9a; @@ -83,4 +83,4 @@ background-color: var(--view-background-color); } </style> -</dom-module>
\ No newline at end of file +</dom-module> diff --git a/tools/bzl/junit.bzl b/tools/bzl/junit.bzl index d711356b40..08d50457f9 100644 --- a/tools/bzl/junit.bzl +++ b/tools/bzl/junit.bzl @@ -64,6 +64,14 @@ _GenSuite = rule( implementation = _impl, ) +POST_JDK8_OPTS = [ + # Enforce JDK 8 compatibility on Java 9, see + # https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-AF5AECA7-07C1-4E7D-BC10-BC7E73DC6C7F + "-Djava.locale.providers=COMPAT,CLDR,SPI", + "--add-modules java.activation", + "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED", +] + def junit_tests(name, srcs, **kwargs): s_name = name + "TestSuite" _GenSuite( @@ -73,13 +81,8 @@ def junit_tests(name, srcs, **kwargs): ) jvm_flags = kwargs.get("jvm_flags", []) jvm_flags = jvm_flags + select({ - "//:java9": [ - # Enforce JDK 8 compatibility on Java 9, see - # https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-AF5AECA7-07C1-4E7D-BC10-BC7E73DC6C7F - "-Djava.locale.providers=COMPAT,CLDR,SPI", - "--add-modules java.activation", - "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED", - ], + "//:java9": POST_JDK8_OPTS, + "//:java10": POST_JDK8_OPTS, "//conditions:default": [], }) native.java_test( diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py index 64d837a2b4..e515a3a27c 100755 --- a/tools/eclipse/project.py +++ b/tools/eclipse/project.py @@ -53,21 +53,29 @@ opts.add_option('--name', help='name of the generated project', opts.add_option('-b', '--batch', action='store_true', dest='batch', help='Bazel batch option') opts.add_option('-j', '--java', action='store', - dest='java', help='Post Java 8 support (9|10|11|...)') + dest='java', help='Post Java 8 support (9)') +opts.add_option('-e', '--edge_java', action='store', + dest='edge_java', help='Post Java 9 support (10|11|...)') args, _ = opts.parse_args() batch_option = '--batch' if args.batch else None custom_java = args.java +edge_java = args.edge_java def _build_bazel_cmd(*args): + build = False cmd = ['bazel'] if batch_option: cmd.append('--batch') for arg in args: + if arg == "build": + build = True cmd.append(arg) - if custom_java: + if custom_java and not edge_java: cmd.append('--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java) cmd.append('--java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java) + if edge_java and build: + cmd.append(edge_java) return cmd |