Merge branch 'stable-2.5'
* stable-2.5: NPE when strategy is cherry-pick and changeMerge.test enabled Fix owner column to link to same status Sort groups on the group list screen Documentation: Add link to test-submit-rule from the prolog cookbook. Do not log RepositoryNotFoundException if non-existing project is accessed Documentation: Add submit rule example on how to implement 1+1=2 Fix OutOfScope exception when auditing ssh auth failure. Fix a NPE caused by GerritCall.getMethod returning null. Add org.apache.mina as dependency to pom.xml Conflicts: gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java Change-Id: Id280ca6afebbe299e5841ddf8850184714f70de7
This commit is contained in:
commit
6738e5f340
@ -48,10 +48,8 @@ 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: It is currently *not possible* to test a Prolog program which implements
|
||||
Gerrit submit rules using the link:pgm-prolog-shell.html[prolog-shell] program.
|
||||
The reason is that the Prolog environment that exposes facts about a change
|
||||
requires a lot of Gerrit server environment to be loaded and running.
|
||||
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
|
||||
----------
|
||||
@ -249,6 +247,18 @@ 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.
|
||||
|
||||
[[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
|
||||
---------------------------------------------------------
|
||||
@ -638,6 +648,42 @@ we have to do that in the `rules.pl` of the `All-Projects` project.
|
||||
remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
|
||||
====
|
||||
|
||||
Example 12: 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].
|
||||
====
|
||||
|
||||
GERRIT
|
||||
------
|
||||
Part of link:index.html[Gerrit Code Review]
|
||||
|
@ -60,8 +60,11 @@ public class PageLinks {
|
||||
}
|
||||
|
||||
public static String toAccountQuery(final String fullname) {
|
||||
String query = op("owner", fullname) + " status:open";
|
||||
return toChangeQuery(query, TOP);
|
||||
return toAccountQuery(fullname, Status.NEW);
|
||||
}
|
||||
|
||||
public static String toAccountQuery(String fullname, Status status) {
|
||||
return toChangeQuery(op("owner", fullname) + " " + status(status), TOP);
|
||||
}
|
||||
|
||||
public static String toChangeQuery(final String query) {
|
||||
@ -73,17 +76,19 @@ public class PageLinks {
|
||||
}
|
||||
|
||||
public static String projectQuery(Project.NameKey proj, Status status) {
|
||||
return status(status) + " " + op("project", proj.get());
|
||||
}
|
||||
|
||||
private static String status(Status status) {
|
||||
switch (status) {
|
||||
case ABANDONED:
|
||||
return "status:abandoned " + op("project", proj.get());
|
||||
|
||||
return "status:abandoned";
|
||||
case MERGED:
|
||||
return "status:merged " + op("project", proj.get());
|
||||
|
||||
return "status:merged";
|
||||
case NEW:
|
||||
case SUBMITTED:
|
||||
default:
|
||||
return "status:open " + op("project", proj.get());
|
||||
return "status:open";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ public class ChangeTable2 extends NavigationTable<ChangeInfo> {
|
||||
}
|
||||
|
||||
table.setWidget(row, C_OWNER, new InlineHyperlink(owner,
|
||||
PageLinks.toAccountQuery(owner)));
|
||||
PageLinks.toAccountQuery(owner, c.status())));
|
||||
|
||||
table.setWidget(
|
||||
row, C_PROJECT, new ProjectLink(c.project_name_key(), c.status()));
|
||||
|
@ -125,13 +125,17 @@ final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall>
|
||||
private void audit() {
|
||||
try {
|
||||
GerritCall call = currentCall.get();
|
||||
Audit note = (Audit) call.getMethod().getAnnotation(Audit.class);
|
||||
MethodHandle method = call.getMethod();
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
Audit note = (Audit) method.getAnnotation(Audit.class);
|
||||
if (note != null) {
|
||||
final String sid = call.getWebSession().getToken();
|
||||
final CurrentUser username = call.getWebSession().getCurrentUser();
|
||||
final List<Object> args =
|
||||
extractParams(note, call);
|
||||
final String what = extractWhat(note, call.getMethod().getName());
|
||||
final String what = extractWhat(note, method.getName());
|
||||
final Object result = call.getResult();
|
||||
|
||||
audit.dispatch(new AuditEvent(sid, username, what, call.getWhen(), args,
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.common.data.GroupList;
|
||||
import com.google.gerrit.common.data.GroupReference;
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
@ -24,10 +26,10 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class VisibleGroups {
|
||||
|
||||
@ -65,8 +67,7 @@ public class VisibleGroups {
|
||||
|
||||
public GroupList get(final Collection<ProjectControl> projects)
|
||||
throws NoSuchGroupException {
|
||||
final Set<AccountGroup> groups =
|
||||
new TreeSet<AccountGroup>(new GroupComparator());
|
||||
Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
|
||||
for (final ProjectControl projectControl : projects) {
|
||||
final Set<GroupReference> groupsRefs = projectControl.getAllGroups();
|
||||
for (final GroupReference groupRef : groupsRefs) {
|
||||
@ -74,10 +75,10 @@ public class VisibleGroups {
|
||||
if (group == null) {
|
||||
throw new NoSuchGroupException(groupRef.getUUID());
|
||||
}
|
||||
groups.add(group);
|
||||
groups.put(group.getGroupUUID(), group);
|
||||
}
|
||||
}
|
||||
return createGroupList(filterGroups(groups));
|
||||
return createGroupList(filterGroups(groups.values()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,17 +90,15 @@ public class VisibleGroups {
|
||||
public GroupList get(final IdentifiedUser user) throws NoSuchGroupException {
|
||||
if (identifiedUser.get().getAccountId().equals(user.getAccountId())
|
||||
|| identifiedUser.get().getCapabilities().canAdministrateServer()) {
|
||||
final Set<AccountGroup.UUID> effective =
|
||||
user.getEffectiveGroups().getKnownGroups();
|
||||
final Set<AccountGroup> groups =
|
||||
new TreeSet<AccountGroup>(new GroupComparator());
|
||||
for (final AccountGroup.UUID groupId : effective) {
|
||||
Set<AccountGroup.UUID> mine = user.getEffectiveGroups().getKnownGroups();
|
||||
Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap();
|
||||
for (final AccountGroup.UUID groupId : mine) {
|
||||
AccountGroup group = groupCache.get(groupId);
|
||||
if (group != null) {
|
||||
groups.add(group);
|
||||
groups.put(groupId, group);
|
||||
}
|
||||
}
|
||||
return createGroupList(filterGroups(groups));
|
||||
return createGroupList(filterGroups(groups.values()));
|
||||
} else {
|
||||
throw new NoSuchGroupException("Groups of user '" + user.getAccountId()
|
||||
+ "' are not visible.");
|
||||
@ -107,7 +106,7 @@ public class VisibleGroups {
|
||||
}
|
||||
|
||||
private List<AccountGroup> filterGroups(final Iterable<AccountGroup> groups) {
|
||||
final List<AccountGroup> filteredGroups = new LinkedList<AccountGroup>();
|
||||
final List<AccountGroup> filteredGroups = Lists.newArrayList();
|
||||
final boolean isAdmin =
|
||||
identifiedUser.get().getCapabilities().canAdministrateServer();
|
||||
for (final AccountGroup group : groups) {
|
||||
@ -123,6 +122,7 @@ public class VisibleGroups {
|
||||
}
|
||||
filteredGroups.add(group);
|
||||
}
|
||||
Collections.sort(filteredGroups, new GroupComparator());
|
||||
return filteredGroups;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -116,7 +117,9 @@ public class ProjectCacheImpl implements ProjectCache {
|
||||
}
|
||||
return state;
|
||||
} catch (ExecutionException e) {
|
||||
log.warn(String.format("Cannot read project %s", projectName.get()), e);
|
||||
if (!(e.getCause() instanceof RepositoryNotFoundException)) {
|
||||
log.warn(String.format("Cannot read project %s", projectName.get()), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,11 @@ limitations under the License.
|
||||
<artifactId>org.eclipse.jgit.junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.mina</groupId>
|
||||
<artifactId>mina-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
|
@ -101,7 +101,7 @@ class SshLog implements LifecycleListener {
|
||||
|
||||
void onLogin() {
|
||||
async.append(log("LOGIN FROM " + session.get().getRemoteAddressAsString()));
|
||||
audit("0", "LOGIN", new String[] {});
|
||||
audit(context.get(), "0", "LOGIN", new String[] {});
|
||||
}
|
||||
|
||||
void onAuthFail(final SshSession sd) {
|
||||
@ -127,7 +127,7 @@ class SshLog implements LifecycleListener {
|
||||
}
|
||||
|
||||
async.append(event);
|
||||
audit("FAIL", "AUTH", new String[] {sd.getRemoteAddressAsString()});
|
||||
audit(null, "FAIL", "AUTH", new String[] {sd.getRemoteAddressAsString()});
|
||||
}
|
||||
|
||||
void onExecute(int exitValue) {
|
||||
@ -165,7 +165,8 @@ class SshLog implements LifecycleListener {
|
||||
event.setProperty(P_STATUS, status);
|
||||
|
||||
async.append(event);
|
||||
audit(status, getCommand(commandLine), CommandFactoryProvider.split(commandLine));
|
||||
audit(context.get(), status, getCommand(commandLine),
|
||||
CommandFactoryProvider.split(commandLine));
|
||||
}
|
||||
|
||||
private String getCommand(String commandLine) {
|
||||
@ -176,7 +177,7 @@ class SshLog implements LifecycleListener {
|
||||
|
||||
void onLogout() {
|
||||
async.append(log("LOGOUT"));
|
||||
audit("0", "LOGOUT", new String[] {});
|
||||
audit(context.get(), "0", "LOGOUT", new String[] {});
|
||||
}
|
||||
|
||||
private LoggingEvent log(final String msg) {
|
||||
@ -415,8 +416,7 @@ class SshLog implements LifecycleListener {
|
||||
}
|
||||
}
|
||||
|
||||
void audit(Object result, String commandName, String[] args) {
|
||||
final Context ctx = context.get();
|
||||
void audit(Context ctx, Object result, String commandName, String[] args) {
|
||||
final String sid = extractSessionId(ctx);
|
||||
final long created = extractCreated(ctx);
|
||||
final String what = extractWhat(commandName, args);
|
||||
|
6
pom.xml
6
pom.xml
@ -559,6 +559,12 @@ limitations under the License.
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.mina</groupId>
|
||||
<artifactId>mina-core</artifactId>
|
||||
<version>2.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
|
Loading…
Reference in New Issue
Block a user