summaryrefslogtreecommitdiffstats
path: root/Documentation/dev-plugins.txt
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/dev-plugins.txt')
-rw-r--r--Documentation/dev-plugins.txt211
1 files changed, 201 insertions, 10 deletions
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 534f0a871a..53b03b1ebe 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -109,6 +109,24 @@ will be performed by scanning all classes in the plugin JAR for
Gerrit-HttpModule: tld.example.project.HttpModuleClassName
----
+=== 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]]
=== Plugin Name
@@ -410,6 +428,10 @@ Update of the account secondary index
+
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.
@@ -482,6 +504,15 @@ 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]]
+== 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]]
== Receive Pack Initializers
@@ -752,7 +783,7 @@ ssh command.
[source, java]
----
public class SshModule extends AbstractModule {
- private static final Logger log = LoggerFactory.getLogger(SshModule.class);
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Override
protected void configure() {
@@ -765,7 +796,7 @@ public class SshModule extends AbstractModule {
public static class BanOptions implements DynamicOptions.DynamicBean {
@Option(name = "--log", aliases = { "-l" }, usage = "Say Hello in the Log")
private void parse(String arg) {
- log.error("Say Hello in the Log " + arg);
+ logger.atSevere().log("Say Hello in the Log %s", arg);
}
}
----
@@ -777,10 +808,12 @@ 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.
+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.
+'changeValue'), to the change query output, when the query command is provided
+the --myplugin-name--all option.
[source, java]
----
@@ -793,7 +826,31 @@ public class Module extends AbstractModule {
}
}
+public class MyQueryOptions implements DynamicBean {
+ @Option(name = "--all", usage = "Include plugin output")
+ public boolean all = false;
+}
+
+public static class HttpModule extends HttpPluginModule {
+ @Override
+ protected void configureServlets() {
+ bind(DynamicBean.class)
+ .annotatedWith(Exports.named(QueryChanges.class))
+ .to(MyQueryOptions.class);
+ }
+}
+
+public static class SshModule extends PluginCommandModule {
+ @Override
+ protected void configureCommands() {
+ bind(DynamicBean.class)
+ .annotatedWith(Exports.named(Query.class))
+ .to(MyQueryOptions.class);
+ }
+}
+
public class AttributeFactory implements ChangeAttributeFactory {
+ protected MyQueryOptions options;
public class PluginAttribute extends PluginDefinedInfo {
public String exampleName;
@@ -807,7 +864,13 @@ public class AttributeFactory implements ChangeAttributeFactory {
@Override
public PluginDefinedInfo create(ChangeData c, ChangeQueryProcessor qp, String plugin) {
- return new PluginAttribute(c);
+ if (options == null) {
+ options = (MyQueryOptions) qp.getDynamicBean(plugin);
+ }
+ if (options.all) {
+ return new PluginAttribute(c);
+ }
+ return null;
}
}
----
@@ -815,7 +878,7 @@ public class AttributeFactory implements ChangeAttributeFactory {
Example
----
-ssh -p 29418 localhost gerrit query "change:1" --format json
+ssh -p 29418 localhost gerrit query --myplugin-name--all "change:1" --format json
Output:
@@ -2236,7 +2299,7 @@ Plugins can hook into the
link:rest-api-accounts.html#create-account[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.api.accounts.AccountExternalIdCreator`
+point `com.google.gerrit.server.account.AccountExternalIdCreator`
must be registered.
[source,java]
@@ -2292,6 +2355,16 @@ By implementing the
`com.google.gerrit.server.git.ChangeReportFormatter` interface, a plugin
may change the formatting of the report.
+[[url-formatting]]
+== 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]]
== Links To External Tools
@@ -2503,7 +2576,7 @@ prefix is configurable by setting the `Gerrit-HttpDocumentationPrefix`
attribute.
Documentation may be written in the Markdown flavor
-link:https://github.com/sirthias/pegdown[pegdown]
+link:https://github.com/vsch/flexmark-java[flexmark-java]
if the file name ends with `.md`. Gerrit will automatically convert
Markdown to HTML if accessed with extension `.html`.
@@ -2703,10 +2776,10 @@ specific checks like IP filters.
[source, java]
----
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.server.mail.receive.MailMessage;
+import com.google.gerrit.mail.MailMessage;
public class MyPlugin implements MailFilter {
- boolean shouldProcessMessage(MailMessage message) {
+ public boolean shouldProcessMessage(MailMessage message) {
// Implement your filter logic here
return true;
}
@@ -2728,10 +2801,128 @@ class MyCommandInterceptor implements SshCreateCommandInterceptor {
@Override
public String intercept(String in) {
return pluginName + " mycommand";
+----
+
+
+[[pre-submit-evaluator]]
+== 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.
+
+.Gerrit's Pre-submit handling with three plugins
+[width="50%",cols="^m,^m,^m,^m",frame="topbot",options="header"]
+|=======================================================
+| 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.
+
+
+[source, java]
+----
+import java.util.Collection;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitRecord.Status;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.SubmitRule;
+
+public class MyPluginRules implements SubmitRule {
+ public Collection<SubmitRecord> evaluate(ChangeData changeData) {
+ // Implement your submitability logic here
+
+ // Assuming we want to prevent this change from being submitted:
+ SubmitRecord record;
+ record.status = Status.NOT_READY;
+ return record;
}
}
----
+Don't forget to register your class!
+
+[source, java]
+----
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.inject.AbstractModule;
+
+public class MyPluginModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(SubmitRule.class).annotatedWith(Exports.named("myPlugin")).to(MyPluginRules.class);
+ }
+}
+----
+
+Plugin authors should also consider binding their SubmitRule using a `Gerrit-BatchModule`.
+See link:dev-plugins.html[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.
+
== SEE ALSO