summaryrefslogtreecommitdiffstats
path: root/Documentation/prolog-cookbook.txt
blob: 19ed98aa6f58975fad520c2b57c34050a7bae1b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
= 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] 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.

[[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] 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] 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.

[[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.

==== 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: 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`
[source,prolog]
----
% master_apprentice(Master, Apprentice).
% Extend this with appropriate user-id 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: 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(R)) :-
    gerrit:pure_revert(U),
    U /= 1,
    R = 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),
    gerrit:pure_revert(1),
    !,
    gerrit:uploader(U),
    R = label('Is-Pure-Revert', ok(U)).

submit_rule(submit(CR, V, R)) :-
    base(CR, V),
    gerrit:pure_revert(U),
    U /= 1,
    R = label('Is-Pure-Revert', 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 `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.

== 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
---------