| %% Copyright (C) 2011 The Android Open Source Project |
| %% |
| %% Licensed under the Apache License, Version 2.0 (the "License"); |
| %% you may not use this file except in compliance with the License. |
| %% You may obtain a copy of the License at |
| %% |
| %% http://www.apache.org/licenses/LICENSE-2.0 |
| %% |
| %% Unless required by applicable law or agreed to in writing, software |
| %% distributed under the License is distributed on an "AS IS" BASIS, |
| %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| %% See the License for the specific language governing permissions and |
| %% limitations under the License. |
| |
| :- package gerrit. |
| '$init' :- init. |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% init: |
| %% |
| %% Initialize the module's private state. These typically take the form of global |
| %% aliased hashes carrying "constant" data about the current change for any |
| %% predicate that needs to obtain it. |
| %% |
| init :- |
| define_hash(commit_labels). |
| |
| define_hash(A) :- hash_exists(A), !, hash_clear(A). |
| define_hash(A) :- atom(A), !, new_hash(_, [alias(A)]). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% commit_label/2: |
| %% |
| %% During rule evaluation of a change, this predicate is defined to |
| %% be a table of labels that pertain to the commit of interest. |
| %% |
| %% commit_label( label('Code-Review', 2), user(12345789) ). |
| %% commit_label( label('Verified', -1), user(8181) ). |
| %% |
| :- public commit_label/2. |
| %% |
| commit_label(L, User) :- L = label(H, _), |
| atom(H), |
| !, |
| hash_get(commit_labels, H, Cached), |
| ( [] == Cached -> |
| get_commit_labels(_), |
| hash_get(commit_labels, H, Rs), ! |
| ; |
| Rs = Cached |
| ), |
| scan_commit_labels(Rs, L, User) |
| . |
| commit_label(Label, User) :- |
| get_commit_labels(Rs), |
| scan_commit_labels(Rs, Label, User). |
| |
| scan_commit_labels([R | Rs], L, U) :- R = commit_label(L, U). |
| scan_commit_labels([_ | Rs], L, U) :- scan_commit_labels(Rs, L, U). |
| scan_commit_labels([], _, _) :- fail. |
| |
| get_commit_labels(Rs) :- |
| hash_contains_key(commit_labels, '$all'), |
| !, |
| hash_get(commit_labels, '$all', Rs) |
| . |
| get_commit_labels(Rs) :- |
| '_load_commit_labels'(Rs), |
| set_commit_labels(Rs). |
| |
| set_commit_labels(Rs) :- |
| define_hash(commit_labels), |
| hash_put(commit_labels, '$all', Rs), |
| index_commit_labels(Rs). |
| |
| index_commit_labels([]). |
| index_commit_labels([R | Rs]) :- |
| R = commit_label(label(H, _), _), |
| atom(H), |
| !, |
| hash_get(commit_labels, H, Tmp), |
| hash_put(commit_labels, H, [R | Tmp]), |
| index_commit_labels(Rs) |
| . |
| index_commit_labels([_ | Rs]) :- |
| index_commit_labels(Rs). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% check_user_label/3: |
| %% |
| %% Check Who can set Label to Val. |
| %% |
| check_user_label(Label, Who, Val) :- |
| hash_get(commit_labels, '$fast_range', true), !, |
| atom(Label), |
| assume_range_from_label(Label, Who, Min, Max), |
| Min @=< Val, Val @=< Max. |
| check_user_label(Label, Who, Val) :- |
| Who = user(_), !, |
| atom(Label), |
| current_user(Who, User), |
| '_check_user_label'(Label, User, Val). |
| check_user_label(Label, test_user(Name), Val) :- |
| clause(user:test_grant(Label, test_user(Name), range(Min, Max)), _), |
| Min @=< Val, Val @=< Max |
| . |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% user_label_range/4: |
| %% |
| %% Lookup the range allowed to be used. |
| %% |
| user_label_range(Label, Who, Min, Max) :- |
| hash_get(commit_labels, '$fast_range', true), !, |
| atom(Label), |
| assume_range_from_label(Label, Who, Min, Max). |
| user_label_range(Label, Who, Min, Max) :- |
| Who = user(_), !, |
| atom(Label), |
| current_user(Who, User), |
| '_user_label_range'(Label, User, Min, Max). |
| user_label_range(Label, test_user(Name), Min, Max) :- |
| clause(user:test_grant(Label, test_user(Name), range(Min, Max)), _) |
| . |
| |
| assume_range_from_label :- |
| hash_put(commit_labels, '$fast_range', true). |
| |
| assume_range_from_label(Label, Who, Min, Max) :- |
| commit_label(label(Label, Value), Who), !, |
| Min = Value, Max = Value. |
| assume_range_from_label(_, _, 0, 0). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% not_same/2: |
| %% |
| :- public not_same/2. |
| %% |
| not_same(ok(A), ok(B)) :- !, A \= B. |
| not_same(label(_, ok(A)), label(_, ok(B))) :- !, A \= B. |
| not_same(_, _). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% can_submit/2: |
| %% |
| %% Executes the SubmitRule for each solution until one where all of the |
| %% states has the format label(_, ok(_)) is found, then cut away any |
| %% remaining choice points leaving this as the last solution. |
| %% |
| :- public can_submit/2. |
| %% |
| can_submit(SubmitRule, S) :- |
| call_rule(SubmitRule, Tmp), |
| Tmp =.. [submit | Ls], |
| ( is_all_ok(Ls) -> S = ok(Tmp), ! ; S = not_ready(Tmp) ). |
| |
| call_rule(P:X, Arg) :- !, F =.. [X, Arg], P:F. |
| call_rule(X, Arg) :- !, F =.. [X, Arg], F. |
| |
| is_all_ok([]). |
| is_all_ok([label(_, ok(__)) | Ls]) :- is_all_ok(Ls). |
| is_all_ok([label(_, may(__)) | Ls]) :- is_all_ok(Ls). |
| is_all_ok(_) :- fail. |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% locate_helper |
| %% |
| %% Returns user:Func if it exists otherwise returns gerrit:Default |
| |
| locate_helper(Func, Default, Arity, user:Func) :- |
| '$compiled_predicate'(user, Func, Arity), !. |
| locate_helper(Func, Default, Arity, user:Func) :- |
| listN(Arity, P), C =.. [Func | P], clause(user:C, _), !. |
| locate_helper(Func, Default, _, gerrit:Default). |
| |
| listN(0, []). |
| listN(N, [_|T]) :- N > 0, N1 is N - 1, listN(N1, T). |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% locate_submit_rule/1: |
| %% |
| %% Finds a submit_rule depending on what rules are available. |
| %% If none are available, use default_submit/1. |
| %% |
| :- public locate_submit_rule/1. |
| %% |
| |
| locate_submit_rule(RuleName) :- |
| locate_helper(submit_rule, default_submit, 1, RuleName). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% get_submit_type/2: |
| %% |
| %% Executes the SubmitTypeRule and return the first solution |
| %% |
| :- public get_submit_type/2. |
| %% |
| get_submit_type(SubmitTypeRule, A) :- |
| call_rule(SubmitTypeRule, A), !. |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% locate_submit_type/1: |
| %% |
| %% Finds a submit_type_rule depending on what rules are available. |
| %% If none are available, use project_default_submit_type/1. |
| %% |
| :- public locate_submit_type/1. |
| %% |
| locate_submit_type(RuleName) :- |
| locate_helper(submit_type, project_default_submit_type, 1, RuleName). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% default_submit/1: |
| %% |
| :- public default_submit/1. |
| %% |
| default_submit(P) :- |
| get_legacy_label_types(LabelTypes), |
| default_submit(LabelTypes, P). |
| |
| % Apply the old "all approval categories must be satisfied" |
| % loop by scanning over all of the label types to build up the |
| % submit record. |
| % |
| default_submit(LabelTypes, P) :- |
| default_submit(LabelTypes, [], Tmp), |
| reverse(Tmp, Ls), |
| P =.. [ submit | Ls]. |
| |
| default_submit([], Out, Out). |
| default_submit([Type | Types], Tmp, Out) :- |
| label_type(Label, Fun, Min, Max) = Type, |
| legacy_submit_rule(Fun, Label, Min, Max, Status), |
| R = label(Label, Status), |
| default_submit(Types, [R | Tmp], Out). |
| |
| |
| %% legacy_submit_rule: |
| %% |
| %% Apply the old -2..+2 style logic. |
| %% |
| legacy_submit_rule('MaxWithBlock', Label, Min, Max, T) :- !, max_with_block(Label, Min, Max, T). |
| legacy_submit_rule('AnyWithBlock', Label, Min, Max, T) :- !, any_with_block(Label, Min, T). |
| legacy_submit_rule('MaxNoBlock', Label, Min, Max, T) :- !, max_no_block(Label, Max, T). |
| legacy_submit_rule('NoBlock', Label, Min, Max, T) :- !, T = may(_). |
| legacy_submit_rule('NoOp', Label, Min, Max, T) :- !, T = may(_). |
| legacy_submit_rule('PatchSetLock', Label, Min, Max, T) :- !, T = may(_). |
| legacy_submit_rule(Fun, Label, Min, Max, T) :- T = impossible(unsupported(Fun)). |
| |
| %% max_with_block: |
| %% |
| %% - The minimum is never used. |
| %% - At least one maximum is used. |
| %% |
| :- public max_with_block/4. |
| %% |
| max_with_block(Min, Max, Label, label(Label, S)) :- |
| number(Min), number(Max), atom(Label), |
| !, |
| max_with_block(Label, Min, Max, S). |
| max_with_block(Label, Min, Max, reject(Who)) :- |
| check_label_range_permission(Label, Min, ok(Who)), |
| ! |
| . |
| max_with_block(Label, Min, Max, ok(Who)) :- |
| \+ check_label_range_permission(Label, Min, ok(_)), |
| check_label_range_permission(Label, Max, ok(Who)), |
| ! |
| . |
| max_with_block(Label, Min, Max, need(Max)) :- |
| true |
| . |
| |
| %TODO Uncomment this clause when group suggesting is possible. |
| %max_with_block(Label, Min, Max, need(Max, Group)) :- |
| % \+ check_label_range_permission(Label, Max, ok(_)), |
| % check_label_range_permission(Label, Max, ask(Group)) |
| % . |
| %max_with_block(Label, Min, Max, impossible(no_access)) :- |
| % \+ check_label_range_permission(Label, Max, ask(Group)) |
| % . |
| |
| %% any_with_block: |
| %% |
| %% - The maximum is never used. |
| %% |
| any_with_block(Label, Min, reject(Who)) :- |
| Min < 0, |
| check_label_range_permission(Label, Min, ok(Who)), |
| ! |
| . |
| any_with_block(Label, Min, may(_)). |
| |
| |
| %% max_no_block: |
| %% |
| %% - At least one maximum is used. |
| %% |
| max_no_block(Max, Label, label(Label, S)) :- |
| number(Max), atom(Label), |
| !, |
| max_no_block(Label, Max, S). |
| max_no_block(Label, Max, ok(Who)) :- |
| check_label_range_permission(Label, Max, ok(Who)), |
| ! |
| . |
| max_no_block(Label, Max, need(Max)) :- |
| true |
| . |
| %TODO Uncomment this clause when group suggesting is possible. |
| %max_no_block(Label, Max, need(Max, Group)) :- |
| % check_label_range_permission(Label, Max, ask(Group)) |
| % . |
| %max_no_block(Label, Max, impossible(no_access)) :- |
| % \+ check_label_range_permission(Label, Max, ask(Group)) |
| % . |
| |
| |
| %% check_label_range_permission: |
| %% |
| check_label_range_permission(Label, ExpValue, ok(Who)) :- |
| commit_label(label(Label, ExpValue), Who), |
| check_user_label(Label, Who, ExpValue) |
| . |
| %TODO Uncomment this clause when group suggesting is possible. |
| %check_label_range_permission(Label, ExpValue, ask(Group)) :- |
| % grant_range(Label, Group, Min, Max), |
| % Min @=< ExpValue, ExpValue @=< Max |
| % . |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% filter_submit_results/3: |
| %% |
| %% Executes the submit_filter against the given list of results, |
| %% returns a list of filtered results. |
| %% |
| :- public filter_submit_results/3. |
| %% |
| filter_submit_results(Filter, In, Out) :- |
| filter_submit_results(Filter, In, [], Tmp), |
| reverse(Tmp, Out). |
| filter_submit_results(Filter, [I | In], Tmp, Out) :- |
| arg(1, I, R), |
| call_submit_filter(Filter, R, S), |
| !, |
| S =.. [submit | Ls], |
| ( is_all_ok(Ls) -> T = ok(S) ; T = not_ready(S) ), |
| filter_submit_results(Filter, In, [T | Tmp], Out). |
| filter_submit_results(Filter, [_ | In], Tmp, Out) :- |
| filter_submit_results(Filter, In, Tmp, Out), |
| ! |
| . |
| filter_submit_results(Filter, [], Out, Out). |
| |
| call_submit_filter(P:X, R, S) :- !, F =.. [X, R, S], P:F. |
| call_submit_filter(X, R, S) :- F =.. [X, R, S], F. |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% filter_submit_type_results/3: |
| %% |
| %% Executes the submit_type_filter against the result, |
| %% returns the filtered result. |
| %% |
| :- public filter_submit_type_results/3. |
| %% |
| filter_submit_type_results(Filter, In, Out) :- call_submit_filter(Filter, In, Out). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% locate_submit_filter/1: |
| %% |
| %% Finds a submit_filter if available. |
| %% |
| :- public locate_submit_filter/1. |
| %% |
| locate_submit_filter(FilterName) :- |
| locate_helper(submit_filter, noop_filter, 2, FilterName). |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% noop_filter/2: |
| %% |
| :- public noop_filter/2. |
| %% |
| noop_filter(In, In). |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% locate_submit_type_filter/1: |
| %% |
| %% Finds a submit_type_filter if available. |
| %% |
| :- public locate_submit_type_filter/1. |
| %% |
| locate_submit_type_filter(FilterName) :- |
| locate_helper(submit_type_filter, noop_filter, 2, FilterName). |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% find_label/3: |
| %% |
| %% Finds labels successively and fails when there are no more results. |
| %% |
| :- public find_label/3. |
| %% |
| find_label([], _, _) :- !, fail. |
| find_label(List, Name, Label) :- |
| List = [_ | _], |
| !, |
| find_label2(List, Name, Label). |
| find_label(S, Name, Label) :- |
| S =.. [submit | Ls], |
| find_label2(Ls, Name, Label). |
| |
| find_label2([L | _ ], Name, L) :- L = label(Name, _). |
| find_label2([_ | Ls], Name, L) :- find_label2(Ls, Name, L). |
| |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| %% |
| %% remove_label/3: |
| %% |
| %% Removes all occurances of label(Name, Status). |
| %% |
| :- public remove_label/3. |
| %% |
| remove_label([], _, []) :- !. |
| remove_label(List, Label, Out) :- |
| List = [_ | _], |
| !, |
| subtract1(List, Label, Out). |
| remove_label(S, Label, Out) :- |
| S =.. [submit | Ls], |
| subtract1(Ls, Label, Tmp), |
| Out =.. [submit | Tmp]. |
| |
| subtract1([], _, []) :- !. |
| subtract1([E | L], E, R) :- !, subtract1(L, E, R). |
| subtract1([H | L], E, [H | R]) :- subtract1(L, E, R). |
| |
| |
| %% commit_author/1: |
| %% |
| :- public commit_author/1. |
| %% |
| commit_author(Author) :- |
| commit_author(Author, _, _). |
| |
| |
| %% commit_committer/1: |
| %% |
| :- public commit_committer/1. |
| %% |
| commit_committer(Committer) :- |
| commit_committer(Committer, _, _). |
| |
| |
| %% commit_delta/1: |
| %% |
| :- public commit_delta/1. |
| %% |
| commit_delta(Regex) :- |
| once(commit_delta(Regex, _, _, _)). |
| |
| |
| %% commit_delta/3: |
| %% |
| :- public commit_delta/3. |
| %% |
| commit_delta(Regex, Type, Path) :- |
| commit_delta(Regex, TmpType, NewPath, OldPath), |
| split_commit_delta(TmpType, NewPath, OldPath, Type, Path). |
| |
| split_commit_delta(rename, NewPath, OldPath, delete, OldPath). |
| split_commit_delta(rename, NewPath, OldPath, add, NewPath) :- !. |
| split_commit_delta(copy, NewPath, OldPath, add, NewPath) :- !. |
| split_commit_delta(Type, Path, _, Type, Path). |
| |
| |
| %% commit_message_matches/1: |
| %% |
| :- public commit_message_matches/1. |
| %% |
| commit_message_matches(Pattern) :- |
| commit_message(Msg), |
| regex_matches(Pattern, Msg). |