Merge branch 'stable-3.0' into stable-3.1
* stable-3.0: UrlFormatter: Fix Javadoc of getSettingsUrl() Add a separator between blame and the edit icon Remove target=_self from commentlinks Update .mailmap ReceiveCommits: Factor repeated "internal server error" text to a constant Update .mailmap Documentation: Add site-path arg to reindex command Isolate log4j dep Update .mailmap Cancel deprecation of change identifiers FakeGroupAuditService: Make auditEvents final ErrorProne: Increase severity of MutableConstantField to ERROR ConfigUpdatedEvent: Use immutable type in field declaration Add missing slf4j dependencies in BUILD file JGitMetricModule: fix WindowCache miss ration which wasn't reported Revert "JGitMetricModule: Replace anonymous Supplier instances with method references" Suppress generic logging on docker start for ElasticSearch container Replace deprecated OptionParser with ArgumentParser Documentation: Replace plugins list with home page Change-Id: Idae7755cb887a8df60028a96fff0a01d5d637f8a
This commit is contained in:
15
.mailmap
15
.mailmap
@@ -5,6 +5,7 @@ Alex Blewitt <alex.blewitt@gmail.com>
|
|||||||
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex <alex.ryazantsev@gmail.com>
|
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex <alex.ryazantsev@gmail.com>
|
||||||
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex.ryazantsev <alex.ryazantsev@gmail.com>
|
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex.ryazantsev <alex.ryazantsev@gmail.com>
|
||||||
Alice Kober-Sotzek <aliceks@google.com> <aliceks@google.com>
|
Alice Kober-Sotzek <aliceks@google.com> <aliceks@google.com>
|
||||||
|
Alexandre Philbert <alexandre.philbert@ericsson.com> <alexandre.philbert@hotmail.com>
|
||||||
Andrew Bonventre <andybons@chromium.org> <andybons@google.com>
|
Andrew Bonventre <andybons@chromium.org> <andybons@google.com>
|
||||||
Becky Siegel <beckysiegel@google.com> beckysiegel <beckysiegel@google.com>
|
Becky Siegel <beckysiegel@google.com> beckysiegel <beckysiegel@google.com>
|
||||||
Ben Rohlfs <brohlfs@google.com> brohlfs <brohlfs@google.com>
|
Ben Rohlfs <brohlfs@google.com> brohlfs <brohlfs@google.com>
|
||||||
@@ -12,6 +13,7 @@ Brad Larson <bklarson@gmail.com>
|
|||||||
Bruce Zu <bruce.zu.run10@gmail.com> <bruce.zu@sonyericsson.com>
|
Bruce Zu <bruce.zu.run10@gmail.com> <bruce.zu@sonyericsson.com>
|
||||||
Bruce Zu <bruce.zu.run10@gmail.com> <bruce.zu@sonymobile.com>
|
Bruce Zu <bruce.zu.run10@gmail.com> <bruce.zu@sonymobile.com>
|
||||||
Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com>
|
Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com>
|
||||||
|
Chad Horohoe <chorohoe@wikimedia.org> <chadh@wikimedia.org>
|
||||||
Changcheng Xiao <xchangcheng@google.com> xchangcheng
|
Changcheng Xiao <xchangcheng@google.com> xchangcheng
|
||||||
Cheng Ke <chengke.info@gmail.com> <chengke.info@gmail.com>
|
Cheng Ke <chengke.info@gmail.com> <chengke.info@gmail.com>
|
||||||
Dariusz Luksza <dluksza@collab.net> <dariusz@luksza.org>
|
Dariusz Luksza <dluksza@collab.net> <dariusz@luksza.org>
|
||||||
@@ -29,13 +31,17 @@ Edwin Kempin <ekempin@google.com>
|
|||||||
Eryk Szymanski <eryksz@gmail.com> <eryksz@google.com>
|
Eryk Szymanski <eryksz@gmail.com> <eryksz@google.com>
|
||||||
Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik@gandaraj.com>
|
Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik@gandaraj.com>
|
||||||
Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik.luthander@sonyericsson.com>
|
Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik.luthander@sonyericsson.com>
|
||||||
|
Gerrit Code Review <no-reply@gerritcodereview.com> <noreply-gerritcodereview@google.com>
|
||||||
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@axis.com>
|
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@axis.com>
|
||||||
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@sonyericsson.com>
|
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@sonyericsson.com>
|
||||||
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@sonymobile.com>
|
Gustaf Lundh <gustaflh@axis.com> <gustaf.lundh@sonymobile.com>
|
||||||
Han-Wen Nienhuys <hanwen@google.com> <hanwen@google.com>
|
Han-Wen Nienhuys <hanwen@google.com> <hanwen@google.com>
|
||||||
|
Hector Oswaldo Caballero <hector.caballero@ericsson.com> <hector.caballero@ericsson.com>
|
||||||
Hugo Arès <hugo.ares@ericsson.com> Hugo Ares <hugo.ares@ericsson.com>
|
Hugo Arès <hugo.ares@ericsson.com> Hugo Ares <hugo.ares@ericsson.com>
|
||||||
|
Hugo Arès <hugo.ares@ericsson.com> <hugares@gmail.com>
|
||||||
Jacek Centkowski <jcentkowski@collab.net> <gemincia.programs@gmail.com>
|
Jacek Centkowski <jcentkowski@collab.net> <gemincia.programs@gmail.com>
|
||||||
Jacek Centkowski <jcentkowski@collab.net> <geminica.programs@gmail.com>
|
Jacek Centkowski <jcentkowski@collab.net> <geminica.programs@gmail.com>
|
||||||
|
James E. Blair <jeblair@redhat.com> <jeblair@hp.com>
|
||||||
Jason Huntley <jhuntley@houghtonassociates.com> jhuntley <jhuntley@houghtonassociates.com>
|
Jason Huntley <jhuntley@houghtonassociates.com> jhuntley <jhuntley@houghtonassociates.com>
|
||||||
Jiří Engelthaler <EngyCZ@gmail.com> <engycz@gmail.com>
|
Jiří Engelthaler <EngyCZ@gmail.com> <engycz@gmail.com>
|
||||||
Joe Onorato <onoratoj@gmail.com> <joeo@android.com>
|
Joe Onorato <onoratoj@gmail.com> <joeo@android.com>
|
||||||
@@ -49,16 +55,24 @@ Lincoln Oliveira Campos Do Nascimento <lincoln.oliveiracamposdonascimento@sonyer
|
|||||||
Luca Milanesio <luca.milanesio@gmail.com> <luca@gitent-scm.com>
|
Luca Milanesio <luca.milanesio@gmail.com> <luca@gitent-scm.com>
|
||||||
Magnus Bäck <magnus.back@axis.com> <baeck@google.com>
|
Magnus Bäck <magnus.back@axis.com> <baeck@google.com>
|
||||||
Magnus Bäck <magnus.back@axis.com> <magnus.back@sonyericsson.com>
|
Magnus Bäck <magnus.back@axis.com> <magnus.back@sonyericsson.com>
|
||||||
|
Marco Miller <marco.miller@ericsson.com> <marco.mmiller@gmail.com>
|
||||||
Mark Derricutt <mark.derricutt@smxemail.com> <mark@talios.com>
|
Mark Derricutt <mark.derricutt@smxemail.com> <mark@talios.com>
|
||||||
Martin Fick <mfick@codeaurora.org> <mogulguy10@gmail.com>
|
Martin Fick <mfick@codeaurora.org> <mogulguy10@gmail.com>
|
||||||
Martin Fick <mfick@codeaurora.org> <mogulguy@yahoo.com>
|
Martin Fick <mfick@codeaurora.org> <mogulguy@yahoo.com>
|
||||||
|
Martin Wallgren <martinwa@axis.com> <martin.wallgren@axis.com>
|
||||||
|
Matthias Sohn <matthias.sohn@sap.com> <matthias.sohn@gmail.com>
|
||||||
Maxime Guerreiro <maximeg@google.com> <maximeg@google.com>
|
Maxime Guerreiro <maximeg@google.com> <maximeg@google.com>
|
||||||
Michael Zhou <moz@google.com> <zhoumotongxue008@gmail.com>
|
Michael Zhou <moz@google.com> <zhoumotongxue008@gmail.com>
|
||||||
|
Monty Taylor <mordred@inaugust.com> <monty.taylor@gmail.com>
|
||||||
Mônica Dionísio <monica.dionisio@sonyericsson.com> monica.dionisio <monica.dionisio@sonyericsson.com>
|
Mônica Dionísio <monica.dionisio@sonyericsson.com> monica.dionisio <monica.dionisio@sonyericsson.com>
|
||||||
Nasser Grainawi <nasser@grainawi.org> <nasser@codeaurora.org>
|
Nasser Grainawi <nasser@grainawi.org> <nasser@codeaurora.org>
|
||||||
Nasser Grainawi <nasser@grainawi.org> <nasserg@quicinc.com>
|
Nasser Grainawi <nasser@grainawi.org> <nasserg@quicinc.com>
|
||||||
|
Orgad Shaneh <orgads@gmail.com> <orgad.shaneh@audiocodes.com>
|
||||||
|
Paladox <thomasmulhall410@yahoo.com> <thomasmulhall410@yahoo.com>
|
||||||
|
Patrick Hiesel <hiesel@google.com> <hiesel@hiesel-macbookpro2.roam.corp.google.com>
|
||||||
Peter Jönsson <peter.joensson@gmail.com> Peter Jönsson <peter.joensson@gmail.com>
|
Peter Jönsson <peter.joensson@gmail.com> Peter Jönsson <peter.joensson@gmail.com>
|
||||||
Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com> rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com>
|
Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com> rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com>
|
||||||
|
Réda Housni Alaoui <reda.housnialaoui@gmail.com> <alaoui.rda@gmail.com>
|
||||||
Richard Möhn <richard.moehn@posteo.de> <richard.moehn@fu-berlin.de>
|
Richard Möhn <richard.moehn@posteo.de> <richard.moehn@fu-berlin.de>
|
||||||
Sam Saccone <samccone@google.com> <samccone@gmail.com>
|
Sam Saccone <samccone@google.com> <samccone@gmail.com>
|
||||||
Sam Saccone <samccone@google.com> <samccone@google.com>
|
Sam Saccone <samccone@google.com> <samccone@google.com>
|
||||||
@@ -70,6 +84,7 @@ Shawn Pearce <sop@google.com>
|
|||||||
Sixin Li <sixin210@gmail.com> sixin li <sixin210@gmail.com>
|
Sixin Li <sixin210@gmail.com> sixin li <sixin210@gmail.com>
|
||||||
Sven Selberg <svense@axis.com> <sven.selberg@axis.com>
|
Sven Selberg <svense@axis.com> <sven.selberg@axis.com>
|
||||||
Sven Selberg <svense@axis.com> <sven.selberg@sonymobile.com>
|
Sven Selberg <svense@axis.com> <sven.selberg@sonymobile.com>
|
||||||
|
Thomas Dräbing <thomas.draebing@sap.com> <thomas.draebing@sap.com>
|
||||||
Tom Wang <twang10@gmail.com> Tom <twang10@gmail.com>
|
Tom Wang <twang10@gmail.com> Tom <twang10@gmail.com>
|
||||||
Tomas Westling <thomas.westling@sonyericsson.com> thomas.westling <thomas.westling@sonyericsson.com>
|
Tomas Westling <thomas.westling@sonyericsson.com> thomas.westling <thomas.westling@sonyericsson.com>
|
||||||
Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> <ulrik.sjolin@gmail.com>
|
Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> <ulrik.sjolin@gmail.com>
|
||||||
|
|||||||
@@ -1158,18 +1158,6 @@ Allow blame on side by side diff. If set to false, blame cannot be used.
|
|||||||
+
|
+
|
||||||
Default is true.
|
Default is true.
|
||||||
|
|
||||||
[[change.api.allowedIdentifier]]change.api.allowedIdentifier::
|
|
||||||
+
|
|
||||||
Change identifier(s) that are allowed on the API. See
|
|
||||||
link:rest-api-changes.html#change-id[Change Id] for more information.
|
|
||||||
+
|
|
||||||
Possible values are `ALL`, `TRIPLET`, `NUMERIC_ID`, `I_HASH`, and
|
|
||||||
`COMMIT_HASH` or any combination of those as a string list.
|
|
||||||
`PROJECT_NUMERIC_ID` is always allowed and doesn't need to be listed
|
|
||||||
explicitly.
|
|
||||||
+
|
|
||||||
Default is `ALL`.
|
|
||||||
|
|
||||||
[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
|
[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
|
||||||
+
|
+
|
||||||
If true, the mergeability bit in
|
If true, the mergeability bit in
|
||||||
|
|||||||
@@ -176,649 +176,8 @@ Configuration]
|
|||||||
[[other-plugins]]
|
[[other-plugins]]
|
||||||
== Other Plugins
|
== Other Plugins
|
||||||
|
|
||||||
Besides core plugins there are many other Gerrit plugins available.
|
Besides core plugins there are many other Gerrit plugins available. This Gerrit home
|
||||||
These plugins are developed and maintained by different parties.
|
link:https://www.gerritcodereview.com/plugins.html[page for plugins] lists them.
|
||||||
The Gerrit Project doesn't guarantee proper functionality of any of
|
|
||||||
these plugins.
|
|
||||||
|
|
||||||
The Gerrit Project doesn't provide binaries for these plugins, but
|
|
||||||
there is one public service that offers the download of pre-built
|
|
||||||
plugin jars:
|
|
||||||
|
|
||||||
* link:https://gerrit-ci.gerritforge.com[CI Server from GerritForge]
|
|
||||||
|
|
||||||
The following list gives an overview of available plugins, but the
|
|
||||||
list may not be complete. You may discover more plugins on
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/?filter=plugins%252F[
|
|
||||||
gerrit-review].
|
|
||||||
|
|
||||||
Note that the documentation and configuration links in the list below are
|
|
||||||
to the plugins' master branch. Please refer to the appropriate branch for
|
|
||||||
the Gerrit version you are using. Be aware that in some cases a stable
|
|
||||||
branch might not exist when the master branch is compatible with multiple
|
|
||||||
versions, or the plugin might not be compatible at all with your version.
|
|
||||||
|
|
||||||
[[admin-console]]
|
|
||||||
=== admin-console
|
|
||||||
|
|
||||||
Plugin to provide administrator-only functionality, intended to
|
|
||||||
simplify common administrative tasks. Currently providing user-level
|
|
||||||
information. Also providing access control information by project or
|
|
||||||
project/account.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/admin-console[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/admin-console/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[analytics]]
|
|
||||||
=== analytics
|
|
||||||
|
|
||||||
Plugin to extract commit and review data from Gerrit projects and
|
|
||||||
expose aggregated metrics over REST and SSH API.
|
|
||||||
Metrics are extracted in JSON format with one record per line, ready to be
|
|
||||||
archived and processed with popular BigData transformation tools such
|
|
||||||
Apache Spark or published and visualized in dashboards.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/analytics[Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/analytics/+doc/master/README.md[Documentation]
|
|
||||||
|
|
||||||
[[avatars-external]]
|
|
||||||
=== avatars-external
|
|
||||||
|
|
||||||
This plugin allows to use an external url to load the avatar images
|
|
||||||
from.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/avatars-external[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/avatars-external/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/avatars-external/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[avatars-gravatar]]
|
|
||||||
=== avatars-gravatar
|
|
||||||
|
|
||||||
Plugin to display user icons from Gravatar.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/avatars-gravatar[
|
|
||||||
Project]
|
|
||||||
|
|
||||||
[[branch-network]]
|
|
||||||
=== branch-network
|
|
||||||
|
|
||||||
This plugin allows the rendering of Git repository branch network in a
|
|
||||||
graphical HTML5 Canvas. It is mainly intended to be used as a
|
|
||||||
"project link" in a gitweb configuration.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/branch-network[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/branch-network/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/branch-network/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[changemessage]]
|
|
||||||
=== changemessage
|
|
||||||
|
|
||||||
This plugin allows to display a static info message on the change screen.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/changemessage[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/changemessage/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Plugin Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/changemessage/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[checks]]
|
|
||||||
=== checks
|
|
||||||
|
|
||||||
The checks plugin provides a REST API and UI extensions for integrating
|
|
||||||
CI systems with Gerrit.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/checks[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/checks/+doc/master/README.md[
|
|
||||||
Plugin Documentation]
|
|
||||||
|
|
||||||
[[egit]]
|
|
||||||
=== egit
|
|
||||||
|
|
||||||
This plugin provides extensions for easier usage with EGit.
|
|
||||||
|
|
||||||
The plugin adds a download command for EGit that allows to copy only
|
|
||||||
the change ref into the clipboard. The change ref is needed for
|
|
||||||
downloading a Gerrit change from within EGit.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/egit[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/egit/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[emoticons]]
|
|
||||||
=== emoticons
|
|
||||||
|
|
||||||
This plugin allows users to see emoticons in comments as images.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/emoticons[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/emoticons/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/emoticons/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[find-owners]]
|
|
||||||
=== find-owners
|
|
||||||
This plugin provides (1) a change review action button `[FIND OWNERS]`
|
|
||||||
that shows owners of changed files to be included as code reviewers, and
|
|
||||||
(2) Prolog predicates to make sure that a CL is submittable
|
|
||||||
only with owner Code-Review +1 votes.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/find-owners[Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/find-owners/+doc/master/src/main/resources/Documentation/about.md[Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/find-owners/+doc/master/src/main/resources/Documentation/config.md[Configuration]
|
|
||||||
|
|
||||||
[[gitblit]]
|
|
||||||
=== gitblit
|
|
||||||
|
|
||||||
GitBlit code-viewer plugin with SSO and Security Access Control.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/gitblit[
|
|
||||||
Project]
|
|
||||||
|
|
||||||
[[github]]
|
|
||||||
=== github
|
|
||||||
|
|
||||||
Plugin to integrate with GitHub: replication, pull-request to Change-Sets
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/github[
|
|
||||||
Project]
|
|
||||||
|
|
||||||
[[healthcheck]]
|
|
||||||
=== healthcheck
|
|
||||||
|
|
||||||
Plugin for monitoring and alerting when Gerrit does not behave properrly.
|
|
||||||
|
|
||||||
When Gerrit Server needs to be available 24x7, it is important to know
|
|
||||||
*beforehand* if something isn't working correctly: this plugin exposes a
|
|
||||||
REST-API that provides the real-time status of the Gerrit internals and can
|
|
||||||
be integrated with real-time monitoring systems and paging platforms.
|
|
||||||
|
|
||||||
Healthcheck metrics (latency and subsystem healthiness) are published as
|
|
||||||
Gerrit internal metrics and can be published to dashboards.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/healthcheck[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/healthcheck/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/healthcheck/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[imagare]]
|
|
||||||
=== imagare
|
|
||||||
|
|
||||||
The imagare plugin allows Gerrit users to upload and share images.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/imagare[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/imagare/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/imagare/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[importer]]
|
|
||||||
=== importer
|
|
||||||
|
|
||||||
The importer plugin allows to import projects from one Gerrit server
|
|
||||||
into another Gerrit server.
|
|
||||||
|
|
||||||
Projects can be imported while both source and target Gerrit server
|
|
||||||
are online. There is no downtime required.
|
|
||||||
|
|
||||||
The git repository and all changes of the project, including approvals
|
|
||||||
and review comments, are imported. Historic timestamps are preserved.
|
|
||||||
|
|
||||||
Project imports can be resumed. This means a project team can continue
|
|
||||||
to work in the source system while the import to the target system is
|
|
||||||
done. By resuming the import the project in the target system can be
|
|
||||||
updated with the missing delta.
|
|
||||||
|
|
||||||
The importer plugin can also be used to copy a project within one Gerrit
|
|
||||||
server, and in combination with the link:#delete-project[delete-project]
|
|
||||||
plugin it can be used to rename a project.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/importer[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/importer/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[its-plugins]]
|
|
||||||
=== Issue Tracker System Plugins
|
|
||||||
|
|
||||||
Plugins to integrate with issue tracker systems (ITS), that (based
|
|
||||||
on events in Gerrit) allows to take actions in the ITS. For example,
|
|
||||||
they can add comments to bugs, or change status of bugs.
|
|
||||||
|
|
||||||
All its-plugins have a common base implementation which is stored in
|
|
||||||
the `its-base` project. `its-base` is not a plugin, but just a
|
|
||||||
framework for the ITS plugins which is packaged within each ITS plugin.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-base[
|
|
||||||
its-base Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-base/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
its-base Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-base/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
its-base Configuration]
|
|
||||||
|
|
||||||
[[its-bugzilla]]
|
|
||||||
==== its-bugzilla
|
|
||||||
|
|
||||||
Plugin to integrate with Bugzilla.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-bugzilla[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-bugzilla/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[its-jira]]
|
|
||||||
==== its-jira
|
|
||||||
|
|
||||||
Plugin to integrate with Jira.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-jira[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-jira/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[its-phabricator]]
|
|
||||||
==== its-phabricator
|
|
||||||
|
|
||||||
Plugin to integrate with Phabricator.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-phabricator[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-phabricator/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[its-rtc]]
|
|
||||||
==== its-rtc
|
|
||||||
|
|
||||||
Plugin to integrate with IBM Rational Team Concert (RTC).
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-rtc[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-rtc/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[its-storyboard]]
|
|
||||||
==== its-storyboard
|
|
||||||
|
|
||||||
Plugin to integrate with Storyboard task tracking system.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/its-storyboard[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/its-storyboard/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[javamelody]]
|
|
||||||
=== javamelody
|
|
||||||
|
|
||||||
This plugin allows to monitor the Gerrit server.
|
|
||||||
|
|
||||||
This plugin integrates JavaMelody in Gerrit in order to retrieve live
|
|
||||||
instrumentation data from Gerrit.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/javamelody[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/javamelody/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
https://gerrit.googlesource.com/plugins/javamelody/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[labelui]]
|
|
||||||
=== labelui
|
|
||||||
|
|
||||||
The labelui plugin adds a user preference that allows users to choose a
|
|
||||||
table control to render the labels/approvals on the change screen
|
|
||||||
(similar to how labels/approvals were rendered on the old change
|
|
||||||
screen).
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/labelui[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/labelui/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[menuextender]]
|
|
||||||
=== menuextender
|
|
||||||
|
|
||||||
The menuextender plugin allows Gerrit administrators to configure
|
|
||||||
additional menu entries from the WebUI.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/menuextender[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/menuextender/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/menuextender/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[metrics-reporter-elasticsearch]]
|
|
||||||
=== metrics-reporter-elasticsearch
|
|
||||||
|
|
||||||
This plugin reports Gerrit metrics to Elasticsearch.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/metrics-reporter-elasticsearch[
|
|
||||||
Project].
|
|
||||||
|
|
||||||
[[metrics-reporter-graphite]]
|
|
||||||
=== metrics-reporter-graphite
|
|
||||||
|
|
||||||
This plugin reports Gerrit metrics to Graphite.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/metrics-reporter-graphite[
|
|
||||||
Project].
|
|
||||||
|
|
||||||
[[metrics-reporter-jmx]]
|
|
||||||
=== metrics-reporter-jmx
|
|
||||||
|
|
||||||
This plugin reports Gerrit metrics to JMX.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/metrics-reporter-jmx[
|
|
||||||
Project].
|
|
||||||
|
|
||||||
[[metrics-reporter-prometheus]]
|
|
||||||
=== metrics-reporter-prometheus
|
|
||||||
|
|
||||||
This plugin exposes Gerrit metrics for consumption by Prometheus.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/metrics-reporter-prometheus[
|
|
||||||
Project].
|
|
||||||
|
|
||||||
[[motd]]
|
|
||||||
=== motd
|
|
||||||
|
|
||||||
This plugin can output messages to clients when pulling/fetching/cloning
|
|
||||||
code from Gerrit Code Review. If the client (and transport mechanism)
|
|
||||||
can support sending the message to the client, it will be displayed to
|
|
||||||
the user (usually prefixed by “remote: ”), but will be silently
|
|
||||||
discarded otherwise.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/motd[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/motd/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/motd/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[oauth-authentication-provider]]
|
|
||||||
=== OAuth authentication provider
|
|
||||||
This plugin enables Gerrit to use OAuth2 protocol for authentication.
|
|
||||||
Several OAuth2 providers are supported:
|
|
||||||
|
|
||||||
* AirVantage
|
|
||||||
* Bitbucket
|
|
||||||
* CAS
|
|
||||||
* CoreOS Dex
|
|
||||||
* Facebook
|
|
||||||
* GitHub
|
|
||||||
* GitLab
|
|
||||||
* Google
|
|
||||||
* Keycloak
|
|
||||||
* Office365
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/oauth[Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/oauth/+doc/master/src/main/resources/Documentation/config.md[Configuration]
|
|
||||||
|
|
||||||
[[owners]]
|
|
||||||
=== owners
|
|
||||||
This plugin provides a Prolog predicate `add_owner_approval/3` that
|
|
||||||
appends `label('Owner-Approval', need(_))` to a provided list.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/owners[Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/owners/+doc/master/README.md[Documentation]
|
|
||||||
|
|
||||||
[[project-download-commands]]
|
|
||||||
=== project-download-commands
|
|
||||||
|
|
||||||
This plugin adds support for project specific download commands.
|
|
||||||
|
|
||||||
Project specific download commands that are defined on a parent project
|
|
||||||
are inherited by the child projects. Child projects can overwrite the
|
|
||||||
inherited download command or remove it by assigning no value to it.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/project-download-commands[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/project-download-commands/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/project-download-commands/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[quota]]
|
|
||||||
=== quota
|
|
||||||
|
|
||||||
This plugin allows to enforce quotas in Gerrit.
|
|
||||||
|
|
||||||
To protect a Gerrit installation it makes sense to limit the resources
|
|
||||||
that a project or group can consume. To do this a Gerrit administrator
|
|
||||||
can use this plugin to define quotas on project namespaces.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/quota[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/quota/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
link:https://gerrit.googlesource.com/plugins/quota/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[rabbitmq]]
|
|
||||||
=== rabbitmq
|
|
||||||
|
|
||||||
A plugin that publishes Gerrit events to a
|
|
||||||
link:https://www.rabbitmq.com/[RabbitMQ] exchange.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/rabbitmq[Project]
|
|
||||||
link:https://gerrit.googlesource.com/plugins/rabbitmq/+/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[readonly]]
|
|
||||||
=== readonly
|
|
||||||
|
|
||||||
A plugin that makes the Gerrit server read-only by rejecting git pushes,
|
|
||||||
blocking HTTP PUT/POST/DELETE requests, and disabling SSH commands.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/readonly[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/readonly/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/readonly/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[ref-protection]]
|
|
||||||
=== ref-protection
|
|
||||||
|
|
||||||
A plugin that protects against commits being lost.
|
|
||||||
|
|
||||||
Backups of deleted or non-fast-forward updated refs are created under the
|
|
||||||
`refs/backups/` namespace.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/ref-protection[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/ref-protection/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[reparent]]
|
|
||||||
=== reparent
|
|
||||||
|
|
||||||
A plugin that provides project reparenting as a self-service for project owners.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/reparent[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reparent/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reparent/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[review-strategy]]
|
|
||||||
=== review-strategy
|
|
||||||
|
|
||||||
This plugin allows users to configure different review strategies.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/review-strategy[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/review-strategy/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[reviewers]]
|
|
||||||
=== reviewers
|
|
||||||
|
|
||||||
A plugin that allows adding default reviewers to a change.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/reviewers[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reviewers/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reviewers/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[reviewers-by-blame]]
|
|
||||||
=== reviewers-by-blame
|
|
||||||
|
|
||||||
A plugin that allows automatically adding reviewers to a change from
|
|
||||||
the git blame computation on the changed files. It will add the users
|
|
||||||
that authored most of the lines touched by the change, since these
|
|
||||||
users should be familiar with the code and can mostly review the
|
|
||||||
change.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/reviewers-by-blame[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reviewers-by-blame/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/reviewers-by-blame/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[groovy-provider]]
|
|
||||||
=== scripting/groovy-provider
|
|
||||||
|
|
||||||
This plugin provides a Groovy runtime environment for Gerrit plugins in Groovy.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/scripting/groovy-provider[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/scripting/groovy-provider/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[saml-authentication-provider]]
|
|
||||||
=== SAML2 authentication provider
|
|
||||||
|
|
||||||
This plugin enables Gerrit to use SAML2 protocol for authentication.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/saml[Project]
|
|
||||||
|
|
||||||
[[scala-provider]]
|
|
||||||
=== scripting/scala-provider
|
|
||||||
|
|
||||||
This plugin provides a Scala runtime environment for Gerrit plugins in Scala.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/scripting/scala-provider[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/scripting/scala-provider/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation]
|
|
||||||
|
|
||||||
[[scripts]]
|
|
||||||
=== scripts
|
|
||||||
|
|
||||||
Repository containing a collection of Gerrit scripting plugins that are intended
|
|
||||||
to provide simple and useful extensions.
|
|
||||||
|
|
||||||
Groovy and Scala scripts require the installation of the corresponding
|
|
||||||
scripting/*-provider plugin in order to be loaded into Gerrit.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/scripts[Project]
|
|
||||||
link:https://gerrit.googlesource.com/plugins/scripts/+doc/master/README.md[Documentation]
|
|
||||||
|
|
||||||
[[server-config]]
|
|
||||||
=== server-config
|
|
||||||
|
|
||||||
This plugin enables access (download and upload) to the server config
|
|
||||||
files. It may be used to change Gerrit config files (like
|
|
||||||
`etc/gerrit.config`) in cases where direct access to the file system
|
|
||||||
where Gerrit's config files are stored is difficult or impossible to
|
|
||||||
get.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/server-config[
|
|
||||||
Project]
|
|
||||||
|
|
||||||
[[serviceuser]]
|
|
||||||
=== serviceuser
|
|
||||||
|
|
||||||
This plugin allows to create service users in Gerrit.
|
|
||||||
|
|
||||||
A service user is a user that is used by another service to communicate
|
|
||||||
with Gerrit. E.g. a service user is needed to run the Gerrit Trigger
|
|
||||||
Plugin in Jenkins. A service user is not able to login into the Gerrit
|
|
||||||
WebUI and it cannot push commits or tags.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/serviceuser[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/serviceuser/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/serviceuser/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[uploadvalidator]]
|
|
||||||
=== uploadvalidator
|
|
||||||
|
|
||||||
This plugin allows to configure upload validations per project.
|
|
||||||
|
|
||||||
Project owners can configure blocked file extensions, required footers
|
|
||||||
and a maximum allowed path length. Pushes of commits that violate these
|
|
||||||
settings are rejected by Gerrit.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/uploadvalidator[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/uploadvalidator/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/uploadvalidator/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[verify-status]]
|
|
||||||
=== verify-status
|
|
||||||
|
|
||||||
This plugin adds a separate channel for Gerrit to store test metadata and
|
|
||||||
view them on the Gerrit UI. The metadata can be stored in the Gerrit database
|
|
||||||
or in a completely separate datastore.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/verify-status[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/verify-status/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/verify-status/+doc/master/src/main/resources/Documentation/database.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[websession-flatfile]]
|
|
||||||
=== websession-flatfile
|
|
||||||
|
|
||||||
This plugin replaces the built-in Gerrit H2 based websession cache with
|
|
||||||
a flatfile based implementation. This implementation is shareable
|
|
||||||
among multiple Gerrit servers, making it useful for cluster
|
|
||||||
Gerrit installations having multiple primary Gerrit nodes.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/websession-flatfile[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/websession-flatfile/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/websession-flatfile/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
[[x-docs]]
|
|
||||||
=== x-docs
|
|
||||||
|
|
||||||
This plugin serves project documentation as HTML pages.
|
|
||||||
|
|
||||||
link:https://gerrit-review.googlesource.com/admin/repos/plugins/x-docs[
|
|
||||||
Project] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/x-docs/+doc/master/src/main/resources/Documentation/about.md[
|
|
||||||
Documentation] |
|
|
||||||
link:https://gerrit.googlesource.com/plugins/x-docs/+doc/master/src/main/resources/Documentation/config.md[
|
|
||||||
Configuration]
|
|
||||||
|
|
||||||
|
|
||||||
GERRIT
|
GERRIT
|
||||||
------
|
------
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ reindex - Rebuild the secondary index
|
|||||||
[verse]
|
[verse]
|
||||||
--
|
--
|
||||||
_java_ -jar gerrit.war _reindex_
|
_java_ -jar gerrit.war _reindex_
|
||||||
|
-d <SITE_PATH>
|
||||||
[--threads]
|
[--threads]
|
||||||
[--changes-schema-version]
|
[--changes-schema-version]
|
||||||
[--verbose]
|
[--verbose]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
# 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 optparse import OptionParser
|
import argparse
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -239,34 +239,34 @@ LINK_SCRIPT = """
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
opts = OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts.add_option('-o', '--out', help='output file')
|
parser.add_argument('-o', '--out', help='output file')
|
||||||
opts.add_option('-s', '--src', help='source file')
|
parser.add_argument('-s', '--src', help='source file')
|
||||||
opts.add_option('-x', '--suffix', help='suffix for included filenames')
|
parser.add_argument('-x', '--suffix', help='suffix for included filenames')
|
||||||
opts.add_option('-b', '--searchbox', action="store_true", default=True,
|
parser.add_argument('-b', '--searchbox', action="store_true", default=True,
|
||||||
help="generate the search boxes")
|
help="generate the search boxes")
|
||||||
opts.add_option('--no-searchbox', action="store_false", dest='searchbox',
|
parser.add_argument('--no-searchbox', action="store_false", dest='searchbox',
|
||||||
help="don't generate the search boxes")
|
help="don't generate the search boxes")
|
||||||
opts.add_option('--site-search', action="store", metavar="SITE",
|
parser.add_argument('--site-search', action="store", metavar="SITE",
|
||||||
help=("generate the search box using google. SITE should " +
|
help=("generate the search box using google. SITE should " +
|
||||||
"point to the domain/path of the site, eg. " +
|
"point to the domain/path of the site, eg. " +
|
||||||
"gerrit-review.googlesource.com/Documentation"))
|
"gerrit-review.googlesource.com/Documentation"))
|
||||||
options, _ = opts.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if options.site_search:
|
if args.site_search:
|
||||||
SEARCH_BOX = (SEARCH_BOX %
|
SEARCH_BOX = (SEARCH_BOX %
|
||||||
GOOGLE_SITE_SEARCH.replace("@SITE@", options.site_search))
|
GOOGLE_SITE_SEARCH.replace("@SITE@", args.site_search))
|
||||||
else:
|
else:
|
||||||
SEARCH_BOX = SEARCH_BOX % BUILTIN_SEARCH
|
SEARCH_BOX = SEARCH_BOX % BUILTIN_SEARCH
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
out_file = open(options.out, 'w', errors='ignore')
|
out_file = open(args.out, 'w', errors='ignore')
|
||||||
src_file = open(options.src, 'r', errors='ignore')
|
src_file = open(args.src, 'r', errors='ignore')
|
||||||
except TypeError:
|
except TypeError:
|
||||||
out_file = open(options.out, 'w')
|
out_file = open(args.out, 'w')
|
||||||
src_file = open(options.src, 'r')
|
src_file = open(args.src, 'r')
|
||||||
last_line = ''
|
last_line = ''
|
||||||
ignore_next_line = False
|
ignore_next_line = False
|
||||||
last_title = ''
|
last_title = ''
|
||||||
@@ -277,14 +277,14 @@ try:
|
|||||||
last_line = ''
|
last_line = ''
|
||||||
elif PAT_SEARCHBOX.match(last_line):
|
elif PAT_SEARCHBOX.match(last_line):
|
||||||
# Case of 'SEARCHBOX\n---------'
|
# Case of 'SEARCHBOX\n---------'
|
||||||
if options.searchbox:
|
if args.searchbox:
|
||||||
out_file.write(SEARCH_BOX)
|
out_file.write(SEARCH_BOX)
|
||||||
last_line = ''
|
last_line = ''
|
||||||
elif PAT_INCLUDE.match(line):
|
elif PAT_INCLUDE.match(line):
|
||||||
# Case of 'include::<filename>'
|
# Case of 'include::<filename>'
|
||||||
match = PAT_INCLUDE.match(line)
|
match = PAT_INCLUDE.match(line)
|
||||||
out_file.write(last_line)
|
out_file.write(last_line)
|
||||||
last_line = match.group(1) + options.suffix + match.group(2) + '\n'
|
last_line = match.group(1) + args.suffix + match.group(2) + '\n'
|
||||||
elif PAT_STARS.match(line):
|
elif PAT_STARS.match(line):
|
||||||
if PAT_TITLE.match(last_line):
|
if PAT_TITLE.match(last_line):
|
||||||
# Case of the title in '.<title>\n****\nget::<url>\n****'
|
# Case of the title in '.<title>\n****\nget::<url>\n****'
|
||||||
@@ -310,5 +310,5 @@ try:
|
|||||||
out_file.close()
|
out_file.close()
|
||||||
except IOError as err:
|
except IOError as err:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"error while expanding %s to %s: %s" % (options.src, options.out, err))
|
"error while expanding %s to %s: %s" % (args.src, args.out, err))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
@@ -5557,8 +5557,7 @@ In this case, use a POST request instead:
|
|||||||
Identifier that uniquely identifies one change. It contains the URL-encoded
|
Identifier that uniquely identifies one change. It contains the URL-encoded
|
||||||
project name as well as the change number: "'$$<project>~<numericId>$$'"
|
project name as well as the change number: "'$$<project>~<numericId>$$'"
|
||||||
|
|
||||||
Depending on the server's configuration, Gerrit can still support the following
|
Gerrit also supports the following identifiers:
|
||||||
deprecated identifiers. These will be removed in a future release:
|
|
||||||
|
|
||||||
* an ID of the change in the format "'$$<project>~<branch>~<Change-Id>$$'",
|
* an ID of the change in the format "'$$<project>~<branch>~<Change-Id>$$'",
|
||||||
where for the branch the `refs/heads/` prefix can be omitted
|
where for the branch the `refs/heads/` prefix can be omitted
|
||||||
@@ -5567,10 +5566,6 @@ deprecated identifiers. These will be removed in a future release:
|
|||||||
("I8473b95934b5732ac55d26311a706c9c2bde9940")
|
("I8473b95934b5732ac55d26311a706c9c2bde9940")
|
||||||
* a numeric change ID ("4247")
|
* a numeric change ID ("4247")
|
||||||
|
|
||||||
If you need more time to migrate off of old change IDs, please see
|
|
||||||
link:config-gerrit.html#change.api.allowedIdentifier[change.api.allowedIdentifier]
|
|
||||||
for more information on how to enable the use of deprecated identifiers.
|
|
||||||
|
|
||||||
[[change-message-id]]
|
[[change-message-id]]
|
||||||
=== \{change-message-id\}
|
=== \{change-message-id\}
|
||||||
ID of a change message returned in a link:#change-message-info[ChangeMessageInfo].
|
ID of a change message returned in a link:#change-message-info[ChangeMessageInfo].
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ TODO(hiesel): Add comments
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
import argparse
|
||||||
import atexit
|
import atexit
|
||||||
import json
|
import json
|
||||||
import optparse
|
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
@@ -275,22 +275,22 @@ def clean_up():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = optparse.OptionParser()
|
p = argparse.ArgumentParser()
|
||||||
p.add_option("-u", "--user_count", action="store",
|
p.add_argument("-u", "--user_count", action="store",
|
||||||
default=100,
|
default=100,
|
||||||
type='int',
|
type=int,
|
||||||
help="number of users to generate")
|
help="number of users to generate")
|
||||||
p.add_option("-p", "--port", action="store",
|
p.add_argument("-p", "--port", action="store",
|
||||||
default=8080,
|
default=8080,
|
||||||
type='int',
|
type=int,
|
||||||
help="port of server")
|
help="port of server")
|
||||||
(options, _) = p.parse_args()
|
args = p.parse_args()
|
||||||
global BASE_URL
|
global BASE_URL
|
||||||
BASE_URL = BASE_URL % options.port
|
BASE_URL = BASE_URL % args.port
|
||||||
print(BASE_URL)
|
print(BASE_URL)
|
||||||
|
|
||||||
set_up()
|
set_up()
|
||||||
gerrit_users = get_random_users(options.user_count)
|
gerrit_users = get_random_users(args.user_count)
|
||||||
|
|
||||||
group_names = create_gerrit_groups()
|
group_names = create_gerrit_groups()
|
||||||
for idx, u in enumerate(gerrit_users):
|
for idx, u in enumerate(gerrit_users):
|
||||||
|
|||||||
@@ -1,25 +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.extensions.restapi;
|
|
||||||
|
|
||||||
/** Named resource was accessed using a deprecated identifier. */
|
|
||||||
public class DeprecatedIdentifierException extends BadRequestException {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/** Requested resource using a deprecated identifier. */
|
|
||||||
public DeprecatedIdentifierException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.metrics.proc;
|
package com.google.gerrit.metrics.proc;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
import com.google.gerrit.metrics.CallbackMetric1;
|
import com.google.gerrit.metrics.CallbackMetric1;
|
||||||
import com.google.gerrit.metrics.Description;
|
import com.google.gerrit.metrics.Description;
|
||||||
import com.google.gerrit.metrics.Description.Units;
|
import com.google.gerrit.metrics.Description.Units;
|
||||||
@@ -34,13 +35,23 @@ public class JGitMetricModule extends MetricModule {
|
|||||||
new Description("Bytes of memory retained in JGit block cache.")
|
new Description("Bytes of memory retained in JGit block cache.")
|
||||||
.setGauge()
|
.setGauge()
|
||||||
.setUnit(Units.BYTES),
|
.setUnit(Units.BYTES),
|
||||||
WindowCacheStats.getStats()::getOpenByteCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getOpenByteCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/open_files",
|
"jgit/block_cache/open_files",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("File handles held open by JGit block cache.").setGauge().setUnit("fds"),
|
new Description("File handles held open by JGit block cache.").setGauge().setUnit("fds"),
|
||||||
WindowCacheStats.getStats()::getOpenFileCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getOpenFileCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/avg_load_time",
|
"jgit/block_cache/avg_load_time",
|
||||||
@@ -48,61 +59,110 @@ public class JGitMetricModule extends MetricModule {
|
|||||||
new Description("Average time to load a cache entry for JGit block cache.")
|
new Description("Average time to load a cache entry for JGit block cache.")
|
||||||
.setGauge()
|
.setGauge()
|
||||||
.setUnit(Units.NANOSECONDS),
|
.setUnit(Units.NANOSECONDS),
|
||||||
WindowCacheStats.getStats()::getAverageLoadTime);
|
new Supplier<Double>() {
|
||||||
|
@Override
|
||||||
|
public Double get() {
|
||||||
|
return WindowCacheStats.getStats().getAverageLoadTime();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/eviction_count",
|
"jgit/block_cache/eviction_count",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("Cache evictions for JGit block cache.").setGauge(),
|
new Description("Cache evictions for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getEvictionCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getEvictionCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/eviction_ratio",
|
"jgit/block_cache/eviction_ratio",
|
||||||
Double.class,
|
Double.class,
|
||||||
new Description("Cache eviction ratio for JGit block cache.").setGauge(),
|
new Description("Cache eviction ratio for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getEvictionRatio);
|
new Supplier<Double>() {
|
||||||
|
@Override
|
||||||
|
public Double get() {
|
||||||
|
return WindowCacheStats.getStats().getEvictionRatio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/hit_count",
|
"jgit/block_cache/hit_count",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("Cache hits for JGit block cache.").setGauge(),
|
new Description("Cache hits for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getHitCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getHitCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/hit_ratio",
|
"jgit/block_cache/hit_ratio",
|
||||||
Double.class,
|
Double.class,
|
||||||
new Description("Cache hit ratio for JGit block cache.").setGauge(),
|
new Description("Cache hit ratio for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getHitRatio);
|
new Supplier<Double>() {
|
||||||
|
@Override
|
||||||
|
public Double get() {
|
||||||
|
return WindowCacheStats.getStats().getHitRatio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/load_failure_count",
|
"jgit/block_cache/load_failure_count",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("Failed cache loads for JGit block cache.").setGauge(),
|
new Description("Failed cache loads for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getLoadFailureCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getLoadFailureCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/load_failure_ratio",
|
"jgit/block_cache/load_failure_ratio",
|
||||||
Double.class,
|
Double.class,
|
||||||
new Description("Failed cache load ratio for JGit block cache.").setGauge(),
|
new Description("Failed cache load ratio for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getLoadFailureRatio);
|
new Supplier<Double>() {
|
||||||
|
@Override
|
||||||
|
public Double get() {
|
||||||
|
return WindowCacheStats.getStats().getLoadFailureRatio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/load_success_count",
|
"jgit/block_cache/load_success_count",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("Successfull cache loads for JGit block cache.").setGauge(),
|
new Description("Successfull cache loads for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getLoadSuccessCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getLoadSuccessCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/miss_count",
|
"jgit/block_cache/miss_count",
|
||||||
Long.class,
|
Long.class,
|
||||||
new Description("Cache misses for JGit block cache.").setGauge(),
|
new Description("Cache misses for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getMissCount);
|
new Supplier<Long>() {
|
||||||
|
@Override
|
||||||
|
public Long get() {
|
||||||
|
return WindowCacheStats.getStats().getMissCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
"jgit/block_cache/miss_ratio",
|
"jgit/block_cache/miss_ratio",
|
||||||
Double.class,
|
Double.class,
|
||||||
new Description("Cache miss ratio for JGit block cache.").setGauge(),
|
new Description("Cache miss ratio for JGit block cache.").setGauge(),
|
||||||
WindowCacheStats.getStats()::getMissRatio);
|
new Supplier<Double>() {
|
||||||
|
@Override
|
||||||
|
public Double get() {
|
||||||
|
return WindowCacheStats.getStats().getMissRatio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
CallbackMetric1<String, Long> repoEnt =
|
CallbackMetric1<String, Long> repoEnt =
|
||||||
metrics.newCallbackMetric(
|
metrics.newCallbackMetric(
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ java_library(
|
|||||||
"//java/com/google/gerrit/lifecycle",
|
"//java/com/google/gerrit/lifecycle",
|
||||||
"//java/com/google/gerrit/metrics",
|
"//java/com/google/gerrit/metrics",
|
||||||
"//java/com/google/gerrit/server",
|
"//java/com/google/gerrit/server",
|
||||||
"//java/com/google/gerrit/server/logging",
|
|
||||||
"//java/com/google/gerrit/server/util/time",
|
"//java/com/google/gerrit/server/util/time",
|
||||||
"//java/com/google/gerrit/sshd",
|
"//java/com/google/gerrit/sshd",
|
||||||
"//java/com/google/gerrit/util/http",
|
"//java/com/google/gerrit/util/http",
|
||||||
|
"//java/com/google/gerrit/util/logging",
|
||||||
"//lib:gson",
|
"//lib:gson",
|
||||||
"//lib:guava",
|
"//lib:guava",
|
||||||
"//lib:jgit",
|
"//lib:jgit",
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import static com.google.gerrit.pgm.http.jetty.HttpLog.P_STATUS;
|
|||||||
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER;
|
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER;
|
||||||
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER_AGENT;
|
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_USER_AGENT;
|
||||||
|
|
||||||
import com.google.gerrit.server.logging.JsonLayout;
|
import com.google.gerrit.util.logging.JsonLayout;
|
||||||
import com.google.gerrit.server.logging.JsonLogEntry;
|
import com.google.gerrit.util.logging.JsonLogEntry;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import org.apache.log4j.spi.LoggingEvent;
|
import org.apache.log4j.spi.LoggingEvent;
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,11 @@ package com.google.gerrit.server.change;
|
|||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.gerrit.entities.Change;
|
import com.google.gerrit.entities.Change;
|
||||||
import com.google.gerrit.entities.Project;
|
import com.google.gerrit.entities.Project;
|
||||||
import com.google.gerrit.exceptions.StorageException;
|
import com.google.gerrit.exceptions.StorageException;
|
||||||
import com.google.gerrit.extensions.restapi.DeprecatedIdentifierException;
|
|
||||||
import com.google.gerrit.extensions.restapi.Url;
|
import com.google.gerrit.extensions.restapi.Url;
|
||||||
import com.google.gerrit.git.ObjectIds;
|
import com.google.gerrit.git.ObjectIds;
|
||||||
import com.google.gerrit.index.IndexConfig;
|
import com.google.gerrit.index.IndexConfig;
|
||||||
@@ -32,8 +30,6 @@ import com.google.gerrit.metrics.Description;
|
|||||||
import com.google.gerrit.metrics.Field;
|
import com.google.gerrit.metrics.Field;
|
||||||
import com.google.gerrit.metrics.MetricMaker;
|
import com.google.gerrit.metrics.MetricMaker;
|
||||||
import com.google.gerrit.server.cache.CacheModule;
|
import com.google.gerrit.server.cache.CacheModule;
|
||||||
import com.google.gerrit.server.config.ConfigUtil;
|
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
|
||||||
import com.google.gerrit.server.logging.Metadata;
|
import com.google.gerrit.server.logging.Metadata;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
@@ -50,7 +46,6 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ChangeFinder {
|
public class ChangeFinder {
|
||||||
@@ -79,7 +74,6 @@ public class ChangeFinder {
|
|||||||
private final Provider<InternalChangeQuery> queryProvider;
|
private final Provider<InternalChangeQuery> queryProvider;
|
||||||
private final ChangeNotes.Factory changeNotesFactory;
|
private final ChangeNotes.Factory changeNotesFactory;
|
||||||
private final Counter1<ChangeIdType> changeIdCounter;
|
private final Counter1<ChangeIdType> changeIdCounter;
|
||||||
private final ImmutableSet<ChangeIdType> allowedIdTypes;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeFinder(
|
ChangeFinder(
|
||||||
@@ -87,8 +81,7 @@ public class ChangeFinder {
|
|||||||
@Named(CACHE_NAME) Cache<Change.Id, String> changeIdProjectCache,
|
@Named(CACHE_NAME) Cache<Change.Id, String> changeIdProjectCache,
|
||||||
Provider<InternalChangeQuery> queryProvider,
|
Provider<InternalChangeQuery> queryProvider,
|
||||||
ChangeNotes.Factory changeNotesFactory,
|
ChangeNotes.Factory changeNotesFactory,
|
||||||
MetricMaker metricMaker,
|
MetricMaker metricMaker) {
|
||||||
@GerritServerConfig Config config) {
|
|
||||||
this.indexConfig = indexConfig;
|
this.indexConfig = indexConfig;
|
||||||
this.changeIdProjectCache = changeIdProjectCache;
|
this.changeIdProjectCache = changeIdProjectCache;
|
||||||
this.queryProvider = queryProvider;
|
this.queryProvider = queryProvider;
|
||||||
@@ -101,11 +94,6 @@ public class ChangeFinder {
|
|||||||
.setUnit("requests"),
|
.setUnit("requests"),
|
||||||
Field.ofEnum(ChangeIdType.class, "change_id_type", Metadata.Builder::changeIdType)
|
Field.ofEnum(ChangeIdType.class, "change_id_type", Metadata.Builder::changeIdType)
|
||||||
.build());
|
.build());
|
||||||
List<ChangeIdType> configuredChangeIdTypes =
|
|
||||||
ConfigUtil.getEnumList(config, "change", "api", "allowedIdentifier", ChangeIdType.ALL);
|
|
||||||
// Ensure that PROJECT_NUMERIC_ID can't be removed
|
|
||||||
configuredChangeIdTypes.add(ChangeIdType.PROJECT_NUMERIC_ID);
|
|
||||||
this.allowedIdTypes = ImmutableSet.copyOf(configuredChangeIdTypes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeNotes findOne(String id) {
|
public ChangeNotes findOne(String id) {
|
||||||
@@ -123,25 +111,6 @@ public class ChangeFinder {
|
|||||||
* @return possibly-empty list of notes for all matching changes; may or may not be visible.
|
* @return possibly-empty list of notes for all matching changes; may or may not be visible.
|
||||||
*/
|
*/
|
||||||
public List<ChangeNotes> find(String id) {
|
public List<ChangeNotes> find(String id) {
|
||||||
try {
|
|
||||||
return find(id, false);
|
|
||||||
} catch (DeprecatedIdentifierException e) {
|
|
||||||
// This can't happen because we don't enforce deprecation
|
|
||||||
throw new StorageException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find changes matching the given identifier.
|
|
||||||
*
|
|
||||||
* @param id change identifier.
|
|
||||||
* @param enforceDeprecation boolean to see if we should throw {@link
|
|
||||||
* DeprecatedIdentifierException} in case the identifier is deprecated
|
|
||||||
* @return possibly-empty list of notes for all matching changes; may or may not be visible.
|
|
||||||
* @throws DeprecatedIdentifierException if the identifier is deprecated.
|
|
||||||
*/
|
|
||||||
public List<ChangeNotes> find(String id, boolean enforceDeprecation)
|
|
||||||
throws DeprecatedIdentifierException {
|
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -152,7 +121,7 @@ public class ChangeFinder {
|
|||||||
// Try project~numericChangeId
|
// Try project~numericChangeId
|
||||||
Integer n = Ints.tryParse(id.substring(z + 1));
|
Integer n = Ints.tryParse(id.substring(z + 1));
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
checkIdType(ChangeIdType.PROJECT_NUMERIC_ID, enforceDeprecation, n.toString());
|
changeIdCounter.increment(ChangeIdType.PROJECT_NUMERIC_ID);
|
||||||
return fromProjectNumber(id.substring(0, z), n.intValue());
|
return fromProjectNumber(id.substring(0, z), n.intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +130,7 @@ public class ChangeFinder {
|
|||||||
// Try numeric changeId
|
// Try numeric changeId
|
||||||
Integer n = Ints.tryParse(id);
|
Integer n = Ints.tryParse(id);
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
checkIdType(ChangeIdType.NUMERIC_ID, enforceDeprecation, n.toString());
|
changeIdCounter.increment(ChangeIdType.NUMERIC_ID);
|
||||||
return find(Change.id(n));
|
return find(Change.id(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +141,7 @@ public class ChangeFinder {
|
|||||||
|
|
||||||
// Try commit hash
|
// Try commit hash
|
||||||
if (id.matches("^([0-9a-fA-F]{" + ObjectIds.ABBREV_STR_LEN + "," + ObjectIds.STR_LEN + "})$")) {
|
if (id.matches("^([0-9a-fA-F]{" + ObjectIds.ABBREV_STR_LEN + "," + ObjectIds.STR_LEN + "})$")) {
|
||||||
checkIdType(ChangeIdType.COMMIT_HASH, enforceDeprecation, id);
|
changeIdCounter.increment(ChangeIdType.COMMIT_HASH);
|
||||||
return asChangeNotes(query.byCommit(id));
|
return asChangeNotes(query.byCommit(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +150,7 @@ public class ChangeFinder {
|
|||||||
Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id, y, z);
|
Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id, y, z);
|
||||||
if (triplet.isPresent()) {
|
if (triplet.isPresent()) {
|
||||||
ChangeTriplet t = triplet.get();
|
ChangeTriplet t = triplet.get();
|
||||||
checkIdType(ChangeIdType.TRIPLET, enforceDeprecation, triplet.get().toString());
|
changeIdCounter.increment(ChangeIdType.TRIPLET);
|
||||||
return asChangeNotes(query.byBranchKey(t.branch(), t.id()));
|
return asChangeNotes(query.byBranchKey(t.branch(), t.id()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +158,7 @@ public class ChangeFinder {
|
|||||||
// Try isolated Ihash... format ("Change-Id: Ihash").
|
// Try isolated Ihash... format ("Change-Id: Ihash").
|
||||||
List<ChangeNotes> notes = asChangeNotes(query.byKeyPrefix(id));
|
List<ChangeNotes> notes = asChangeNotes(query.byKeyPrefix(id));
|
||||||
if (!notes.isEmpty()) {
|
if (!notes.isEmpty()) {
|
||||||
checkIdType(ChangeIdType.I_HASH, enforceDeprecation, id);
|
changeIdCounter.increment(ChangeIdType.I_HASH);
|
||||||
}
|
}
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
@@ -258,18 +227,4 @@ public class ChangeFinder {
|
|||||||
}
|
}
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIdType(ChangeIdType type, boolean enforceDeprecation, String val)
|
|
||||||
throws DeprecatedIdentifierException {
|
|
||||||
if (enforceDeprecation
|
|
||||||
&& !allowedIdTypes.contains(ChangeIdType.ALL)
|
|
||||||
&& !allowedIdTypes.contains(type)) {
|
|
||||||
throw new DeprecatedIdentifierException(
|
|
||||||
String.format(
|
|
||||||
"The provided change identifier %s is deprecated. "
|
|
||||||
+ "Use 'project~changeNumber' instead.",
|
|
||||||
val));
|
|
||||||
}
|
|
||||||
changeIdCounter.increment(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public interface UrlFormatter {
|
|||||||
.map(url -> url + String.format("@%s%d", side == 0 ? "a" : "", startLine));
|
.map(url -> url + String.format("@%s%d", side == 0 ? "a" : "", startLine));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a URL pointing to a section of the settings page. */
|
/** Returns a URL pointing to the settings page. */
|
||||||
default Optional<String> getSettingsUrl() {
|
default Optional<String> getSettingsUrl() {
|
||||||
return getWebUrl().map(url -> url + "settings");
|
return getWebUrl().map(url -> url + "settings");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ class ReceiveCommits {
|
|||||||
private static final String CANNOT_DELETE_CHANGES = "Cannot delete from '" + REFS_CHANGES + "'";
|
private static final String CANNOT_DELETE_CHANGES = "Cannot delete from '" + REFS_CHANGES + "'";
|
||||||
private static final String CANNOT_DELETE_CONFIG =
|
private static final String CANNOT_DELETE_CONFIG =
|
||||||
"Cannot delete project configuration from '" + RefNames.REFS_CONFIG + "'";
|
"Cannot delete project configuration from '" + RefNames.REFS_CONFIG + "'";
|
||||||
|
private static final String INTERNAL_SERVER_ERROR = "internal server error";
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
ReceiveCommits create(
|
ReceiveCommits create(
|
||||||
@@ -582,7 +583,7 @@ class ReceiveCommits {
|
|||||||
commands =
|
commands =
|
||||||
commands.stream().map(c -> wrapReceiveCommand(c, commandProgress)).collect(toList());
|
commands.stream().map(c -> wrapReceiveCommand(c, commandProgress)).collect(toList());
|
||||||
processCommandsUnsafe(commands, progress);
|
processCommandsUnsafe(commands, progress);
|
||||||
rejectRemaining(commands, "internal server error");
|
rejectRemaining(commands, INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
// This sends error messages before the 'done' string of the progress monitor is sent.
|
// This sends error messages before the 'done' string of the progress monitor is sent.
|
||||||
// Currently, the test framework relies on this ordering to understand if pushes completed
|
// Currently, the test framework relies on this ordering to understand if pushes completed
|
||||||
@@ -710,7 +711,7 @@ class ReceiveCommits {
|
|||||||
logger.atFine().log("Added %d additional ref updates", added);
|
logger.atFine().log("Added %d additional ref updates", added);
|
||||||
bu.execute();
|
bu.execute();
|
||||||
} catch (UpdateException | RestApiException e) {
|
} catch (UpdateException | RestApiException e) {
|
||||||
rejectRemaining(cmds, "internal server error");
|
rejectRemaining(cmds, INTERNAL_SERVER_ERROR);
|
||||||
logger.atFine().withCause(e).log("update failed:");
|
logger.atFine().withCause(e).log("update failed:");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +947,7 @@ class ReceiveCommits {
|
|||||||
} catch (RestApiException | IOException e) {
|
} catch (RestApiException | IOException e) {
|
||||||
logger.atSevere().withCause(e).log(
|
logger.atSevere().withCause(e).log(
|
||||||
"Can't insert change/patch set for %s", project.getName());
|
"Can't insert change/patch set for %s", project.getName());
|
||||||
reject(magicBranchCmd, "internal server error: " + e.getMessage());
|
reject(magicBranchCmd, String.format("%s: %s", INTERNAL_SERVER_ERROR, e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (magicBranch != null && magicBranch.submit) {
|
if (magicBranch != null && magicBranch.submit) {
|
||||||
@@ -1886,7 +1887,7 @@ class ReceiveCommits {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.atWarning().withCause(e).log(
|
logger.atWarning().withCause(e).log(
|
||||||
"Project %s cannot read %s", project.getName(), id.name());
|
"Project %s cannot read %s", project.getName(), id.name());
|
||||||
reject(cmd, "internal server error");
|
reject(cmd, INTERNAL_SERVER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1912,7 +1913,7 @@ class ReceiveCommits {
|
|||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.atWarning().withCause(ex).log(
|
logger.atWarning().withCause(ex).log(
|
||||||
"Error walking to %s in project %s", destBranch, project.getName());
|
"Error walking to %s in project %s", destBranch, project.getName());
|
||||||
reject(cmd, "internal server error");
|
reject(cmd, INTERNAL_SERVER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2329,7 +2330,7 @@ class ReceiveCommits {
|
|||||||
logger.atFine().log("Finished updating groups from GroupCollector");
|
logger.atFine().log("Finished updating groups from GroupCollector");
|
||||||
} catch (StorageException e) {
|
} catch (StorageException e) {
|
||||||
logger.atSevere().withCause(e).log("Error collecting groups for changes");
|
logger.atSevere().withCause(e).log("Error collecting groups for changes");
|
||||||
reject(magicBranch.cmd, "internal server error");
|
reject(magicBranch.cmd, INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return newChanges;
|
return newChanges;
|
||||||
}
|
}
|
||||||
@@ -2652,11 +2653,11 @@ class ReceiveCommits {
|
|||||||
} catch (StorageException err) {
|
} catch (StorageException err) {
|
||||||
logger.atSevere().withCause(err).log(
|
logger.atSevere().withCause(err).log(
|
||||||
"Cannot read database before replacement for project %s", project.getName());
|
"Cannot read database before replacement for project %s", project.getName());
|
||||||
rejectRemainingRequests(replaceByChange.values(), "internal server error");
|
rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
|
||||||
} catch (IOException | PermissionBackendException err) {
|
} catch (IOException | PermissionBackendException err) {
|
||||||
logger.atSevere().withCause(err).log(
|
logger.atSevere().withCause(err).log(
|
||||||
"Cannot read repository before replacement for project %s", project.getName());
|
"Cannot read repository before replacement for project %s", project.getName());
|
||||||
rejectRemainingRequests(replaceByChange.values(), "internal server error");
|
rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
logger.atFine().log("Read %d changes to replace", replaceByChange.size());
|
logger.atFine().log("Read %d changes to replace", replaceByChange.size());
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
|
|||||||
@Override
|
@Override
|
||||||
public ChangeResource parse(TopLevelResource root, IdString id)
|
public ChangeResource parse(TopLevelResource root, IdString id)
|
||||||
throws RestApiException, PermissionBackendException, IOException {
|
throws RestApiException, PermissionBackendException, IOException {
|
||||||
List<ChangeNotes> notes = changeFinder.find(id.encoded(), true);
|
List<ChangeNotes> notes = changeFinder.find(id.encoded());
|
||||||
if (notes.isEmpty()) {
|
if (notes.isEmpty()) {
|
||||||
throw new ResourceNotFoundException(id);
|
throw new ResourceNotFoundException(id);
|
||||||
} else if (notes.size() != 1) {
|
} else if (notes.size() != 1) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ java_library(
|
|||||||
"//java/com/google/gerrit/server/restapi",
|
"//java/com/google/gerrit/server/restapi",
|
||||||
"//java/com/google/gerrit/server/util/time",
|
"//java/com/google/gerrit/server/util/time",
|
||||||
"//java/com/google/gerrit/util/cli",
|
"//java/com/google/gerrit/util/cli",
|
||||||
|
"//java/com/google/gerrit/util/logging",
|
||||||
"//lib:args4j",
|
"//lib:args4j",
|
||||||
"//lib:gson",
|
"//lib:gson",
|
||||||
"//lib:guava",
|
"//lib:guava",
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import static com.google.gerrit.sshd.SshLog.P_STATUS;
|
|||||||
import static com.google.gerrit.sshd.SshLog.P_USER_NAME;
|
import static com.google.gerrit.sshd.SshLog.P_USER_NAME;
|
||||||
import static com.google.gerrit.sshd.SshLog.P_WAIT;
|
import static com.google.gerrit.sshd.SshLog.P_WAIT;
|
||||||
|
|
||||||
import com.google.gerrit.server.logging.JsonLayout;
|
import com.google.gerrit.util.logging.JsonLayout;
|
||||||
import com.google.gerrit.server.logging.JsonLogEntry;
|
import com.google.gerrit.util.logging.JsonLogEntry;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import org.apache.log4j.spi.LoggingEvent;
|
import org.apache.log4j.spi.LoggingEvent;
|
||||||
|
|
||||||
|
|||||||
13
java/com/google/gerrit/util/logging/BUILD
Normal file
13
java/com/google/gerrit/util/logging/BUILD
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
load("@rules_java//java:defs.bzl", "java_library")
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "logging",
|
||||||
|
srcs = glob(
|
||||||
|
["*.java"],
|
||||||
|
),
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//lib:gson",
|
||||||
|
"//lib/log:log4j",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
package com.google.gerrit.server.logging;
|
package com.google.gerrit.util.logging;
|
||||||
|
|
||||||
import com.google.gson.FieldNamingPolicy;
|
import com.google.gson.FieldNamingPolicy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
package com.google.gerrit.server.logging;
|
package com.google.gerrit.util.logging;
|
||||||
|
|
||||||
import org.apache.log4j.spi.LoggingEvent;
|
import org.apache.log4j.spi.LoggingEvent;
|
||||||
|
|
||||||
@@ -18,14 +18,12 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
|
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
|
||||||
|
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
import com.google.gerrit.acceptance.GerritConfig;
|
|
||||||
import com.google.gerrit.acceptance.NoHttpd;
|
import com.google.gerrit.acceptance.NoHttpd;
|
||||||
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
|
||||||
import com.google.gerrit.entities.Project;
|
import com.google.gerrit.entities.Project;
|
||||||
import com.google.gerrit.extensions.api.changes.ChangeApi;
|
import com.google.gerrit.extensions.api.changes.ChangeApi;
|
||||||
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.restapi.DeprecatedIdentifierException;
|
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -140,28 +138,4 @@ public class ChangeIdIT extends AbstractDaemonTest {
|
|||||||
public void wrongChangeIdReturnsNotFound() throws Exception {
|
public void wrongChangeIdReturnsNotFound() throws Exception {
|
||||||
assertThrows(ResourceNotFoundException.class, () -> gApi.changes().id("I1234567890"));
|
assertThrows(ResourceNotFoundException.class, () -> gApi.changes().id("I1234567890"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@GerritConfig(
|
|
||||||
name = "change.api.allowedIdentifier",
|
|
||||||
values = {"PROJECT_NUMERIC_ID", "NUMERIC_ID"})
|
|
||||||
public void deprecatedChangeIdReturnsBadRequest() throws Exception {
|
|
||||||
// project~changeNumber still works
|
|
||||||
ChangeApi cApi1 = gApi.changes().id(project.get(), changeInfo._number);
|
|
||||||
assertThat(cApi1.get().changeId).isEqualTo(changeInfo.changeId);
|
|
||||||
// Change number still works
|
|
||||||
ChangeApi cApi2 = gApi.changes().id(changeInfo._number);
|
|
||||||
assertThat(cApi2.get().changeId).isEqualTo(changeInfo.changeId);
|
|
||||||
// IHash throws
|
|
||||||
ChangeInfo ci =
|
|
||||||
gApi.changes().create(new ChangeInput(project.get(), "master", "different message")).get();
|
|
||||||
DeprecatedIdentifierException thrown =
|
|
||||||
assertThrows(DeprecatedIdentifierException.class, () -> gApi.changes().id(ci.changeId));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains(
|
|
||||||
"The provided change identifier "
|
|
||||||
+ ci.changeId
|
|
||||||
+ " is deprecated. Use 'project~changeNumber' instead.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ java_library(
|
|||||||
"//lib:junit",
|
"//lib:junit",
|
||||||
"//lib/guice",
|
"//lib/guice",
|
||||||
"//lib/httpcomponents:httpcore",
|
"//lib/httpcomponents:httpcore",
|
||||||
|
"//lib/log:api",
|
||||||
"//lib/testcontainers",
|
"//lib/testcontainers",
|
||||||
"//lib/testcontainers:testcontainers-elasticsearch",
|
"//lib/testcontainers:testcontainers-elasticsearch",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ package com.google.gerrit.elasticsearch;
|
|||||||
|
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.junit.AssumptionViolatedException;
|
import org.junit.AssumptionViolatedException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||||
|
|
||||||
/* Helper class for running ES integration tests in docker container */
|
/* Helper class for running ES integration tests in docker container */
|
||||||
@@ -74,6 +76,11 @@ public class ElasticContainer extends ElasticsearchContainer {
|
|||||||
super(getImageName(version));
|
super(getImageName(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Logger logger() {
|
||||||
|
return LoggerFactory.getLogger("org.testcontainers");
|
||||||
|
}
|
||||||
|
|
||||||
public HttpHost getHttpHost() {
|
public HttpHost getHttpHost() {
|
||||||
return new HttpHost(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_PORT));
|
return new HttpHost(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_PORT));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ java_library(
|
|||||||
name = "api",
|
name = "api",
|
||||||
data = ["//lib:LICENSE-slf4j"],
|
data = ["//lib:LICENSE-slf4j"],
|
||||||
visibility = [
|
visibility = [
|
||||||
|
"//javatests/com/google/gerrit/elasticsearch:__pkg__",
|
||||||
"//lib:__pkg__",
|
"//lib:__pkg__",
|
||||||
"//plugins:__pkg__",
|
"//plugins:__pkg__",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -290,13 +290,15 @@ limitations under the License.
|
|||||||
<span class="separator"></span>
|
<span class="separator"></span>
|
||||||
</span>
|
</span>
|
||||||
<template is="dom-if" if="[[_computeIsLoggedIn(_loggedIn)]]">
|
<template is="dom-if" if="[[_computeIsLoggedIn(_loggedIn)]]">
|
||||||
<span class="editButton">
|
|
||||||
<a href$="[[_computeEditURL(_change, _patchRange, _path)]]">
|
|
||||||
<iron-icon icon="gr-icons:edit"></iron-icon>
|
|
||||||
</a>
|
|
||||||
<span class="separator"></span>
|
<span class="separator"></span>
|
||||||
|
<span class="editButton">
|
||||||
|
<gr-button
|
||||||
|
link
|
||||||
|
title="Edit current file"
|
||||||
|
on-click="_goToEditFile">edit</gr-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<span class="separator"></span>
|
||||||
<div class$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]">
|
<div class$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]">
|
||||||
<span>Diff view:</span>
|
<span>Diff view:</span>
|
||||||
<gr-diff-mode-selector
|
<gr-diff-mode-selector
|
||||||
|
|||||||
@@ -566,12 +566,11 @@
|
|||||||
return this._getDiffUrl(this._change, this._patchRange, newPath.path);
|
return this._getDiffUrl(this._change, this._patchRange, newPath.path);
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeEditURL(change, patchRange, path) {
|
_goToEditFile() {
|
||||||
if ([change, patchRange, path].some(arg => arg === undefined)) {
|
// TODO(taoalpha): add a shortcut for editing
|
||||||
return '';
|
const editUrl = Gerrit.Nav.getEditUrlForDiff(
|
||||||
}
|
this._change, this._path, this._patchRange.patchNum);
|
||||||
return Gerrit.Nav.getEditUrlForDiff(
|
return Gerrit.Nav.navigateToRelativeUrl(editUrl);
|
||||||
change, path, patchRange.patchNum);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -365,6 +365,53 @@ limitations under the License.
|
|||||||
PARENT), 'Should navigate to /c/42/1');
|
PARENT), 'Should navigate to /c/42/1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('edit should redirect to edit page', done => {
|
||||||
|
element._loggedIn = true;
|
||||||
|
element._path = 't.txt';
|
||||||
|
element._patchRange = {
|
||||||
|
basePatchNum: PARENT,
|
||||||
|
patchNum: '1',
|
||||||
|
};
|
||||||
|
element._change = {
|
||||||
|
_number: 42,
|
||||||
|
revisions: {
|
||||||
|
a: {_number: 1, commit: {parents: []}},
|
||||||
|
b: {_number: 2, commit: {parents: []}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const redirectStub = sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl');
|
||||||
|
flush(() => {
|
||||||
|
const editBtn = Polymer.dom(element.root)
|
||||||
|
.querySelector('.editButton gr-button');
|
||||||
|
assert.isTrue(!!editBtn);
|
||||||
|
MockInteractions.tap(editBtn);
|
||||||
|
assert.isTrue(redirectStub.called);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('edit hidden when not logged in', done => {
|
||||||
|
element._loggedIn = false;
|
||||||
|
element._path = 't.txt';
|
||||||
|
element._patchRange = {
|
||||||
|
basePatchNum: PARENT,
|
||||||
|
patchNum: '1',
|
||||||
|
};
|
||||||
|
element._change = {
|
||||||
|
_number: 42,
|
||||||
|
revisions: {
|
||||||
|
a: {_number: 1, commit: {parents: []}},
|
||||||
|
b: {_number: 2, commit: {parents: []}},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
flush(() => {
|
||||||
|
const editBtn = Polymer.dom(element.root)
|
||||||
|
.querySelector('.editButton gr-button');
|
||||||
|
assert.isFalse(!!editBtn);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
suite('diff prefs hidden', () => {
|
suite('diff prefs hidden', () => {
|
||||||
test('when no prefs or logged out', () => {
|
test('when no prefs or logged out', () => {
|
||||||
element.disableDiffPrefs = false;
|
element.disableDiffPrefs = false;
|
||||||
|
|||||||
@@ -72,10 +72,11 @@
|
|||||||
// Ensure that external links originating from HTML commentlink configs
|
// Ensure that external links originating from HTML commentlink configs
|
||||||
// open in a new tab. @see Issue 5567
|
// open in a new tab. @see Issue 5567
|
||||||
// Ensure links to the same host originating from commentlink configs
|
// Ensure links to the same host originating from commentlink configs
|
||||||
// open in the same tab. @see Issue 4616
|
// open in the same tab. When target is not set - default is _self
|
||||||
|
// @see Issue 4616
|
||||||
output.querySelectorAll('a').forEach(anchor => {
|
output.querySelectorAll('a').forEach(anchor => {
|
||||||
if (anchor.hostname === window.location.hostname) {
|
if (anchor.hostname === window.location.hostname) {
|
||||||
anchor.setAttribute('target', '_self');
|
anchor.removeAttribute('target');
|
||||||
} else {
|
} else {
|
||||||
anchor.setAttribute('target', '_blank');
|
anchor.setAttribute('target', '_blank');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ limitations under the License.
|
|||||||
const linkEl = element.$.output.childNodes[1];
|
const linkEl = element.$.output.childNodes[1];
|
||||||
assert.equal(textNode.textContent, prefix);
|
assert.equal(textNode.textContent, prefix);
|
||||||
const url = '/q/' + changeID;
|
const url = '/q/' + changeID;
|
||||||
assert.equal(linkEl.target, '_self');
|
assert.isFalse(linkEl.hasAttribute('target'));
|
||||||
// Since url is a path, the host is added automatically.
|
// Since url is a path, the host is added automatically.
|
||||||
assert.isTrue(linkEl.href.endsWith(url));
|
assert.isTrue(linkEl.href.endsWith(url));
|
||||||
assert.equal(linkEl.textContent, changeID);
|
assert.equal(linkEl.textContent, changeID);
|
||||||
@@ -162,7 +162,7 @@ limitations under the License.
|
|||||||
const linkEl = element.$.output.childNodes[1];
|
const linkEl = element.$.output.childNodes[1];
|
||||||
assert.equal(textNode.textContent, prefix);
|
assert.equal(textNode.textContent, prefix);
|
||||||
const url = '/r/q/' + changeID;
|
const url = '/r/q/' + changeID;
|
||||||
assert.equal(linkEl.target, '_self');
|
assert.isFalse(linkEl.hasAttribute('target'));
|
||||||
// Since url is a path, the host is added automatically.
|
// Since url is a path, the host is added automatically.
|
||||||
assert.isTrue(linkEl.href.endsWith(url));
|
assert.isTrue(linkEl.href.endsWith(url));
|
||||||
assert.equal(linkEl.textContent, changeID);
|
assert.equal(linkEl.textContent, changeID);
|
||||||
@@ -203,7 +203,7 @@ limitations under the License.
|
|||||||
|
|
||||||
assert.equal(textNode.textContent, prefix);
|
assert.equal(textNode.textContent, prefix);
|
||||||
|
|
||||||
assert.equal(changeLinkEl.target, '_self');
|
assert.isFalse(changeLinkEl.hasAttribute('target'));
|
||||||
assert.isTrue(changeLinkEl.href.endsWith(changeUrl));
|
assert.isTrue(changeLinkEl.href.endsWith(changeUrl));
|
||||||
assert.equal(changeLinkEl.textContent, changeID);
|
assert.equal(changeLinkEl.textContent, changeID);
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from optparse import OptionParser
|
|
||||||
from os import link, makedirs, path, remove
|
from os import link, makedirs, path, remove
|
||||||
import shutil
|
import shutil
|
||||||
from subprocess import check_call, CalledProcessError
|
from subprocess import check_call, CalledProcessError
|
||||||
@@ -75,13 +75,13 @@ def cache_entry(args):
|
|||||||
return path.join(CACHE_DIR, name)
|
return path.join(CACHE_DIR, name)
|
||||||
|
|
||||||
|
|
||||||
opts = OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts.add_option('-o', help='local output file')
|
parser.add_argument('-o', help='local output file')
|
||||||
opts.add_option('-u', help='URL to download')
|
parser.add_argument('-u', help='URL to download')
|
||||||
opts.add_option('-v', help='expected content SHA-1')
|
parser.add_argument('-v', help='expected content SHA-1')
|
||||||
opts.add_option('-x', action='append', help='file to delete from ZIP')
|
parser.add_argument('-x', action='append', help='file to delete from ZIP')
|
||||||
opts.add_option('--exclude_java_sources', action='store_true')
|
parser.add_argument('--exclude_java_sources', action='store_true')
|
||||||
args, _ = opts.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
root_dir = args.o
|
root_dir = args.o
|
||||||
while root_dir and path.dirname(root_dir) != root_dir:
|
while root_dir and path.dirname(root_dir) != root_dir:
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ python tools/js/bower2bazel.py -w lib/js/bower_archives.bzl \
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
import optparse
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -141,11 +141,11 @@ def bower_command(args):
|
|||||||
os.getcwd() + "/tools/js/run_npm_binary.py", sorted(fs)[0]] + args
|
os.getcwd() + "/tools/js/run_npm_binary.py", sorted(fs)[0]] + args
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main():
|
||||||
opts = optparse.OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts.add_option('-w', help='.bzl output for WORKSPACE')
|
parser.add_argument('-w', help='.bzl output for WORKSPACE')
|
||||||
opts.add_option('-b', help='.bzl output for //lib:BUILD')
|
parser.add_argument('-b', help='.bzl output for //lib:BUILD')
|
||||||
opts, args = opts.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
target_str = subprocess.check_output([
|
target_str = subprocess.check_output([
|
||||||
"bazel", "query", "kind(bower_component_bundle, //polygerrit-ui/...)"])
|
"bazel", "query", "kind(bower_component_bundle, //polygerrit-ui/...)"])
|
||||||
@@ -166,12 +166,12 @@ def main(args):
|
|||||||
cmd = bower_command(["install"])
|
cmd = bower_command(["install"])
|
||||||
|
|
||||||
build_out = sys.stdout
|
build_out = sys.stdout
|
||||||
if opts.b:
|
if args.b:
|
||||||
build_out = open(opts.b + ".tmp", 'w')
|
build_out = open(args.b + ".tmp", 'w')
|
||||||
|
|
||||||
ws_out = sys.stdout
|
ws_out = sys.stdout
|
||||||
if opts.b:
|
if args.b:
|
||||||
ws_out = open(opts.w + ".tmp", 'w')
|
ws_out = open(args.w + ".tmp", 'w')
|
||||||
|
|
||||||
header = """# DO NOT EDIT
|
header = """# DO NOT EDIT
|
||||||
# generated with the following command:
|
# generated with the following command:
|
||||||
@@ -193,8 +193,8 @@ def main(args):
|
|||||||
build_out.close()
|
build_out.close()
|
||||||
|
|
||||||
os.chdir(oldwd)
|
os.chdir(oldwd)
|
||||||
os.rename(opts.w + ".tmp", opts.w)
|
os.rename(args.w + ".tmp", args.w)
|
||||||
os.rename(opts.b + ".tmp", opts.b)
|
os.rename(args.b + ".tmp", args.b)
|
||||||
|
|
||||||
|
|
||||||
def dump_workspace(data, seeds, out):
|
def dump_workspace(data, seeds, out):
|
||||||
@@ -259,4 +259,4 @@ def interpret_bower_json(seeds, ws_out, build_out):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main()
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import optparse
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -80,44 +80,44 @@ def cache_entry(name, package, version, sha1):
|
|||||||
return os.path.join(CACHE_DIR, '%s-%s.zip-%s' % (name, version, sha1))
|
return os.path.join(CACHE_DIR, '%s-%s.zip-%s' % (name, version, sha1))
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main():
|
||||||
opts = optparse.OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts.add_option('-n', help='short name of component')
|
parser.add_argument('-n', help='short name of component')
|
||||||
opts.add_option('-b', help='bower command')
|
parser.add_argument('-b', help='bower command')
|
||||||
opts.add_option('-p', help='full package name of component')
|
parser.add_argument('-p', help='full package name of component')
|
||||||
opts.add_option('-v', help='version number')
|
parser.add_argument('-v', help='version number')
|
||||||
opts.add_option('-s', help='expected content sha1')
|
parser.add_argument('-s', help='expected content sha1')
|
||||||
opts.add_option('-o', help='output file location')
|
parser.add_argument('-o', help='output file location')
|
||||||
opts, args_ = opts.parse_args(args)
|
args = parser.parse_args()
|
||||||
|
|
||||||
assert opts.p
|
assert args.p
|
||||||
assert opts.v
|
assert args.v
|
||||||
assert opts.n
|
assert args.n
|
||||||
|
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
outzip = os.path.join(cwd, opts.o)
|
outzip = os.path.join(cwd, args.o)
|
||||||
cached = cache_entry(opts.n, opts.p, opts.v, opts.s)
|
cached = cache_entry(args.n, args.p, args.v, args.s)
|
||||||
|
|
||||||
if not os.path.exists(cached):
|
if not os.path.exists(cached):
|
||||||
info = bower_info(opts.b, opts.n, opts.p, opts.v)
|
info = bower_info(args.b, args.n, args.p, args.v)
|
||||||
ignore_deps(info)
|
ignore_deps(info)
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
bower_cmd(
|
bower_cmd(
|
||||||
opts.b, '--quiet', 'install', '%s#%s' % (opts.p, opts.v)))
|
args.b, '--quiet', 'install', '%s#%s' % (args.p, args.v)))
|
||||||
bc = os.path.join(cwd, 'bower_components')
|
bc = os.path.join(cwd, 'bower_components')
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
['zip', '-q', '--exclude', '.bower.json', '-r', cached, opts.n],
|
['zip', '-q', '--exclude', '.bower.json', '-r', cached, args.n],
|
||||||
cwd=bc)
|
cwd=bc)
|
||||||
|
|
||||||
if opts.s:
|
if args.s:
|
||||||
path = os.path.join(bc, opts.n)
|
path = os.path.join(bc, args.n)
|
||||||
sha1 = bowerutil.hash_bower_component(
|
sha1 = bowerutil.hash_bower_component(
|
||||||
hashlib.sha1(), path).hexdigest()
|
hashlib.sha1(), path).hexdigest()
|
||||||
if opts.s != sha1:
|
if args.s != sha1:
|
||||||
print((
|
print((
|
||||||
'%s#%s:\n'
|
'%s#%s:\n'
|
||||||
'expected %s\n'
|
'expected %s\n'
|
||||||
'received %s\n') % (opts.p, opts.v, opts.s, sha1),
|
'received %s\n') % (args.p, args.v, args.s, sha1),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
os.remove(cached)
|
os.remove(cached)
|
||||||
@@ -132,4 +132,4 @@ def main(args):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main(sys.argv[1:]))
|
sys.exit(main())
|
||||||
|
|||||||
@@ -14,20 +14,20 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from optparse import OptionParser
|
import argparse
|
||||||
from os import path, environ
|
from os import path, environ
|
||||||
from subprocess import check_output, CalledProcessError
|
from subprocess import check_output, CalledProcessError
|
||||||
from sys import stderr
|
from sys import stderr
|
||||||
|
|
||||||
opts = OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts.add_option('--repository', help='maven repository id')
|
parser.add_argument('--repository', help='maven repository id')
|
||||||
opts.add_option('--url', help='maven repository url')
|
parser.add_argument('--url', help='maven repository url')
|
||||||
opts.add_option('-o')
|
parser.add_argument('-o')
|
||||||
opts.add_option('-a', help='action (valid actions are: install,deploy)')
|
parser.add_argument('-a', help='action (valid actions are: install,deploy)')
|
||||||
opts.add_option('-v', help='gerrit version')
|
parser.add_argument('-v', help='gerrit version')
|
||||||
opts.add_option('-s', action='append', help='triplet of artifactId:type:path')
|
parser.add_argument('-s', action='append', help='triplet of artifactId:type:path')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
args, ctx = opts.parse_args()
|
|
||||||
if not args.v:
|
if not args.v:
|
||||||
print('version is empty', file=stderr)
|
print('version is empty', file=stderr)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
@@ -14,21 +14,16 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from optparse import OptionParser
|
import argparse
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
parser = OptionParser()
|
parser = argparse.ArgumentParser()
|
||||||
opts, args = parser.parse_args()
|
parser.add_argument('version')
|
||||||
|
args = parser.parse_args()
|
||||||
if not len(args):
|
|
||||||
parser.error('not enough arguments')
|
|
||||||
elif len(args) > 1:
|
|
||||||
parser.error('too many arguments')
|
|
||||||
|
|
||||||
DEST_PATTERN = r'\g<1>%s\g<3>' % args[0]
|
|
||||||
|
|
||||||
|
DEST_PATTERN = r'\g<1>%s\g<3>' % args.version
|
||||||
|
|
||||||
def replace_in_file(filename, src_pattern):
|
def replace_in_file(filename, src_pattern):
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user