Merge "Add ssh command "test-submit-rule""
This commit is contained in:
@@ -150,6 +150,9 @@ link:cmd-plugin-remove.html[gerrit plugin remove]::
|
|||||||
link:cmd-plugin-remove.html[gerrit plugin rm]::
|
link:cmd-plugin-remove.html[gerrit plugin rm]::
|
||||||
Alias for 'gerrit plugin remove'.
|
Alias for 'gerrit plugin remove'.
|
||||||
|
|
||||||
|
link:cmd-test-submit-rule.html[gerrit test-submit-rule]::
|
||||||
|
Test prolog submit rules.
|
||||||
|
|
||||||
link:cmd-kill.html[kill]::
|
link:cmd-kill.html[kill]::
|
||||||
Kills a scheduled or running task.
|
Kills a scheduled or running task.
|
||||||
|
|
||||||
|
|||||||
93
Documentation/cmd-test-submit-rule.txt
Normal file
93
Documentation/cmd-test-submit-rule.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
gerrit test-submit-rule
|
||||||
|
=======================
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
gerrit test-submit-rule - Test prolog submit rules with a chosen changeset.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
[verse]
|
||||||
|
'ssh' -p <port> <host> 'gerrit test-submit-rule'
|
||||||
|
[-s]
|
||||||
|
[--no-filters]
|
||||||
|
[--format {TEXT | JSON}]
|
||||||
|
CHANGE
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
Provides a way to test prolog link:prolog-cookbook.html[submit rules].
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
-s::
|
||||||
|
Reads a rules.pl file from stdin instead of rules.pl in refs/meta/config.
|
||||||
|
|
||||||
|
--no-filters::
|
||||||
|
Don't run the submit_filter/2 from the parent projects of the specified change.
|
||||||
|
|
||||||
|
--format::
|
||||||
|
What output format to display the results in.
|
||||||
|
+
|
||||||
|
--
|
||||||
|
`text`:: Simple text based format.
|
||||||
|
`json`:: A JSON object described in link:json.html#submitRecord[submit record].
|
||||||
|
`json_compact`:: Minimized JSON output.
|
||||||
|
--
|
||||||
|
|
||||||
|
ACCESS
|
||||||
|
------
|
||||||
|
Can be used by anyone that has permission to read the specified changeset.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
--------
|
||||||
|
|
||||||
|
|
||||||
|
Test submit_rule from stdin.
|
||||||
|
====
|
||||||
|
$ cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule -s I78f2c6673db24e4e92ed32f604c960dc952437d9
|
||||||
|
Non-Author-Code-Review: NOT_READY
|
||||||
|
Verified: NOT_READY
|
||||||
|
Code-Review: NOT_READY by Anonymous Coward <test@email.com>
|
||||||
|
|
||||||
|
NOT_READY
|
||||||
|
====
|
||||||
|
|
||||||
|
Test submit_rule from stdin and return the results as JSON.
|
||||||
|
====
|
||||||
|
cat non-author-codereview.pl | ssh -p 29418 review.example.com gerrit test-submit-rule --format=JSON -s I78f2c6673db24e4e92ed32f604c960dc952437d9
|
||||||
|
{
|
||||||
|
"approvals": [
|
||||||
|
{
|
||||||
|
"type": "Verified",
|
||||||
|
"value": "NEED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Code-Review",
|
||||||
|
"value": "OK",
|
||||||
|
"by": {
|
||||||
|
"email": "test@email.com",
|
||||||
|
"username": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"value": "NOT_READY"
|
||||||
|
}
|
||||||
|
====
|
||||||
|
|
||||||
|
Test the active submit_rule from the refs/meta/config branch, ignoring filters in the project parents.
|
||||||
|
====
|
||||||
|
$ ssh -p 29418 review.example.com gerrit test-submit-rule I78f2c6673db24e4e92ed32f604c960dc952437d9 --no-filters
|
||||||
|
Verified: NOT_READY
|
||||||
|
Code-Review: NOT_READY by Anonymous Coward <test@email.com>
|
||||||
|
|
||||||
|
NOT_READY
|
||||||
|
====
|
||||||
|
|
||||||
|
SCRIPTING
|
||||||
|
---------
|
||||||
|
Can be used either interactively for testing new prolog submit rules, or from a script to check the submit status of a change.
|
||||||
|
|
||||||
|
GERRIT
|
||||||
|
------
|
||||||
|
Part of link:index.html[Gerrit Code Review]
|
||||||
@@ -571,7 +571,7 @@ public class ChangeControl {
|
|||||||
&& who.arg(0).isInteger();
|
&& who.arg(0).isInteger();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Term toListTerm(List<Term> terms) {
|
public static Term toListTerm(List<Term> terms) {
|
||||||
Term list = Prolog.Nil;
|
Term list = Prolog.Nil;
|
||||||
for (int i = terms.size() - 1; i >= 0; i--) {
|
for (int i = terms.size() - 1; i >= 0; i--) {
|
||||||
list = new ListTerm(terms.get(i), list);
|
list = new ListTerm(terms.get(i), list);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class MasterCommandModule extends CommandModule {
|
|||||||
command(gerrit, "rename-group").to(RenameGroupCommand.class);
|
command(gerrit, "rename-group").to(RenameGroupCommand.class);
|
||||||
command(gerrit, "create-project").to(CreateProjectCommand.class);
|
command(gerrit, "create-project").to(CreateProjectCommand.class);
|
||||||
command(gerrit, "gsql").to(AdminQueryShell.class);
|
command(gerrit, "gsql").to(AdminQueryShell.class);
|
||||||
|
command(gerrit, "test-submit-rule").to(TestSubmitRule.class);
|
||||||
command(gerrit, "set-reviewers").to(SetReviewersCommand.class);
|
command(gerrit, "set-reviewers").to(SetReviewersCommand.class);
|
||||||
command(gerrit, "receive-pack").to(Receive.class);
|
command(gerrit, "receive-pack").to(Receive.class);
|
||||||
command(gerrit, "set-project-parent").to(AdminSetParent.class);
|
command(gerrit, "set-project-parent").to(AdminSetParent.class);
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
// Copyright (C) 2012 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 com.google.gerrit.sshd.commands;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.data.AccountInfo;
|
||||||
|
import com.google.gerrit.common.data.SubmitRecord;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gerrit.rules.PrologEnvironment;
|
||||||
|
import com.google.gerrit.rules.StoredValues;
|
||||||
|
import com.google.gerrit.server.OutputFormat;
|
||||||
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
|
import com.google.gerrit.server.events.AccountAttribute;
|
||||||
|
import com.google.gerrit.server.events.SubmitLabelAttribute;
|
||||||
|
import com.google.gerrit.server.events.SubmitRecordAttribute;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
|
import com.google.gerrit.sshd.SshCommand;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
import com.googlecode.prolog_cafe.compiler.CompileException;
|
||||||
|
import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
|
||||||
|
import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
|
||||||
|
import com.googlecode.prolog_cafe.lang.ListTerm;
|
||||||
|
import com.googlecode.prolog_cafe.lang.Prolog;
|
||||||
|
import com.googlecode.prolog_cafe.lang.PrologClassLoader;
|
||||||
|
import com.googlecode.prolog_cafe.lang.PrologException;
|
||||||
|
import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
|
||||||
|
import com.googlecode.prolog_cafe.lang.SymbolTerm;
|
||||||
|
import com.googlecode.prolog_cafe.lang.Term;
|
||||||
|
import com.googlecode.prolog_cafe.lang.VariableTerm;
|
||||||
|
|
||||||
|
import org.kohsuke.args4j.Argument;
|
||||||
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Command that allows testing of prolog submit-rules in a live instance. */
|
||||||
|
final class TestSubmitRule extends SshCommand {
|
||||||
|
@Inject
|
||||||
|
private ReviewDb db;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PrologEnvironment.Factory envFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ChangeControl.Factory ccFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AccountCache accountCache;
|
||||||
|
|
||||||
|
final @AnonymousCowardName String anonymousCowardName;
|
||||||
|
|
||||||
|
@Argument(index = 0, required = true, usage = "ChangeId to load in prolog environment")
|
||||||
|
private String changeId;
|
||||||
|
|
||||||
|
@Option(name = "-s",
|
||||||
|
usage = "Read prolog script from stdin instead of reading rules.pl from the refs/meta/config branch")
|
||||||
|
private boolean useStdin;
|
||||||
|
|
||||||
|
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
|
||||||
|
private OutputFormat format = OutputFormat.TEXT;
|
||||||
|
|
||||||
|
@Option(name = "--no-filters", aliases = {"-n"},
|
||||||
|
usage = "Don't run the submit_filter/2 from the parent projects")
|
||||||
|
private boolean skipSubmitFilters;
|
||||||
|
|
||||||
|
private static final String[] PACKAGE_LIST = {Prolog.BUILTIN, "gerrit"};
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TestSubmitRule(@AnonymousCowardName String anonymous) {
|
||||||
|
anonymousCowardName = anonymous;
|
||||||
|
}
|
||||||
|
private PrologMachineCopy newMachine() {
|
||||||
|
BufferingPrologControl ctl = new BufferingPrologControl();
|
||||||
|
ctl.setMaxDatabaseSize(16 * 1024);
|
||||||
|
ctl.setPrologClassLoader(new PrologClassLoader(getClass().getClassLoader()));
|
||||||
|
return PrologMachineCopy.save(ctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws UnloggedFailure {
|
||||||
|
InputStreamReader inReader = new InputStreamReader(in);
|
||||||
|
|
||||||
|
try {
|
||||||
|
PrologEnvironment pcl;
|
||||||
|
|
||||||
|
List<Change> changeList =
|
||||||
|
db.changes().byKey(new Change.Key(changeId)).toList();
|
||||||
|
if (changeList.size() != 1)
|
||||||
|
throw new UnloggedFailure(1, "Invalid ChangeId");
|
||||||
|
|
||||||
|
Change c = changeList.get(0);
|
||||||
|
PatchSet ps = db.patchSets().get(c.currentPatchSetId());
|
||||||
|
// Will throw exception if current user can not access this change, and
|
||||||
|
// thus will leak information that a change-id is valid even though the
|
||||||
|
// user are not allowed to see the change.
|
||||||
|
// See http://code.google.com/p/gerrit/issues/detail?id=1586
|
||||||
|
ChangeControl cc = ccFactory.controlFor(c);
|
||||||
|
ProjectState projectState = cc.getProjectControl().getProjectState();
|
||||||
|
|
||||||
|
if (useStdin) {
|
||||||
|
pcl = envFactory.create(newMachine());
|
||||||
|
} else {
|
||||||
|
pcl = projectState.newPrologEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
pcl.set(StoredValues.REVIEW_DB, db);
|
||||||
|
pcl.set(StoredValues.CHANGE, c);
|
||||||
|
pcl.set(StoredValues.PATCH_SET, ps);
|
||||||
|
pcl.set(StoredValues.CHANGE_CONTROL, cc);
|
||||||
|
if (useStdin) {
|
||||||
|
pcl.initialize(PACKAGE_LIST);
|
||||||
|
pcl.execute(Prolog.BUILTIN, "consult_stream",
|
||||||
|
SymbolTerm.intern("stdin"), new JavaObjectTerm(inReader));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Term> results = new ArrayList<Term>();
|
||||||
|
Term submitRule =
|
||||||
|
pcl.once("gerrit", "locate_submit_rule", new VariableTerm());
|
||||||
|
|
||||||
|
for (Term[] template : pcl.all("gerrit", "can_submit", submitRule,
|
||||||
|
new VariableTerm())) {
|
||||||
|
results.add(template[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipSubmitFilters) {
|
||||||
|
runSubmitFilters(projectState, results, pcl);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SubmitRecord> res = cc.resultsToSubmitRecord(submitRule, results);
|
||||||
|
for (SubmitRecord r : res) {
|
||||||
|
if (format.isJson()) {
|
||||||
|
SubmitRecordAttribute submitRecord = new SubmitRecordAttribute();
|
||||||
|
submitRecord.status = r.status.name();
|
||||||
|
|
||||||
|
List<SubmitLabelAttribute> submitLabels = new LinkedList<SubmitLabelAttribute>();
|
||||||
|
for(SubmitRecord.Label l : r.labels) {
|
||||||
|
SubmitLabelAttribute label = new SubmitLabelAttribute();
|
||||||
|
label.label = l.label;
|
||||||
|
label.status= l.status.name();
|
||||||
|
if(l.appliedBy != null) {
|
||||||
|
Account a = accountCache.get(l.appliedBy).getAccount();
|
||||||
|
label.by = new AccountAttribute();
|
||||||
|
label.by.email = a.getPreferredEmail();
|
||||||
|
label.by.name = a.getFullName();
|
||||||
|
label.by.username = a.getUserName();
|
||||||
|
}
|
||||||
|
submitLabels.add(label);
|
||||||
|
}
|
||||||
|
submitRecord.labels = submitLabels;
|
||||||
|
format.newGson().toJson(submitRecord, new TypeToken<SubmitRecordAttribute>() {}.getType(), stdout);
|
||||||
|
stdout.print('\n');
|
||||||
|
} else {
|
||||||
|
for(SubmitRecord.Label l : r.labels) {
|
||||||
|
stdout.print(l.label + ": " + l.status);
|
||||||
|
if(l.appliedBy != null) {
|
||||||
|
AccountInfo a = new AccountInfo(accountCache.get(l.appliedBy).getAccount());
|
||||||
|
stdout.print(" by " + a.getNameEmail(anonymousCowardName));
|
||||||
|
}
|
||||||
|
stdout.print('\n');
|
||||||
|
}
|
||||||
|
stdout.print("\n" + r.status.name() + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new UnloggedFailure("Processing of prolog script failed: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runSubmitFilters(ProjectState projectState, List<Term> results,
|
||||||
|
PrologEnvironment pcl) throws UnloggedFailure {
|
||||||
|
ProjectState parentState = projectState.getParentState();
|
||||||
|
PrologEnvironment childEnv = pcl;
|
||||||
|
Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
|
||||||
|
projectsSeen.add(projectState.getProject().getNameKey());
|
||||||
|
|
||||||
|
while (parentState != null) {
|
||||||
|
if (!projectsSeen.add(parentState.getProject().getNameKey())) {
|
||||||
|
// parent has been seen before, stop walk up inheritance tree
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PrologEnvironment parentEnv;
|
||||||
|
try {
|
||||||
|
parentEnv = parentState.newPrologEnvironment();
|
||||||
|
} catch (CompileException err) {
|
||||||
|
throw new UnloggedFailure("Cannot consult rules.pl for "
|
||||||
|
+ parentState.getProject().getName() + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentEnv.copyStoredValues(childEnv);
|
||||||
|
Term filterRule =
|
||||||
|
parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm());
|
||||||
|
if (filterRule != null) {
|
||||||
|
try {
|
||||||
|
Term resultsTerm = ChangeControl.toListTerm(results);
|
||||||
|
results.clear();
|
||||||
|
Term[] template =
|
||||||
|
parentEnv.once("gerrit", "filter_submit_results", filterRule,
|
||||||
|
resultsTerm, new VariableTerm());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<? extends Term> termList =
|
||||||
|
((ListTerm) template[2]).toJava();
|
||||||
|
results.addAll(termList);
|
||||||
|
} catch (PrologException err) {
|
||||||
|
throw new UnloggedFailure("Exception calling " + filterRule + " of "
|
||||||
|
+ parentState.getProject().getName() + err);
|
||||||
|
} catch (RuntimeException err) {
|
||||||
|
throw new UnloggedFailure("Exception calling " + filterRule + " of "
|
||||||
|
+ parentState.getProject().getName() + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parentState = parentState.getParentState();
|
||||||
|
childEnv = parentEnv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user