Add per-project prolog submit rule files

When loading the prolog environment, now checks refs/meta/config
branch for a file called rules.pl. If it exists, consult the
file. Expects a predicate called submit_rule. If no file is found,
uses the default_submit predicate in common_rules.pl.

Change-Id: Ia76d0dc5aba67cf3c68c56589fe013c63157bca5
This commit is contained in:
Jason Tsay
2011-06-09 16:17:01 -07:00
committed by Shawn O. Pearce
parent 9f2535231b
commit f93796cd53
4 changed files with 85 additions and 15 deletions

View File

@@ -72,6 +72,7 @@ public class ProjectConfig extends VersionedMetaData {
private Map<AccountGroup.UUID, GroupReference> groupsByUUID; private Map<AccountGroup.UUID, GroupReference> groupsByUUID;
private Map<String, AccessSection> accessSections; private Map<String, AccessSection> accessSections;
private List<ValidationError> validationErrors; private List<ValidationError> validationErrors;
private String prologRules;
public static ProjectConfig read(MetaDataUpdate update) throws IOException, public static ProjectConfig read(MetaDataUpdate update) throws IOException,
ConfigInvalidException { ConfigInvalidException {
@@ -148,6 +149,17 @@ public class ProjectConfig extends VersionedMetaData {
return groupsByUUID.get(uuid); return groupsByUUID.get(uuid);
} }
/**
* @return the project's Prolog based rules.pl script,
* if present in the branch. Null if there are no rules.
*/
public String getPrologRules() {
if (prologRules.equals("")) {
return null;
}
return prologRules;
}
/** /**
* Check all GroupReferences use current group name, repairing stale ones. * Check all GroupReferences use current group name, repairing stale ones.
* *
@@ -188,6 +200,7 @@ public class ProjectConfig extends VersionedMetaData {
protected void onLoad() throws IOException, ConfigInvalidException { protected void onLoad() throws IOException, ConfigInvalidException {
Map<String, GroupReference> groupsByName = readGroupList(); Map<String, GroupReference> groupsByName = readGroupList();
prologRules = readUTF8("rules.pl");
Config rc = readConfig(PROJECT_CONFIG); Config rc = readConfig(PROJECT_CONFIG);
project = new Project(projectName); project = new Project(projectName);

View File

@@ -28,10 +28,10 @@ import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.googlecode.prolog_cafe.compiler.CompileException;
import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.PrologException;
import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.StructureTerm;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term; import com.googlecode.prolog_cafe.lang.Term;
import com.googlecode.prolog_cafe.lang.VariableTerm; import com.googlecode.prolog_cafe.lang.VariableTerm;
@@ -252,25 +252,36 @@ public class ChangeControl {
return result; return result;
} }
PrologEnvironment env = getProjectControl().getProjectState().newPrologEnvironment(); PrologEnvironment env;
try {
env = getProjectControl().getProjectState().newPrologEnvironment();
} catch (CompileException err) {
log.error("cannot consult rules.pl", err);
return new CanSubmitResult("Error reading submit rule");
}
env.set(StoredValues.REVIEW_DB, db); env.set(StoredValues.REVIEW_DB, db);
env.set(StoredValues.CHANGE, change); env.set(StoredValues.CHANGE, change);
env.set(StoredValues.PATCH_SET_ID, patchSetId); env.set(StoredValues.PATCH_SET_ID, patchSetId);
env.set(StoredValues.CHANGE_CONTROL, this); env.set(StoredValues.CHANGE_CONTROL, this);
StructureTerm submitRule = SymbolTerm.makeSymbol( Term submitRule = env.once("com.google.gerrit.rules.common", "locate_submit_rule",
"com.google.gerrit.rules.common", "default_submit", 1); new VariableTerm());
if (submitRule == null) {
log.error("Error in locate_submit_rule: no submit_rule found");
return new CanSubmitResult("Error in finding submit rule");
}
List<Term> results = new ArrayList<Term>(); List<Term> results = new ArrayList<Term>();
try { try {
for (Term[] template : env.all( for (Term[] template : env.all(
"com.google.gerrit.rules.common", "can_submit", "com.google.gerrit.rules.common", "can_submit",
submitRule, submitRule,
new VariableTerm())) { new VariableTerm())) {
results.add(template[1]); results.add(template[1]);
} }
} catch (PrologException err) { } catch (PrologException err) {
log.error("PrologException calling "+submitRule, err); log.error("PrologException calling " + submitRule, err);
return new CanSubmitResult("Error in submit rule"); return new CanSubmitResult("Error in submit rule");
} }
@@ -319,7 +330,7 @@ public class ChangeControl {
continue; continue;
} else if ("reject".equals(status.name())) { } else if ("reject".equals(status.name())) {
return new CanSubmitResult("Submit blocked by "+ label); return new CanSubmitResult("Submit blocked by " + label);
} else if ("need".equals(status.name())) { } else if ("need".equals(status.name())) {
if (status.isStructure() && status.arg(0).isInteger()) { if (status.isStructure() && status.arg(0).isInteger()) {
@@ -340,4 +351,4 @@ public class ChangeControl {
return CanSubmitResult.OK; return CanSubmitResult.OK;
} }
} }

View File

@@ -29,10 +29,17 @@ import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.googlecode.prolog_cafe.compiler.CompileException;
import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import java.io.IOException; import java.io.IOException;
import java.io.PushbackReader;
import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -121,9 +128,24 @@ public class ProjectState {
} }
/** @return Construct a new PrologEnvironment for the calling thread. */ /** @return Construct a new PrologEnvironment for the calling thread. */
public PrologEnvironment newPrologEnvironment() { public PrologEnvironment newPrologEnvironment() throws CompileException {
// TODO Replace this with a per-project ClassLoader to isolate rules. // TODO Replace this with a per-project ClassLoader to isolate rules.
return envFactory.create(getClass().getClassLoader()); PrologEnvironment env = envFactory.create(getClass().getClassLoader());
//consult rules.pl at refs/meta/config branch for custom submit rules
String rules = getConfig().getPrologRules();
if (rules != null) {
PushbackReader in =
new PushbackReader(new StringReader(rules), Prolog.PUSHBACK_SIZE);
JavaObjectTerm streamObject = new JavaObjectTerm(in);
if (!env.execute(Prolog.BUILTIN, "consult_stream",
SymbolTerm.makeSymbol("rules.pl"), streamObject)) {
throw new CompileException("Cannot consult rules.pl " +
getProject().getName() + " " + getConfig().getRevision());
}
}
return env;
} }
public Project getProject() { public Project getProject() {

View File

@@ -143,7 +143,7 @@ not_same(_, _).
%% %%
%% can_submit/2: %% can_submit/2:
%% %%
%% Execute the SubmitRule for each solution until one where all of the %% Executes the SubmitRule for each solution until one where all of the
%% states has the format label(_, ok(_)) is found, then cut away any %% states has the format label(_, ok(_)) is found, then cut away any
%% remaining choice points leaving this as the last solution. %% remaining choice points leaving this as the last solution.
%% %%
@@ -162,6 +162,30 @@ is_all_ok([label(_, ok(__)) | Ls]) :- is_all_ok(Ls).
is_all_ok(_) :- fail. is_all_ok(_) :- fail.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% 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) :-
clause(user:submit_rule(_), _),
!,
RuleName = user:submit_rule
.
locate_submit_rule(RuleName) :-
'$compiled_predicate'(user, submit_rule, 1),
!,
RuleName = user:submit_rule
.
locate_submit_rule(RuleName) :-
RuleName = 'com.google.gerrit.rules.common':default_submit.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% default_submit/1: %% default_submit/1: