Merge "Merge branch 'stable-3.0'"
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# The project view file (.bazelproject) is used to import Gerrit Bazel packages into the IDE.
|
# The project view file (.bazelproject) is used to import Gerrit Bazel packages into the IDE.
|
||||||
#
|
#
|
||||||
# See: https://ij.bazel.io/docs/project-views.html
|
# See: https://ij.bazel.build/docs/project-views.html
|
||||||
|
|
||||||
directories:
|
directories:
|
||||||
.
|
.
|
||||||
|
|||||||
5
.gitreview
Normal file
5
.gitreview
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[gerrit]
|
||||||
|
host=gerrit-review.googlesource.com
|
||||||
|
scheme=https
|
||||||
|
project=gerrit.git
|
||||||
|
defaultbranch=master
|
||||||
@@ -1389,6 +1389,13 @@ Whether changes which are mergeable should be auto-abandoned.
|
|||||||
+
|
+
|
||||||
By default `true`.
|
By default `true`.
|
||||||
|
|
||||||
|
[[changeCleanup.cleanupAccountPatchReview]]changeCleanup.cleanupAccountPatchReview::
|
||||||
|
+
|
||||||
|
Whether accountPatchReview data should be also removed when change
|
||||||
|
gets auto-abandoned.
|
||||||
|
+
|
||||||
|
By default `false`.
|
||||||
|
|
||||||
[[changeCleanup.abandonMessage]]changeCleanup.abandonMessage::
|
[[changeCleanup.abandonMessage]]changeCleanup.abandonMessage::
|
||||||
+
|
+
|
||||||
Change message that should be posted when a change is abandoned.
|
Change message that should be posted when a change is abandoned.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ To build Gerrit from source, you need:
|
|||||||
* Python 2 or 3
|
* Python 2 or 3
|
||||||
* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm)]
|
* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm)]
|
||||||
* Bower (`sudo npm install -g bower`)
|
* Bower (`sudo npm install -g bower`)
|
||||||
* link:https://www.bazel.io/versions/master/docs/install.html[Bazel]
|
* link:https://docs.bazel.build/versions/master/install.html[Bazel]
|
||||||
* Maven
|
* Maven
|
||||||
* zip, unzip
|
* zip, unzip
|
||||||
* gcc
|
* gcc
|
||||||
@@ -225,7 +225,7 @@ is not regenerated.
|
|||||||
|
|
||||||
=== IntelliJ
|
=== IntelliJ
|
||||||
|
|
||||||
The Gerrit build works with Bazel's link:https://ij.bazel.io[IntelliJ plugin].
|
The Gerrit build works with Bazel's link:https://ij.bazel.build[IntelliJ plugin].
|
||||||
Please follow the instructions on <<dev-intellij#,IntelliJ Setup>>.
|
Please follow the instructions on <<dev-intellij#,IntelliJ Setup>>.
|
||||||
|
|
||||||
=== Eclipse
|
=== Eclipse
|
||||||
@@ -245,7 +245,7 @@ and then follow the link:dev-eclipse.html#setup[setup instructions].
|
|||||||
If an updated classpath is needed, the Eclipse project can be
|
If an updated classpath is needed, the Eclipse project can be
|
||||||
refreshed and missing dependency JARs can be downloaded by running
|
refreshed and missing dependency JARs can be downloaded by running
|
||||||
`project.py` again. For IntelliJ, you need to click the `Sync Project
|
`project.py` again. For IntelliJ, you need to click the `Sync Project
|
||||||
with BUILD Files` button of link:https://ij.bazel.io[IntelliJ plugin].
|
with BUILD Files` button of link:https://ij.bazel.build[Bazel plugin].
|
||||||
|
|
||||||
[[documentation]]
|
[[documentation]]
|
||||||
=== Documentation
|
=== Documentation
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ by clicking the 'Obtain Password' link on the
|
|||||||
link:https://gerrit-review.googlesource.com/#/settings/http-password[HTTP
|
link:https://gerrit-review.googlesource.com/#/settings/http-password[HTTP
|
||||||
Password tab of the user settings page].
|
Password tab of the user settings page].
|
||||||
|
|
||||||
|
Alternately, you may use the
|
||||||
|
link:https://pypi.org/project/git-review/[git-review] tool to submit changes
|
||||||
|
to Gerrit. If you do, it will set up the Change-Id hook and `gerrit` remote
|
||||||
|
for you. You will still need to do the HTTP access step.
|
||||||
|
|
||||||
[[style]]
|
[[style]]
|
||||||
== Style
|
== Style
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
= Gerrit Code Review - IntelliJ Setup
|
= Gerrit Code Review - IntelliJ IDEA Setup
|
||||||
|
|
||||||
== Prerequisites
|
== Prerequisites
|
||||||
You need an installation of IntelliJ version 2016.2 or later. The latest version
|
|
||||||
might not yet be in-sync with the Bazel plugin for IntelliJ. It usually becomes
|
=== Bazel
|
||||||
so quite quickly after new IDEA versions get released, though. It should then be
|
|
||||||
possible to use the fairly latest IntelliJ release with an updated Bazel plugin.
|
Bazel must be installed as described by
|
||||||
|
<<dev-bazel#installation,Building with Bazel - Installation>>.
|
||||||
|
|
||||||
|
It's strongly recommended to verify you can build your Gerrit tree with Bazel
|
||||||
|
for Java 8 from the command line first. Ensure that at least
|
||||||
|
`bazel build gerrit` runs successfully before you proceed.
|
||||||
|
|
||||||
|
=== IntelliJ version and Bazel plugin
|
||||||
|
|
||||||
|
Before downloading IntelliJ, look at the
|
||||||
|
link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[JetBrains plugin repository page of the Bazel plugin]
|
||||||
|
to see what version of the IntelliJ IDEA it is actually compatible with.
|
||||||
|
|
||||||
|
Also note that the version of the Bazel plugin used in turn may or may not be
|
||||||
|
compatible with the Bazel version used.
|
||||||
|
|
||||||
In addition, Java 8 must be specified on your path or via `JAVA_HOME` so that
|
In addition, Java 8 must be specified on your path or via `JAVA_HOME` so that
|
||||||
building with Bazel via the Bazel plugin is possible.
|
building with Bazel via the Bazel plugin is possible.
|
||||||
@@ -13,46 +27,64 @@ TIP: If the synchronization of the project with the BUILD files using the Bazel
|
|||||||
plugin fails and IntelliJ reports the error **Could not get Bazel roots**, this
|
plugin fails and IntelliJ reports the error **Could not get Bazel roots**, this
|
||||||
indicates that the Bazel plugin couldn't find Java 8.
|
indicates that the Bazel plugin couldn't find Java 8.
|
||||||
|
|
||||||
Bazel must be installed as described by
|
=== Installation of IntelliJ IDEA
|
||||||
<<dev-bazel#installation,Building with Bazel - Installation>>.
|
|
||||||
|
Please refer to the
|
||||||
|
link:https://www.jetbrains.com/help/idea/installation-guide.html[installation guide provided by Jetbrains]
|
||||||
|
to install it on your platform. Make sure to install a version compatible with
|
||||||
|
the Bazel plugin as mentioned above.
|
||||||
|
|
||||||
== Installation of the Bazel plugin
|
== Installation of the Bazel plugin
|
||||||
|
|
||||||
|
The plugin is usually installed using the Jetbrains plugin repository as shown
|
||||||
|
in the steps below, but it's also possible to
|
||||||
|
link:https://github.com/bazelbuild/intellij[build it from source].
|
||||||
|
|
||||||
. Go to *File -> Settings -> Plugins*.
|
. Go to *File -> Settings -> Plugins*.
|
||||||
. Click on *Browse Repositories*.
|
+
|
||||||
. Search for the plugin `IntelliJ with Bazel`.
|
(Or, from the welcome screen, *Configure -> Plugins*)
|
||||||
|
. Activate the *Marketplace* tab.
|
||||||
|
. Search for the plugin `Bazel` (by Google).
|
||||||
|
+
|
||||||
|
TIP: In case the Bazel plugin is not listed, or if it shows an outdated version,
|
||||||
|
verify the compatibility between the Bazel plugin and IntelliJ IDEA on link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[the JetBrains plugin page].
|
||||||
. Install it.
|
. Install it.
|
||||||
. Restart IntelliJ.
|
. Restart IntelliJ IDEA.
|
||||||
|
|
||||||
TIP: If your project's Bazel build fails with **Cannot run program "bazel": No
|
[TIP]
|
||||||
such file or directory**, then you may have to set the binary location in the
|
====
|
||||||
Bazel plugin settings:
|
If your project's Bazel build fails with **Cannot run program "bazel": No such
|
||||||
|
file or directory**, then you may have to set the binary location in the Bazel
|
||||||
|
plugin settings:
|
||||||
|
|
||||||
. Go to Preferences -> Other Settings -> Bazel Settings.
|
. Go to *Preferences -> Other Settings -> Bazel Settings*.
|
||||||
. Set the Bazel binary location.
|
. Set the *Bazel binary location*.
|
||||||
|
====
|
||||||
|
|
||||||
== Creation of IntelliJ project
|
== Creation of the project
|
||||||
|
|
||||||
. Go to *File -> Import Bazel Project*.
|
. Go to *File -> Import Bazel Project*.
|
||||||
|
+
|
||||||
|
(Or, from the welcome screen, *Import Bazel Project* should already be shown in
|
||||||
|
there.)
|
||||||
. For *Use existing bazel workspace -> Workspace*, select the directory
|
. For *Use existing bazel workspace -> Workspace*, select the directory
|
||||||
containing the Gerrit source code.
|
containing the Gerrit source code.
|
||||||
. Choose *Import from workspace* and select the `.bazelproject` file which is
|
. Choose *Import from workspace* and select the `.bazelproject` file which is
|
||||||
located in the top directory of the Gerrit source code.
|
located in the top directory of the Gerrit source code.
|
||||||
. Adjust the path of the project data directory and the name of the project if
|
. Adjust the path of the project data directory and the name of the project if
|
||||||
desired.
|
desired.
|
||||||
|
. Finish the creation of the project.
|
||||||
|
. Verify that you can now build the project. Hit the button with the Bazel icon
|
||||||
|
(located on the top-right by default) to synchronize the project. Note that
|
||||||
|
warnings may be present in the build.
|
||||||
|
|
||||||
|
At this point all the basic functionality should be working such as Java class
|
||||||
|
inspection and running <<unit-tests,unit tests>>.
|
||||||
|
|
||||||
TIP: The project data directory can be separate from the source code. One
|
TIP: The project data directory can be separate from the source code. One
|
||||||
advantage of this is that project files don't need to be excluded from version
|
advantage of this is that project files don't need to be excluded from version
|
||||||
control.
|
control.
|
||||||
|
|
||||||
Unfortunately, the created project seems to have a broken output path. To fix
|
|
||||||
it, please complete the following steps:
|
|
||||||
|
|
||||||
. Go to *File -> Project Structure -> Project Settings -> Modules*.
|
|
||||||
. Switch to the tab *Paths*.
|
|
||||||
. Click on *Inherit project compile output path*.
|
|
||||||
. Click on *Use module compile output path*.
|
|
||||||
|
|
||||||
== Recommended settings
|
== Recommended settings
|
||||||
|
|
||||||
=== Code style
|
=== Code style
|
||||||
@@ -61,17 +93,20 @@ it, please complete the following steps:
|
|||||||
Install the `google-java-format` plugin by following these steps:
|
Install the `google-java-format` plugin by following these steps:
|
||||||
|
|
||||||
. Go to *File -> Settings -> Plugins*.
|
. Go to *File -> Settings -> Plugins*.
|
||||||
. Click on *Browse Repositories*.
|
. Activate the *Marketplace* tab.
|
||||||
. Search for the plugin `google-java-format`.
|
. Search for the plugin `google-java-format` by Google.
|
||||||
. Install it.
|
. Install it.
|
||||||
. Restart IntelliJ.
|
. Restart IntelliJ IDEA.
|
||||||
|
|
||||||
Every time you start IntelliJ, make sure to use *Code -> Reformat with
|
Every time you start IntelliJ IDEA, make sure to use *Code -> Reformat with
|
||||||
google-java-format* on an arbitrary line of code. This replaces the default
|
google-java-format* on an arbitrary line of code. This replaces the default
|
||||||
CodeStyleManager with a custom one. Thus, uses of *Reformat Code* either via
|
CodeStyleManager with a custom one. Thus, uses of *Reformat Code* either via
|
||||||
*Code -> Reformat Code*, keyboard shortcuts, or the commit dialog will use the
|
*Code -> Reformat Code*, keyboard shortcuts, or the commit dialog will use the
|
||||||
custom style defined by the `google-java-format` plugin.
|
custom style defined by the `google-java-format` plugin.
|
||||||
|
|
||||||
|
Please refer to the documentation on the <<dev-contributing#style,code style>>
|
||||||
|
for which version of `google-java-format` is used with Gerrit.
|
||||||
|
|
||||||
==== Code style settings
|
==== Code style settings
|
||||||
The `google-java-format` plugin is the preferred way to format the code. As it
|
The `google-java-format` plugin is the preferred way to format the code. As it
|
||||||
only kicks in on demand, it's also recommended to have code style settings
|
only kicks in on demand, it's also recommended to have code style settings
|
||||||
@@ -84,11 +119,10 @@ to be as close as possible. So before submitting code, please make sure to run
|
|||||||
https://raw.githubusercontent.com/google/styleguide/gh-pages/intellij-java-google-style.xml[
|
https://raw.githubusercontent.com/google/styleguide/gh-pages/intellij-java-google-style.xml[
|
||||||
intellij-java-google-style.xml].
|
intellij-java-google-style.xml].
|
||||||
. Go to *File -> Settings -> Editor -> Code Style*.
|
. Go to *File -> Settings -> Editor -> Code Style*.
|
||||||
. Click on *Manage*.
|
. Click on the wrench icon with the tooltip _Show Scheme Actions_.
|
||||||
. Click on *Import*.
|
. Click on *Import Scheme*.
|
||||||
. Choose `IntelliJ IDEA Code Style XML`.
|
|
||||||
. Select the previously downloaded file `intellij-java-google-style.xml`.
|
. Select the previously downloaded file `intellij-java-google-style.xml`.
|
||||||
. Make sure that `Google Style` is chosen as *Scheme*.
|
. Make sure that `GoogleStyle` is chosen as the current *Scheme*.
|
||||||
|
|
||||||
In addition, the EditorConfig settings (which ensure a consistent style between
|
In addition, the EditorConfig settings (which ensure a consistent style between
|
||||||
Eclipse, IntelliJ, and other editors) should be applied on top of that. Those
|
Eclipse, IntelliJ, and other editors) should be applied on top of that. Those
|
||||||
@@ -97,7 +131,7 @@ will automatically pick up those settings if the EditorConfig plugin is enabled
|
|||||||
and configured correctly as can be verified by:
|
and configured correctly as can be verified by:
|
||||||
|
|
||||||
. Go to *File -> Settings -> Plugins*.
|
. Go to *File -> Settings -> Plugins*.
|
||||||
. Ensure that the EditorConfig plugin is enabled.
|
. Ensure that the *EditorConfig* plugin (by JetBrains) is enabled.
|
||||||
. Go to *File -> Settings -> Editor -> Code Style*.
|
. Go to *File -> Settings -> Editor -> Code Style*.
|
||||||
. Ensure that *Enable EditorConfig support* is checked.
|
. Ensure that *Enable EditorConfig support* is checked.
|
||||||
|
|
||||||
@@ -105,35 +139,42 @@ NOTE: If IntelliJ notifies you later on that the EditorConfig settings override
|
|||||||
the code style settings, simply confirm that.
|
the code style settings, simply confirm that.
|
||||||
|
|
||||||
=== Copyright
|
=== Copyright
|
||||||
Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
|
|
||||||
|
. Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
|
||||||
contents) to `$(project_data_directory)/.idea`. If it already exists, replace
|
contents) to `$(project_data_directory)/.idea`. If it already exists, replace
|
||||||
it. Then go to *File -> Settings -> Editor -> Copyright -> Copyright Profiles*,
|
it. If you didn't select a custom data directory the command could look like
|
||||||
and import `Gerrit_Copyright.xml` to IntelliJ in case it doesn't pick the
|
this, as run from the Gerrit source tree checkout as working directory:
|
||||||
copyright up automatically.
|
+
|
||||||
|
----
|
||||||
|
cp -r tools/intellij/copyright .ijwb/.idea/
|
||||||
|
----
|
||||||
|
. Go to *File -> Settings -> Editor -> Copyright -> Copyright Profiles*.
|
||||||
|
. Verify that the *Gerrit Copyright* is now present there.
|
||||||
|
+
|
||||||
|
Only in case it hasn't picked up the copyright profile automatically, import
|
||||||
|
the `Gerrit_Copyright.xml` from that folder manually.
|
||||||
|
|
||||||
=== File header
|
=== Git integration
|
||||||
By default, IntelliJ adds a file header containing the name of the author and
|
This section is only relevant in case you want to use the Git integration
|
||||||
the current date to new files. To disable that, follow these steps:
|
plugin in IntelliJ IDEA.
|
||||||
|
|
||||||
. Go to *File -> Settings -> Editor -> File and Code Templates*.
|
|
||||||
. Select the tab *Includes*.
|
|
||||||
. Select *File Header*.
|
|
||||||
. Remove the template code in the right editor.
|
|
||||||
|
|
||||||
=== Commit message
|
|
||||||
To simplify the creation of commit messages which are compliant with the
|
To simplify the creation of commit messages which are compliant with the
|
||||||
<<dev-contributing#commit-message,Commit Message>> format, do the following:
|
<<dev-contributing#commit-message,Commit Message>> format, do the following:
|
||||||
|
|
||||||
. Go to *File -> Settings -> Version Control*.
|
. Go to *File -> Settings -> Version Control -> Commit Dialog*.
|
||||||
. Check *Commit message right margin (columns)*.
|
. In the *Commit message inspections*, activate the three inspections:
|
||||||
. Make sure that 72 is specified as value.
|
* *Blank line between subject and body*,
|
||||||
. Check *Wrap when typing reaches right margin*.
|
* *Limit body line* and
|
||||||
|
* *Limit subject line*.
|
||||||
|
. For the limit line inspections, make sure that 72 is specified as value.
|
||||||
|
. For *Limit body line*, tick *Show right margin* and *Wrap when typing reaches
|
||||||
|
right margin*.
|
||||||
|
|
||||||
In addition, you should follow the instructions of
|
In addition, you should follow the instructions of
|
||||||
<<dev-contributing#git_commit_settings,this section>> (if you haven't
|
<<dev-contributing#git_commit_settings,this section>> (if you haven't
|
||||||
done so already):
|
done so already):
|
||||||
|
|
||||||
* Install the Git hook for the `Change-Id` line.
|
* Install the Git commit message hook for the `Change-Id` line.
|
||||||
* Set up the HTTP access.
|
* Set up the HTTP access.
|
||||||
|
|
||||||
Setting up the HTTP access will allow you to commit changes via IntelliJ without
|
Setting up the HTTP access will allow you to commit changes via IntelliJ without
|
||||||
@@ -145,29 +186,18 @@ Run configurations can be accessed on the toolbar. To edit them or add new ones,
|
|||||||
choose *Edit Configurations* on the drop-down list of the run configurations
|
choose *Edit Configurations* on the drop-down list of the run configurations
|
||||||
or go to *Run -> Edit Configurations*.
|
or go to *Run -> Edit Configurations*.
|
||||||
|
|
||||||
=== Pre-configured run configurations
|
[[runconfigurations-daemon]]
|
||||||
|
|
||||||
In order to be able to use the pre-configured run configurations, the following
|
|
||||||
steps are necessary:
|
|
||||||
|
|
||||||
. Make sure that the folder `runConfigurations` exists within
|
|
||||||
`$(project_data_directory)/.idea`. If it doesn't exist, create it.
|
|
||||||
. Specify the IntelliJ path variable `GERRIT_TESTSITE`. (This configuration is
|
|
||||||
shared among all IntelliJ projects.)
|
|
||||||
.. Go to *Settings -> Appearance & Behavior -> Path Variables*.
|
|
||||||
.. Click on the *+* to add a new path variable.
|
|
||||||
.. Specify `GERRIT_TESTSITE` as name and the path to your local test site as
|
|
||||||
value.
|
|
||||||
|
|
||||||
The copied run configurations will be added automatically to the available run
|
|
||||||
configurations of the IntelliJ project.
|
|
||||||
|
|
||||||
==== Gerrit Daemon
|
==== Gerrit Daemon
|
||||||
WARNING: At the moment running this configuration results in a
|
[WARNING]
|
||||||
|
====
|
||||||
|
At the moment running this (local) configuration results in a
|
||||||
`java.io.FileNotFoundException`. To debug a local Gerrit server with IntelliJ,
|
`java.io.FileNotFoundException`. To debug a local Gerrit server with IntelliJ,
|
||||||
use the instructions of <<dev-readme#run_daemon,Running the Daemon>> in
|
use the instructions of <<dev-readme#run_daemon,Running the Daemon>> in
|
||||||
combination with <<remote-debug,Debugging a remote Gerrit server>>.
|
combination with <<remote-debug,Debugging a remote Gerrit server>>.
|
||||||
|
|
||||||
|
(link:https://bugs.chromium.org/p/gerrit/issues/detail?id=11360[Issue 11360])
|
||||||
|
====
|
||||||
|
|
||||||
Copy `$(gerrit_source_code)/tools/intellij/gerrit_daemon.xml` to
|
Copy `$(gerrit_source_code)/tools/intellij/gerrit_daemon.xml` to
|
||||||
`$(project_data_directory)/.idea/runConfigurations/`.
|
`$(project_data_directory)/.idea/runConfigurations/`.
|
||||||
|
|
||||||
@@ -177,10 +207,12 @@ This run configuration starts the Gerrit daemon similarly as
|
|||||||
NOTE: The <<dev-readme#init,Site Initialization>> has to be completed
|
NOTE: The <<dev-readme#init,Site Initialization>> has to be completed
|
||||||
before this run configuration works properly.
|
before this run configuration works properly.
|
||||||
|
|
||||||
|
[[unit-tests]]
|
||||||
=== Unit tests
|
=== Unit tests
|
||||||
To create run configurations for unit tests, run or debug them via a right-click
|
To create run configurations for unit tests, run or debug them via a right-click
|
||||||
on a method, class, file, or package. The created run configuration is a
|
on a method, class, file, or package. The created run configuration is a
|
||||||
temporary one and can be saved to make it permanent.
|
temporary one and can be saved to make it permanent by selecting *Create
|
||||||
|
'Bazel test [...]'...* from the context menu.
|
||||||
|
|
||||||
Normally, this approach generates JUnit run configurations. When the Bazel
|
Normally, this approach generates JUnit run configurations. When the Bazel
|
||||||
plugin manages a project, it intercepts the creation and creates a Bazel test
|
plugin manages a project, it intercepts the creation and creates a Bazel test
|
||||||
@@ -193,10 +225,15 @@ IntelliJ via a `Remote debug configuration`.
|
|||||||
|
|
||||||
. Go to *Run -> Edit Configurations*.
|
. Go to *Run -> Edit Configurations*.
|
||||||
. Click on the *+* to add a new configuration.
|
. Click on the *+* to add a new configuration.
|
||||||
. Choose *Remote*.
|
. Choose *Remote* from the *Templates*.
|
||||||
. Adjust *Configuration -> Settings -> Host* and *Port*.
|
. Adjust *Configuration -> Settings -> Host* and *Port*.
|
||||||
. Start this configuration in `Debug` mode.
|
. Start this configuration in `Debug` mode.
|
||||||
|
|
||||||
|
TIP: This run configuration dialog also shows the line for the JVM as startup
|
||||||
|
flag that you can copy to include in your
|
||||||
|
`$(gerrit_test_site)/etc/gerrit.config` in the `[container]` section in order
|
||||||
|
to work-around the <<runconfigurations-daemon,local run configuration issue>>.
|
||||||
|
|
||||||
GERRIT
|
GERRIT
|
||||||
------
|
------
|
||||||
Part of link:index.html[Gerrit Code Review]
|
Part of link:index.html[Gerrit Code Review]
|
||||||
|
|||||||
@@ -251,7 +251,10 @@ link:https://gerrit-review.googlesource.com/admin/repos/gerrit,branches[
|
|||||||
Gerrit Web UI] or by push.
|
Gerrit Web UI] or by push.
|
||||||
|
|
||||||
* Push the commits done on `stable-$version` to `refs/for/stable-$version` and
|
* Push the commits done on `stable-$version` to `refs/for/stable-$version` and
|
||||||
get them merged
|
get them merged.
|
||||||
|
|
||||||
|
* Create a change updating the `defaultbranch` field in the `.gitreview`
|
||||||
|
to match the branch name created.
|
||||||
|
|
||||||
|
|
||||||
[[push-tag]]
|
[[push-tag]]
|
||||||
|
|||||||
@@ -4934,12 +4934,22 @@ need the FileInfo should make two requests.
|
|||||||
The request parameter `q` changes the response to return a list
|
The request parameter `q` changes the response to return a list
|
||||||
of all files (modified or unmodified) that contain that substring
|
of all files (modified or unmodified) that contain that substring
|
||||||
in the path name. This is useful to implement suggestion services
|
in the path name. This is useful to implement suggestion services
|
||||||
finding a file by partial name.
|
finding a file by partial name. Clients that also need the FileInfo
|
||||||
|
should make two requests.
|
||||||
|
|
||||||
The integer-valued request parameter `parent` changes the response to return a
|
For merge commits only, the integer-valued request parameter `parent`
|
||||||
list of the files which are different in this commit compared to the given
|
changes the response to return a map of the files which are different
|
||||||
parent commit. This is useful for supporting review of merge commits. The value
|
in this commit compared to the given parent commit. The value is the
|
||||||
is the 1-based index of the parent's position in the commit object.
|
1-based index of the parent's position in the commit object. If not
|
||||||
|
specified, the response contains a map of the files different in the
|
||||||
|
auto merge result.
|
||||||
|
|
||||||
|
The request parameter `base` changes the response to return a map of the
|
||||||
|
files which are different in this commit compared to the given revision. The
|
||||||
|
revision must correspond to a patch set in the change.
|
||||||
|
|
||||||
|
The `reviewed`, `q`, `parent`, and `base` options are mutually exclusive.
|
||||||
|
That is, only one of them may be used at a time.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -1062,8 +1062,8 @@ maven_jar(
|
|||||||
# and httpasyncclient as necessary.
|
# and httpasyncclient as necessary.
|
||||||
maven_jar(
|
maven_jar(
|
||||||
name = "elasticsearch-rest-client",
|
name = "elasticsearch-rest-client",
|
||||||
artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.2.0",
|
artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.3.1",
|
||||||
sha1 = "39cf34068b0af284eaa9b8bd86a131cb24b322d5",
|
sha1 = "f5793c89b50a159cbb3e15e17bb981ff854cbe51",
|
||||||
)
|
)
|
||||||
|
|
||||||
maven_jar(
|
maven_jar(
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public @interface UsedAt {
|
|||||||
/** Enumeration of projects that call a method/type/field. */
|
/** Enumeration of projects that call a method/type/field. */
|
||||||
enum Project {
|
enum Project {
|
||||||
GOOGLE,
|
GOOGLE,
|
||||||
|
COLLABNET,
|
||||||
PLUGIN_CHECKS,
|
PLUGIN_CHECKS,
|
||||||
PLUGIN_DELETE_PROJECT,
|
PLUGIN_DELETE_PROJECT,
|
||||||
PLUGIN_SERVICEUSER,
|
PLUGIN_SERVICEUSER,
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ public enum ElasticVersion {
|
|||||||
V6_5("6.5.*"),
|
V6_5("6.5.*"),
|
||||||
V6_6("6.6.*"),
|
V6_6("6.6.*"),
|
||||||
V6_7("6.7.*"),
|
V6_7("6.7.*"),
|
||||||
|
V6_8("6.8.*"),
|
||||||
V7_0("7.0.*"),
|
V7_0("7.0.*"),
|
||||||
V7_1("7.1.*"),
|
V7_1("7.1.*"),
|
||||||
V7_2("7.2.*");
|
V7_2("7.2.*"),
|
||||||
|
V7_3("7.3.*");
|
||||||
|
|
||||||
private final String version;
|
private final String version;
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
|
|||||||
@@ -335,6 +335,15 @@ public interface ChangeApi {
|
|||||||
*/
|
*/
|
||||||
Map<String, List<CommentInfo>> comments() throws RestApiException;
|
Map<String, List<CommentInfo>> comments() throws RestApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all published comments on a change as a list.
|
||||||
|
*
|
||||||
|
* @return comments as a list; comments have the {@code revision} field set to indicate their
|
||||||
|
* patch set.
|
||||||
|
* @throws RestApiException
|
||||||
|
*/
|
||||||
|
List<CommentInfo> commentsAsList() throws RestApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all robot comments on a change.
|
* Get all robot comments on a change.
|
||||||
*
|
*
|
||||||
@@ -353,6 +362,15 @@ public interface ChangeApi {
|
|||||||
*/
|
*/
|
||||||
Map<String, List<CommentInfo>> drafts() throws RestApiException;
|
Map<String, List<CommentInfo>> drafts() throws RestApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all draft comments for the current user on a change as a list.
|
||||||
|
*
|
||||||
|
* @return drafts as a list; comments have the {@code revision} field set to indicate their patch
|
||||||
|
* set.
|
||||||
|
* @throws RestApiException
|
||||||
|
*/
|
||||||
|
List<CommentInfo> draftsAsList() throws RestApiException;
|
||||||
|
|
||||||
ChangeInfo check() throws RestApiException;
|
ChangeInfo check() throws RestApiException;
|
||||||
|
|
||||||
ChangeInfo check(FixInput fix) throws RestApiException;
|
ChangeInfo check(FixInput fix) throws RestApiException;
|
||||||
@@ -580,6 +598,11 @@ public interface ChangeApi {
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommentInfo> commentsAsList() throws RestApiException {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
|
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
@@ -590,6 +613,11 @@ public interface ChangeApi {
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommentInfo> draftsAsList() throws RestApiException {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeInfo check() throws RestApiException {
|
public ChangeInfo check() throws RestApiException {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ package com.google.gerrit.extensions.api.projects;
|
|||||||
import com.google.gerrit.extensions.api.changes.ChangeApi;
|
import com.google.gerrit.extensions.api.changes.ChangeApi;
|
||||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||||
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
||||||
|
import com.google.gerrit.extensions.common.CommitInfo;
|
||||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
|
|
||||||
public interface CommitApi {
|
public interface CommitApi {
|
||||||
|
CommitInfo get() throws RestApiException;
|
||||||
|
|
||||||
ChangeApi cherryPick(CherryPickInput input) throws RestApiException;
|
ChangeApi cherryPick(CherryPickInput input) throws RestApiException;
|
||||||
|
|
||||||
@@ -28,6 +30,11 @@ public interface CommitApi {
|
|||||||
|
|
||||||
/** A default implementation for source compatibility when adding new methods to the interface. */
|
/** A default implementation for source compatibility when adding new methods to the interface. */
|
||||||
class NotImplemented implements CommitApi {
|
class NotImplemented implements CommitApi {
|
||||||
|
@Override
|
||||||
|
public CommitInfo get() throws RestApiException {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
|
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public final class GerritLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a proram.
|
* Invokes a program.
|
||||||
*
|
*
|
||||||
* <p>Creates a new classloader to load and run the program class. To reuse a classloader across
|
* <p>Creates a new classloader to load and run the program class. To reuse a classloader across
|
||||||
* calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
|
* calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
|
||||||
@@ -176,7 +176,7 @@ public final class GerritLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a proram in the provided {@code ClassLoader}.
|
* Invokes a program in the provided {@code ClassLoader}.
|
||||||
*
|
*
|
||||||
* @param loader classloader to load program class from.
|
* @param loader classloader to load program class from.
|
||||||
* @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the
|
* @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the
|
||||||
|
|||||||
@@ -558,6 +558,15 @@ class ChangeApiImpl implements ChangeApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommentInfo> commentsAsList() throws RestApiException {
|
||||||
|
try {
|
||||||
|
return listComments.getComments(change);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw asRestApiException("Cannot get comments", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
|
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
|
||||||
try {
|
try {
|
||||||
@@ -576,6 +585,15 @@ class ChangeApiImpl implements ChangeApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommentInfo> draftsAsList() throws RestApiException {
|
||||||
|
try {
|
||||||
|
return listDrafts.getComments(change);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw asRestApiException("Cannot get drafts", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeInfo check() throws RestApiException {
|
public ChangeInfo check() throws RestApiException {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ import com.google.gerrit.extensions.api.changes.Changes;
|
|||||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||||
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
||||||
import com.google.gerrit.extensions.api.projects.CommitApi;
|
import com.google.gerrit.extensions.api.projects.CommitApi;
|
||||||
|
import com.google.gerrit.extensions.common.CommitInfo;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.server.project.CommitResource;
|
import com.google.gerrit.server.project.CommitResource;
|
||||||
import com.google.gerrit.server.restapi.change.CherryPickCommit;
|
import com.google.gerrit.server.restapi.change.CherryPickCommit;
|
||||||
import com.google.gerrit.server.restapi.project.CommitIncludedIn;
|
import com.google.gerrit.server.restapi.project.CommitIncludedIn;
|
||||||
|
import com.google.gerrit.server.restapi.project.GetCommit;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ public class CommitApiImpl implements CommitApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Changes changes;
|
private final Changes changes;
|
||||||
|
private final GetCommit getCommit;
|
||||||
private final CherryPickCommit cherryPickCommit;
|
private final CherryPickCommit cherryPickCommit;
|
||||||
private final CommitIncludedIn includedIn;
|
private final CommitIncludedIn includedIn;
|
||||||
private final CommitResource commitResource;
|
private final CommitResource commitResource;
|
||||||
@@ -41,15 +44,26 @@ public class CommitApiImpl implements CommitApi {
|
|||||||
@Inject
|
@Inject
|
||||||
CommitApiImpl(
|
CommitApiImpl(
|
||||||
Changes changes,
|
Changes changes,
|
||||||
|
GetCommit getCommit,
|
||||||
CherryPickCommit cherryPickCommit,
|
CherryPickCommit cherryPickCommit,
|
||||||
CommitIncludedIn includedIn,
|
CommitIncludedIn includedIn,
|
||||||
@Assisted CommitResource commitResource) {
|
@Assisted CommitResource commitResource) {
|
||||||
this.changes = changes;
|
this.changes = changes;
|
||||||
|
this.getCommit = getCommit;
|
||||||
this.cherryPickCommit = cherryPickCommit;
|
this.cherryPickCommit = cherryPickCommit;
|
||||||
this.includedIn = includedIn;
|
this.includedIn = includedIn;
|
||||||
this.commitResource = commitResource;
|
this.commitResource = commitResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommitInfo get() throws RestApiException {
|
||||||
|
try {
|
||||||
|
return getCommit.apply(commitResource).value();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw asRestApiException("Cannot get commit info", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
|
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public abstract class AuthRequest {
|
|||||||
/**
|
/**
|
||||||
* Returns the username to be authenticated.
|
* Returns the username to be authenticated.
|
||||||
*
|
*
|
||||||
* @return username for authentication or null for anonymous access.
|
* @return username for authentication or {@code empty} for anonymous access.
|
||||||
*/
|
*/
|
||||||
public final Optional<String> getUsername() {
|
public final Optional<String> getUsername() {
|
||||||
return username;
|
return username;
|
||||||
@@ -40,7 +40,7 @@ public abstract class AuthRequest {
|
|||||||
/**
|
/**
|
||||||
* Returns the user's credentials
|
* Returns the user's credentials
|
||||||
*
|
*
|
||||||
* @return user's credentials or null
|
* @return user's credentials or {@code empty}.
|
||||||
*/
|
*/
|
||||||
public final Optional<String> getPassword() {
|
public final Optional<String> getPassword() {
|
||||||
return password;
|
return password;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import com.google.gerrit.extensions.restapi.RestApiException;
|
|||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
|
import com.google.gerrit.server.config.ChangeCleanupConfig;
|
||||||
|
import com.google.gerrit.server.plugincontext.PluginItemContext;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
import com.google.gerrit.server.update.BatchUpdate;
|
import com.google.gerrit.server.update.BatchUpdate;
|
||||||
import com.google.gerrit.server.update.UpdateException;
|
import com.google.gerrit.server.update.UpdateException;
|
||||||
@@ -30,10 +32,17 @@ import java.util.Collection;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class BatchAbandon {
|
public class BatchAbandon {
|
||||||
private final AbandonOp.Factory abandonOpFactory;
|
private final AbandonOp.Factory abandonOpFactory;
|
||||||
|
private final ChangeCleanupConfig cfg;
|
||||||
|
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BatchAbandon(AbandonOp.Factory abandonOpFactory) {
|
BatchAbandon(
|
||||||
|
AbandonOp.Factory abandonOpFactory,
|
||||||
|
ChangeCleanupConfig cfg,
|
||||||
|
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
|
||||||
this.abandonOpFactory = abandonOpFactory;
|
this.abandonOpFactory = abandonOpFactory;
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.accountPatchReviewStore = accountPatchReviewStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +76,10 @@ public class BatchAbandon {
|
|||||||
u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
|
u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
|
||||||
}
|
}
|
||||||
u.execute();
|
u.execute();
|
||||||
|
|
||||||
|
if (cfg.getCleanupAccountPatchReview()) {
|
||||||
|
cleanupAccountPatchReview(changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,4 +101,10 @@ public class BatchAbandon {
|
|||||||
throws RestApiException, UpdateException {
|
throws RestApiException, UpdateException {
|
||||||
batchAbandon(updateFactory, project, user, changes, "", NotifyResolver.Result.all());
|
batchAbandon(updateFactory, project, user, changes, "", NotifyResolver.Result.all());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanupAccountPatchReview(Collection<ChangeData> changes) {
|
||||||
|
for (ChangeData change : changes) {
|
||||||
|
accountPatchReviewStore.run(s -> s.clearReviewed(change.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class ChangeCleanupConfig {
|
|||||||
private static String KEY_ABANDON_AFTER = "abandonAfter";
|
private static String KEY_ABANDON_AFTER = "abandonAfter";
|
||||||
private static String KEY_ABANDON_IF_MERGEABLE = "abandonIfMergeable";
|
private static String KEY_ABANDON_IF_MERGEABLE = "abandonIfMergeable";
|
||||||
private static String KEY_ABANDON_MESSAGE = "abandonMessage";
|
private static String KEY_ABANDON_MESSAGE = "abandonMessage";
|
||||||
|
private static String KEY_CLEANUP_ACCOUNT_PATCH_REVIEW = "cleanupAccountPatchReview";
|
||||||
private static String DEFAULT_ABANDON_MESSAGE =
|
private static String DEFAULT_ABANDON_MESSAGE =
|
||||||
"Auto-Abandoned due to inactivity, see "
|
"Auto-Abandoned due to inactivity, see "
|
||||||
+ "${URL}\n"
|
+ "${URL}\n"
|
||||||
@@ -39,6 +40,7 @@ public class ChangeCleanupConfig {
|
|||||||
private final Optional<Schedule> schedule;
|
private final Optional<Schedule> schedule;
|
||||||
private final long abandonAfter;
|
private final long abandonAfter;
|
||||||
private final boolean abandonIfMergeable;
|
private final boolean abandonIfMergeable;
|
||||||
|
private final boolean cleanupAccountPatchReview;
|
||||||
private final String abandonMessage;
|
private final String abandonMessage;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -47,6 +49,8 @@ public class ChangeCleanupConfig {
|
|||||||
schedule = ScheduleConfig.createSchedule(cfg, SECTION);
|
schedule = ScheduleConfig.createSchedule(cfg, SECTION);
|
||||||
abandonAfter = readAbandonAfter(cfg);
|
abandonAfter = readAbandonAfter(cfg);
|
||||||
abandonIfMergeable = cfg.getBoolean(SECTION, null, KEY_ABANDON_IF_MERGEABLE, true);
|
abandonIfMergeable = cfg.getBoolean(SECTION, null, KEY_ABANDON_IF_MERGEABLE, true);
|
||||||
|
cleanupAccountPatchReview =
|
||||||
|
cfg.getBoolean(SECTION, null, KEY_CLEANUP_ACCOUNT_PATCH_REVIEW, false);
|
||||||
abandonMessage = readAbandonMessage(cfg);
|
abandonMessage = readAbandonMessage(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +77,10 @@ public class ChangeCleanupConfig {
|
|||||||
return abandonIfMergeable;
|
return abandonIfMergeable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getCleanupAccountPatchReview() {
|
||||||
|
return cleanupAccountPatchReview;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAbandonMessage() {
|
public String getAbandonMessage() {
|
||||||
String docUrl =
|
String docUrl =
|
||||||
urlFormatter.get().getDocUrl("user-change-cleanup.html", "auto-abandon").orElse("");
|
urlFormatter.get().getDocUrl("user-change-cleanup.html", "auto-abandon").orElse("");
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter {
|
|||||||
private static final int SUBJECT_CROP_RANGE = 10;
|
private static final int SUBJECT_CROP_RANGE = 10;
|
||||||
private static final String NEW_CHANGE_INDICATOR = " [NEW]";
|
private static final String NEW_CHANGE_INDICATOR = " [NEW]";
|
||||||
|
|
||||||
private final DynamicItem<UrlFormatter> urlFormatter;
|
protected final DynamicItem<UrlFormatter> urlFormatter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DefaultChangeReportFormatter(DynamicItem<UrlFormatter> urlFormatter) {
|
public DefaultChangeReportFormatter(DynamicItem<UrlFormatter> urlFormatter) {
|
||||||
this.urlFormatter = urlFormatter;
|
this.urlFormatter = urlFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ package com.google.gerrit.server.query.account;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.flogger.FluentLogger;
|
||||||
|
import com.google.gerrit.common.UsedAt;
|
||||||
import com.google.gerrit.index.FieldDef;
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.IndexConfig;
|
import com.google.gerrit.index.IndexConfig;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
@@ -41,6 +44,8 @@ import java.util.Set;
|
|||||||
* holding on to a single instance.
|
* holding on to a single instance.
|
||||||
*/
|
*/
|
||||||
public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> {
|
public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> {
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
InternalAccountQuery(
|
InternalAccountQuery(
|
||||||
AccountQueryProcessor queryProcessor,
|
AccountQueryProcessor queryProcessor,
|
||||||
@@ -61,6 +66,23 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
|
|||||||
return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
|
return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UsedAt(UsedAt.Project.COLLABNET)
|
||||||
|
public AccountState oneByExternalId(ExternalId.Key externalId) {
|
||||||
|
List<AccountState> accountStates = byExternalId(externalId);
|
||||||
|
if (accountStates.size() == 1) {
|
||||||
|
return accountStates.get(0);
|
||||||
|
} else if (accountStates.size() > 0) {
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: ");
|
||||||
|
Joiner.on(", ")
|
||||||
|
.appendTo(
|
||||||
|
msg,
|
||||||
|
accountStates.stream().map(a -> a.getAccount().id().toString()).collect(toList()));
|
||||||
|
logger.atWarning().log(msg.toString());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public List<AccountState> byFullName(String fullName) {
|
public List<AccountState> byFullName(String fullName) {
|
||||||
return query(AccountPredicates.fullName(fullName));
|
return query(AccountPredicates.fullName(fullName));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.google.common.flogger.FluentLogger;
|
|||||||
import com.google.common.hash.Hasher;
|
import com.google.common.hash.Hasher;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.extensions.api.GerritApi;
|
||||||
import com.google.gerrit.extensions.common.FileInfo;
|
import com.google.gerrit.extensions.common.FileInfo;
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
@@ -27,8 +28,8 @@ import com.google.gerrit.extensions.restapi.CacheControl;
|
|||||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||||
import com.google.gerrit.extensions.restapi.ETagView;
|
import com.google.gerrit.extensions.restapi.ETagView;
|
||||||
import com.google.gerrit.extensions.restapi.IdString;
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
|
||||||
import com.google.gerrit.extensions.restapi.Response;
|
import com.google.gerrit.extensions.restapi.Response;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
@@ -118,6 +119,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
|
|||||||
private final PatchListCache patchListCache;
|
private final PatchListCache patchListCache;
|
||||||
private final PatchSetUtil psUtil;
|
private final PatchSetUtil psUtil;
|
||||||
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
|
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
|
||||||
|
private final GerritApi gApi;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ListFiles(
|
ListFiles(
|
||||||
@@ -127,7 +129,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
|
|||||||
GitRepositoryManager gitManager,
|
GitRepositoryManager gitManager,
|
||||||
PatchListCache patchListCache,
|
PatchListCache patchListCache,
|
||||||
PatchSetUtil psUtil,
|
PatchSetUtil psUtil,
|
||||||
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
|
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
|
||||||
|
GerritApi gApi) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.fileInfoJson = fileInfoJson;
|
this.fileInfoJson = fileInfoJson;
|
||||||
this.revisions = revisions;
|
this.revisions = revisions;
|
||||||
@@ -135,6 +138,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
|
|||||||
this.patchListCache = patchListCache;
|
this.patchListCache = patchListCache;
|
||||||
this.psUtil = psUtil;
|
this.psUtil = psUtil;
|
||||||
this.accountPatchReviewStore = accountPatchReviewStore;
|
this.accountPatchReviewStore = accountPatchReviewStore;
|
||||||
|
this.gApi = gApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListFiles setReviewed(boolean r) {
|
public ListFiles setReviewed(boolean r) {
|
||||||
@@ -144,9 +148,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(RevisionResource resource)
|
public Response<?> apply(RevisionResource resource)
|
||||||
throws AuthException, BadRequestException, ResourceNotFoundException,
|
throws RestApiException, RepositoryNotFoundException, IOException,
|
||||||
RepositoryNotFoundException, IOException, PatchListNotAvailableException,
|
PatchListNotAvailableException, PermissionBackendException {
|
||||||
PermissionBackendException {
|
|
||||||
checkOptions();
|
checkOptions();
|
||||||
if (reviewed) {
|
if (reviewed) {
|
||||||
return Response.ok(reviewed(resource));
|
return Response.ok(reviewed(resource));
|
||||||
@@ -164,7 +167,17 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
|
|||||||
resource.getChange(),
|
resource.getChange(),
|
||||||
resource.getPatchSet().commitId(),
|
resource.getPatchSet().commitId(),
|
||||||
baseResource.getPatchSet()));
|
baseResource.getPatchSet()));
|
||||||
} else if (parentNum > 0) {
|
} else if (parentNum != 0) {
|
||||||
|
int parents =
|
||||||
|
gApi.changes()
|
||||||
|
.id(resource.getChange().getChangeId())
|
||||||
|
.revision(resource.getPatchSet().id().get())
|
||||||
|
.commit(false)
|
||||||
|
.parents
|
||||||
|
.size();
|
||||||
|
if (parentNum < 0 || parentNum > parents) {
|
||||||
|
throw new BadRequestException(String.format("invalid parent number: %d", parentNum));
|
||||||
|
}
|
||||||
r =
|
r =
|
||||||
Response.ok(
|
Response.ok(
|
||||||
fileInfoJson.toFileInfoMap(
|
fileInfoJson.toFileInfoMap(
|
||||||
|
|||||||
@@ -14,46 +14,37 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.restapi.change;
|
package com.google.gerrit.server.restapi.change;
|
||||||
|
|
||||||
import com.google.gerrit.extensions.common.CommentInfo;
|
import com.google.gerrit.reviewdb.client.Comment;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
|
||||||
import com.google.gerrit.extensions.restapi.Response;
|
|
||||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
|
||||||
import com.google.gerrit.server.CommentsUtil;
|
import com.google.gerrit.server.CommentsUtil;
|
||||||
import com.google.gerrit.server.change.ChangeResource;
|
import com.google.gerrit.server.change.ChangeResource;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ListChangeComments implements RestReadView<ChangeResource> {
|
public class ListChangeComments extends ListChangeDrafts {
|
||||||
private final ChangeData.Factory changeDataFactory;
|
|
||||||
private final Provider<CommentJson> commentJson;
|
|
||||||
private final CommentsUtil commentsUtil;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ListChangeComments(
|
ListChangeComments(
|
||||||
ChangeData.Factory changeDataFactory,
|
ChangeData.Factory changeDataFactory,
|
||||||
Provider<CommentJson> commentJson,
|
Provider<CommentJson> commentJson,
|
||||||
CommentsUtil commentsUtil) {
|
CommentsUtil commentsUtil) {
|
||||||
this.changeDataFactory = changeDataFactory;
|
super(changeDataFactory, commentJson, commentsUtil);
|
||||||
this.commentJson = commentJson;
|
|
||||||
this.commentsUtil = commentsUtil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
|
protected Iterable<Comment> listComments(ChangeResource rsrc) {
|
||||||
throws AuthException, PermissionBackendException {
|
|
||||||
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
|
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
|
||||||
return Response.ok(
|
return commentsUtil.publishedByChange(cd.notes());
|
||||||
commentJson
|
}
|
||||||
.get()
|
|
||||||
.setFillAccounts(true)
|
@Override
|
||||||
.setFillPatchSet(true)
|
protected boolean includeAuthorInfo() {
|
||||||
.newCommentFormatter()
|
return true;
|
||||||
.format(commentsUtil.publishedByChange(cd.notes())));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requireAuthentication() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.google.gerrit.server.CommentsUtil;
|
|||||||
import com.google.gerrit.server.change.ChangeResource;
|
import com.google.gerrit.server.change.ChangeResource;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
|
import com.google.gerrit.server.restapi.change.CommentJson.CommentFormatter;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -31,9 +32,9 @@ import java.util.Map;
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ListChangeDrafts implements RestReadView<ChangeResource> {
|
public class ListChangeDrafts implements RestReadView<ChangeResource> {
|
||||||
private final ChangeData.Factory changeDataFactory;
|
protected final ChangeData.Factory changeDataFactory;
|
||||||
private final Provider<CommentJson> commentJson;
|
protected final Provider<CommentJson> commentJson;
|
||||||
private final CommentsUtil commentsUtil;
|
protected final CommentsUtil commentsUtil;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ListChangeDrafts(
|
ListChangeDrafts(
|
||||||
@@ -45,21 +46,41 @@ public class ListChangeDrafts implements RestReadView<ChangeResource> {
|
|||||||
this.commentsUtil = commentsUtil;
|
this.commentsUtil = commentsUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Iterable<Comment> listComments(ChangeResource rsrc) {
|
||||||
|
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
|
||||||
|
return commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean includeAuthorInfo() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requireAuthentication() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
|
public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
|
||||||
throws AuthException, PermissionBackendException {
|
throws AuthException, PermissionBackendException {
|
||||||
if (!rsrc.getUser().isIdentifiedUser()) {
|
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
|
||||||
throw new AuthException("Authentication required");
|
throw new AuthException("Authentication required");
|
||||||
}
|
}
|
||||||
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
|
return Response.ok(getCommentFormatter().format(listComments(rsrc)));
|
||||||
List<Comment> drafts =
|
}
|
||||||
commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
|
|
||||||
return Response.ok(
|
public List<CommentInfo> getComments(ChangeResource rsrc)
|
||||||
commentJson
|
throws AuthException, PermissionBackendException {
|
||||||
.get()
|
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
|
||||||
.setFillAccounts(false)
|
throw new AuthException("Authentication required");
|
||||||
.setFillPatchSet(true)
|
}
|
||||||
.newCommentFormatter()
|
return getCommentFormatter().formatAsList(listComments(rsrc));
|
||||||
.format(drafts));
|
}
|
||||||
|
|
||||||
|
private CommentFormatter getCommentFormatter() {
|
||||||
|
return commentJson
|
||||||
|
.get()
|
||||||
|
.setFillAccounts(includeAuthorInfo())
|
||||||
|
.setFillPatchSet(true)
|
||||||
|
.newCommentFormatter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ public class PrologEnvironment extends BufferingPrologControl {
|
|||||||
public void setPredicate(Predicate goal) {
|
public void setPredicate(Predicate goal) {
|
||||||
super.setPredicate(goal);
|
super.setPredicate(goal);
|
||||||
int reductionLimit = args.reductionLimit(goal);
|
int reductionLimit = args.reductionLimit(goal);
|
||||||
logger.atFine().log("setting reductionLimit %d", reductionLimit);
|
|
||||||
setReductionLimit(reductionLimit);
|
setReductionLimit(reductionLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +222,9 @@ public class PrologEnvironment extends BufferingPrologControl {
|
|||||||
|
|
||||||
private int reductionLimit(Predicate goal) {
|
private int reductionLimit(Predicate goal) {
|
||||||
if (goal.getClass() == CONSULT_STREAM_2) {
|
if (goal.getClass() == CONSULT_STREAM_2) {
|
||||||
|
logger.atFine().log(
|
||||||
|
"predicate class is CONSULT_STREAM_2: override reductionLimit with compileLimit (%d)",
|
||||||
|
compileLimit);
|
||||||
return compileLimit;
|
return compileLimit;
|
||||||
}
|
}
|
||||||
return reductionLimit;
|
return reductionLimit;
|
||||||
|
|||||||
@@ -320,6 +320,21 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
|
RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWithInvalidEmailAddress() throws Exception {
|
||||||
|
AccountInput input = new AccountInput();
|
||||||
|
input.username = name("test");
|
||||||
|
input.email = "invalid email address";
|
||||||
|
|
||||||
|
// Invalid email address should cause the creation to fail
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(BadRequestException.class, () -> gApi.accounts().create(input));
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("invalid email address");
|
||||||
|
|
||||||
|
// The account should not have been created
|
||||||
|
assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id(input.username).get());
|
||||||
|
}
|
||||||
|
|
||||||
private Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
|
private Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
|
||||||
String name = "foo";
|
String name = "foo";
|
||||||
TestAccount foo = accountCreator.create(name);
|
TestAccount foo = accountCreator.create(name);
|
||||||
|
|||||||
@@ -90,10 +90,12 @@ import com.google.gerrit.common.data.LabelType;
|
|||||||
import com.google.gerrit.common.data.Permission;
|
import com.google.gerrit.common.data.Permission;
|
||||||
import com.google.gerrit.exceptions.StorageException;
|
import com.google.gerrit.exceptions.StorageException;
|
||||||
import com.google.gerrit.extensions.annotations.Exports;
|
import com.google.gerrit.extensions.annotations.Exports;
|
||||||
|
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
|
||||||
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
|
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
|
||||||
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
|
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
|
||||||
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
|
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
|
||||||
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
|
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
|
||||||
|
import com.google.gerrit.extensions.api.changes.DraftApi;
|
||||||
import com.google.gerrit.extensions.api.changes.DraftInput;
|
import com.google.gerrit.extensions.api.changes.DraftInput;
|
||||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||||
import com.google.gerrit.extensions.api.changes.NotifyInfo;
|
import com.google.gerrit.extensions.api.changes.NotifyInfo;
|
||||||
@@ -4189,7 +4191,7 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
submittableAfterLosingPermissions("Label");
|
submittableAfterLosingPermissions("Label");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submittableAfterLosingPermissions(String label) throws Exception {
|
private void submittableAfterLosingPermissions(String label) throws Exception {
|
||||||
String codeReviewLabel = "Code-Review";
|
String codeReviewLabel = "Code-Review";
|
||||||
AccountGroup.UUID registered = REGISTERED_USERS;
|
AccountGroup.UUID registered = REGISTERED_USERS;
|
||||||
projectOperations
|
projectOperations
|
||||||
@@ -4242,6 +4244,46 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
gApi.changes().id(changeId).current().submit();
|
gApi.changes().id(changeId).current().submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void draftCommentsShouldNotUpdateChangeTimestamp() throws Exception {
|
||||||
|
String changeId = createNewChange();
|
||||||
|
Timestamp changeTs = getChangeLastUpdate(changeId);
|
||||||
|
DraftApi draftApi = addDraftComment(changeId);
|
||||||
|
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
|
||||||
|
draftApi.delete();
|
||||||
|
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletingAllDraftCommentsShouldNotUpdateChangeTimestamp() throws Exception {
|
||||||
|
String changeId = createNewChange();
|
||||||
|
Timestamp changeTs = getChangeLastUpdate(changeId);
|
||||||
|
addDraftComment(changeId);
|
||||||
|
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
|
||||||
|
gApi.accounts().self().deleteDraftComments(new DeleteDraftCommentsInput());
|
||||||
|
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timestamp getChangeLastUpdate(String changeId) throws RestApiException {
|
||||||
|
Timestamp changeTs = gApi.changes().id(changeId).get().updated;
|
||||||
|
return changeTs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createNewChange() throws Exception {
|
||||||
|
TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
|
||||||
|
PushOneCommit.Result result =
|
||||||
|
pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
|
||||||
|
String changeId = result.getChangeId();
|
||||||
|
return changeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DraftApi addDraftComment(String changeId) throws RestApiException {
|
||||||
|
DraftInput comment = new DraftInput();
|
||||||
|
comment.message = "foo";
|
||||||
|
comment.path = "/foo";
|
||||||
|
return gApi.changes().id(changeId).current().createDraft(comment);
|
||||||
|
}
|
||||||
|
|
||||||
private String getCommitMessage(String changeId) throws RestApiException, IOException {
|
private String getCommitMessage(String changeId) throws RestApiException, IOException {
|
||||||
return gApi.changes().id(changeId).current().file("/COMMIT_MSG").content().asString();
|
return gApi.changes().id(changeId).current().file("/COMMIT_MSG").content().asString();
|
||||||
}
|
}
|
||||||
|
|||||||
160
javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
Normal file
160
javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright (C) 2017 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.api.project;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
||||||
|
|
||||||
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
|
import com.google.gerrit.acceptance.NoHttpd;
|
||||||
|
import com.google.gerrit.acceptance.PushOneCommit.Result;
|
||||||
|
import com.google.gerrit.acceptance.TestAccount;
|
||||||
|
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
||||||
|
import com.google.gerrit.common.data.Permission;
|
||||||
|
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||||
|
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
||||||
|
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||||
|
import com.google.gerrit.extensions.api.projects.BranchInput;
|
||||||
|
import com.google.gerrit.extensions.api.projects.TagInput;
|
||||||
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||||
|
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
||||||
|
import com.google.gerrit.extensions.common.CommitInfo;
|
||||||
|
import com.google.gerrit.extensions.common.GitPerson;
|
||||||
|
import com.google.gerrit.extensions.common.RevisionInfo;
|
||||||
|
import com.google.gerrit.reviewdb.client.BranchNameKey;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@NoHttpd
|
||||||
|
public class CommitIT extends AbstractDaemonTest {
|
||||||
|
@Inject private ProjectOperations projectOperations;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCommitInfo() throws Exception {
|
||||||
|
Result result = createChange();
|
||||||
|
String commitId = result.getCommit().getId().name();
|
||||||
|
CommitInfo info = gApi.projects().name(project.get()).commit(commitId).get();
|
||||||
|
assertThat(info.commit).isEqualTo(commitId);
|
||||||
|
assertThat(info.parents.stream().map(c -> c.commit).collect(toList()))
|
||||||
|
.containsExactly(result.getCommit().getParent(0).name());
|
||||||
|
assertThat(info.subject).isEqualTo(result.getCommit().getShortMessage());
|
||||||
|
assertPerson(info.author, admin);
|
||||||
|
assertPerson(info.committer, admin);
|
||||||
|
assertThat(info.webLinks).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void includedInOpenChange() throws Exception {
|
||||||
|
Result result = createChange();
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void includedInMergedChange() throws Exception {
|
||||||
|
Result result = createChange();
|
||||||
|
gApi.changes()
|
||||||
|
.id(result.getChangeId())
|
||||||
|
.revision(result.getCommit().name())
|
||||||
|
.review(ReviewInput.approve());
|
||||||
|
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
|
||||||
|
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
|
||||||
|
|
||||||
|
projectOperations
|
||||||
|
.project(project)
|
||||||
|
.forUpdate()
|
||||||
|
.add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
|
||||||
|
.update();
|
||||||
|
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
|
||||||
|
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
|
||||||
|
|
||||||
|
createBranch(BranchNameKey.create(project, "test-branch"));
|
||||||
|
|
||||||
|
assertThat(getIncludedIn(result.getCommit().getId()).branches)
|
||||||
|
.containsExactly("master", "test-branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cherryPickCommitWithoutChangeId() throws Exception {
|
||||||
|
// This test is a little superfluous, since the current cherry-pick code ignores
|
||||||
|
// the commit message of the to-be-cherry-picked change, using the one in
|
||||||
|
// CherryPickInput instead.
|
||||||
|
CherryPickInput input = new CherryPickInput();
|
||||||
|
input.destination = "foo";
|
||||||
|
input.message = "it goes to foo branch";
|
||||||
|
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
|
||||||
|
|
||||||
|
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
|
||||||
|
ChangeInfo changeInfo =
|
||||||
|
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
|
||||||
|
|
||||||
|
assertThat(changeInfo.messages).hasSize(1);
|
||||||
|
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
|
||||||
|
String expectedMessage =
|
||||||
|
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
|
||||||
|
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
|
||||||
|
|
||||||
|
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
|
||||||
|
assertThat(revInfo).isNotNull();
|
||||||
|
CommitInfo commitInfo = revInfo.commit;
|
||||||
|
assertThat(commitInfo.message)
|
||||||
|
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cherryPickCommitWithChangeId() throws Exception {
|
||||||
|
CherryPickInput input = new CherryPickInput();
|
||||||
|
input.destination = "foo";
|
||||||
|
|
||||||
|
RevCommit revCommit = createChange().getCommit();
|
||||||
|
List<String> footers = revCommit.getFooterLines("Change-Id");
|
||||||
|
assertThat(footers).hasSize(1);
|
||||||
|
String changeId = footers.get(0);
|
||||||
|
|
||||||
|
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
|
||||||
|
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
|
||||||
|
|
||||||
|
ChangeInfo changeInfo =
|
||||||
|
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
|
||||||
|
|
||||||
|
assertThat(changeInfo.messages).hasSize(1);
|
||||||
|
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
|
||||||
|
String expectedMessage =
|
||||||
|
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
|
||||||
|
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
|
||||||
|
|
||||||
|
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
|
||||||
|
assertThat(revInfo).isNotNull();
|
||||||
|
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
|
||||||
|
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertPerson(GitPerson actual, TestAccount expected) {
|
||||||
|
assertThat(actual.email).isEqualTo(expected.email());
|
||||||
|
assertThat(actual.name).isEqualTo(expected.fullName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
// Copyright (C) 2017 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.api.project;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
|
|
||||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
|
||||||
|
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
|
||||||
import com.google.gerrit.acceptance.NoHttpd;
|
|
||||||
import com.google.gerrit.acceptance.PushOneCommit.Result;
|
|
||||||
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
|
||||||
import com.google.gerrit.common.data.Permission;
|
|
||||||
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
|
|
||||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
|
||||||
import com.google.gerrit.extensions.api.projects.TagInput;
|
|
||||||
import com.google.gerrit.reviewdb.client.BranchNameKey;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
@NoHttpd
|
|
||||||
public class CommitIncludedInIT extends AbstractDaemonTest {
|
|
||||||
@Inject private ProjectOperations projectOperations;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void includedInOpenChange() throws Exception {
|
|
||||||
Result result = createChange();
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void includedInMergedChange() throws Exception {
|
|
||||||
Result result = createChange();
|
|
||||||
gApi.changes()
|
|
||||||
.id(result.getChangeId())
|
|
||||||
.revision(result.getCommit().name())
|
|
||||||
.review(ReviewInput.approve());
|
|
||||||
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
|
|
||||||
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
|
|
||||||
|
|
||||||
projectOperations
|
|
||||||
.project(project)
|
|
||||||
.forUpdate()
|
|
||||||
.add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
|
|
||||||
.update();
|
|
||||||
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
|
|
||||||
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
|
|
||||||
|
|
||||||
createBranch(BranchNameKey.create(project, "test-branch"));
|
|
||||||
|
|
||||||
assertThat(getIncludedIn(result.getCommit().getId()).branches)
|
|
||||||
.containsExactly("master", "test-branch");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
|
|
||||||
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1085,30 +1085,76 @@ public class RevisionIT extends AbstractDaemonTest {
|
|||||||
PushOneCommit.Result r = createChange();
|
PushOneCommit.Result r = createChange();
|
||||||
Map<String, FileInfo> files =
|
Map<String, FileInfo> files =
|
||||||
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files();
|
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files();
|
||||||
assertThat(files).hasSize(2);
|
assertThat(files.keySet()).containsExactly(FILE_NAME, COMMIT_MSG);
|
||||||
assertThat(Iterables.all(files.keySet(), f -> f.matches(FILE_NAME + '|' + COMMIT_MSG)))
|
|
||||||
.isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void filesOnMergeCommitChange() throws Exception {
|
public void filesOnMergeCommitChange() throws Exception {
|
||||||
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
|
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
|
||||||
|
|
||||||
// list files against auto-merge
|
// List files against auto-merge
|
||||||
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet())
|
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet())
|
||||||
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar");
|
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar");
|
||||||
|
|
||||||
// list files against parent 1
|
// List files against parent 1
|
||||||
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(1).keySet())
|
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(1).keySet())
|
||||||
.containsExactly(COMMIT_MSG, MERGE_LIST, "bar");
|
.containsExactly(COMMIT_MSG, MERGE_LIST, "bar");
|
||||||
|
|
||||||
// list files against parent 2
|
// List files against parent 2
|
||||||
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(2).keySet())
|
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(2).keySet())
|
||||||
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo");
|
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filesOnMergeCommitChangeWithInvalidParent() throws Exception {
|
||||||
|
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
|
||||||
|
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class,
|
||||||
|
() ->
|
||||||
|
gApi.changes()
|
||||||
|
.id(r.getChangeId())
|
||||||
|
.revision(r.getCommit().name())
|
||||||
|
.files(3)
|
||||||
|
.keySet());
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 3");
|
||||||
|
thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class,
|
||||||
|
() ->
|
||||||
|
gApi.changes()
|
||||||
|
.id(r.getChangeId())
|
||||||
|
.revision(r.getCommit().name())
|
||||||
|
.files(-1)
|
||||||
|
.keySet());
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void listFilesWithInvalidParent() throws Exception {
|
||||||
|
PushOneCommit.Result result1 = createChange();
|
||||||
|
String changeId = result1.getChangeId();
|
||||||
|
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
|
||||||
|
String revId2 = result2.getCommit().name();
|
||||||
|
|
||||||
|
BadRequestException thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class,
|
||||||
|
() -> gApi.changes().id(changeId).revision(revId2).files(2).keySet());
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 2");
|
||||||
|
|
||||||
|
thrown =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class,
|
||||||
|
() -> gApi.changes().id(changeId).revision(revId2).files(-1).keySet());
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void listFilesOnDifferentBases() throws Exception {
|
public void listFilesOnDifferentBases() throws Exception {
|
||||||
|
RevCommit initialCommit = getHead(repo(), "HEAD");
|
||||||
|
|
||||||
PushOneCommit.Result result1 = createChange();
|
PushOneCommit.Result result1 = createChange();
|
||||||
String changeId = result1.getChangeId();
|
String changeId = result1.getChangeId();
|
||||||
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
|
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
|
||||||
@@ -1131,6 +1177,19 @@ public class RevisionIT extends AbstractDaemonTest {
|
|||||||
.containsExactly(COMMIT_MSG, "b.txt", "c.txt");
|
.containsExactly(COMMIT_MSG, "b.txt", "c.txt");
|
||||||
assertThat(gApi.changes().id(changeId).revision(revId3).files(revId2).keySet())
|
assertThat(gApi.changes().id(changeId).revision(revId3).files(revId2).keySet())
|
||||||
.containsExactly(COMMIT_MSG, "c.txt");
|
.containsExactly(COMMIT_MSG, "c.txt");
|
||||||
|
|
||||||
|
ResourceNotFoundException thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceNotFoundException.class,
|
||||||
|
() -> gApi.changes().id(changeId).revision(revId3).files(initialCommit.getName()));
|
||||||
|
assertThat(thrown).hasMessageThat().contains(initialCommit.getName());
|
||||||
|
|
||||||
|
String invalidRev = "deadbeef";
|
||||||
|
thrown =
|
||||||
|
assertThrows(
|
||||||
|
ResourceNotFoundException.class,
|
||||||
|
() -> gApi.changes().id(changeId).revision(revId3).files(invalidRev));
|
||||||
|
assertThat(thrown).hasMessageThat().contains(invalidRev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ public class ElasticReindexIT extends AbstractReindexTests {
|
|||||||
|
|
||||||
@ConfigSuite.Config
|
@ConfigSuite.Config
|
||||||
public static Config elasticsearchV6() {
|
public static Config elasticsearchV6() {
|
||||||
return getConfig(ElasticVersion.V6_7);
|
return getConfig(ElasticVersion.V6_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConfigSuite.Config
|
@ConfigSuite.Config
|
||||||
public static Config elasticsearchV7() {
|
public static Config elasticsearchV7() {
|
||||||
return getConfig(ElasticVersion.V7_2);
|
return getConfig(ElasticVersion.V7_3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -35,15 +35,11 @@ import com.google.gerrit.extensions.api.changes.ChangeApi;
|
|||||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||||
import com.google.gerrit.extensions.api.projects.BranchInput;
|
|
||||||
import com.google.gerrit.extensions.client.ChangeStatus;
|
import com.google.gerrit.extensions.client.ChangeStatus;
|
||||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||||
import com.google.gerrit.extensions.common.ChangeInput;
|
import com.google.gerrit.extensions.common.ChangeInput;
|
||||||
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
|
||||||
import com.google.gerrit.extensions.common.CommitInfo;
|
|
||||||
import com.google.gerrit.extensions.common.MergeInput;
|
import com.google.gerrit.extensions.common.MergeInput;
|
||||||
import com.google.gerrit.extensions.common.RevisionInfo;
|
|
||||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
@@ -56,7 +52,6 @@ import com.google.gerrit.server.submit.ChangeAlreadyMergedException;
|
|||||||
import com.google.gerrit.testing.FakeEmailSender.Message;
|
import com.google.gerrit.testing.FakeEmailSender.Message;
|
||||||
import com.google.gerrit.testing.TestTimeUtil;
|
import com.google.gerrit.testing.TestTimeUtil;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@@ -409,60 +404,6 @@ public class CreateChangeIT extends AbstractDaemonTest {
|
|||||||
assertCreateSucceeds(in);
|
assertCreateSucceeds(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void cherryPickCommitWithoutChangeId() throws Exception {
|
|
||||||
// This test is a little superfluous, since the current cherry-pick code ignores
|
|
||||||
// the commit message of the to-be-cherry-picked change, using the one in
|
|
||||||
// CherryPickInput instead.
|
|
||||||
CherryPickInput input = new CherryPickInput();
|
|
||||||
input.destination = "foo";
|
|
||||||
input.message = "it goes to foo branch";
|
|
||||||
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
|
|
||||||
|
|
||||||
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
|
|
||||||
ChangeInfo changeInfo =
|
|
||||||
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
|
|
||||||
|
|
||||||
assertThat(changeInfo.messages).hasSize(1);
|
|
||||||
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
|
|
||||||
String expectedMessage =
|
|
||||||
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
|
|
||||||
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
|
|
||||||
|
|
||||||
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
CommitInfo commitInfo = revInfo.commit;
|
|
||||||
assertThat(commitInfo.message)
|
|
||||||
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void cherryPickCommitWithChangeId() throws Exception {
|
|
||||||
CherryPickInput input = new CherryPickInput();
|
|
||||||
input.destination = "foo";
|
|
||||||
|
|
||||||
RevCommit revCommit = createChange().getCommit();
|
|
||||||
List<String> footers = revCommit.getFooterLines("Change-Id");
|
|
||||||
assertThat(footers).hasSize(1);
|
|
||||||
String changeId = footers.get(0);
|
|
||||||
|
|
||||||
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
|
|
||||||
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
|
|
||||||
|
|
||||||
ChangeInfo changeInfo =
|
|
||||||
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
|
|
||||||
|
|
||||||
assertThat(changeInfo.messages).hasSize(1);
|
|
||||||
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
|
|
||||||
String expectedMessage =
|
|
||||||
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
|
|
||||||
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
|
|
||||||
|
|
||||||
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createChangeOnExistingBranchNotPermitted() throws Exception {
|
public void createChangeOnExistingBranchNotPermitted() throws Exception {
|
||||||
createBranch(BranchNameKey.create(project, "foo"));
|
createBranch(BranchNameKey.create(project, "foo"));
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
assertThat(result).hasSize(1);
|
assertThat(result).hasSize(1);
|
||||||
CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
|
CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
|
||||||
assertThat(comment).isEqualTo(infoToDraft(path).apply(actual));
|
assertThat(comment).isEqualTo(infoToDraft(path).apply(actual));
|
||||||
|
|
||||||
|
List<CommentInfo> list = getDraftCommentsAsList(changeId);
|
||||||
|
assertThat(list).hasSize(1);
|
||||||
|
actual = list.get(0);
|
||||||
|
assertThat(comment).isEqualTo(infoToDraft(path).apply(actual));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +139,10 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
|
Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
|
||||||
assertThat(result).hasSize(1);
|
assertThat(result).hasSize(1);
|
||||||
assertThat(result.get(path).stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
|
assertThat(result.get(path).stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
|
||||||
|
|
||||||
|
List<CommentInfo> list = getDraftCommentsAsList(changeId);
|
||||||
|
assertThat(list).hasSize(4);
|
||||||
|
assertThat(list.stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +244,9 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
|
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
|
||||||
assertThat(result).isNotEmpty();
|
assertThat(result).isNotEmpty();
|
||||||
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
|
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
|
||||||
|
|
||||||
|
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
|
||||||
|
assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for the commit message comments on the auto-merge are not possible
|
// for the commit message comments on the auto-merge are not possible
|
||||||
@@ -253,6 +265,9 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
|
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
|
||||||
assertThat(result).isNotEmpty();
|
assertThat(result).isNotEmpty();
|
||||||
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
|
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
|
||||||
|
|
||||||
|
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
|
||||||
|
assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +323,7 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
String changeId = r.getChangeId();
|
String changeId = r.getChangeId();
|
||||||
String revId = r.getCommit().getName();
|
String revId = r.getCommit().getName();
|
||||||
assertThat(getPublishedComments(changeId, revId)).isEmpty();
|
assertThat(getPublishedComments(changeId, revId)).isEmpty();
|
||||||
|
assertThat(getPublishedCommentsAsList(changeId)).isEmpty();
|
||||||
|
|
||||||
List<CommentInput> expectedComments = new ArrayList<>();
|
List<CommentInput> expectedComments = new ArrayList<>();
|
||||||
for (Integer line : lines) {
|
for (Integer line : lines) {
|
||||||
@@ -324,6 +340,9 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
List<CommentInfo> actualComments = result.get(file);
|
List<CommentInfo> actualComments = result.get(file);
|
||||||
assertThat(actualComments.stream().map(infoToInput(file)))
|
assertThat(actualComments.stream().map(infoToInput(file)))
|
||||||
.containsExactlyElementsIn(expectedComments);
|
.containsExactlyElementsIn(expectedComments);
|
||||||
|
|
||||||
|
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
|
||||||
|
assertThat(list.stream().map(infoToInput(file))).containsExactlyElementsIn(expectedComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1125,11 +1144,19 @@ public class CommentsIT extends AbstractDaemonTest {
|
|||||||
return gApi.changes().id(changeId).revision(revId).comments();
|
return gApi.changes().id(changeId).revision(revId).comments();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<CommentInfo> getPublishedCommentsAsList(String changeId) throws Exception {
|
||||||
|
return gApi.changes().id(changeId).commentsAsList();
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, List<CommentInfo>> getDraftComments(String changeId, String revId)
|
private Map<String, List<CommentInfo>> getDraftComments(String changeId, String revId)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return gApi.changes().id(changeId).revision(revId).drafts();
|
return gApi.changes().id(changeId).revision(revId).drafts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<CommentInfo> getDraftCommentsAsList(String changeId) throws Exception {
|
||||||
|
return gApi.changes().id(changeId).draftsAsList();
|
||||||
|
}
|
||||||
|
|
||||||
private CommentInfo getDraftComment(String changeId, String revId, String uuid) throws Exception {
|
private CommentInfo getDraftComment(String changeId, String revId, String uuid) throws Exception {
|
||||||
return gApi.changes().id(changeId).revision(revId).draft(uuid).get();
|
return gApi.changes().id(changeId).revision(revId).draft(uuid).get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ public class ElasticIndexIT extends AbstractIndexTests {
|
|||||||
|
|
||||||
@ConfigSuite.Config
|
@ConfigSuite.Config
|
||||||
public static Config elasticsearchV6() {
|
public static Config elasticsearchV6() {
|
||||||
return getConfig(ElasticVersion.V6_7);
|
return getConfig(ElasticVersion.V6_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConfigSuite.Config
|
@ConfigSuite.Config
|
||||||
public static Config elasticsearchV7() {
|
public static Config elasticsearchV7() {
|
||||||
return getConfig(ElasticVersion.V7_2);
|
return getConfig(ElasticVersion.V7_3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -50,12 +50,16 @@ public class ElasticContainer extends ElasticsearchContainer {
|
|||||||
return "blacktop/elasticsearch:6.6.2";
|
return "blacktop/elasticsearch:6.6.2";
|
||||||
case V6_7:
|
case V6_7:
|
||||||
return "blacktop/elasticsearch:6.7.2";
|
return "blacktop/elasticsearch:6.7.2";
|
||||||
|
case V6_8:
|
||||||
|
return "blacktop/elasticsearch:6.8.2";
|
||||||
case V7_0:
|
case V7_0:
|
||||||
return "blacktop/elasticsearch:7.0.1";
|
return "blacktop/elasticsearch:7.0.1";
|
||||||
case V7_1:
|
case V7_1:
|
||||||
return "blacktop/elasticsearch:7.1.1";
|
return "blacktop/elasticsearch:7.1.1";
|
||||||
case V7_2:
|
case V7_2:
|
||||||
return "blacktop/elasticsearch:7.2.0";
|
return "blacktop/elasticsearch:7.2.1";
|
||||||
|
case V7_3:
|
||||||
|
return "blacktop/elasticsearch:7.3.1";
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("No tests for version: " + version.name());
|
throw new IllegalStateException("No tests for version: " + version.name());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
|
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
|
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
|
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
|
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
|
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
|
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
client = HttpAsyncClients.createDefault();
|
client = HttpAsyncClients.createDefault();
|
||||||
client.start();
|
client.start();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
|
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
|
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
|
||||||
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public class ElasticVersionTest {
|
|||||||
assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7);
|
assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7);
|
||||||
assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7);
|
assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7);
|
||||||
|
|
||||||
|
assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
|
||||||
|
assertThat(ElasticVersion.forVersion("6.8.1")).isEqualTo(ElasticVersion.V6_8);
|
||||||
|
|
||||||
assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0);
|
assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0);
|
||||||
assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0);
|
assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0);
|
||||||
|
|
||||||
@@ -51,6 +54,9 @@ public class ElasticVersionTest {
|
|||||||
|
|
||||||
assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2);
|
assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2);
|
||||||
assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2);
|
assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2);
|
||||||
|
|
||||||
|
assertThat(ElasticVersion.forVersion("7.3.0")).isEqualTo(ElasticVersion.V7_3);
|
||||||
|
assertThat(ElasticVersion.forVersion("7.3.1")).isEqualTo(ElasticVersion.V7_3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -74,9 +80,11 @@ public class ElasticVersionTest {
|
|||||||
assertThat(ElasticVersion.V6_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
assertThat(ElasticVersion.V6_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
|
assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
|
||||||
|
assertThat(ElasticVersion.V6_8.isAtLeastMinorVersion(ElasticVersion.V6_8)).isTrue();
|
||||||
assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
|
assertThat(ElasticVersion.V7_3.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,9 +96,11 @@ public class ElasticVersionTest {
|
|||||||
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
|
||||||
|
assertThat(ElasticVersion.V6_8.isV6OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V7_2.isV6OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_2.isV6OrLater()).isTrue();
|
||||||
|
assertThat(ElasticVersion.V7_3.isV6OrLater()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -102,8 +112,10 @@ public class ElasticVersionTest {
|
|||||||
assertThat(ElasticVersion.V6_5.isV7OrLater()).isFalse();
|
assertThat(ElasticVersion.V6_5.isV7OrLater()).isFalse();
|
||||||
assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
|
assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
|
||||||
assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
|
assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
|
||||||
|
assertThat(ElasticVersion.V6_8.isV7OrLater()).isFalse();
|
||||||
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
|
||||||
assertThat(ElasticVersion.V7_2.isV7OrLater()).isTrue();
|
assertThat(ElasticVersion.V7_2.isV7OrLater()).isTrue();
|
||||||
|
assertThat(ElasticVersion.V7_3.isV7OrLater()).isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
203
lib/highlightjs/highlight.min.js
vendored
203
lib/highlightjs/highlight.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -121,9 +121,11 @@ shortcuts are.
|
|||||||
|
|
||||||
const Shortcut = {
|
const Shortcut = {
|
||||||
OPEN_SHORTCUT_HELP_DIALOG: 'OPEN_SHORTCUT_HELP_DIALOG',
|
OPEN_SHORTCUT_HELP_DIALOG: 'OPEN_SHORTCUT_HELP_DIALOG',
|
||||||
|
GO_TO_USER_DASHBOARD: 'GO_TO_USER_DASHBOARD',
|
||||||
GO_TO_OPENED_CHANGES: 'GO_TO_OPENED_CHANGES',
|
GO_TO_OPENED_CHANGES: 'GO_TO_OPENED_CHANGES',
|
||||||
GO_TO_MERGED_CHANGES: 'GO_TO_MERGED_CHANGES',
|
GO_TO_MERGED_CHANGES: 'GO_TO_MERGED_CHANGES',
|
||||||
GO_TO_ABANDONED_CHANGES: 'GO_TO_ABANDONED_CHANGES',
|
GO_TO_ABANDONED_CHANGES: 'GO_TO_ABANDONED_CHANGES',
|
||||||
|
GO_TO_WATCHED_CHANGES: 'GO_TO_WATCHED_CHANGES',
|
||||||
|
|
||||||
CURSOR_NEXT_CHANGE: 'CURSOR_NEXT_CHANGE',
|
CURSOR_NEXT_CHANGE: 'CURSOR_NEXT_CHANGE',
|
||||||
CURSOR_PREV_CHANGE: 'CURSOR_PREV_CHANGE',
|
CURSOR_PREV_CHANGE: 'CURSOR_PREV_CHANGE',
|
||||||
@@ -192,12 +194,16 @@ shortcuts are.
|
|||||||
_describe(Shortcut.SEARCH, ShortcutSection.EVERYWHERE, 'Search');
|
_describe(Shortcut.SEARCH, ShortcutSection.EVERYWHERE, 'Search');
|
||||||
_describe(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, ShortcutSection.EVERYWHERE,
|
_describe(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, ShortcutSection.EVERYWHERE,
|
||||||
'Show this dialog');
|
'Show this dialog');
|
||||||
|
_describe(Shortcut.GO_TO_USER_DASHBOARD, ShortcutSection.EVERYWHERE,
|
||||||
|
'Go to User Dashboard');
|
||||||
_describe(Shortcut.GO_TO_OPENED_CHANGES, ShortcutSection.EVERYWHERE,
|
_describe(Shortcut.GO_TO_OPENED_CHANGES, ShortcutSection.EVERYWHERE,
|
||||||
'Go to Opened Changes');
|
'Go to Opened Changes');
|
||||||
_describe(Shortcut.GO_TO_MERGED_CHANGES, ShortcutSection.EVERYWHERE,
|
_describe(Shortcut.GO_TO_MERGED_CHANGES, ShortcutSection.EVERYWHERE,
|
||||||
'Go to Merged Changes');
|
'Go to Merged Changes');
|
||||||
_describe(Shortcut.GO_TO_ABANDONED_CHANGES, ShortcutSection.EVERYWHERE,
|
_describe(Shortcut.GO_TO_ABANDONED_CHANGES, ShortcutSection.EVERYWHERE,
|
||||||
'Go to Abandoned Changes');
|
'Go to Abandoned Changes');
|
||||||
|
_describe(Shortcut.GO_TO_WATCHED_CHANGES, ShortcutSection.EVERYWHERE,
|
||||||
|
'Go to Watched Changes');
|
||||||
|
|
||||||
_describe(Shortcut.CURSOR_NEXT_CHANGE, ShortcutSection.ACTIONS,
|
_describe(Shortcut.CURSOR_NEXT_CHANGE, ShortcutSection.ACTIONS,
|
||||||
'Select next change');
|
'Select next change');
|
||||||
|
|||||||
@@ -204,10 +204,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (!values[key]) { return; }
|
let text = values[key];
|
||||||
|
if (!text) { text = ''; }
|
||||||
// The value from the server being used to choose which item is
|
// The value from the server being used to choose which item is
|
||||||
// selected is in integer form, so this must be converted.
|
// selected is in integer form, so this must be converted.
|
||||||
valuesArr.push({value: parseInt(key, 10), text: values[key]});
|
valuesArr.push({value: parseInt(key, 10), text});
|
||||||
}
|
}
|
||||||
return valuesArr;
|
return valuesArr;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -349,6 +349,13 @@ limitations under the License.
|
|||||||
return this._navigate(this.getUrlForSearchQuery(query, opt_offset));
|
return this._navigate(this.getUrlForSearchQuery(query, opt_offset));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the user's dashboard
|
||||||
|
*/
|
||||||
|
navigateToUserDashboard() {
|
||||||
|
return this._navigate(this.getUrlForUserDashboard('self'));
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} change The change object.
|
* @param {!Object} change The change object.
|
||||||
* @param {number=} opt_patchNum
|
* @param {number=} opt_patchNum
|
||||||
|
|||||||
@@ -1424,9 +1424,13 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
_handleSettingsLegacyRoute(data) {
|
_handleSettingsLegacyRoute(data) {
|
||||||
|
// email tokens may contain '+' but no space.
|
||||||
|
// The parameter parsing replaces all '+' with a space,
|
||||||
|
// undo that to have valid tokens.
|
||||||
|
const token = data.params[0].replace(/ /g, '+');
|
||||||
this._setParams({
|
this._setParams({
|
||||||
view: Gerrit.Nav.View.SETTINGS,
|
view: Gerrit.Nav.View.SETTINGS,
|
||||||
emailToken: data.params[0],
|
emailToken: token,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -672,6 +672,14 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('_handleSettingsLegacyRoute with +', () => {
|
||||||
|
const data = {params: {0: 'my-token test'}};
|
||||||
|
assertDataToParams(data, '_handleSettingsLegacyRoute', {
|
||||||
|
view: Gerrit.Nav.View.SETTINGS,
|
||||||
|
emailToken: 'my-token+test',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('_handleSettingsRoute', () => {
|
test('_handleSettingsRoute', () => {
|
||||||
const data = {};
|
const data = {};
|
||||||
assertDataToParams(data, '_handleSettingsRoute', {
|
assertDataToParams(data, '_handleSettingsRoute', {
|
||||||
|
|||||||
@@ -106,9 +106,11 @@
|
|||||||
keyboardShortcuts() {
|
keyboardShortcuts() {
|
||||||
return {
|
return {
|
||||||
[this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
|
[this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
|
||||||
|
[this.Shortcut.GO_TO_USER_DASHBOARD]: '_goToUserDashboard',
|
||||||
[this.Shortcut.GO_TO_OPENED_CHANGES]: '_goToOpenedChanges',
|
[this.Shortcut.GO_TO_OPENED_CHANGES]: '_goToOpenedChanges',
|
||||||
[this.Shortcut.GO_TO_MERGED_CHANGES]: '_goToMergedChanges',
|
[this.Shortcut.GO_TO_MERGED_CHANGES]: '_goToMergedChanges',
|
||||||
[this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
|
[this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
|
||||||
|
[this.Shortcut.GO_TO_WATCHED_CHANGES]: '_goToWatchedChanges',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -172,12 +174,16 @@
|
|||||||
|
|
||||||
this.bindShortcut(
|
this.bindShortcut(
|
||||||
this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
|
this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
|
||||||
|
this.bindShortcut(
|
||||||
|
this.Shortcut.GO_TO_USER_DASHBOARD, this.GO_KEY, 'i');
|
||||||
this.bindShortcut(
|
this.bindShortcut(
|
||||||
this.Shortcut.GO_TO_OPENED_CHANGES, this.GO_KEY, 'o');
|
this.Shortcut.GO_TO_OPENED_CHANGES, this.GO_KEY, 'o');
|
||||||
this.bindShortcut(
|
this.bindShortcut(
|
||||||
this.Shortcut.GO_TO_MERGED_CHANGES, this.GO_KEY, 'm');
|
this.Shortcut.GO_TO_MERGED_CHANGES, this.GO_KEY, 'm');
|
||||||
this.bindShortcut(
|
this.bindShortcut(
|
||||||
this.Shortcut.GO_TO_ABANDONED_CHANGES, this.GO_KEY, 'a');
|
this.Shortcut.GO_TO_ABANDONED_CHANGES, this.GO_KEY, 'a');
|
||||||
|
this.bindShortcut(
|
||||||
|
this.Shortcut.GO_TO_WATCHED_CHANGES, this.GO_KEY, 'w');
|
||||||
|
|
||||||
this.bindShortcut(
|
this.bindShortcut(
|
||||||
this.Shortcut.CURSOR_NEXT_CHANGE, 'j');
|
this.Shortcut.CURSOR_NEXT_CHANGE, 'j');
|
||||||
@@ -401,6 +407,10 @@
|
|||||||
Gerrit.Nav.navigateToStatusSearch('open');
|
Gerrit.Nav.navigateToStatusSearch('open');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_goToUserDashboard() {
|
||||||
|
Gerrit.Nav.navigateToUserDashboard();
|
||||||
|
},
|
||||||
|
|
||||||
_goToMergedChanges() {
|
_goToMergedChanges() {
|
||||||
Gerrit.Nav.navigateToStatusSearch('merged');
|
Gerrit.Nav.navigateToStatusSearch('merged');
|
||||||
},
|
},
|
||||||
@@ -409,6 +419,11 @@
|
|||||||
Gerrit.Nav.navigateToStatusSearch('abandoned');
|
Gerrit.Nav.navigateToStatusSearch('abandoned');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_goToWatchedChanges() {
|
||||||
|
// The query is hardcoded, and doesn't respect custom menu entries
|
||||||
|
Gerrit.Nav.navigateToSearchQuery('is:watched is:open');
|
||||||
|
},
|
||||||
|
|
||||||
_computePluginScreenName({plugin, screen}) {
|
_computePluginScreenName({plugin, screen}) {
|
||||||
return Gerrit._getPluginScreenName(plugin, screen);
|
return Gerrit._getPluginScreenName(plugin, screen);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||||
<link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
|
<link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
|
||||||
@@ -48,9 +49,12 @@ limitations under the License.
|
|||||||
.deleteButton:not(.show) {
|
.deleteButton:not(.show) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.space {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="gr-form-styles">
|
<div class="gr-form-styles">
|
||||||
<fieldset>
|
<fieldset class="space">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -80,6 +84,13 @@ limitations under the License.
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<dom-if if="[[_showLinkAnotherIdentity]]">
|
||||||
|
<fieldset>
|
||||||
|
<a href$="[[_computeLinkAnotherIdentity()]]">
|
||||||
|
<gr-button id="linkAnotherIdentity" link>Link Another Identity</gr-button>
|
||||||
|
</a>
|
||||||
|
</fieldset>
|
||||||
|
</dom-if>
|
||||||
</div>
|
</div>
|
||||||
<gr-overlay id="overlay" with-backdrop>
|
<gr-overlay id="overlay" with-backdrop>
|
||||||
<gr-confirm-delete-item-dialog
|
<gr-confirm-delete-item-dialog
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const AUTH = [
|
||||||
|
'OPENID',
|
||||||
|
'OAUTH',
|
||||||
|
];
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'gr-identities',
|
is: 'gr-identities',
|
||||||
_legacyUndefinedCheck: true,
|
_legacyUndefinedCheck: true,
|
||||||
@@ -24,8 +29,17 @@
|
|||||||
properties: {
|
properties: {
|
||||||
_identities: Object,
|
_identities: Object,
|
||||||
_idName: String,
|
_idName: String,
|
||||||
|
serverConfig: Object,
|
||||||
|
_showLinkAnotherIdentity: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: '_computeShowLinkAnotherIdentity(serverConfig)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
behaviors: [
|
||||||
|
Gerrit.BaseUrlBehavior,
|
||||||
|
],
|
||||||
|
|
||||||
loadData() {
|
loadData() {
|
||||||
return this.$.restAPI.getExternalIds().then(id => {
|
return this.$.restAPI.getExternalIds().then(id => {
|
||||||
this._identities = id;
|
this._identities = id;
|
||||||
@@ -64,5 +78,24 @@
|
|||||||
filterIdentities(item) {
|
filterIdentities(item) {
|
||||||
return !item.identity.startsWith('username:');
|
return !item.identity.startsWith('username:');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_computeShowLinkAnotherIdentity(config) {
|
||||||
|
if (config && config.auth &&
|
||||||
|
config.auth.git_basic_auth_policy) {
|
||||||
|
return AUTH.includes(
|
||||||
|
config.auth.git_basic_auth_policy.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeLinkAnotherIdentity() {
|
||||||
|
const baseUrl = this.getBaseUrl() || '';
|
||||||
|
let pathname = window.location.pathname;
|
||||||
|
if (baseUrl) {
|
||||||
|
pathname = '/' + pathname.substring(baseUrl.length);
|
||||||
|
}
|
||||||
|
return baseUrl + '/login/' + encodeURIComponent(pathname) + '?link';
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -124,5 +124,65 @@ limitations under the License.
|
|||||||
MockInteractions.tap(deleteBtn);
|
MockInteractions.tap(deleteBtn);
|
||||||
assert.isTrue(deleteItem.called);
|
assert.isTrue(deleteItem.called);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('_computeShowLinkAnotherIdentity', () => {
|
||||||
|
let serverConfig;
|
||||||
|
|
||||||
|
serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'OAUTH',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
|
||||||
|
serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'OpenID',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
|
||||||
|
serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'HTTP_LDAP',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
|
||||||
|
serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'LDAP',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
|
||||||
|
serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'HTTP',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
|
||||||
|
serverConfig = {};
|
||||||
|
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_showLinkAnotherIdentity', () => {
|
||||||
|
element.serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'OAUTH',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isTrue(element._showLinkAnotherIdentity);
|
||||||
|
|
||||||
|
element.serverConfig = {
|
||||||
|
auth: {
|
||||||
|
git_basic_auth_policy: 'LDAP',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(element._showLinkAnotherIdentity);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ limitations under the License.
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<h2 id="Identities">Identities</h2>
|
<h2 id="Identities">Identities</h2>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<gr-identities id="identities"></gr-identities>
|
<gr-identities id="identities" server-config="[[_serverConfig]]"></gr-identities>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
|
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
|
||||||
<h2 id="Agreements">Agreements</h2>
|
<h2 id="Agreements">Agreements</h2>
|
||||||
|
|||||||
@@ -397,19 +397,6 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('installGwt calls _pluginInstalled', () => {
|
|
||||||
sandbox.stub(Gerrit, '_pluginInstalled');
|
|
||||||
Gerrit.installGwt('http://test.com/plugins/testplugin/static/test.js');
|
|
||||||
assert.isTrue(Gerrit._pluginInstalled.calledOnce);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('installGwt returns a plugin', () => {
|
|
||||||
const plugin = Gerrit.installGwt(
|
|
||||||
'http://test.com/plugins/testplugin/static/test.js');
|
|
||||||
assert.isOk(plugin);
|
|
||||||
assert.isOk(plugin._loadedGwt);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('attributeHelper', () => {
|
test('attributeHelper', () => {
|
||||||
assert.isOk(plugin.attributeHelper());
|
assert.isOk(plugin.attributeHelper());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,9 +69,9 @@
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
GrPluginRestApi.prototype.fetch = function(method, url, opt_payload,
|
GrPluginRestApi.prototype.fetch = function(method, url, opt_payload,
|
||||||
opt_errFn) {
|
opt_errFn, opt_contentType) {
|
||||||
return getRestApi().send(method, this.opt_prefix + url, opt_payload,
|
return getRestApi().send(method, this.opt_prefix + url, opt_payload,
|
||||||
opt_errFn);
|
opt_errFn, opt_contentType);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,20 +84,21 @@
|
|||||||
* @return {!Promise} resolves on success, rejects on error.
|
* @return {!Promise} resolves on success, rejects on error.
|
||||||
*/
|
*/
|
||||||
GrPluginRestApi.prototype.send = function(method, url, opt_payload,
|
GrPluginRestApi.prototype.send = function(method, url, opt_payload,
|
||||||
opt_errFn) {
|
opt_errFn, opt_contentType) {
|
||||||
return this.fetch(method, url, opt_payload, opt_errFn).then(response => {
|
return this.fetch(method, url, opt_payload, opt_errFn, opt_contentType)
|
||||||
if (response.status < 200 || response.status >= 300) {
|
.then(response => {
|
||||||
return response.text().then(text => {
|
if (response.status < 200 || response.status >= 300) {
|
||||||
if (text) {
|
return response.text().then(text => {
|
||||||
return Promise.reject(text);
|
if (text) {
|
||||||
|
return Promise.reject(text);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(response.status);
|
return getRestApi().getResponseObject(response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return getRestApi().getResponseObject(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,16 +113,18 @@
|
|||||||
* @param {string} url URL without base path or plugin prefix
|
* @param {string} url URL without base path or plugin prefix
|
||||||
* @return {!Promise} resolves on success, rejects on error.
|
* @return {!Promise} resolves on success, rejects on error.
|
||||||
*/
|
*/
|
||||||
GrPluginRestApi.prototype.post = function(url, opt_payload) {
|
GrPluginRestApi.prototype.post = function(url, opt_payload, opt_errFn,
|
||||||
return this.send('POST', url, opt_payload);
|
opt_contentType) {
|
||||||
|
return this.send('POST', url, opt_payload, opt_errFn, opt_contentType);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} url URL without base path or plugin prefix
|
* @param {string} url URL without base path or plugin prefix
|
||||||
* @return {!Promise} resolves on success, rejects on error.
|
* @return {!Promise} resolves on success, rejects on error.
|
||||||
*/
|
*/
|
||||||
GrPluginRestApi.prototype.put = function(url, opt_payload) {
|
GrPluginRestApi.prototype.put = function(url, opt_payload, opt_errFn,
|
||||||
return this.send('PUT', url, opt_payload);
|
opt_contentType) {
|
||||||
|
return this.send('PUT', url, opt_payload, opt_errFn, opt_contentType);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -99,10 +99,6 @@
|
|||||||
STYLE: 'style',
|
STYLE: 'style',
|
||||||
};
|
};
|
||||||
|
|
||||||
// GWT JSNI uses $wnd to refer to window.
|
|
||||||
// http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
|
|
||||||
window.$wnd = window;
|
|
||||||
|
|
||||||
function flushPreinstalls() {
|
function flushPreinstalls() {
|
||||||
if (window.Gerrit.flushPreinstalls) {
|
if (window.Gerrit.flushPreinstalls) {
|
||||||
window.Gerrit.flushPreinstalls();
|
window.Gerrit.flushPreinstalls();
|
||||||
@@ -607,23 +603,6 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Install "stepping stones" API for GWT-compiled plugins by default.
|
|
||||||
* @deprecated best effort support, will be removed with GWT UI.
|
|
||||||
*/
|
|
||||||
Gerrit.installGwt = function(url) {
|
|
||||||
const name = getPluginNameFromUrl(url);
|
|
||||||
let plugin;
|
|
||||||
try {
|
|
||||||
plugin = _plugins[name] || new Plugin(url);
|
|
||||||
plugin.deprecated.install();
|
|
||||||
Gerrit._pluginInstalled(url);
|
|
||||||
} catch (e) {
|
|
||||||
Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
|
|
||||||
}
|
|
||||||
return plugin;
|
|
||||||
};
|
|
||||||
|
|
||||||
Gerrit.awaitPluginsLoaded = function() {
|
Gerrit.awaitPluginsLoaded = function() {
|
||||||
if (!_allPluginsPromise) {
|
if (!_allPluginsPromise) {
|
||||||
if (Gerrit._arePluginsLoaded()) {
|
if (Gerrit._arePluginsLoaded()) {
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ def _war_impl(ctx):
|
|||||||
# Add lib
|
# Add lib
|
||||||
transitive_libs = []
|
transitive_libs = []
|
||||||
for j in ctx.attr.libs:
|
for j in ctx.attr.libs:
|
||||||
if hasattr(j, "java"):
|
if JavaInfo in j:
|
||||||
transitive_libs.append(j.java.transitive_runtime_deps)
|
transitive_libs.append(j[JavaInfo].transitive_runtime_deps)
|
||||||
elif hasattr(j, "files"):
|
elif hasattr(j, "files"):
|
||||||
transitive_libs.append(j.files)
|
transitive_libs.append(j.files)
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ def _war_impl(ctx):
|
|||||||
# Add pgm lib
|
# Add pgm lib
|
||||||
transitive_pgmlibs = []
|
transitive_pgmlibs = []
|
||||||
for j in ctx.attr.pgmlibs:
|
for j in ctx.attr.pgmlibs:
|
||||||
transitive_pgmlibs.append(j.java.transitive_runtime_deps)
|
transitive_pgmlibs.append(j[JavaInfo].transitive_runtime_deps)
|
||||||
|
|
||||||
transitive_pgmlib_deps = depset(transitive = transitive_pgmlibs)
|
transitive_pgmlib_deps = depset(transitive = transitive_pgmlibs)
|
||||||
for dep in transitive_pgmlib_deps.to_list():
|
for dep in transitive_pgmlib_deps.to_list():
|
||||||
|
|||||||
@@ -12,17 +12,12 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
# TODO(davido): use Google style for importing instead:
|
import argparse
|
||||||
# import optparse
|
import os
|
||||||
# ...
|
import subprocess
|
||||||
# optparse.OptionParser
|
import xml.dom.minidom
|
||||||
from optparse import OptionParser
|
|
||||||
from os import environ, path, makedirs
|
|
||||||
from subprocess import CalledProcessError, check_call, check_output
|
|
||||||
from xml.dom import minidom
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -39,30 +34,57 @@ cp_targets = {
|
|||||||
MAIN: '//tools/eclipse:main_classpath_collect',
|
MAIN: '//tools/eclipse:main_classpath_collect',
|
||||||
}
|
}
|
||||||
|
|
||||||
ROOT = path.abspath(__file__)
|
ROOT = os.path.abspath(__file__)
|
||||||
while not path.exists(path.join(ROOT, 'WORKSPACE')):
|
while not os.path.exists(os.path.join(ROOT, 'WORKSPACE')):
|
||||||
ROOT = path.dirname(ROOT)
|
ROOT = os.path.dirname(ROOT)
|
||||||
|
|
||||||
opts = OptionParser()
|
opts = argparse.ArgumentParser("Create Eclipse Project")
|
||||||
opts.add_option('--plugins', help='create eclipse projects for plugins',
|
opts.add_argument('--plugins', help='create eclipse projects for plugins',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
opts.add_option('--name', help='name of the generated project',
|
opts.add_argument('--name', help='name of the generated project',
|
||||||
action='store', default='gerrit', dest='project_name')
|
action='store', default='gerrit', dest='project_name')
|
||||||
opts.add_option('-b', '--batch', action='store_true',
|
opts.add_argument('-b', '--batch', action='store_true',
|
||||||
dest='batch', help='Bazel batch option')
|
dest='batch', help='Bazel batch option')
|
||||||
opts.add_option('-j', '--java', action='store',
|
opts.add_argument('-j', '--java', action='store',
|
||||||
dest='java', help='Post Java 8 support (9)')
|
dest='java', help='Post Java 8 support (9)')
|
||||||
opts.add_option('-e', '--edge_java', action='store',
|
opts.add_argument('-e', '--edge_java', action='store',
|
||||||
dest='edge_java', help='Post Java 9 support (10|11|...)')
|
dest='edge_java', help='Post Java 9 support (10|11|...)')
|
||||||
opts.add_option('--bazel', help='name of the bazel executable',
|
opts.add_argument('--bazel',
|
||||||
action='store', default='bazel', dest='bazel_exe')
|
help=('name of the bazel executable. Defaults to using'
|
||||||
|
' bazelisk if found, or bazel if bazelisk is not'
|
||||||
|
' found.'),
|
||||||
|
action='store', default=None, dest='bazel_exe')
|
||||||
|
|
||||||
|
args = opts.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def find_bazel():
|
||||||
|
if args.bazel_exe:
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(
|
||||||
|
['which', args.bazel_exe]).strip().decode('UTF-8')
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print('Bazel command: %s not found' % args.bazel_exe, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(
|
||||||
|
['which', 'bazelisk']).strip().decode('UTF-8')
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(
|
||||||
|
['which', 'bazel']).strip().decode('UTF-8')
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Neither bazelisk nor bazel found. Please see"
|
||||||
|
" Documentation/dev-bazel for instructions on installing"
|
||||||
|
" one of them.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
args, _ = opts.parse_args()
|
|
||||||
|
|
||||||
batch_option = '--batch' if args.batch else None
|
batch_option = '--batch' if args.batch else None
|
||||||
custom_java = args.java
|
custom_java = args.java
|
||||||
edge_java = args.edge_java
|
edge_java = args.edge_java
|
||||||
bazel_exe = args.bazel_exe
|
bazel_exe = find_bazel()
|
||||||
|
|
||||||
|
|
||||||
def _build_bazel_cmd(*args):
|
def _build_bazel_cmd(*args):
|
||||||
build = False
|
build = False
|
||||||
@@ -82,23 +104,23 @@ def _build_bazel_cmd(*args):
|
|||||||
|
|
||||||
|
|
||||||
def retrieve_ext_location():
|
def retrieve_ext_location():
|
||||||
return check_output(_build_bazel_cmd('info', 'output_base')).strip()
|
return subprocess.check_output(_build_bazel_cmd('info', 'output_base')).strip()
|
||||||
|
|
||||||
|
|
||||||
def gen_bazel_path(ext_location):
|
def gen_bazel_path(ext_location):
|
||||||
bazel = check_output(['which', bazel_exe]).strip().decode('UTF-8')
|
bazel = subprocess.check_output(['which', bazel_exe]).strip().decode('UTF-8')
|
||||||
with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
|
with open(os.path.join(ROOT, ".bazel_path"), 'w') as fd:
|
||||||
fd.write("output_base=%s\n" % ext_location)
|
fd.write("output_base=%s\n" % ext_location)
|
||||||
fd.write("bazel=%s\n" % bazel)
|
fd.write("bazel=%s\n" % bazel)
|
||||||
fd.write("PATH=%s\n" % environ["PATH"])
|
fd.write("PATH=%s\n" % os.environ["PATH"])
|
||||||
|
|
||||||
|
|
||||||
def _query_classpath(target):
|
def _query_classpath(target):
|
||||||
deps = []
|
deps = []
|
||||||
t = cp_targets[target]
|
t = cp_targets[target]
|
||||||
try:
|
try:
|
||||||
check_call(_build_bazel_cmd('build', t))
|
subprocess.check_call(_build_bazel_cmd('build', t))
|
||||||
except CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
exit(1)
|
exit(1)
|
||||||
name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath'
|
name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath'
|
||||||
deps = [line.rstrip('\n') for line in open(name)]
|
deps = [line.rstrip('\n') for line in open(name)]
|
||||||
@@ -106,7 +128,7 @@ def _query_classpath(target):
|
|||||||
|
|
||||||
|
|
||||||
def gen_project(name='gerrit', root=ROOT):
|
def gen_project(name='gerrit', root=ROOT):
|
||||||
p = path.join(root, '.project')
|
p = os.path.join(root, '.project')
|
||||||
with open(p, 'w') as fd:
|
with open(p, 'w') as fd:
|
||||||
print("""\
|
print("""\
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@@ -125,9 +147,9 @@ def gen_project(name='gerrit', root=ROOT):
|
|||||||
|
|
||||||
|
|
||||||
def gen_plugin_classpath(root):
|
def gen_plugin_classpath(root):
|
||||||
p = path.join(root, '.classpath')
|
p = os.path.join(root, '.classpath')
|
||||||
with open(p, 'w') as fd:
|
with open(p, 'w') as fd:
|
||||||
if path.exists(path.join(root, 'src', 'test', 'java')):
|
if os.path.exists(os.path.join(root, 'src', 'test', 'java')):
|
||||||
testpath = """
|
testpath = """
|
||||||
<classpathentry excluding="**/BUILD" kind="src" path="src/test/java"\
|
<classpathentry excluding="**/BUILD" kind="src" path="src/test/java"\
|
||||||
out="eclipse-out/test">
|
out="eclipse-out/test">
|
||||||
@@ -147,7 +169,7 @@ def gen_plugin_classpath(root):
|
|||||||
|
|
||||||
def gen_classpath(ext):
|
def gen_classpath(ext):
|
||||||
def make_classpath():
|
def make_classpath():
|
||||||
impl = minidom.getDOMImplementation()
|
impl = xml.dom.minidom.getDOMImplementation()
|
||||||
return impl.createDocument(None, 'classpath', None)
|
return impl.createDocument(None, 'classpath', None)
|
||||||
|
|
||||||
def classpathentry(kind, path, src=None, out=None, exported=None):
|
def classpathentry(kind, path, src=None, out=None, exported=None):
|
||||||
@@ -222,7 +244,7 @@ def gen_classpath(ext):
|
|||||||
"external/bazel_tools/tools/jdk/TestRunner_deploy.jar"):
|
"external/bazel_tools/tools/jdk/TestRunner_deploy.jar"):
|
||||||
continue
|
continue
|
||||||
if p.startswith("external"):
|
if p.startswith("external"):
|
||||||
p = path.join(ext, p)
|
p = os.path.join(ext, p)
|
||||||
lib.add(p)
|
lib.add(p)
|
||||||
|
|
||||||
classpathentry('src', 'java')
|
classpathentry('src', 'java')
|
||||||
@@ -239,11 +261,11 @@ def gen_classpath(ext):
|
|||||||
continue
|
continue
|
||||||
out = 'eclipse-out/' + s
|
out = 'eclipse-out/' + s
|
||||||
|
|
||||||
p = path.join(s, 'java')
|
p = os.path.join(s, 'java')
|
||||||
if path.exists(p):
|
if os.path.exists(p):
|
||||||
classpathentry('src', p, out=out + '/main')
|
classpathentry('src', p, out=out + '/main')
|
||||||
p = path.join(s, 'javatests')
|
p = os.path.join(s, 'javatests')
|
||||||
if path.exists(p):
|
if os.path.exists(p):
|
||||||
classpathentry('src', p, out=out + '/test')
|
classpathentry('src', p, out=out + '/test')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -255,8 +277,8 @@ def gen_classpath(ext):
|
|||||||
o = 'eclipse-out/test'
|
o = 'eclipse-out/test'
|
||||||
|
|
||||||
for srctype in ['java', 'resources']:
|
for srctype in ['java', 'resources']:
|
||||||
p = path.join(s, 'src', env, srctype)
|
p = os.path.join(s, 'src', env, srctype)
|
||||||
if path.exists(p):
|
if os.path.exists(p):
|
||||||
classpathentry('src', p, out=o)
|
classpathentry('src', p, out=o)
|
||||||
|
|
||||||
for libs in [lib]:
|
for libs in [lib]:
|
||||||
@@ -266,8 +288,8 @@ def gen_classpath(ext):
|
|||||||
if m:
|
if m:
|
||||||
prefix = m.group(1)
|
prefix = m.group(1)
|
||||||
suffix = m.group(2)
|
suffix = m.group(2)
|
||||||
p = path.join(prefix, "jar", "%s-src.jar" % suffix)
|
p = os.path.join(prefix, "jar", "%s-src.jar" % suffix)
|
||||||
if path.exists(p):
|
if os.path.exists(p):
|
||||||
s = p
|
s = p
|
||||||
if args.plugins:
|
if args.plugins:
|
||||||
classpathentry('lib', j, s, exported=True)
|
classpathentry('lib', j, s, exported=True)
|
||||||
@@ -285,13 +307,13 @@ def gen_classpath(ext):
|
|||||||
classpathentry('src', '.apt_generated')
|
classpathentry('src', '.apt_generated')
|
||||||
classpathentry('src', '.apt_generated_tests', out="eclipse-out/test")
|
classpathentry('src', '.apt_generated_tests', out="eclipse-out/test")
|
||||||
|
|
||||||
p = path.join(ROOT, '.classpath')
|
p = os.path.join(ROOT, '.classpath')
|
||||||
with open(p, 'w') as fd:
|
with open(p, 'w') as fd:
|
||||||
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
|
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
|
||||||
|
|
||||||
if args.plugins:
|
if args.plugins:
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
plugindir = path.join(ROOT, plugin)
|
plugindir = os.path.join(ROOT, plugin)
|
||||||
try:
|
try:
|
||||||
gen_project(plugin.replace('plugins/', ""), plugindir)
|
gen_project(plugin.replace('plugins/', ""), plugindir)
|
||||||
gen_plugin_classpath(plugindir)
|
gen_plugin_classpath(plugindir)
|
||||||
@@ -301,17 +323,17 @@ def gen_classpath(ext):
|
|||||||
|
|
||||||
|
|
||||||
def gen_factorypath(ext):
|
def gen_factorypath(ext):
|
||||||
doc = minidom.getDOMImplementation().createDocument(None, 'factorypath',
|
doc = xml.dom.minidom.getDOMImplementation().createDocument(
|
||||||
None)
|
None, 'factorypath', None)
|
||||||
for jar in _query_classpath(AUTO):
|
for jar in _query_classpath(AUTO):
|
||||||
e = doc.createElement('factorypathentry')
|
e = doc.createElement('factorypathentry')
|
||||||
e.setAttribute('kind', 'EXTJAR')
|
e.setAttribute('kind', 'EXTJAR')
|
||||||
e.setAttribute('id', path.join(ext, jar))
|
e.setAttribute('id', os.path.join(ext, jar))
|
||||||
e.setAttribute('enabled', 'true')
|
e.setAttribute('enabled', 'true')
|
||||||
e.setAttribute('runInBatchMode', 'false')
|
e.setAttribute('runInBatchMode', 'false')
|
||||||
doc.documentElement.appendChild(e)
|
doc.documentElement.appendChild(e)
|
||||||
|
|
||||||
p = path.join(ROOT, '.factorypath')
|
p = os.path.join(ROOT, '.factorypath')
|
||||||
with open(p, 'w') as fd:
|
with open(p, 'w') as fd:
|
||||||
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
|
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
|
||||||
|
|
||||||
@@ -324,8 +346,8 @@ try:
|
|||||||
gen_bazel_path(ext_location)
|
gen_bazel_path(ext_location)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_call(_build_bazel_cmd('build', MAIN))
|
subprocess.check_call(_build_bazel_cmd('build', MAIN))
|
||||||
except CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
exit(1)
|
exit(1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('Interrupted by user', file=sys.stderr)
|
print('Interrupted by user', file=sys.stderr)
|
||||||
|
|||||||
Reference in New Issue
Block a user