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:
		
				
					committed by
					
						
						Shawn O. Pearce
					
				
			
			
				
	
			
			
			
						parent
						
							9f2535231b
						
					
				
				
					commit
					f93796cd53
				
			@@ -72,6 +72,7 @@ public class ProjectConfig extends VersionedMetaData {
 | 
			
		||||
  private Map<AccountGroup.UUID, GroupReference> groupsByUUID;
 | 
			
		||||
  private Map<String, AccessSection> accessSections;
 | 
			
		||||
  private List<ValidationError> validationErrors;
 | 
			
		||||
  private String prologRules;
 | 
			
		||||
 | 
			
		||||
  public static ProjectConfig read(MetaDataUpdate update) throws IOException,
 | 
			
		||||
      ConfigInvalidException {
 | 
			
		||||
@@ -148,6 +149,17 @@ public class ProjectConfig extends VersionedMetaData {
 | 
			
		||||
    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.
 | 
			
		||||
   *
 | 
			
		||||
@@ -188,6 +200,7 @@ public class ProjectConfig extends VersionedMetaData {
 | 
			
		||||
  protected void onLoad() throws IOException, ConfigInvalidException {
 | 
			
		||||
    Map<String, GroupReference> groupsByName = readGroupList();
 | 
			
		||||
 | 
			
		||||
    prologRules = readUTF8("rules.pl");
 | 
			
		||||
    Config rc = readConfig(PROJECT_CONFIG);
 | 
			
		||||
    project = new Project(projectName);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,10 @@ import com.google.gwtorm.client.OrmException;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
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.PrologException;
 | 
			
		||||
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.VariableTerm;
 | 
			
		||||
 | 
			
		||||
@@ -252,25 +252,36 @@ public class ChangeControl {
 | 
			
		||||
      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.CHANGE, change);
 | 
			
		||||
    env.set(StoredValues.PATCH_SET_ID, patchSetId);
 | 
			
		||||
    env.set(StoredValues.CHANGE_CONTROL, this);
 | 
			
		||||
 | 
			
		||||
    StructureTerm submitRule = SymbolTerm.makeSymbol(
 | 
			
		||||
        "com.google.gerrit.rules.common", "default_submit", 1);
 | 
			
		||||
    Term submitRule = env.once("com.google.gerrit.rules.common", "locate_submit_rule",
 | 
			
		||||
        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>();
 | 
			
		||||
    try {
 | 
			
		||||
      for (Term[] template : env.all(
 | 
			
		||||
              "com.google.gerrit.rules.common", "can_submit",
 | 
			
		||||
              submitRule,
 | 
			
		||||
              new VariableTerm())) {
 | 
			
		||||
          results.add(template[1]);
 | 
			
		||||
        }
 | 
			
		||||
          "com.google.gerrit.rules.common", "can_submit",
 | 
			
		||||
          submitRule,
 | 
			
		||||
          new VariableTerm())) {
 | 
			
		||||
        results.add(template[1]);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (PrologException err) {
 | 
			
		||||
      log.error("PrologException calling "+submitRule, err);
 | 
			
		||||
      log.error("PrologException calling " + submitRule, err);
 | 
			
		||||
      return new CanSubmitResult("Error in submit rule");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -319,7 +330,7 @@ public class ChangeControl {
 | 
			
		||||
        continue;
 | 
			
		||||
 | 
			
		||||
      } 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())) {
 | 
			
		||||
        if (status.isStructure() && status.arg(0).isInteger()) {
 | 
			
		||||
@@ -340,4 +351,4 @@ public class ChangeControl {
 | 
			
		||||
 | 
			
		||||
    return CanSubmitResult.OK;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -29,10 +29,17 @@ import com.google.gerrit.server.git.ProjectConfig;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
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.Repository;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PushbackReader;
 | 
			
		||||
import java.io.StringReader;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
@@ -121,9 +128,24 @@ public class ProjectState {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @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.
 | 
			
		||||
    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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -143,7 +143,7 @@ not_same(_, _).
 | 
			
		||||
%%
 | 
			
		||||
%% 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
 | 
			
		||||
%%   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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 | 
			
		||||
%%
 | 
			
		||||
%% 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:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user