1155 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1155 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
:linkattrs:
 | 
						|
= Gerrit Code Review - Prolog Submit Rules Cookbook
 | 
						|
 | 
						|
[[SubmitRule]]
 | 
						|
== Submit Rule
 | 
						|
A _Submit Rule_ in Gerrit is logic that defines when a change is submittable.
 | 
						|
By default, a change is submittable when it gets at least one
 | 
						|
highest vote in each voting category and has no lowest vote (aka veto vote) in
 | 
						|
any category.  Typically, this means that a change needs `Code-Review+2`,
 | 
						|
`Verified+1` and has neither `Code-Review-2` nor `Verified-1` to become
 | 
						|
submittable.
 | 
						|
 | 
						|
While this rule is a good default, there are projects which need more
 | 
						|
flexibility for defining when a change is submittable.  In Gerrit, it is
 | 
						|
possible to use Prolog based rules to provide project specific submit rules and
 | 
						|
replace the default submit rules. Using Prolog based rules, project owners can
 | 
						|
define a set of criteria which must be fulfilled for a change to become
 | 
						|
submittable. For a change that is not submittable, the set of needed criteria
 | 
						|
is displayed in the Gerrit UI.
 | 
						|
 | 
						|
[NOTE]
 | 
						|
Loading and executing Prolog submit rules may be disabled by setting
 | 
						|
`rules.enable=false` in the Gerrit config file (see
 | 
						|
link:config-gerrit.html#_a_id_rules_a_section_rules[rules section])
 | 
						|
 | 
						|
link:https://groups.google.com/d/topic/repo-discuss/wJxTGhlHZMM/discussion[This
 | 
						|
discussion thread,role=external,window=_blank] explains why Prolog was chosen for the purpose of writing
 | 
						|
project specific submit rules.
 | 
						|
link:http://gerrit-documentation.googlecode.com/svn/ReleaseNotes/ReleaseNotes-2.2.2.html[Gerrit
 | 
						|
2.2.2 ReleaseNotes,role=external,window=_blank] introduces Prolog support in Gerrit.
 | 
						|
 | 
						|
[[SubmitType]]
 | 
						|
== Submit Type
 | 
						|
A _Submit Type_ is a strategy that is used on submit to integrate the
 | 
						|
change into the destination branch. Supported submit types are:
 | 
						|
 | 
						|
* `Fast Forward Only`
 | 
						|
* `Merge If Necessary`
 | 
						|
* `Merge Always`
 | 
						|
* `Cherry Pick`
 | 
						|
* `Rebase If Necessary`
 | 
						|
 | 
						|
_Submit Type_ is a project global setting. This means that the same submit type
 | 
						|
is used for all changes of one project.
 | 
						|
 | 
						|
Projects which need more flexibility in choosing, or enforcing, a submit type
 | 
						|
can use Prolog based submit type which replaces the project's default submit
 | 
						|
type.
 | 
						|
 | 
						|
Prolog based submit type computes a submit type for each change. The computed
 | 
						|
submit type is shown on the change screen for each change.
 | 
						|
 | 
						|
When submitting changes in a batch using "Submit including ancestors" or "Submit
 | 
						|
whole topic", submit type rules may not be used to mix submit types on a single
 | 
						|
branch, and trying to submit such a batch will fail. This avoids potentially
 | 
						|
confusing behavior and spurious submit failures. It is recommended to only use
 | 
						|
submit type rules to change submit types for an entire branch, which avoids this
 | 
						|
situation.
 | 
						|
 | 
						|
== Prolog Language
 | 
						|
This document is not a complete Prolog tutorial.
 | 
						|
link:http://en.wikipedia.org/wiki/Prolog[This Wikipedia page on Prolog,role=external,window=_blank] is a
 | 
						|
good starting point for learning the Prolog language. This document will only
 | 
						|
explain some elements of Prolog that are necessary to understand the provided
 | 
						|
examples.
 | 
						|
 | 
						|
== Prolog in Gerrit
 | 
						|
Gerrit uses its own link:https://gerrit.googlesource.com/prolog-cafe/[fork,role=external,window=_blank] of the
 | 
						|
original link:http://kaminari.istc.kobe-u.ac.jp/PrologCafe/[prolog-cafe,role=external,window=_blank]
 | 
						|
project. Gerrit embeds the prolog-cafe library and can interpret Prolog programs
 | 
						|
at runtime.
 | 
						|
 | 
						|
== Interactive Prolog Cafe Shell
 | 
						|
For interactive testing and playing with Prolog, Gerrit provides the
 | 
						|
link:pgm-prolog-shell.html[prolog-shell] program which opens an interactive
 | 
						|
Prolog interpreter shell.
 | 
						|
 | 
						|
For batch or unit tests, see the examples in Gerrit source directory
 | 
						|
link:https://gerrit.googlesource.com/gerrit/+/refs/heads/master/prologtests/examples/[prologtests/examples,role=external,window=_blank].
 | 
						|
 | 
						|
[NOTE]
 | 
						|
The interactive shell is just a prolog shell, it does not load
 | 
						|
a gerrit server environment and thus is not intended for
 | 
						|
xref:TestingSubmitRules[testing submit rules].
 | 
						|
 | 
						|
== SWI-Prolog
 | 
						|
Instead of using the link:pgm-prolog-shell.html[prolog-shell] program one can
 | 
						|
also use the link:http://www.swi-prolog.org/[SWI-Prolog,role=external,window=_blank] environment. It
 | 
						|
provides a better shell interface and a graphical source-level debugger.
 | 
						|
 | 
						|
[[RulesFile]]
 | 
						|
== The rules.pl file
 | 
						|
This section explains how to create and edit project specific submit rules. How
 | 
						|
to actually write the submit rules is explained in the next section.
 | 
						|
 | 
						|
Project specific submit rules are stored in the `rules.pl` file in the
 | 
						|
`refs/meta/config` branch of that project.  Therefore, we need to fetch and
 | 
						|
checkout the `refs/meta/config` branch in order to create or edit the `rules.pl`
 | 
						|
file:
 | 
						|
 | 
						|
----
 | 
						|
  $ git fetch origin refs/meta/config:config
 | 
						|
  $ git checkout config
 | 
						|
  ... edit or create the rules.pl file
 | 
						|
  $ git add rules.pl
 | 
						|
  $ git commit -m "My submit rules"
 | 
						|
  $ git push origin HEAD:refs/meta/config
 | 
						|
----
 | 
						|
 | 
						|
[[HowToWriteSubmitRules]]
 | 
						|
== How to write submit rules
 | 
						|
Whenever Gerrit needs to evaluate submit rules for a change `C` from project `P`
 | 
						|
it will first initialize the embedded Prolog interpreter by:
 | 
						|
 | 
						|
* consulting a set of facts about the change `C`
 | 
						|
* consulting the `rules.pl` from the project `P`
 | 
						|
 | 
						|
Conceptually we can imagine that Gerrit adds a set of facts about the change
 | 
						|
`C` on top of the `rules.pl` file and then consults it. The set of facts about
 | 
						|
the change `C` will look like:
 | 
						|
 | 
						|
----
 | 
						|
  :- package gerrit.                                                   <1>
 | 
						|
 | 
						|
  commit_author(user(1000000), 'John Doe', 'john.doe@example.com').    <2>
 | 
						|
  commit_committer(user(1000000), 'John Doe', 'john.doe@example.com'). <3>
 | 
						|
  commit_message('Add plugin support to Gerrit').                      <4>
 | 
						|
  ...
 | 
						|
----
 | 
						|
 | 
						|
<1> Gerrit will provide its facts in a package named `gerrit`. This means we
 | 
						|
have to use qualified names when writing our code and referencing these facts.
 | 
						|
For example: `gerrit:commit_author(ID, N, M)`
 | 
						|
<2> user ID, full name and email address of the commit author
 | 
						|
<3> user ID, full name and email address of the commit committer
 | 
						|
<4> commit message
 | 
						|
 | 
						|
A complete set of facts which Gerrit provides about the change is listed in the
 | 
						|
link:prolog-change-facts.html[Prolog Facts for Gerrit Change].
 | 
						|
 | 
						|
By default, Gerrit will search for a `submit_rule/1` predicate in the `rules.pl`
 | 
						|
file, evaluate the `submit_rule(X)` and then inspect the value of `X` in order
 | 
						|
to decide whether the change is submittable or not and also to find the set of
 | 
						|
needed criteria for the change to become submittable. This means that Gerrit has
 | 
						|
an expectation on the format and value of the result of the `submit_rule`
 | 
						|
predicate which is expected to be a `submit` term of the following format:
 | 
						|
 | 
						|
----
 | 
						|
  submit(label(label-name, status) [, label(label-name, status)]*)
 | 
						|
----
 | 
						|
 | 
						|
where `label-name` is usually `'Code-Review'` or `'Verified'` but could also
 | 
						|
be any other string (see examples below). The `status` is one of:
 | 
						|
 | 
						|
* `ok(user(ID))`. This status is used to tell that this label/category has been
 | 
						|
  met.
 | 
						|
* `need(_)` is used to tell that this label/category is needed for the change to
 | 
						|
  become submittable.
 | 
						|
* `reject(user(ID))`. This status is used to tell that this label/category is
 | 
						|
  blocking submission of the change.
 | 
						|
* `impossible(_)` is used when the logic knows that the change cannot be submitted
 | 
						|
  as-is. This is meant for cases where the logic requires members of a specific
 | 
						|
  group to apply a specific label on a change, but no users are in that group.
 | 
						|
  This is usually caused by misconfiguration of permissions.
 | 
						|
* `may(_)` allows expression of approval categories that are optional, i.e.
 | 
						|
  could either be set or unset without ever influencing whether the change
 | 
						|
  could be submitted.
 | 
						|
 | 
						|
[NOTE]
 | 
						|
For a change to be submittable all `label` terms contained in the returned
 | 
						|
`submit` term must have either `ok` or `may` status.
 | 
						|
 | 
						|
[IMPORTANT]
 | 
						|
Gerrit will let the Prolog engine continue searching for solutions of
 | 
						|
the `submit_rule(X)` query until it finds the first one where all labels in the
 | 
						|
return result have either status `ok` or `may` or there are no more solutions.
 | 
						|
If a solution where all labels have status `ok` is found then all previously
 | 
						|
found solutions are ignored. Otherwise, all labels names with status `need`
 | 
						|
from all solutions will be displayed in the UI indicating the set of conditions
 | 
						|
needed for the change to become submittable.
 | 
						|
 | 
						|
Here some examples of possible return values from the `submit_rule` predicate:
 | 
						|
 | 
						|
----
 | 
						|
  submit(label('Code-Review', ok(user(ID))))                        <1>
 | 
						|
  submit(label('Code-Review', ok(user(ID))),
 | 
						|
      label('Verified', reject(user(ID))))                          <2>
 | 
						|
  submit(label('Author-is-John-Doe', need(_))                       <3>
 | 
						|
----
 | 
						|
 | 
						|
<1> label `'Code-Review'` is met. As there are no other labels in the
 | 
						|
    return result, the change is submittable.
 | 
						|
<2> label `'Verified'` is rejected. Change is not submittable.
 | 
						|
<3> label `'Author-is-John-Doe'` is needed for the change to become submittable.
 | 
						|
    Note that this tells nothing about how this criteria will be met. It is up
 | 
						|
    to the implementer of the `submit_rule` to return
 | 
						|
    `label('Author-is-John-Doe', ok(user(ID)))` when this criteria is met. Most
 | 
						|
    likely, it will have to match against `gerrit:commit_author` in order to
 | 
						|
    check if this criteria is met. This will become clear through the examples
 | 
						|
    below.
 | 
						|
 | 
						|
Of course, when implementing the `submit_rule` we will use the facts about the
 | 
						|
change that are already provided by Gerrit.
 | 
						|
 | 
						|
Another aspect of the return result from the `submit_rule` predicate is that
 | 
						|
Gerrit uses it to decide which set of labels to display on the change review
 | 
						|
screen for voting. If the return result contains label `'ABC'` and if the label
 | 
						|
`'ABC'` is link:config-labels.html[defined for the project] then voting for the
 | 
						|
label `'ABC'` will be displayed. Otherwise, it is not displayed. Note that the
 | 
						|
project doesn't need a defined label for each label contained in the result of
 | 
						|
`submit_rule` predicate.  For example, the decision whether
 | 
						|
`'Author-is-John-Doe'` label is met will probably not be made by explicit voting
 | 
						|
but, instead, by inspecting the facts about the change.
 | 
						|
 | 
						|
[[SubmitFilter]]
 | 
						|
== Submit Filter
 | 
						|
Another mechanism of changing the default submit rules is to implement the
 | 
						|
`submit_filter/2` predicate. While Gerrit will search for the `submit_rule` only
 | 
						|
in the `rules.pl` file of the current project, the `submit_filter` will be
 | 
						|
searched for in the `rules.pl` of all parent projects of the current project,
 | 
						|
but not in the `rules.pl` of the current project. The search will start from the
 | 
						|
immediate parent of the current project, then in the parent project of that
 | 
						|
project and so on until, and including, the `'All-Projects'` project.
 | 
						|
 | 
						|
The purpose of the submit filter is, as its name says, to filter the results
 | 
						|
of the `submit_rule`. Therefore, the `submit_filter` predicate has two
 | 
						|
parameters:
 | 
						|
 | 
						|
----
 | 
						|
  submit_filter(In, Out) :- ...
 | 
						|
----
 | 
						|
Gerrit will invoke `submit_filter` with the `In` parameter containing a `submit`
 | 
						|
structure produced by the `submit_rule` and will take the value of the `Out`
 | 
						|
parameter as the result.
 | 
						|
 | 
						|
The `Out` value of a `submit_filter` will become the `In` value for the
 | 
						|
next `submit_filter` in the parent line. The value of the `Out` parameter
 | 
						|
of the top-most `submit_filter` is the final result of the submit rule that
 | 
						|
is used to decide whether a change is submittable or not.
 | 
						|
 | 
						|
[IMPORTANT]
 | 
						|
`submit_filter` is a mechanism for Gerrit administrators to implement
 | 
						|
and enforce submit rules that would apply to all projects while `submit_rule` is
 | 
						|
a mechanism for project owners to implement project specific submit rules.
 | 
						|
However, project owners who own several projects could also make use of
 | 
						|
`submit_filter` by using a common parent project for all their projects and
 | 
						|
implementing the `submit_filter` in this common parent project. This way they
 | 
						|
can avoid implementing the same `submit_rule` in all their projects.
 | 
						|
 | 
						|
The following "drawing" illustrates the order of the invocation and the chaining
 | 
						|
of the results of the `submit_rule` and `submit_filter` predicates.
 | 
						|
----
 | 
						|
  All-Projects
 | 
						|
  ^   submit_filter(B, S) :- ...  <4>
 | 
						|
  |
 | 
						|
  Parent-3
 | 
						|
  ^   <no submit filter here>
 | 
						|
  |
 | 
						|
  Parent-2
 | 
						|
  ^   submit_filter(A, B) :- ...  <3>
 | 
						|
  |
 | 
						|
  Parent-1
 | 
						|
  ^   submit_filter(X, A) :- ...  <2>
 | 
						|
  |
 | 
						|
  MyProject
 | 
						|
      submit_rule(X) :- ...       <1>
 | 
						|
----
 | 
						|
 | 
						|
<1> The `submit_rule` of `MyProject` is invoked first.
 | 
						|
<2> The result `X` is filtered through the `submit_filter` from the `Parent-1`
 | 
						|
project.
 | 
						|
<3> The result of `submit_filter` from `Parent-1` project is filtered by the
 | 
						|
`submit_filter` in the `Parent-2` project. Since `Parent-3` project doesn't have
 | 
						|
a `submit_filter` it is skipped.
 | 
						|
<4> The result of `submit_filter` from `Parent-2` project is filtered by the
 | 
						|
`submit_filter` in the `All-Projects` project. The value in `S` is the final
 | 
						|
value of the submit rule evaluation.
 | 
						|
 | 
						|
[NOTE]
 | 
						|
If `MyProject` doesn't define its own `submit_rule` Gerrit will invoke the
 | 
						|
default implementation of submit rule that is named `gerrit:default_submit` and
 | 
						|
its result will be filtered as described above.
 | 
						|
 | 
						|
[[HowToWriteSubmitType]]
 | 
						|
== How to write submit type
 | 
						|
Writing custom submit type logic in Prolog is similar to
 | 
						|
xref:HowToWriteSubmitRules[writing submit rules]. The only difference is that
 | 
						|
one has to implement a `submit_type` predicate (instead of the `submit_rule`)
 | 
						|
and that the return result of the `submit_type` has to be an atom that
 | 
						|
represents one of the supported submit types:
 | 
						|
 | 
						|
* `fast_forward_only`
 | 
						|
* `merge_if_necessary`
 | 
						|
* `merge_always`
 | 
						|
* `cherry_pick`
 | 
						|
* `rebase_if_necessary`
 | 
						|
 | 
						|
== Submit Type Filter
 | 
						|
Submit type filter works the same way as the xref:SubmitFilter[Submit Filter]
 | 
						|
where the name of the filter predicate is `submit_type_filter`.
 | 
						|
 | 
						|
----
 | 
						|
  submit_type_filter(In, Out).
 | 
						|
----
 | 
						|
 | 
						|
Gerrit will invoke `submit_type_filter` with the `In` parameter containing a
 | 
						|
result of the `submit_type` and will take the value of the `Out` parameter as
 | 
						|
the result.
 | 
						|
 | 
						|
[[TestingSubmitRules]]
 | 
						|
== Testing submit rules
 | 
						|
The prolog environment running the `submit_rule` is loaded with state describing
 | 
						|
the change that is being evaluated. The easiest way to load this state is to
 | 
						|
test your `submit_rule` against a real change on a running gerrit instance. The
 | 
						|
command link:cmd-test-submit-rule.html[test-submit rule] loads a specific change
 | 
						|
and executes the `submit_rule`. It optionally reads the rule from from `stdin`
 | 
						|
to facilitate easy testing.
 | 
						|
 | 
						|
----
 | 
						|
  $ cat rules.pl | ssh gerrit_srv gerrit test-submit rule I45e080b105a50a625cc8e1fb5b357c0bfabe6d68 -s
 | 
						|
----
 | 
						|
 | 
						|
== Prolog vs Gerrit plugin for project specific submit rules
 | 
						|
Since version 2.5 Gerrit supports plugins and extension points. A plugin or an
 | 
						|
extension point could also be used as another means to provide custom submit
 | 
						|
rules. One could ask for a guideline when to use Prolog based submit rules and
 | 
						|
when to go for writing a new plugin. Writing a Prolog program is usually much
 | 
						|
faster than writing a Gerrit plugin. Prolog based submit rules can be pushed
 | 
						|
to a project by project owners while Gerrit plugins could only be installed by
 | 
						|
Gerrit administrators. In addition, Prolog based submit rules can be pushed
 | 
						|
for review by pushing to `refs/for/refs/meta/config` branch.
 | 
						|
 | 
						|
On the other hand, Prolog based submit rules get a limited amount of facts about
 | 
						|
the change exposed to them. Gerrit plugins get full access to Gerrit internals
 | 
						|
and can potentially check more things than Prolog based rules.
 | 
						|
 | 
						|
From version 2.6 Gerrit plugins can contribute Prolog predicates. This way, we
 | 
						|
can make use of the plugin provided predicates when writing Prolog based rules.
 | 
						|
 | 
						|
== Examples - Submit Rule
 | 
						|
The following examples should serve as a cookbook for developing own submit
 | 
						|
rules. Some of them are too trivial to be used in production and their only
 | 
						|
purpose is to provide step by step introduction and understanding.
 | 
						|
 | 
						|
Some of the examples will implement the `submit_rule` and some will implement
 | 
						|
the `submit_filter` just to show both possibilities.  Remember that
 | 
						|
`submit_rule` is only invoked from the current project and `submit_filter` is
 | 
						|
invoked from all parent projects. This is the most important fact in deciding
 | 
						|
whether to implement `submit_rule` or `submit_filter`.
 | 
						|
 | 
						|
=== Example 1: Make every change submittable
 | 
						|
Let's start with a most trivial example where we would make every change
 | 
						|
submittable regardless of the votes it has:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(W)) :-
 | 
						|
    W = label('Any-Label-Name', ok(user(1000000))).
 | 
						|
----
 | 
						|
 | 
						|
In this case we make no use of facts about the change. We don't need it as we
 | 
						|
are simply making every change submittable. Note that, in this case, the Gerrit
 | 
						|
UI will not show the UI for voting for the standard `'Code-Review'` and
 | 
						|
`'Verified'` categories as labels with these names are not part of the return
 | 
						|
result. The `'Any-Label-Name'` could really be any string.
 | 
						|
 | 
						|
The `user(1000000)` represents the user whose account ID is `1000000`.
 | 
						|
 | 
						|
[NOTE]
 | 
						|
Instead of the account ID `1000000` we could have used any other account ID.
 | 
						|
The following examples will use `user(ID)` instead of `user(1000000)` because
 | 
						|
it is easier to read and doesn't suggest that there is anything special with
 | 
						|
the account ID `1000000`.
 | 
						|
 | 
						|
=== Example 2: Every change submittable and voting in the standard categories possible
 | 
						|
This is continuation of the previous example where, in addition, to making
 | 
						|
every change submittable we want to enable voting in the standard
 | 
						|
`'Code-Review'` and `'Verified'` categories.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR, V)) :-
 | 
						|
    CR = label('Code-Review', ok(user(ID))),
 | 
						|
    V = label('Verified', ok(user(ID))).
 | 
						|
----
 | 
						|
 | 
						|
Since for every change all label statuses are `'ok'` every change will be
 | 
						|
submittable. Voting in the standard labels will be shown in the UI as the
 | 
						|
standard label names are included in the return result.
 | 
						|
 | 
						|
=== Example 3: Nothing is submittable
 | 
						|
This example shows how to make all changes non-submittable regardless of the
 | 
						|
votes they have.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(R)) :-
 | 
						|
    R = label('Any-Label-Name', reject(user(ID))).
 | 
						|
----
 | 
						|
 | 
						|
Since for any change we return only one label with status `reject`, no change
 | 
						|
will be submittable. The UI will, however, not indicate what is needed for a
 | 
						|
change to become submittable as we return no labels with status `need`.
 | 
						|
 | 
						|
=== Example 4: Nothing is submittable but UI shows several 'Need ...' criteria
 | 
						|
In this example no change is submittable but here we show how to present 'Need
 | 
						|
<label>' information to the user in the UI.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
% In the UI this will show: Need Any-Label-Name
 | 
						|
submit_rule(submit(N)) :-
 | 
						|
    N = label('Any-Label-Name', need(_)).
 | 
						|
 | 
						|
% We could define more "need" labels by adding more rules
 | 
						|
submit_rule(submit(N)) :-
 | 
						|
    N = label('Another-Label-Name', need(_)).
 | 
						|
 | 
						|
% or by providing more than one need label in the same rule
 | 
						|
submit_rule(submit(NX, NY)) :-
 | 
						|
    NX = label('X-Label-Name', need(_)),
 | 
						|
    NY = label('Y-Label-Name', need(_)).
 | 
						|
----
 | 
						|
 | 
						|
In the UI this will show:
 | 
						|
 | 
						|
* `Need Any-Label-Name`
 | 
						|
* `Need Another-Label-Name`
 | 
						|
* `Need X-Label-Name`
 | 
						|
* `Need Y-Label-Name`
 | 
						|
 | 
						|
From the example above we can see a few more things:
 | 
						|
 | 
						|
* comment in Prolog starts with the `%` character
 | 
						|
* there could be multiple `submit_rule` predicates. Since Prolog, by default,
 | 
						|
  tries to find all solutions for a query, the result will be union of all
 | 
						|
  solutions. Therefore, we see all 4 `need` labels in the UI.
 | 
						|
 | 
						|
=== Example 5: The 'Need ...' labels not shown when change is submittable
 | 
						|
This example shows that, when there is a solution for `submit_rule(X)` where all
 | 
						|
labels have status `ok` then Gerrit will not show any labels with the `need`
 | 
						|
status from any of the previous `submit_rule(X)` solutions.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(N)) :-
 | 
						|
    N = label('Some-Condition', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(OK)) :-
 | 
						|
    OK = label('Another-Condition', ok(user(ID))).
 | 
						|
----
 | 
						|
 | 
						|
The `'Need Some-Condition'` will not be shown in the UI because of the result of
 | 
						|
the second rule.
 | 
						|
 | 
						|
The same is valid if the two rules are swapped:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(OK)) :-
 | 
						|
    OK = label('Another-Condition', ok(user(ID))).
 | 
						|
 | 
						|
submit_rule(submit(N)) :-
 | 
						|
    N = label('Some-Condition', need(_)).
 | 
						|
----
 | 
						|
 | 
						|
The result of the first rule will stop search for any further solutions.
 | 
						|
 | 
						|
=== Example 6: Make change submittable if commit author is "John Doe"
 | 
						|
This is the first example where we will use the Prolog facts about a change that
 | 
						|
are automatically exposed by Gerrit. Our goal is to make any change submittable
 | 
						|
when the commit author is named `'John Doe'`. In the very first
 | 
						|
step let's make sure Gerrit UI shows `'Need Author-is-John-Doe'` in
 | 
						|
the UI to clearly indicate to the user what is needed for a change to become
 | 
						|
submittable:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
----
 | 
						|
 | 
						|
This will show:
 | 
						|
 | 
						|
* `Need Author-is-John-Doe`
 | 
						|
 | 
						|
in the UI but no change will be submittable yet. Let's add another rule:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(A, 'John Doe', _),
 | 
						|
    Author = label('Author-is-John-Doe', ok(A)).
 | 
						|
----
 | 
						|
 | 
						|
In the second rule we return `ok` status for the `'Author-is-John-Doe'` label
 | 
						|
if there is a `commit_author` fact where the full name is `'John Doe'`. If
 | 
						|
author of a change is `'John Doe'` then the second rule will return a solution
 | 
						|
where all labels have `ok` status and the change will become submittable. If
 | 
						|
author of a change is not `'John Doe'` then only the first rule will produce a
 | 
						|
solution. The UI will show `'Need Author-is-John-Doe'` but, as expected, the
 | 
						|
change will not be submittable.
 | 
						|
 | 
						|
Instead of checking by full name we could also check by the email address:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(_, _, 'john.doe@example.com'),
 | 
						|
    gerrit:uploader(U),
 | 
						|
    Author = label('Author-is-John-Doe', ok(U)).
 | 
						|
----
 | 
						|
 | 
						|
or by user id (assuming it is `1000000`):
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    U = user(1000000),
 | 
						|
    gerrit:commit_author(U, _, _),
 | 
						|
    Author = label('Author-is-John-Doe', ok(U)).
 | 
						|
----
 | 
						|
 | 
						|
or by a combination of these 3 attributes:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(_, 'John Doe', 'john.doe@example.com'),
 | 
						|
    gerrit:uploader(U),
 | 
						|
    Author = label('Author-is-John-Doe', ok(U)).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 7: Make change submittable if commit message starts with "Fix "
 | 
						|
Besides showing how to make use of the commit message text the purpose of this
 | 
						|
example is also to show how to match only a part of a string symbol. Similarly
 | 
						|
like commit author the commit message is provided as a string symbol which is
 | 
						|
an atom in Prolog terms. When working with an atom we could only match against
 | 
						|
the whole value. To match only part of a string symbol we have, at least, two
 | 
						|
options:
 | 
						|
 | 
						|
* convert the string symbol into a list of characters and then perform
 | 
						|
  the "classical" list matching
 | 
						|
* use the `regex_matches/2` or, even more convenient, the
 | 
						|
  `gerrit:commit_message_matches/1` predicate
 | 
						|
 | 
						|
Let's implement both options:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    gerrit:commit_message(M), name(M, L), starts_with(L, "Fix "),
 | 
						|
    gerrit:uploader(U),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(U)).
 | 
						|
 | 
						|
starts_with(L, []).
 | 
						|
starts_with([H|T1], [H|T2]) :- starts_with(T1, T2).
 | 
						|
----
 | 
						|
 | 
						|
[NOTE]
 | 
						|
The `name/2` embedded predicate is used to convert a string symbol into a
 | 
						|
list of characters. A string `abc` is converted into a list of characters `[97,
 | 
						|
98, 99]`.  A double quoted string in Prolog is just a shortcut for creating a
 | 
						|
list of characters. `"abc"` is a shortcut for `[97, 98, 99]`. This is why we use
 | 
						|
double quotes for the `"Trivial Fix"` in the example above.
 | 
						|
 | 
						|
The `starts_with` predicate is self explaining.
 | 
						|
 | 
						|
Using the `gerrit:commit_message_matches` predicate is probably more efficient:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', need(_)).
 | 
						|
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    gerrit:commit_message_matches('^Fix '),
 | 
						|
    gerrit:uploader(U),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(U)).
 | 
						|
----
 | 
						|
 | 
						|
The previous example could also be written so that it first checks if the commit
 | 
						|
message starts with 'Fix '. If true then it sets OK for that category and stops
 | 
						|
further backtracking by using the cut `!` operator:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    gerrit:commit_message_matches('^Fix '),
 | 
						|
    gerrit:uploader(U),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(U)),
 | 
						|
    !.
 | 
						|
 | 
						|
% Message does not start with 'Fix ' so Fix is needed to submit
 | 
						|
submit_rule(submit(Fix)) :-
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', need(_)).
 | 
						|
----
 | 
						|
 | 
						|
== The default submit policy
 | 
						|
All examples until now concentrate on one particular aspect of change data.
 | 
						|
However, in real-life scenarios we would rather want to reuse Gerrit's default
 | 
						|
submit policy and extend/change it for our specific purpose.  This could be
 | 
						|
done in one of the following ways:
 | 
						|
 | 
						|
* understand how the default submit policy is implemented and use that as a
 | 
						|
  template for implementing custom submit rules,
 | 
						|
* invoke the default submit rule implementation and then perform further
 | 
						|
  actions on its return result.
 | 
						|
 | 
						|
=== Default submit rule implementation
 | 
						|
The default submit rule with the two default categories, `Code-Review` and
 | 
						|
`Verified`, can be implemented as:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(V, CR)) :-
 | 
						|
    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
 | 
						|
    gerrit:max_with_block(-1, 1, 'Verified', V).
 | 
						|
----
 | 
						|
 | 
						|
Once this implementation is understood it can be customized to implement
 | 
						|
project specific submit rules. Note, that this implementation hardcodes
 | 
						|
the two default categories. Introducing a new category in the database would
 | 
						|
require introducing the same category here or a `submit_filter` in a parent
 | 
						|
project would have to care about including the new category in the result of
 | 
						|
this `submit_rule`. On the other side, this example is easy to read and
 | 
						|
understand.
 | 
						|
 | 
						|
=== Reusing the default submit policy
 | 
						|
To get results of Gerrit's default submit policy we use the
 | 
						|
`gerrit:default_submit` predicate.  The `gerrit:default_submit(X)` includes all
 | 
						|
categories from the database.  This means that if we write a submit rule like
 | 
						|
this:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(X) :- gerrit:default_submit(X).
 | 
						|
----
 | 
						|
 | 
						|
it is equivalent to not using `rules.pl` at all. We just delegate to
 | 
						|
default logic. However, once we invoke the `gerrit:default_submit(X)` we can
 | 
						|
perform further actions on the return result `X` and apply our specific
 | 
						|
logic. The following pattern illustrates this technique:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(S) :- gerrit:default_submit(R), project_specific_policy(R, S).
 | 
						|
 | 
						|
project_specific_policy(R, S) :- ...
 | 
						|
----
 | 
						|
 | 
						|
In the following examples both styles will be shown.
 | 
						|
 | 
						|
[[NonAuthorCodeReview]]
 | 
						|
=== Example 8: Make change submittable only if `Code-Review+2` is given by a non author
 | 
						|
In this example we introduce a new label `Non-Author-Code-Review` and make it
 | 
						|
satisfied if there is at least one `Code-Review+2` from a non author. All other
 | 
						|
default policies like the `Verified` category and vetoing changes still apply.
 | 
						|
 | 
						|
==== Reusing the `gerrit:default_submit`
 | 
						|
First, we invoke `gerrit:default_submit` to compute the result for the default
 | 
						|
submit policy and then add the `Non-Author-Code-Review` label to it.  The
 | 
						|
`Non-Author-Code-Review` label is added with status `ok` if such an approval
 | 
						|
exists or with status `need` if it doesn't exist.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(S) :-
 | 
						|
    gerrit:default_submit(X),
 | 
						|
    X =.. [submit | Ls],
 | 
						|
    add_non_author_approval(Ls, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
 | 
						|
add_non_author_approval(S1, S2) :-
 | 
						|
    gerrit:commit_author(A),
 | 
						|
    gerrit:commit_label(label('Code-Review', 2), R),
 | 
						|
    R \= A, !,
 | 
						|
    S2 = [label('Non-Author-Code-Review', ok(R)) | S1].
 | 
						|
add_non_author_approval(S1, [label('Non-Author-Code-Review', need(_)) | S1]).
 | 
						|
----
 | 
						|
 | 
						|
This example uses the `univ` operator `=..` to "unpack" the result of the
 | 
						|
default_submit, which is a structure of the form `submit(label('Code-Review',
 | 
						|
ok(user(ID))), label('Verified', need(_)), ...)` into a list like `[submit,
 | 
						|
label('Code-Review', ok(user(ID))), label('Verified', need(_)), ...]`.  Then we
 | 
						|
process the tail of the list (the list of labels) as a Prolog list, which is
 | 
						|
much easier than processing a structure. In the end we use the same `univ`
 | 
						|
operator to convert the resulting list of labels back into a `submit` structure
 | 
						|
which is expected as a return result. The `univ` operator works both ways.
 | 
						|
 | 
						|
In `add_non_author_approval` we use the `cut` operator `!` to prevent Prolog
 | 
						|
from searching for more solutions once the `cut` point is reached. This is
 | 
						|
important because in the second `add_non_author_approval` rule we just add the
 | 
						|
`label('Non-Author-Code-Review', need(_))` without first checking that there
 | 
						|
is no non author `Code-Review+2`. The second rule will only be reached
 | 
						|
if the `cut` in the first rule is not reached and it only happens if a
 | 
						|
predicate before the `cut` fails.
 | 
						|
 | 
						|
This fact can be bypassed by users who have
 | 
						|
link:access-control.html#category_forge_author[Forge Author] permission.
 | 
						|
 | 
						|
==== Don't use `gerrit:default_submit`
 | 
						|
Let's implement the same submit rule the other way, without reusing the
 | 
						|
`gerrit:default_submit`:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR, V)) :-
 | 
						|
    base(CR, V),
 | 
						|
    CR = label(_, ok(Reviewer)),
 | 
						|
    gerrit:commit_author(Author),
 | 
						|
    Author \= Reviewer,
 | 
						|
    !.
 | 
						|
 | 
						|
submit_rule(submit(CR, V, N)) :-
 | 
						|
    base(CR, V),
 | 
						|
    N = label('Non-Author-Code-Review', need(_)).
 | 
						|
 | 
						|
base(CR, V) :-
 | 
						|
    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
 | 
						|
    gerrit:max_with_block(-1, 1, 'Verified', V).
 | 
						|
----
 | 
						|
 | 
						|
The latter implementation is probably easier to understand and the code looks
 | 
						|
cleaner. Note, however, that the latter implementation will always return the
 | 
						|
two standard categories only (`Code-Review` and `Verified`) even if a new
 | 
						|
category has been inserted into the database. To include the new category
 | 
						|
the `rules.pl` would need to be modified or a `submit_filter` in a parent
 | 
						|
project would have to care about including the new category in the result
 | 
						|
of this `submit_rule`.
 | 
						|
 | 
						|
The former example, however, would include any newly added category as it
 | 
						|
invokes the `gerrit:default_submit` and then modifies its result.
 | 
						|
 | 
						|
Which of these two behaviors is desired will always depend on how a particular
 | 
						|
Gerrit server is managed.
 | 
						|
 | 
						|
=== Example 9: Remove the `Verified` category
 | 
						|
A project has no build and test. It consists of only text files and needs only
 | 
						|
code review.  We want to remove the `Verified` category from this project so
 | 
						|
that `Code-Review+2` is the only criteria for a change to become submittable.
 | 
						|
We also want the UI to not show the `Verified` category in the table with
 | 
						|
votes and on the voting screen.
 | 
						|
 | 
						|
This is quite simple without reusing the `gerrit:default_submit`:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR)) :-
 | 
						|
    gerrit:max_with_block(-2, 2, 'Code-Review', CR).
 | 
						|
----
 | 
						|
 | 
						|
Implementing the same rule by reusing `gerrit:default_submit` is a bit more complex:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(S) :-
 | 
						|
    gerrit:default_submit(X),
 | 
						|
    X =.. [submit | Ls],
 | 
						|
    remove_verified_category(Ls, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
 | 
						|
remove_verified_category([], []).
 | 
						|
remove_verified_category([label('Verified', _) | T], R) :- remove_verified_category(T, R), !.
 | 
						|
remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 10: Combine examples 8 and 9
 | 
						|
In this example we want to both remove the verified and have the four eyes
 | 
						|
principle.  This means we want a combination of examples 8 and 9.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(S) :-
 | 
						|
    gerrit:default_submit(X),
 | 
						|
    X =.. [submit | Ls],
 | 
						|
    remove_verified_category(Ls, R1),
 | 
						|
    add_non_author_approval(R1, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
----
 | 
						|
 | 
						|
The `remove_verified_category` and `add_non_author_approval` predicates are the
 | 
						|
same as defined in the previous two examples.
 | 
						|
 | 
						|
Without reusing the `gerrit:default_submit` the same example may be implemented
 | 
						|
as:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR)) :-
 | 
						|
    base(CR),
 | 
						|
    CR = label(_, ok(Reviewer)),
 | 
						|
    gerrit:commit_author(Author),
 | 
						|
    Author \= Reviewer,
 | 
						|
    !.
 | 
						|
 | 
						|
submit_rule(submit(CR, N)) :-
 | 
						|
    base(CR),
 | 
						|
    N = label('Non-Author-Code-Review', need(_)).
 | 
						|
 | 
						|
base(CR) :-
 | 
						|
    gerrit:max_with_block(-2, 2, 'Code-Review', CR).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 11: Remove the `Verified` category from all projects
 | 
						|
Example 9, implements `submit_rule` that removes the `Verified` category from
 | 
						|
one project. In this example we do the same but we want to remove the `Verified`
 | 
						|
category from all projects. This means we have to implement `submit_filter` and
 | 
						|
we have to do that in the `rules.pl` of the `All-Projects` project.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_filter(In, Out) :-
 | 
						|
    In =.. [submit | Ls],
 | 
						|
    remove_verified_category(Ls, R),
 | 
						|
    Out =.. [submit | R].
 | 
						|
 | 
						|
remove_verified_category([], []).
 | 
						|
remove_verified_category([label('Verified', _) | T], R) :- remove_verified_category(T, R), !.
 | 
						|
remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 12: On release branches require DrNo in addition to project rules
 | 
						|
A new category 'DrNo' is added to the database and is required for release
 | 
						|
branches. To mark a branch as a release branch we use
 | 
						|
`drno('refs/heads/branch')`.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
drno('refs/heads/master').
 | 
						|
drno('refs/heads/stable-2.3').
 | 
						|
drno('refs/heads/stable-2.4').
 | 
						|
drno('refs/heads/stable-2.5').
 | 
						|
drno('refs/heads/stable-2.5').
 | 
						|
 | 
						|
submit_filter(In, Out) :-
 | 
						|
    gerrit:change_branch(Branch),
 | 
						|
    drno(Branch),
 | 
						|
    !,
 | 
						|
    In =.. [submit | I],
 | 
						|
    gerrit:max_with_block(-1, 1, 'DrNo', DrNo),
 | 
						|
    Out =.. [submit, DrNo | I].
 | 
						|
 | 
						|
submit_filter(In, Out) :- In = Out.
 | 
						|
----
 | 
						|
 | 
						|
=== Example 13: 1+1=2 Code-Review
 | 
						|
In this example we introduce accumulative voting to determine if a change is
 | 
						|
submittable or not. We modify the standard `Code-Review` to be accumulative, and
 | 
						|
make the change submittable if the total score is `2` or higher.
 | 
						|
 | 
						|
The code in this example is very similar to Example 8, with the addition of
 | 
						|
`findall/3` and `gerrit:remove_label`.
 | 
						|
 | 
						|
The `findall/3` embedded predicate is used to form a list of all objects that
 | 
						|
satisfy a specified Goal. In this example it is used to get a list of all the
 | 
						|
`Code-Review` scores. `gerrit:remove_label` is a built-in helper that is
 | 
						|
implemented similarly to the `remove_verified_category` as seen in the previous
 | 
						|
example.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
sum_list([], 0).
 | 
						|
sum_list([H | Rest], Sum) :- sum_list(Rest,Tmp), Sum is H + Tmp.
 | 
						|
 | 
						|
add_category_min_score(In, Category, Min,  P) :-
 | 
						|
    findall(X, gerrit:commit_label(label(Category,X),R),Z),
 | 
						|
    sum_list(Z, Sum),
 | 
						|
    Sum >= Min, !,
 | 
						|
    gerrit:commit_label(label(Category, V), U),
 | 
						|
    V >= 1,
 | 
						|
    !,
 | 
						|
    P = [label(Category,ok(U)) | In].
 | 
						|
 | 
						|
add_category_min_score(In, Category,Min,P) :-
 | 
						|
    P = [label(Category,need(Min)) | In].
 | 
						|
 | 
						|
submit_rule(S) :-
 | 
						|
    gerrit:default_submit(X),
 | 
						|
    X =.. [submit | Ls],
 | 
						|
    gerrit:remove_label(Ls,label('Code-Review',_),NoCR),
 | 
						|
    add_category_min_score(NoCR,'Code-Review', 2, Labels),
 | 
						|
    S =.. [submit | Labels].
 | 
						|
----
 | 
						|
 | 
						|
Implementing the same example without using `gerrit:default_submit`:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR, V)) :-
 | 
						|
    sum(2, 'Code-Review', CR),
 | 
						|
    gerrit:max_with_block(-1, 1, 'Verified', V).
 | 
						|
 | 
						|
% Sum the votes in a category. Uses a helper function score/2
 | 
						|
% to select out only the score values the given category.
 | 
						|
sum(VotesNeeded, Category, label(Category, ok(_))) :-
 | 
						|
    findall(Score, score(Category, Score), All),
 | 
						|
    sum_list(All, Sum),
 | 
						|
    Sum >= VotesNeeded,
 | 
						|
    !.
 | 
						|
sum(VotesNeeded, Category, label(Category, need(VotesNeeded))).
 | 
						|
 | 
						|
score(Category, Score) :-
 | 
						|
    gerrit:commit_label(label(Category, Score), User).
 | 
						|
 | 
						|
% Simple Prolog routine to sum a list of integers.
 | 
						|
sum_list(List, Sum)   :- sum_list(List, 0, Sum).
 | 
						|
sum_list([X|T], Y, S) :- Z is X + Y, sum_list(T, Z, S).
 | 
						|
sum_list([], S, S).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 14: Mentor and Mentee
 | 
						|
The mentor and mentee example allow you to specify a user (the `mentor`)
 | 
						|
that must approve all changes done by another user (the `mentee`).
 | 
						|
 | 
						|
The code first checks if the commit author is in the apprentice database.
 | 
						|
If the commit is done by a `mentee`, it will check if there is a `+2`
 | 
						|
review by the associated `mentor`.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
% mentor_mentee(Mentor, Mentee).
 | 
						|
% Extend this with appropriate user-id for your mentor/mentee setup.
 | 
						|
mentor_mentee(user(1000064), user(1000000)).
 | 
						|
 | 
						|
submit_rule(S) :-
 | 
						|
    gerrit:default_submit(In),
 | 
						|
    In =.. [submit | Ls],
 | 
						|
    add_mentee_mentor(Ls, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
 | 
						|
check_mentor_approval(S1, S2, Mentor) :-
 | 
						|
    gerrit:commit_label(label('Code-Review', 2), R),
 | 
						|
    R = Mentor, !,
 | 
						|
    S2 = [label('Mentor-Approval', ok(R)) | S1].
 | 
						|
check_mentor_approval(S1, [label('Mentor-Approval', need(_)) | S1], _).
 | 
						|
 | 
						|
add_mentee_mentor(S1, S2) :-
 | 
						|
    gerrit:commit_author(Id),
 | 
						|
    mentor_mentee(Mentor, Id),
 | 
						|
    !,
 | 
						|
    check_mentor_approval(S1, S2, Mentor).
 | 
						|
 | 
						|
add_mentee_mentor(S, S).
 | 
						|
----
 | 
						|
 | 
						|
=== Example 15: Make change submittable if all comments have been resolved
 | 
						|
In this example we will use the `unresolved_comments_count` fact about a
 | 
						|
change. Our goal is to block the submission of any change with some
 | 
						|
unresolved comments. Basically, it can be achieved by the following rules:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(R)) :-
 | 
						|
    gerrit:unresolved_comments_count(0),
 | 
						|
    !,
 | 
						|
    gerrit:uploader(U),
 | 
						|
    R = label('All-Comments-Resolved', ok(U)).
 | 
						|
 | 
						|
submit_rule(submit(R)) :-
 | 
						|
    gerrit:unresolved_comments_count(U),
 | 
						|
    U > 0,
 | 
						|
    R = label('All-Comments-Resolved', need(_)).
 | 
						|
----
 | 
						|
 | 
						|
Suppose currently a change is submittable if it gets `+2` for `Code-Review`
 | 
						|
and `+1` for `Verified`. It can be extended to support the above rules as
 | 
						|
follows:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR, V, R)) :-
 | 
						|
    base(CR, V),
 | 
						|
    gerrit:unresolved_comments_count(0),
 | 
						|
    !,
 | 
						|
    gerrit:uploader(U),
 | 
						|
    R = label('All-Comments-Resolved', ok(U)).
 | 
						|
 | 
						|
submit_rule(submit(CR, V, R)) :-
 | 
						|
    base(CR, V),
 | 
						|
    gerrit:unresolved_comments_count(U),
 | 
						|
    U > 0,
 | 
						|
    R = label('All-Comments-Resolved', need(_)).
 | 
						|
 | 
						|
base(CR, V) :-
 | 
						|
    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
 | 
						|
    gerrit:max_with_block(-1, 1, 'Verified', V).
 | 
						|
----
 | 
						|
 | 
						|
Note that a new label as `All-Comments-Resolved` should not be configured.
 | 
						|
It's only used to show `'Needs All-Comments-Resolved'` in the UI to clearly
 | 
						|
indicate to the user that all the comments have to be resolved for the
 | 
						|
change to become submittable.
 | 
						|
 | 
						|
=== Example 16: Make change submittable if it is a pure revert
 | 
						|
In this example we will use the `pure_revert` fact about a
 | 
						|
change. Our goal is to block the submission of any change that is not a
 | 
						|
pure revert. Basically, it can be achieved by the following rules:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(R)) :-
 | 
						|
    gerrit:pure_revert(1),
 | 
						|
    !,
 | 
						|
    gerrit:uploader(U),
 | 
						|
    R = label('Is-Pure-Revert', ok(U)).
 | 
						|
 | 
						|
submit_rule(submit(label('Is-Pure-Revert', need(_)))).
 | 
						|
----
 | 
						|
 | 
						|
Suppose currently a change is submittable if it gets `+2` for `Code-Review`
 | 
						|
and `+1` for `Verified`. It can be extended to support the above rules as
 | 
						|
follows:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(CR, V, R)) :-
 | 
						|
  base(CR, V),
 | 
						|
  set_pure_revert_label(R).
 | 
						|
 | 
						|
base(CR, V) :-
 | 
						|
  gerrit:max_with_block(-2, 2, 'Code-Review', CR),
 | 
						|
  gerrit:max_with_block(-1, 1, 'Verified', V).
 | 
						|
 | 
						|
set_pure_revert_label(R) :-
 | 
						|
  gerrit:pure_revert(1),
 | 
						|
  !,
 | 
						|
  gerrit:uploader(U),
 | 
						|
  R = label('Is-Pure-Revert', ok(U)).
 | 
						|
 | 
						|
set_pure_revert_label(label('Is-Pure-Revert', need(_))).
 | 
						|
----
 | 
						|
 | 
						|
Note that a new label as `Is-Pure-Revert` should not be configured.
 | 
						|
It's only used to show `'Needs Is-Pure-Revert'` in the UI to clearly
 | 
						|
indicate to the user that the change has to be a pure revert in order
 | 
						|
to become submittable.
 | 
						|
 | 
						|
=== Example 17: Make a change submittable if it doesn't include specific files
 | 
						|
 | 
						|
We can block any change which contains a submodule file change:
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_rule(submit(R)) :-
 | 
						|
  gerrit:includes_file(file(_,_,'SUBMODULE')),
 | 
						|
  !,
 | 
						|
  R = label('All-Submodules-Resolved', need(_)).
 | 
						|
submit_rule(submit(label('All-Submodules-Resolved', ok(A)))) :-
 | 
						|
  gerrit:commit_author(A).
 | 
						|
----
 | 
						|
 | 
						|
We can also block specific files, modification type, or file type,
 | 
						|
by changing include_files/1 to a different parameter. E.g,
 | 
						|
include_files('a.txt',_,_) includes any update to "a.txt", and
 | 
						|
('a.txt','D',_) includes any deletion to "a.txt". Also, (_,_,_) includes
 | 
						|
any file (other than magic file).
 | 
						|
 | 
						|
An inclusive list of possible arguments using the code above with variations
 | 
						|
of include_file:
 | 
						|
The first parameter is the file name.
 | 
						|
The second is the modification type ('A' for 'added', 'M' for 'modified',
 | 
						|
'D' for 'deleted', 'R' for 'renamed', 'C' for 'COPIED' and 'W' for 'rewrite').
 | 
						|
The third argument is the type of file, with the options being a submodule
 | 
						|
file 'SUBMODULE' and a non-submodule file being 'REGULAR'.
 | 
						|
 | 
						|
== Examples - Submit Type
 | 
						|
The following examples show how to implement own submit type rules.
 | 
						|
 | 
						|
=== Example 1: Set a `Cherry Pick` submit type for all changes
 | 
						|
This example sets the `Cherry Pick` submit type for all changes. It overrides
 | 
						|
whatever is set as project default submit type.
 | 
						|
 | 
						|
rules.pl
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_type(cherry_pick).
 | 
						|
----
 | 
						|
 | 
						|
[[SubmitTypePerBranch]]
 | 
						|
=== Example 2: `Fast Forward Only` for all `+refs/heads/stable*+` branches
 | 
						|
For all `+refs/heads/stable*+` branches we would like to enforce the `Fast
 | 
						|
Forward Only` submit type. A reason for this decision may be a need to never
 | 
						|
break the build in the stable branches.  For all other branches, those not
 | 
						|
matching the `+refs/heads/stable*+` pattern, we would like to use the project's
 | 
						|
default submit type as defined on the project settings page.
 | 
						|
 | 
						|
`rules.pl`
 | 
						|
[source,prolog]
 | 
						|
----
 | 
						|
submit_type(fast_forward_only) :-
 | 
						|
    gerrit:change_branch(B), regex_matches('refs/heads/stable.*', B),
 | 
						|
    !.
 | 
						|
submit_type(T) :- gerrit:project_default_submit_type(T).
 | 
						|
----
 | 
						|
 | 
						|
The first `submit_type` predicate defines the `Fast Forward Only` submit type
 | 
						|
for `+refs/heads/stable.*+` branches. The second `submit_type` predicate returns
 | 
						|
the project's default submit type.
 | 
						|
 | 
						|
GERRIT
 | 
						|
------
 | 
						|
Part of link:index.html[Gerrit Code Review]
 | 
						|
 | 
						|
SEARCHBOX
 | 
						|
---------
 |