Depending on how tightly the extension code is coupled with the Gerrit
server code, there is a distinction between plugins and extensions.

A plugin in Gerrit is tightly coupled code that runs in the same
JVM as Gerrit. It has full access to all server internals. Plugins
are tightly coupled to a specific major.minor server version and
may require source code changes to compile against a different
server version.

Plugins may require a specific major.minor.patch server version
and may need rebuild and revalidation across different
patch levels. A different patch level may only add new
API interfaces and never change or extend existing ones.

An extension in Gerrit runs inside of the same JVM as Gerrit
in the same way as a plugin, but has limited visibility to the
server’s internals. The limited visibility reduces the extension’s
dependencies, enabling it to be compatible across a wider range
of server versions.

Most of this documentation refers to either type as a plugin.

Getting started

To get started with the development of a plugin clone the sample
plugin:

$ git clone https://gerrit.googlesource.com/plugins/cookbook-plugin

This is a project that demonstrates the various features of the
plugin API. It can be taken as an example to develop an own plugin.

When starting from this example one should take care to adapt the
Gerrit-ApiVersion in the BUILD to the version of Gerrit for which
the plugin is developed.

API

There are two different API formats offered against which plugins can
be developed:

gerrit-extension-api.jar

A stable but thin interface. Suitable for extensions that need
to be notified of events, but do not require tight coupling to
the internals of Gerrit. Extensions built against this API can
expect to be binary compatible across a wide range of server
versions.

gerrit-plugin-api.jar

The complete internals of the Gerrit server, permitting a
plugin to tightly couple itself and provide additional
functionality that is not possible as an extension. Plugins
built against this API are expected to break at the source
code level between every major.minor Gerrit release. A plugin
that compiles against 2.5 will probably need source code level
changes to work with 2.6, 2.7, and so on.

ApiType

Plugins using the tightly coupled gerrit-plugin-api.jar must
declare this API dependency in the manifest to gain access to server
internals. If no Gerrit-ApiType is specified the stable extension
API will be assumed. This may cause ClassNotFoundExceptions when
loading a plugin that needs the plugin API.

Gerrit-ApiType: plugin

Explicit Registration

Plugins that use explicit Guice registration must name the Guice
modules in the manifest. Up to three modules can be named in the
manifest. Gerrit-Module supplies bindings to the core server;
Gerrit-SshModule supplies SSH commands to the SSH server (if
enabled); Gerrit-HttpModule supplies servlets and filters to the HTTP
server (if enabled). If no modules are named automatic registration
will be performed by scanning all classes in the plugin JAR for
@Listen and @Export("") annotations.

Batch runtime

Gerrit can be run as a server, serving HTTP or SSH requests, or as an
offline program. Plugins can contribute Guice modules to this batch
runtime by binding Gerrit-BatchModule to one of their classes.
The Guice injector is bound to less classes, and some Gerrit features
will be absent - on purpose.

This feature was originally introduced to support plugins during an
offline reindexing task.

Gerrit-BatchModule: tld.example.project.CoreModuleClassName

In this runtime, only the module designated by Gerrit-BatchModule is
enabled, not Gerrit-SysModule.

Plugin Name

A plugin can optionally provide its own plugin name.

Gerrit-PluginName: replication

This is useful for plugins that contribute plugin-owned capabilities that
are stored in the project.config file. Another use case is to be able to put
project specific plugin configuration section in project.config. In this
case it is advantageous to reserve the plugin name to access the configuration
section in the project.config file.

If Gerrit-PluginName is omitted, then the plugin’s name is determined from
the plugin file name.

If a plugin provides its own name, then that plugin cannot be deployed
multiple times under different file names on one Gerrit site.

For Maven driven plugins, the following line must be included in the pom.xml
file:

The canonical web URL may be injected into any .jar plugin regardless of
whether or not the plugin provides an HTTP servlet.

Reload Method

If a plugin holds an exclusive resource that must be released before
loading the plugin again (for example listening on a network port or
acquiring a file lock) the manifest must declare Gerrit-ReloadMode
to be restart. Otherwise the preferred method of reload will
be used, as it enables the server to hot-patch an updated plugin
with no down time.

Gerrit-ReloadMode: restart

In either mode ('restart' or 'reload') any plugin or extension can
be updated without restarting the Gerrit server. The difference is
how Gerrit handles the upgrade:

restart

The old plugin is completely stopped. All registrations of SSH
commands and HTTP servlets are removed. All registrations of any
extension points are removed. All registered LifecycleListeners
have their stop() method invoked in reverse order. The new
plugin is started, and registrations are made from the new
plugin. There is a brief window where neither the old nor the
new plugin is connected to the server. This means SSH commands
and HTTP servlets will return not found errors, and the plugin
will not be notified of events that occurred during the restart.

reload

The new plugin is started. Its LifecycleListeners are permitted
to perform their start() methods. All SSH and HTTP registrations
are atomically swapped out from the old plugin to the new plugin,
ensuring the server never returns a not found error. All extension
point listeners are atomically swapped out from the old plugin to
the new plugin, ensuring no events are missed (however some events
may still route to the old plugin if the swap wasn’t complete yet).
The old plugin is stopped.

Init step

Plugins can contribute their own "init step" during the Gerrit init
wizard. This is useful for guiding the Gerrit administrator through
the settings needed by the plugin to work properly.

For instance plugins to integrate Jira issues to Gerrit changes may
contribute their own "init step" to allow configuring the Jira URL,
credentials and possibly verify connectivity to validate them.

Gerrit-InitStep: tld.example.project.MyInitStep

MyInitStep needs to follow the standard Gerrit InitStep syntax
and behavior: writing to the console using the injected ConsoleUI
and accessing / changing configuration settings using Section.Factory.

In addition to the standard Gerrit init injections, plugins receive
the @PluginName String injection containing their own plugin name.

During their initialization plugins may get access to the
project.config file of the All-Projects project and they are able
to store configuration parameters in it. For this a plugin InitStep
can get com.google.gerrit.pgm.init.api.AllProjectsConfig injected:

Bear in mind that the Plugin’s InitStep class will be loaded but
the standard Gerrit runtime environment is not available and the plugin’s
own Guice modules were not initialized.
This means the InitStep for a plugin is not executed in the same way that
the plugin executes within the server, and may mean a plugin author cannot
trivially reuse runtime code during init.

For instance a plugin that wants to verify connectivity may need to statically
call the constructor of their connection class, passing in values obtained
from the Section.Factory rather than from an injected Config object.

Plugins' InitSteps are executed during the "Gerrit Plugin init" phase, after
the extraction of the plugins embedded in the distribution .war file into
$GERRIT_SITE/plugins and before the DB Schema initialization or upgrade.

A plugin’s InitStep cannot refer to Gerrit’s DB Schema or any other Gerrit
runtime objects injected at startup.

Classpath

Each plugin is loaded into its own ClassLoader, isolating plugins
from each other. A plugin or extension inherits the Java runtime
and the Gerrit API chosen by Gerrit-ApiType (extension or plugin)
from the hosting server.

Plugins are loaded from a single JAR file. If a plugin needs
additional libraries, it must include those dependencies within
its own JAR. Plugins built using Maven may be able to use the
shade plugin
to package additional dependencies. Relocating (or renaming) classes
should not be necessary due to the ClassLoader isolation.

Listening to Events

Certain operations in Gerrit trigger events. Plugins may receive
notifications of these events by implementing the corresponding
listeners.

com.google.gerrit.common.EventListener:

Allows to listen to events without user visibility restrictions. These
are the same events that are also streamed by
the gerrit stream-events command.

com.google.gerrit.common.UserScopedEventListener:

Allows to listen to events visible to the specified user. These are the
same events that are also streamed
by the gerrit stream-events command.

com.google.gerrit.extensions.events.LifecycleListener:

Plugin start and stop

com.google.gerrit.extensions.events.NewProjectCreatedListener:

Project creation

com.google.gerrit.extensions.events.ProjectDeletedListener:

Project deletion

com.google.gerrit.extensions.events.HeadUpdatedListener:

Update of HEAD on a project

com.google.gerrit.extensions.events.UsageDataPublishedListener:

Publication of usage data

com.google.gerrit.extensions.events.GarbageCollectorListener:

Garbage collection ran on a project

com.google.gerrit.server.extensions.events.ChangeIndexedListener:

Update of the change secondary index

com.google.gerrit.server.extensions.events.AccountIndexedListener:

Update of the account secondary index

com.google.gerrit.server.extensions.events.GroupIndexedListener:

Update of the group secondary index

com.google.gerrit.server.extensions.events.ProjectIndexedListener:

Update of the project secondary index

com.google.gerrit.httpd.WebLoginListener:

User login or logout interactively on the Web user interface.

The event listener is under the Gerrit http package to automatically
inherit the javax.servlet.http dependencies and allowing to influence
the login or logout flow with additional redirections.

Sending Events to the Events Stream

Plugins may send events to the events stream where consumers of
Gerrit’s stream-events ssh command will receive them.

To send an event, the plugin must invoke one of the postEvent
methods in the EventDispatcher interface, passing an instance of
its own custom event class derived from
com.google.gerrit.server.events.Event.

Plugins which define new Events should register them via the
com.google.gerrit.server.events.EventTypes.registerClass()
method. This will make the EventType known to the system.
Deserializing events with the
com.google.gerrit.server.events.EventDeserializer class requires
that the event be registered in EventTypes.

Modifying the Stream Event Flow

It is possible to modify the stream event flow from plugins by registering
an com.google.gerrit.server.events.EventDispatcher. A plugin may register
a Dispatcher class to replace the internal Dispatcher. EventDispatcher is
a DynamicItem, so Gerrit may only have one copy.

Validation Listeners

Certain operations in Gerrit can be validated by plugins by
implementing the corresponding listeners.

Change Message Modifier

com.google.gerrit.server.git.ChangeMessageModifier:
plugins implementing this can modify commit message of the change being
submitted by Rebase Always and Cherry Pick submit strategies as well as
change being queried with COMMIT_FOOTERS option.

Merge Super Set Computation

The algorithm to compute the merge super set to detect changes that
should be submitted together can be customized by implementing
com.google.gerrit.server.git.MergeSuperSetComputation.
MergeSuperSetComputation is a DynamicItem, so Gerrit may only have one
implementation.

Receive Pack Initializers

Plugins may provide ReceivePackInitializer instances, which will be
invoked by Gerrit just before a ReceivePack instance will be used.
Usually, plugins will make use of the setXXX methods on the ReceivePack
to set additional properties on it.

The interactions with the core Gerrit ReceivePack initialization and
between ReceivePackInitializers can be complex. Please read the
ReceivePack Javadoc and Gerrit AsyncReceiveCommits implementation
carefully.

Post Receive-Pack Hooks

Plugins may register PostReceiveHook instances in order to get
notified when JGit successfully receives a pack. This may be useful
for those plugins which would like to monitor changes in Git
repositories.

Upload Pack Initializers

Plugins may provide UploadPackInitializer instances, which will be
invoked by Gerrit just before a UploadPack instance will be used.
Usually, plugins will make use of the setXXX methods on the UploadPack
to set additional properties on it.

The interactions with the core Gerrit UploadPack initialization and
between UploadPackInitializers can be complex. Please read the
UploadPack Javadoc and Gerrit Upload/UploadFactory implementations
carefully.

Pre Upload-Pack Hooks

Plugins may register PreUploadHook instances in order to get
notified when JGit is about to upload a pack. This may be useful
for those plugins which would like to monitor usage in Git
repositories.

Post Upload-Pack Hooks

Plugins may register PostUploadHook instances in order to get notified after
JGit is done uploading a pack.

SSH Commands

Plugins may provide commands that can be accessed through the SSH
interface (extensions do not have this option).

If the plugin above is deployed under sh.jar file in $site/plugins
directory, generic commands can be called without specifying the
actual SSH command. Note in the example below, that the called commands
ls and ps was not explicitly bound:

Search Operators

Plugins can define new search operators to extend change searching by
implementing the ChangeQueryBuilder.ChangeOperatorFactory interface
and registering it to an operator name in the plugin module’s
configure() method. The search operator name is defined during
registration via the DynamicMap annotation mechanism. The plugin
name will get appended to the annotated name, with an underscore
in between, leading to the final operator name. An example
registration looks like this:

If this is registered in the myplugin plugin, then the resulting
operator will be named sample_myplugin.

The search operator itself is implemented by ensuring that the
create() method of the class implementing the
ChangeQueryBuilder.ChangeOperatorFactory interface returns a
Predicate<ChangeData>. Here is a sample operator factory
definition which creates a MyPredicate:

Search Operands

Plugins can define new search operands to extend change searching.
Plugin methods implementing search operands (returning a
Predicate<ChangeData>), must be defined on a class implementing
one of the ChangeQueryBuilder.ChangeOperandsFactory interfaces
(.e.g., ChangeQueryBuilder.ChangeHasOperandFactory). The specific
ChangeOperandFactory class must also be bound to the DynamicSet from
a module’s configure() method in the plugin.

The new operand, when used in a search would appear as:
operatorName:operandName_pluginName

A sample ChangeHasOperandFactory class implementing, and registering, a
new has:sample_pluginName operand is shown below:

Command Options

Plugins can provide additional options for each of the gerrit ssh and the
REST API commands by implementing the DynamicBean interface and registering
it to a command class name in the plugin module’s configure() method. The
plugin’s name will be prepended to the name of each @Option annotation found
on the DynamicBean object provided by the plugin. The example below shows a
plugin that adds an option to log a value from the gerrit 'ban-commits'
ssh command.

Query Attributes

Plugins can provide additional attributes to be returned in Gerrit queries by
implementing the ChangeAttributeFactory interface and registering it to the
ChangeQueryProcessor.ChangeAttributeFactory class in the plugin module’s
'configure()' method. The new attribute(s) will be output under a "plugin"
attribute in the change query output. This can be further controlled with an
option registered in the Http and Ssh modules' 'configure*()' methods.

The example below shows a plugin that adds two attributes ('exampleName' and
'changeValue'), to the change query output, when the query command is provided
the --myplugin-name—​all option.

Simple Configuration in gerrit.config

In Gerrit, global configuration is stored in the gerrit.config file.
If a plugin needs global configuration, this configuration should be
stored in a plugin subsection in the gerrit.config file.

This approach of storing the plugin configuration is only suitable for
plugins that have a simple configuration that only consists of
key-value pairs. With this approach it is not possible to have
subsections in the plugin configuration. Plugins that require a complex
configuration need to store their configuration in their
own configuration file where they can make use of
subsections. On the other hand storing the plugin configuration in a
'plugin' subsection in the gerrit.config file has the advantage that
administrators have all configuration parameters in one file, instead
of having one configuration file per plugin.

To avoid conflicts with other plugins, it is recommended that plugins
only use the plugin subsection with their own name. For example the
helloworld plugin should store its configuration in the
plugin.helloworld subsection:

[plugin "helloworld"]
language = Latin

Via the com.google.gerrit.server.config.PluginConfigFactory class a
plugin can easily access its configuration and there is no need for a
plugin to parse the gerrit.config file on its own:

Configuration in own config file

Plugins can store their configuration in an own configuration file.
This makes sense if the plugin configuration is rather complex and
requires the usage of subsections. Plugins that have a simple
key-value pair configuration can store their configuration in a
plugin subsection of the gerrit.config
file.

The plugin configuration file must be named after the plugin and must
be located in the etc folder of the review site. For example a
configuration file for a default-reviewer plugin could look like
this:

Plugins that have sensitive configuration settings can store those settings in
an own secure configuration file. The plugin’s secure configuration file must be
named after the plugin and must be located in the etc folder of the review
site. For example a secure configuration file for a default-reviewer plugin
could look like this:

$site_path/etc/default-reviewer.secure.config

[auth]
password = secret

Via the com.google.gerrit.server.config.PluginConfigFactory class a
plugin can easily access its configuration:

Simple Project Specific Configuration in project.config

In Gerrit, project specific configuration is stored in the project’s
project.config file on the refs/meta/config branch. If a plugin
needs configuration on project level (e.g. to enable its functionality
only for certain projects), this configuration should be stored in a
plugin subsection in the project’s project.config file.

This approach of storing the plugin configuration is only suitable for
plugins that have a simple configuration that only consists of
key-value pairs. With this approach it is not possible to have
subsections in the plugin configuration. Plugins that require a complex
configuration need to store their configuration in their
own configuration file where they
can make use of subsections. On the other hand storing the plugin
configuration in a 'plugin' subsection in the project.config file has
the advantage that project owners have all configuration parameters in
one file, instead of having one configuration file per plugin.

To avoid conflicts with other plugins, it is recommended that plugins
only use the plugin subsection with their own name. For example the
helloworld plugin should store its configuration in the
plugin.helloworld subsection:

[plugin "helloworld"]
enabled = true

Via the com.google.gerrit.server.config.PluginConfigFactory class a
plugin can easily access its project specific configuration and there
is no need for a plugin to parse the project.config file on its own:

Project owners can edit the project configuration by fetching the
refs/meta/config branch, editing the project.config file and
pushing the commit back.

Plugin configuration values that are stored in the project.config
file can be exposed in the ProjectInfoScreen to allow project owners
to see and edit them from the UI.

For this an instance of ProjectConfigEntry needs to be bound for each
parameter. The export name must be a valid Git variable name. The
variable name is case-insensitive, allows only alphanumeric characters
and '-', and must start with an alphabetic character.

The example below shows how the parameters plugin.helloworld.enabled
and plugin.helloworld.language are bound to be editable from the
Web UI. For the parameter plugin.helloworld.enabled "Enable Greeting"
is provided as display name and the default value is set to true.
For the parameter plugin.helloworld.language "Preferred Language"
is provided as display name and "en" is set as default value.

By overwriting the onUpdate method of ProjectConfigEntry plugins
can be notified when this configuration parameter is updated on a
project.

Referencing groups in project.config

Plugins can refer to groups so that when they are renamed, the project
config will also be updated in this section. The proper format to use is
the same as for any other group reference in the project.config, as shown below.

group group_name

The file groups must also contains the mapping of the group name and its UUID,
refer to file groups

Project Specific Configuration in own config file

Plugins can store their project specific configuration in an own
configuration file in the projects refs/meta/config branch.
This makes sense if the plugins project specific configuration is
rather complex and requires the usage of subsections. Plugins that
have a simple key-value pair configuration can store their project
specific configuration in a plugin subsection of the project.config file.

The plugin configuration file in the refs/meta/config branch must be
named after the plugin. For example a configuration file for a
default-reviewer plugin could look like this:

With a plugin-owned capability defined in this way, it is possible to restrict
usage of an SSH command or UiAction to members of the group that were granted
this capability in the usual way, using the RequiresCapability annotation:

Capability scope was introduced to differentiate between plugin-owned
capabilities and core capabilities. Per default the scope of the
@RequiresCapability annotation is CapabilityScope.CONTEXT, that means:

when @RequiresCapability is used within a plugin the scope of the
capability is assumed to be that plugin.

If @RequiresCapability is used within the core Gerrit Code Review server
(and thus is outside of a plugin) the scope is the core server and will use
the GlobalCapability known to Gerrit Code Review server.

If a plugin needs to use a core capability name (e.g. "administrateServer")
this can be specified by setting scope = CapabilityScope.CORE:

Change Screen panel ordering may be specified in the
project config. Values may be either "plugin name" or
"plugin name"."panel name".
Panels not specified in the config will be added
to the end in load order. Panels specified in the config that
are not found will be ignored.

Actions

Plugins can contribute UI actions on core Gerrit pages. This is useful
for workflow customization or exposing plugin functionality through the
UI in addition to SSH commands and the REST API.

For instance a plugin to integrate Jira with Gerrit changes may
contribute a "File bug" button to allow filing a bug from the change
page or plugins to integrate continuous integration systems may
contribute a "Schedule" button to allow a CI build to be scheduled
manually from the patch set panel.

Sometimes plugins may want to be able to change the state of a patch set or
change in the UiAction.apply() method and reflect these changes on the core
UI. For example a buildbot plugin which exposes a 'Schedule' button on the
patch set panel may want to disable that button after the build was scheduled
and update the tooltip of that button. But because of Gerrit’s caching
strategy the following must be taken into consideration.

The browser is allowed to cache the UiAction information until something on
the change is modified. More accurately the change row needs to be modified in
the database to have a more recent lastUpdatedOn or a new rowVersion, or
the refs/meta/config of the project or any parents needs to change to a new
SHA-1. The ETag SHA-1 computation code can be found in the
ChangeResource.getETag() method.

The easiest way to accomplish this is to update lastUpdatedOn of the change:

In some use cases more user input must be gathered, for that UiAction can be
combined with the JavaScript API. This would display a small popup near the
activation button to gather additional input from the user. The JS file is
typically put in the static folder within the plugin’s directory:

If UiAction is annotated with the @RequiresCapability annotation, then the
capability check is done during the UiAction gathering, so the plugin author
doesn’t have to set UiAction.Description.setVisible() explicitly in this
case.

The following prerequisites must be met, to satisfy the capability check:

user is authenticated

user is a member of a group which has the Administrate Server capability, or

user is a member of a group which has the required capability

The apply method is called when the button is clicked. If UiAction is
combined with JavaScript API (its own JavaScript function is provided),
then a popup dialog is normally opened to gather additional user input.
A new button is placed on the popup dialog to actually send the request.

Every UiAction exposes a REST API endpoint. The endpoint from the example above
can be accessed from any REST client, i. e.:

Action Visitors

In addition to providing new actions, plugins can have fine-grained control
over the ActionInfo map, modifying or
removing existing actions, including those contributed by core.

Visitors are provided the ActionInfo,
which is mutable, along with copies of the
ChangeInfo and
RevisionInfo. They can modify the
action, or return false to exclude it from the resulting map.

These operations only affect the action buttons that are displayed in the UI;
the underlying REST API endpoints are not affected. Multiple plugins may
implement the visitor interface, but the order in which they are run is
undefined.

For example, to exclude "Cherry-Pick" only from certain projects, and rename
"Abandon":

The name that is provided to the GwtPlugin must match the GWT
module name compiled into the plugin. The name of the GWT module
can be explicitly set in the GWT module XML file by specifying
the rename-to attribute on the module. It is important that the
module name be unique across all plugins installed on the server,
as the module name determines the JavaScript namespace used by the
compiled plugin code.

<module rename-to="hello_gwt_plugin">

The actual GWT code must be implemented in a class that extends
com.google.gerrit.plugin.client.PluginEntryPoint:

By defining an urlAlias Gerrit
administrators can map plugin screens into the Gerrit URL namespace or
even replace Gerrit screens by plugin screens.

Plugins may also programatically add URL aliases in the preferences of
of a user. This way certain screens can be replaced for certain users.
E.g. the plugin may offer a user preferences setting for choosing a
screen that then sets/unsets a URL alias for the user.

Plugin Settings Screen

If a plugin implements a screen for administrating its settings that is
available under "#/x/<plugin-name>/settings" it is automatically linked
from the plugin list screen.

HTTP Servlets

Plugins or extensions may register additional HTTP servlets, and
wrap them with HTTP filters.

The auto registration only works for standard servlet mappings like
/foo or /foo/*. Regex style bindings must use a Guice ServletModule
to register the HTTP servlets and declare it explicitly in the manifest
with the Gerrit-HttpModule attribute:

For a plugin installed as name helloworld, the servlet implemented
by HelloServlet class will be available to users as:

$ curl http://review.example.com/plugins/helloworld/print

Data Directory

Plugins can request a data directory with a @PluginData Path (or File,
deprecated) dependency. A data directory will be created automatically
by the server in $site_path/data/$plugin_name and passed to the
plugin.

SecureStore

SecureStore allows to change the way Gerrit stores sensitive data like
passwords.

In order to replace the default SecureStore (no-op) implementation,
a class that extends com.google.gerrit.server.securestore.SecureStore
needs to be provided (with dependencies) in a separate jar file. Then
SwitchSecureStore must be run to
switch implementations.

The SecureStore implementation is instantiated using a Guice injector
which binds the File annotated with the @SitePath annotation.
This means that a SecureStore implementation class can get access to
the site_path like in the following example:

No Guice bindings or modules are required. Gerrit will automatically
discover and bind the implementation.

Account Creation

Plugins can hook into the
account creation REST API and
inject additional external identifiers for an account that represents a user
in some external user store. For that, an implementation of the extension
point com.google.gerrit.server.account.AccountExternalIdCreator
must be registered.

The download schemes, download commands and clone commands which are
used most often are provided by the Gerrit core plugin
download-commands.

Included In

For merged changes the Included In
drop-down panel shows the branches and tags in which the change is
included.

Plugins can add additional systems in which the change can be included
by implementing com.google.gerrit.extensions.config.ExternalIncludedIn,
e.g. a plugin can provide a list of servers on which the change was
deployed.

Change Report Formatting

When a change is pushed for review from the command line, Gerrit reports
the change(s) received with their URL and subject.

By implementing the
com.google.gerrit.server.git.ChangeReportFormatter interface, a plugin
may change the formatting of the report.

URL Formatting

URLs to various parts of Gerrit are usually formed by adding suffixes to
the canonical web URL.

By implementing the
com.google.gerrit.server.config.UrlFormatter interface, a plugin may
change the format of the URL.

Links To External Tools

Gerrit has extension points that enables development of a
light-weight plugin that links commits to external
tools (GitBlit, CGit, company specific resources etc).

PatchSetWebLinks will appear to the right of the commit-SHA1 in the UI.

ParentWebLinks will appear to the right of the SHA1 of the parent
revisions in the UI. The implementation should in most use cases direct
to the same external service as PatchSetWebLink; it is provided as a
separate interface because not all users want to have links for the
parent revisions.

FileWebLinks will appear in the side-by-side diff screen on the right
side of the patch selection on each side.

DiffWebLinks will appear in the side-by-side and unified diff screen in
the header next to the navigation icons.

ProjectWebLinks will appear in the project list in the
Repository Browser column.

BranchWebLinks will appear in the branch list in the last column.

FileHistoryWebLinks will appear on the access rights screen.

TagWebLinks will appear in the tag list in the last column.

If a get*WebLink implementation returns null, the link will be omitted. This
allows the plugin to selectively "enable" itself on a per-project/branch/file
basis.

LFS Storage Plugins

Gerrit provides an extension point that enables development of
LFS (Large File Storage) storage plugins. Gerrit core exposes the default LFS
protocol endpoint <project-name>/info/lfs/objects/batch and forwards the requests
to the configured lfs.plugin plugin which implements
the LFS protocol. By exposing the default LFS endpoint, the git-lfs client can be
used without any configuration.

Metrics

Metrics Reporting

To send Gerrit’s metrics data to an external reporting backend, a plugin can
get a MetricRegistry injected and register an instance of a class that
implements the Reporter interface from
DropWizard Metrics.

AccountPatchReviewStore

The AccountPatchReviewStore is used to store reviewed flags on changes.
A reviewed flag is a tuple of (patch set ID, file, account ID) and
records whether the user has reviewed a file in a patch set. Each user
can easily have thousands of reviewed flags and the number of reviewed
flags is growing without bound. The store must be able handle this data
volume efficiently.

Gerrit implements this extension point, but plugins may bind another
implementation, e.g. one that supports multi-master.

Documentation

If a plugin does not register a filter or servlet to handle URLs
/Documentation/* or /static/*, the core Gerrit server will
automatically export these resources over HTTP from the plugin JAR.

Static resources under the static/ directory in the JAR will be
available as /plugins/helloworld/static/resource. This prefix is
configurable by setting the Gerrit-HttpStaticPrefix attribute.

Documentation files under the Documentation/ directory in the JAR
will be available as /plugins/helloworld/Documentation/resource. This
prefix is configurable by setting the Gerrit-HttpDocumentationPrefix
attribute.

Documentation may be written in the Markdown flavor
flexmark-java
if the file name ends with .md. Gerrit will automatically convert
Markdown to HTML if accessed with extension .html.

Within the Markdown documentation files macros can be used that allow
to write documentation with reasonably accurate examples that adjust
automatically based on the installation.

The following macros are supported:

Macro

Replacement

@PLUGIN@

name of the plugin

@URL@

Gerrit Web URL

@SSH_HOST@

SSH Host

@SSH_PORT@

SSH Port

The macros will be replaced when the documentation files are rendered
from Markdown to HTML.

Macros that start with \ such as \@KEEP@ will render as @KEEP@
even if there is an expansion for KEEP in the future.

Automatic Index

If a plugin does not handle its / URL itself, Gerrit will
redirect clients to the plugin’s /Documentation/index.html.
Requests for /Documentation/ (bare directory) will also redirect
to /Documentation/index.html.

If neither resource Documentation/index.html or
Documentation/index.md exists in the plugin JAR, Gerrit will
automatically generate an index page for the plugin’s documentation
tree by scanning every .md and .html file in the Documentation/
directory.

For any discovered Markdown (*.md) file, Gerrit will parse the
header of the file and extract the first level one title. This
title text will be used as display text for a link to the HTML
version of the page.

For any discovered HTML (.html) file, Gerrit will use the name
of the file, minus the .html extension, as the link text. Any
hyphens in the file name will be replaced with spaces.

If a discovered file is named about.md or about.html, its
content will be inserted in an 'About' section at the top of the
auto-generated index page. If both about.md and about.html
exist, only the first discovered file will be used.

If a discovered file name beings with cmd- it will be clustered
into a 'Commands' section of the generated index page.

If a discovered file name beings with servlet- it will be clustered
into a 'Servlets' section of the generated index page.

If a discovered file name beings with rest-api- it will be clustered
into a 'REST APIs' section of the generated index page.

All other files are clustered under a 'Documentation' section.

Some optional information from the manifest is extracted and
displayed as part of the index page, if present in the manifest:

Field

Source Attribute

Name

Implementation-Title

Vendor

Implementation-Vendor

Version

Implementation-Version

URL

Implementation-URL

API Version

Gerrit-ApiVersion

Deployment

Compiled plugins and extensions can be deployed to a running Gerrit
server using the plugin install command.

Web UI plugins distributed as a single .js file (or .html file for
Polygerrit) can be deployed without the overhead of JAR packaging. For
more information refer to plugin install
command.

Plugins can also be copied directly into the server’s directory at
$site_path/plugins/$name.(jar|js|html). For Web UI plugins, the name
of the file, minus the .js or .html extension, will be used as the
plugin name. For JAR plugins, the value of the Gerrit-PluginName
manifest attribute will be used, if provided, otherwise the name of
the file, minus the .jar extension, will be used.

For Web UI plugins, the plugin version is derived from the filename.
If the filename contains one or more hyphens, the version is taken
from the portion following the last hyphen. For example if the plugin
filename is my-plugin-1.0.js the version will be 1.0. For JAR
plugins, the version is taken from the Version attribute in the
manifest.

Unless disabled, servers periodically scan the $site_path/plugins
directory for updated plugins. The time can be adjusted by
plugins.checkFrequency.

Known issues and bugs

Error handling in UI when using the REST API

When a plugin invokes a REST endpoint in the UI, it provides an
AsyncCallback to handle the result. At the moment the
onFailure(Throwable) of the callback is never invoked, even if there
is an error. Errors are always handled by the Gerrit core UI which
shows the error dialog. This means currently plugins cannot do any
error handling and e.g. ignore expected errors.

In the following example the REST endpoint would return '404 Not
Found' if the user has no username and the Gerrit core UI would
display an error dialog for this. However having no username is
not an error and the plugin may like to handle this case.

Reviewer Suggestion Plugins

Gerrit provides an extension point that enables Plugins to rank
the list of reviewer suggestion a user receives upon clicking "Add Reviewer" on
the change screen.

Gerrit supports both a default suggestion that appears when the user has not yet
typed anything and a filtered suggestion that is shown as the user starts
typing.

Plugins receive a candidate list and can return a Set of suggested reviewers
containing the Account.Id and a score for each reviewer. The candidate list is
non-binding and plugins can choose to return reviewers not initially contained in
the candidate list.

Server administrators can configure the overall weight of each plugin by setting
the addreviewer.pluginName-exportName.weight value in gerrit.config.

Pre-submit Validation Plugins

Gerrit provides an extension point that enables plugins to prevent a change
from being submitted.

Important

This extension point must NOT be used for long or slow operations, like
calling external programs or content, running unit tests…​
Slow operations will hurt the whole Gerrit instance.

This can be used to implement custom rules that changes have to match to become
submittable. A more concrete example: the Prolog rules engine can be
implemented using this.

Gerrit calls the plugins once per change and caches the results. Although it is
possible to predict when this interface will be triggered, this should not be
considered as a feature. Plugins should only rely on the internal state of the
ChangeData, not on external values like date and time, remote content or
randomness.

Plugins are expected to support rules inheritance themselves, providing ways
to configure it and handling the logic behind it.
Please note that no inheritance is sometimes better than badly handled
inheritance: mis-communication and strange behaviors caused by inheritance
may and will confuse the users. Each plugins is responsible for handling the
project hierarchy and taking wise actions. Gerrit does not enforce it.

Once Gerrit has gathered every plugins' SubmitRecords, it stores them.

Plugins accept or reject a given change using SubmitRecord.Status.
If a change is ready to be submitted, OK. If it is not ready and requires
modifications, NOT_READY. Other statuses are available for particular cases.
A change can be submitted if all the plugins accept the change.

Plugins may also decide not to vote on a given change by returning an empty
Collection (ie: the plugin is not enabled for this repository), or to vote
several times (ie: one SubmitRecord per project in the hierarchy).
The results are handled as if multiple plugins voted for the change.

If a plugin decides not to vote, it’s name will not be displayed in the UI and
it will not be recoded in the database.

Table 1. Gerrit’s Pre-submit handling with three plugins

Plugin A

Plugin B

Plugin C

Final decision

OK

OK

OK

OK

OK

OK

/

OK

OK

OK

RULE_ERROR

NOT_READY

OK

NOT_READY

OK

NOT_READY

NOT_READY

OK

OK

NOT_READY

This makes composing plugins really easy.

If a plugin places a veto on a change, it can’t be submitted.

If a plugin isn’t enabled for a project (or isn’t needed for this change),
it returns an empty collection.

If all the plugins answer OK, the change can be submitted.

A more rare case, but worth documenting: if there are no installed plugins,
the labels will be compared to the rules defined in the project’s config,
and the permission system will be used to allow or deny a submit request.

Some rules are defined internally to provide a common base ground (and sanity):
changes that are marked as WIP or that are closed (abandoned, merged) can’t be merged.

Plugin authors should also consider binding their SubmitRule using a Gerrit-BatchModule.
See Batch runtime for more informations.

The SubmitRule extension point allows you to write complex rules, but writing
small self-contained rules should be preferred: doing so allows end users to
compose several rules to form more complex submit checks.

The SubmitRequirement class allows rules to communicate what the user needs
to change in order to be compliant. These requirements should be kept once they
are met, but marked as OK. If the requirements were not displayed, reviewers
would need to use their precious time to manually check that they were met.