This provides the entry point of the documentation search feature. Change-Id: Iea2b707995c0e042829db509927384dcec7aaf43
		
			
				
	
	
		
			1068 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1068 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
Gerrit Code Review - Prolog Submit Rules Cookbook
 | 
						|
=================================================
 | 
						|
 | 
						|
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.enabled=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] 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] introduces Prolog support in Gerrit.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
Prolog Language
 | 
						|
---------------
 | 
						|
This document is not a complete Prolog tutorial.
 | 
						|
link:http://en.wikipedia.org/wiki/Prolog[This Wikipedia page on Prolog] 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://code.google.com/p/prolog-cafe/[fork] of the
 | 
						|
original link:http://kaminari.istc.kobe-u.ac.jp/PrologCafe/[prolog-cafe]
 | 
						|
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.
 | 
						|
 | 
						|
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] environment. It
 | 
						|
provides a better shell interface and a graphical source-level debugger.
 | 
						|
 | 
						|
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))` or just `ok(_)` if user info is not important. 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 change to
 | 
						|
   become submittable
 | 
						|
* `reject(user(ID))` or just `reject(_)`. This status is used to tell that label/category
 | 
						|
   is blocking change submission
 | 
						|
* `impossible(_)` is used when the logic knows that the change cannot be submitted as-is.
 | 
						|
   Administrative intervention is probably required. This is meant for cases
 | 
						|
   where the logic requires members of "FooEng" to score "Code-Review +2" on a
 | 
						|
   change, but nobody is in group "FooEng". It is to hint at permissions
 | 
						|
   misconfigurations.
 | 
						|
* `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(_)))                               <1>
 | 
						|
  submit(label('Code-Review', ok(_)), label('Verified', reject(_))) <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(_))` 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 the similar top
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(W)) :-
 | 
						|
    W = label('Any-Label-Name', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(CR, V)) :-
 | 
						|
    CR = label('Code-Review', ok(_)),
 | 
						|
    V = label('Verified', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(R)) :-
 | 
						|
    R = label('Any-Label-Name', reject(_)).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  % 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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(N)) :-
 | 
						|
    N = label('Some-Condition', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(OK)) :-
 | 
						|
    OK = label('Another-Condition', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
The 'Need Some-Condition' will not be show in the UI because of the result of
 | 
						|
the second rule.
 | 
						|
 | 
						|
The same is valid if the two rules are swapped:
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(OK)) :-
 | 
						|
    OK = label('Another-Condition', ok(_)).
 | 
						|
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(_, 'John Doe', _),
 | 
						|
    Author = label('Author-is-John-Doe', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(_, _, 'john.doe@example.com'),
 | 
						|
    Author = label('Author-is-John-Doe', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
or by user id (assuming it is 1000000):
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(user(1000000), _, _),
 | 
						|
    Author = label('Author-is-John-Doe', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
or by a combination of these 3 attributes:
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    Author = label('Author-is-John-Doe', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(Author)) :-
 | 
						|
    gerrit:commit_author(_, 'John Doe', 'john.doe@example.com'),
 | 
						|
    Author = label('Author-is-John-Doe', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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 "),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(_)).
 | 
						|
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Fix)) :-
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', need(_)).
 | 
						|
 | 
						|
  submit_rule(submit(Fix)) :-
 | 
						|
    gerrit:commit_message_matches('^Fix '),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(_)).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(submit(Fix)) :-
 | 
						|
    gerrit:commit_message_matches('^Fix '),
 | 
						|
    Fix = label('Commit-Message-starts-with-Fix', ok(_)),
 | 
						|
    !.
 | 
						|
 | 
						|
  % 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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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:
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(X) :- gerrit:default_submit(X).
 | 
						|
====
 | 
						|
then this 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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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.
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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(_)), label('Verified', need(_)) ...)` into a list like `[submit,
 | 
						|
label('Code-Review', ok(_)), 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.
 | 
						|
 | 
						|
Don't use `gerrit:default_submit`
 | 
						|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
						|
Let's implement the same submit rule the other way, without reusing the
 | 
						|
`gerrit:default_submit`:
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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 7 and 8.
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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, !,
 | 
						|
    P = [label(Category,ok(R)) | 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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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: Master and apprentice
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
The master and apprentice example allow you to specify a user (the `master`)
 | 
						|
that must approve all changes done by another user (the `apprentice`).
 | 
						|
 | 
						|
The code first checks if the commit author is in the apprentice database.
 | 
						|
If the commit is done by an apprentice, it will check if there is a +2
 | 
						|
review by the associated `master`.
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  % master_apprentice(Master, Apprentice).
 | 
						|
  % Extend this with appropriate user-id's for your master/apprentice setup.
 | 
						|
  master_apprentice(user(1000064), user(1000000)).
 | 
						|
 | 
						|
  submit_rule(S) :-
 | 
						|
    gerrit:default_submit(In),
 | 
						|
    In =.. [submit | Ls],
 | 
						|
    add_apprentice_master(Ls, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
 | 
						|
  check_master_approval(S1, S2, Master) :-
 | 
						|
    gerrit:commit_label(label('Code-Review', 2), R),
 | 
						|
    R = Master, !,
 | 
						|
    S2 = [label('Master-Approval', ok(R)) | S1].
 | 
						|
  check_master_approval(S1, [label('Master-Approval', need(_)) | S1], _).
 | 
						|
 | 
						|
  add_apprentice_master(S1, S2) :-
 | 
						|
    gerrit:commit_author(Id),
 | 
						|
    master_apprentice(Master, Id),
 | 
						|
    !,
 | 
						|
    check_master_approval(S1, S2, Master).
 | 
						|
 | 
						|
  add_apprentice_master(S, S).
 | 
						|
====
 | 
						|
 | 
						|
Example 15: Only allow Author to submit change
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
This example adds a new needed category `Patchset-Author` for any user that is
 | 
						|
not the author of the patch. This effectively blocks all users except the author
 | 
						|
from submitting the change. This could result in an impossible situation if the
 | 
						|
author does not have permissions for submitting the change.
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_rule(S) :-
 | 
						|
    gerrit:default_submit(In),
 | 
						|
    In =.. [submit | Ls],
 | 
						|
    only_allow_author_to_submit(Ls, R),
 | 
						|
    S =.. [submit | R].
 | 
						|
 | 
						|
  only_allow_author_to_submit(S, S) :-
 | 
						|
    gerrit:commit_author(Id),
 | 
						|
    gerrit:current_user(Id),
 | 
						|
    !.
 | 
						|
 | 
						|
  only_allow_author_to_submit(S1, [label('Patchset-Author', need(_)) | S1]).
 | 
						|
====
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_type(cherry_pick).
 | 
						|
====
 | 
						|
 | 
						|
 | 
						|
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
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  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.
 | 
						|
 | 
						|
Example 3: Don't require `Fast Forward Only` if only documentation was changed
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
Like in the previous example we want the `Fast Forward Only` submit type for
 | 
						|
the `refs/heads/stable*` branches.  However, if only documentation was changed
 | 
						|
(only `*.txt` files), then we allow project's default submit type for such
 | 
						|
changes.
 | 
						|
 | 
						|
.rules.pl
 | 
						|
[caption=""]
 | 
						|
====
 | 
						|
  submit_type(fast_forward_only) :-
 | 
						|
    gerrit:commit_delta('(?<!\.txt)$'),
 | 
						|
    gerrit:change_branch(B), regex_matches('refs/heads/stable.*', B),
 | 
						|
    !.
 | 
						|
  submit_type(T) :- gerrit:project_default_submit_type(T)
 | 
						|
====
 | 
						|
 | 
						|
The `gerrit:commit_delta('(?<!\.txt)$')` succeeds if the change contains a file
 | 
						|
whose name doesn't end with `.txt` The rest of this rule is same like in the
 | 
						|
previous example.
 | 
						|
 | 
						|
If all file names in the change end with `.txt`, then the
 | 
						|
`gerrit:commit_delta('(?<!\.txt)$')` will fail as no file name will match this
 | 
						|
regular expression.
 | 
						|
 | 
						|
GERRIT
 | 
						|
------
 | 
						|
Part of link:index.html[Gerrit Code Review]
 | 
						|
 | 
						|
SEARCHBOX
 | 
						|
---------
 |