Move plugin config from YAML to git config
Instead of a YAML file stored in a project,
we use the standard plugin config mechanism
and store it in All-Projects refs/meta/config.
Change-Id: I14afffb8f80774fc871f153e321abc4e828974db
diff --git a/.gitignore b/.gitignore
index 21cb77a..fbdceef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@
.buckversion
.watchmanconfig
/eclipse-out
+bin/
diff --git a/BUILD b/BUILD
index a8d88ae..887d6fa 100644
--- a/BUILD
+++ b/BUILD
@@ -13,7 +13,6 @@
resources = glob(["src/main/resources/**/*"]),
deps = [
"@re2j//jar",
- "@yaml//jar",
],
)
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 0396eea..ebb3c56 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -1,16 +1,11 @@
-load("//tools/bzl:maven_jar.bzl", "maven_jar")
+"""External plugin dependencies for the Automerger plugin."""
+load('//tools/bzl:maven_jar.bzl', 'maven_jar')
def external_plugin_deps():
maven_jar(
- name = 'yaml',
- artifact = 'org.yaml:snakeyaml:1.17',
- sha1 = '7a27ea250c5130b2922b86dea63cbb1cc10a660c',
- )
-
- maven_jar(
name = 'mockito',
- artifact = 'org.mockito:mockito-all:1.10.19',
- sha1 = '539df70269cc254a58cccc5d8e43286b4a73bf30',
+ artifact = 'org.mockito:mockito-all:1.9.5',
+ sha1 = '79a8984096fc6591c1e3690e07d41be506356fa5',
)
maven_jar(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergeChangeAction.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergeChangeAction.java
index f7b2e29..0990ec5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergeChangeAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergeChangeAction.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.automerger;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -26,6 +27,7 @@
import com.google.inject.Provider;
import java.io.IOException;
import java.util.Map;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,12 +55,11 @@
* @param rev RevisionResource of the change whose page we are clicking the button.
* @param input A map of branch to whether or not the merge should be "-s ours".
* @return HTTP 200 on success.
+ * @throws IOException
* @throws RestApiException
- * @throws FailedMergeException
*/
@Override
- public Object apply(RevisionResource rev, Input input)
- throws RestApiException, FailedMergeException {
+ public Object apply(RevisionResource rev, Input input) throws IOException, RestApiException {
Map<String, Boolean> branchMap = input.branchMap;
Change change = rev.getChange();
@@ -79,7 +80,12 @@
log.debug("Multiple downstream merge input: {}", mdsMergeInput.dsBranchMap);
- dsCreator.createMergesAndHandleConflicts(mdsMergeInput);
+ try {
+ dsCreator.createMergesAndHandleConflicts(mdsMergeInput);
+ } catch (ConfigInvalidException e) {
+ throw new ResourceConflictException(
+ "Automerger configuration file is invalid: " + e.getMessage());
+ }
return Response.none();
}
@@ -101,7 +107,7 @@
} else {
desc = desc.setVisible(user.get() instanceof IdentifiedUser);
}
- } catch (RestApiException | IOException e) {
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
log.error("Failed to recreate automerges for {} on {}", project, branch);
desc = desc.setVisible(false);
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergerModule.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergerModule.java
index 9b21b2c..9d61595 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergerModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/AutomergerModule.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
-import com.google.gerrit.extensions.events.ChangeMergedListener;
import com.google.gerrit.extensions.events.ChangeRestoredListener;
import com.google.gerrit.extensions.events.CommentAddedListener;
import com.google.gerrit.extensions.events.DraftPublishedListener;
@@ -36,7 +35,6 @@
protected void configure() {
DynamicSet.bind(binder(), CommentAddedListener.class).to(DownstreamCreator.class);
DynamicSet.bind(binder(), ChangeAbandonedListener.class).to(DownstreamCreator.class);
- DynamicSet.bind(binder(), ChangeMergedListener.class).to(DownstreamCreator.class);
DynamicSet.bind(binder(), ChangeRestoredListener.class).to(DownstreamCreator.class);
DynamicSet.bind(binder(), DraftPublishedListener.class).to(DownstreamCreator.class);
DynamicSet.bind(binder(), RevisionCreatedListener.class).to(DownstreamCreator.class);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigDownstreamAction.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigDownstreamAction.java
index 5c9f060..339f966 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigDownstreamAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigDownstreamAction.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.automerger;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -24,6 +25,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
/** The logic behind auto-filling the branch map, aka the input to AutomergeChangeAction. */
class ConfigDownstreamAction
@@ -57,13 +59,18 @@
String branchName = rev.getChange().getDest().getShortName();
String projectName = rev.getProject().get();
- Set<String> downstreamBranches = config.getDownstreamBranches(branchName, projectName);
- Map<String, Boolean> downstreamMap = new HashMap<>();
- for (String downstreamBranch : downstreamBranches) {
- boolean isSkipMerge = config.isSkipMerge(branchName, downstreamBranch, input.subject);
- downstreamMap.put(downstreamBranch, !isSkipMerge);
+ try {
+ Set<String> downstreamBranches = config.getDownstreamBranches(branchName, projectName);
+ Map<String, Boolean> downstreamMap = new HashMap<>();
+ for (String downstreamBranch : downstreamBranches) {
+ boolean isSkipMerge = config.isSkipMerge(branchName, downstreamBranch, input.subject);
+ downstreamMap.put(downstreamBranch, !isSkipMerge);
+ }
+ return Response.created(downstreamMap);
+ } catch (ConfigInvalidException e) {
+ throw new ResourceConflictException(
+ "Automerger configuration file is invalid: " + e.getMessage());
}
- return Response.created(downstreamMap);
}
static class Input {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
index 54310ee..5d439b6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
@@ -14,109 +14,113 @@
package com.googlesource.gerrit.plugins.automerger;
-import com.google.common.base.Charsets;
-import com.google.common.io.CharStreams;
+import com.google.common.base.Joiner;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import com.google.re2j.Pattern;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.Yaml;
-/** Class to read the config and swap it out of memory if the config has changed. */
+/** Class to read the config. */
@Singleton
public class ConfigLoader {
private static final Logger log = LoggerFactory.getLogger(ConfigLoader.class);
- public final String configProject;
- public final String configProjectBranch;
- public final String configFilename;
- public final List<String> configOptionKeys;
+ private static final String BRANCH_DELIMITER = ":";
- protected GerritApi gApi;
- private volatile LoadedConfig config;
+ private final GerritApi gApi;
+ private final String pluginName;
+ private final AllProjectsName allProjectsName;
+ private final PluginConfigFactory cfgFactory;
/**
- * Read static configuration from config_keys.yaml and try to load initial dynamic configuration.
- *
- * <p>If loading dynamic configuration fails, logs and treats configuration as empty. Callers can
- * call {@link loadConfig} to retry.
+ * Class to handle getting information from the config.
*
* @param gApi API to access gerrit information.
- * @throws IOException if reading config_keys.yaml failed
+ * @param allProjectsName The name of the top-level project.
+ * @param pluginName The name of the plugin we are running.
+ * @param cfgFactory Factory to generate the plugin config.
*/
@Inject
- public ConfigLoader(GerritApi gApi) throws IOException, RestApiException {
+ public ConfigLoader(
+ GerritApi gApi,
+ AllProjectsName allProjectsName,
+ @PluginName String pluginName,
+ PluginConfigFactory cfgFactory) {
this.gApi = gApi;
+ this.pluginName = pluginName;
+ this.cfgFactory = cfgFactory;
+ this.allProjectsName = allProjectsName;
+ }
- String configKeysPath = "/config/config_keys.yaml";
- try (InputStreamReader streamReader =
- new InputStreamReader(getClass().getResourceAsStream(configKeysPath), Charsets.UTF_8)) {
-
- String automergerConfigYamlString = CharStreams.toString(streamReader);
- Map<String, Object> automergerConfig =
- (Map<String, Object>) (new Yaml().load(automergerConfigYamlString));
- configProject = (String) automergerConfig.get("config_project");
- configProjectBranch = (String) automergerConfig.get("config_project_branch");
- configFilename = (String) automergerConfig.get("config_filename");
- configOptionKeys = (List<String>) automergerConfig.get("config_option_keys");
-
- loadConfig();
+ private Config getConfig() throws ConfigInvalidException {
+ try {
+ return cfgFactory.getProjectPluginConfig(allProjectsName, pluginName);
+ } catch (NoSuchProjectException e) {
+ throw new ConfigInvalidException(
+ "Config invalid because " + allProjectsName.get() + " does not exist!");
}
}
/**
- * Swap out the current config for a new, up to date config.
- *
- * @throws IOException
- * @throws RestApiException
- */
- public void loadConfig() throws IOException, RestApiException {
- config =
- new LoadedConfig(
- gApi, configProject, configProjectBranch, configFilename, configOptionKeys);
- }
-
- /**
- * Detects whether to skip a change based on the configuration. ( )
+ * Detects whether to skip a change based on the configuration.
*
* @param fromBranch Branch we are merging from.
* @param toBranch Branch we are merging to.
* @param commitMessage Commit message of the change.
* @return True if we match blank_merge_regex and merge_all is false, or we match
* always_blank_merge_regex
+ * @throws ConfigInvalidException
*/
- public boolean isSkipMerge(String fromBranch, String toBranch, String commitMessage) {
- return config.isSkipMerge(fromBranch, toBranch, commitMessage);
+ public boolean isSkipMerge(String fromBranch, String toBranch, String commitMessage)
+ throws ConfigInvalidException {
+ Pattern alwaysBlankMergePattern = getConfigPattern("alwaysBlankMerge");
+ if (alwaysBlankMergePattern.matches(commitMessage)) {
+ return true;
+ }
+
+ Pattern blankMergePattern = getConfigPattern("blankMerge");
+ // If regex matches blank_merge (DO NOT MERGE), skip iff merge_all is false
+ if (blankMergePattern.matches(commitMessage)) {
+ return !getMergeAll(fromBranch, toBranch);
+ }
+ return false;
}
- /**
- * Get the merge configuration for a pair of branches.
- *
- * @param fromBranch Branch we are merging from.
- * @param toBranch Branch we are merging to.
- * @return The configuration for the given input.
- */
- public Map<String, Object> getConfig(String fromBranch, String toBranch) {
- return config.getMergeConfig(fromBranch, toBranch);
+ private Pattern getConfigPattern(String key) throws ConfigInvalidException {
+ String[] patternList = getConfig().getStringList("global", null, key);
+ Set<String> mergeStrings = new HashSet<>(Arrays.asList(patternList));
+ return Pattern.compile(Joiner.on("|").join(mergeStrings), Pattern.DOTALL);
+ }
+
+ private boolean getMergeAll(String fromBranch, String toBranch) throws ConfigInvalidException {
+ return getConfig()
+ .getBoolean("automerger", fromBranch + BRANCH_DELIMITER + toBranch, "mergeAll", false);
}
/**
* Returns the name of the automerge label (i.e. the label to vote -1 if we have a merge conflict)
*
* @return Returns the name of the automerge label.
+ * @throws ConfigInvalidException
*/
- public String getAutomergeLabel() {
- return config.getAutomergeLabel();
+ public String getAutomergeLabel() throws ConfigInvalidException {
+ String automergeLabel = getConfig().getString("global", null, "automergeLabel");
+ return automergeLabel != null ? automergeLabel : "Verified";
}
/**
@@ -127,29 +131,13 @@
* @return The projects that are in scope of the given projects.
* @throws RestApiException
* @throws IOException
+ * @throws ConfigInvalidException
*/
public Set<String> getProjectsInScope(String fromBranch, String toBranch)
- throws RestApiException, IOException {
+ throws RestApiException, IOException, ConfigInvalidException {
try {
- Set<String> projectSet = new HashSet<String>();
-
- Set<String> fromProjectSet = getManifestProjects(fromBranch);
- projectSet.addAll(fromProjectSet);
-
- Set<String> toProjectSet = getManifestProjects(fromBranch, toBranch);
- // Take intersection of project sets, unless one is empty.
- if (projectSet.isEmpty()) {
- projectSet = toProjectSet;
- } else if (!toProjectSet.isEmpty()) {
- projectSet.retainAll(toProjectSet);
- }
-
- // The lower the level a config is applied, the higher priority it has
- // For example, a project ignored in the global config but added in the branch config will
- // be added to the final project set, not ignored
- applyConfig(projectSet, config.getGlobal());
- applyConfig(projectSet, config.getMergeConfig(fromBranch));
- applyConfig(projectSet, config.getMergeConfig(fromBranch, toBranch));
+ Set<String> projectSet = getManifestProjects(fromBranch, toBranch);
+ projectSet = applyConfig(fromBranch, toBranch, projectSet);
log.debug("Project set for {} to {} is {}", fromBranch, toBranch, projectSet);
return projectSet;
@@ -167,20 +155,24 @@
* @return The branches downstream of the given branch for the given project.
* @throws RestApiException
* @throws IOException
+ * @throws ConfigInvalidException
*/
public Set<String> getDownstreamBranches(String fromBranch, String project)
- throws RestApiException, IOException {
+ throws RestApiException, IOException, ConfigInvalidException {
Set<String> downstreamBranches = new HashSet<String>();
- Map<String, Object> fromBranchConfig = config.getMergeConfig(fromBranch);
-
- if (fromBranchConfig != null) {
- for (String key : fromBranchConfig.keySet()) {
- if (!configOptionKeys.contains(key)) {
- // If it's not a config option, then the key is the toBranch
- Set<String> projectsInScope = getProjectsInScope(fromBranch, key);
- if (projectsInScope.contains(project)) {
- downstreamBranches.add(key);
- }
+ // List all subsections of automerger, split by :
+ Set<String> subsections = getConfig().getSubsections(pluginName);
+ for (String subsection : subsections) {
+ // Subsections are of the form "fromBranch:toBranch"
+ String[] branchPair = subsection.split(Pattern.quote(BRANCH_DELIMITER));
+ if (branchPair.length != 2) {
+ throw new ConfigInvalidException("Automerger config branch pair malformed: " + subsection);
+ }
+ if (fromBranch.equals(branchPair[0])) {
+ // If fromBranches match, check if project is in both their manifests
+ Set<String> projectsInScope = getProjectsInScope(branchPair[0], branchPair[1]);
+ if (projectsInScope.contains(project)) {
+ downstreamBranches.add(branchPair[1]);
}
}
}
@@ -188,43 +180,52 @@
}
// Returns overriden manifest config if specified, default if not
- private Map<String, String> getManifestInfoFromConfig(Map<String, Object> configMap) {
- if (configMap.containsKey("manifest")) {
- return (Map<String, String>) configMap.get("manifest");
+ private String getManifestFile() throws ConfigInvalidException {
+ String manifestFile = getConfig().getString("global", null, "manifestFile");
+ if (manifestFile == null) {
+ throw new ConfigInvalidException("manifestFile not specified.");
}
- return config.getDefaultManifestInfo();
+ return manifestFile;
}
- // Returns contents of manifest file for the given branch.
- // If manifest does not exist, return empty set.
- private Set<String> getManifestProjects(String fromBranch) throws RestApiException, IOException {
- Map<String, Object> fromBranchConfig = config.getMergeConfig(fromBranch);
- if (fromBranchConfig == null) {
- return new HashSet<>();
+ // Returns overriden manifest config if specified, default if not
+ private String getManifestProject() throws ConfigInvalidException {
+ String manifestProject = getConfig().getString("global", null, "manifestProject");
+ if (manifestProject == null) {
+ throw new ConfigInvalidException("manifestProject not specified.");
}
- Map<String, String> manifestProjectInfo = getManifestInfoFromConfig(fromBranchConfig);
- return getManifestProjectsForBranch(manifestProjectInfo, fromBranch);
+ return manifestProject;
}
// Returns contents of manifest file for the given branch pair
// If manifest does not exist, return empty set.
private Set<String> getManifestProjects(String fromBranch, String toBranch)
- throws RestApiException, IOException {
- Map<String, Object> toBranchConfig = config.getMergeConfig(fromBranch, toBranch);
- if (toBranchConfig == null) {
- return new HashSet<>();
+ throws RestApiException, IOException, ConfigInvalidException {
+ boolean ignoreSourceManifest =
+ getConfig()
+ .getBoolean(
+ "automerger",
+ fromBranch + BRANCH_DELIMITER + toBranch,
+ "ignoreSourceManifest",
+ false);
+
+ Set<String> toProjects =
+ getProjectsInManifest(getManifestProject(), getManifestFile(), toBranch);
+ if (ignoreSourceManifest) {
+ return toProjects;
}
- Map<String, String> manifestProjectInfo = getManifestInfoFromConfig(toBranchConfig);
- return getManifestProjectsForBranch(manifestProjectInfo, toBranch);
+
+ Set<String> fromProjects =
+ getProjectsInManifest(getManifestProject(), getManifestFile(), fromBranch);
+ fromProjects.retainAll(toProjects);
+ return fromProjects;
}
- private Set<String> getManifestProjectsForBranch(
- Map<String, String> manifestProjectInfo, String branch) throws RestApiException, IOException {
- String manifestProject = manifestProjectInfo.get("project");
- String manifestFile = manifestProjectInfo.get("file");
- try {
- BinaryResult manifestConfig =
- gApi.projects().name(manifestProject).branch(branch).file(manifestFile);
+ private Set<String> getProjectsInManifest(
+ String manifestProject, String manifestFile, String branch)
+ throws RestApiException, IOException {
+ try (BinaryResult manifestConfig =
+ gApi.projects().name(manifestProject).branch(branch).file(manifestFile)) {
ManifestReader manifestReader = new ManifestReader(branch, manifestConfig.asString());
return manifestReader.getProjects();
} catch (ResourceNotFoundException e) {
@@ -233,24 +234,32 @@
}
}
- private void applyConfig(Set<String> projects, Map<String, Object> givenConfig) {
- if (givenConfig == null) {
- return;
- }
- if (givenConfig.containsKey("set_projects")) {
- List<String> setProjects = (ArrayList<String>) givenConfig.get("set_projects");
+ private Set<String> applyConfig(String fromBranch, String toBranch, Set<String> inputProjects)
+ throws ConfigInvalidException {
+ Set<String> projects = new HashSet<>(inputProjects);
+ List<String> setProjects =
+ Arrays.asList(
+ getConfig()
+ .getStringList(
+ "automerger", fromBranch + BRANCH_DELIMITER + toBranch, "setProjects"));
+ if (!setProjects.isEmpty()) {
projects.clear();
projects.addAll(setProjects);
// if we set projects we can ignore the rest
- return;
+ return projects;
}
- if (givenConfig.containsKey("add_projects")) {
- List<String> addProjects = (List<String>) givenConfig.get("add_projects");
- projects.addAll(addProjects);
- }
- if (givenConfig.containsKey("ignore_projects")) {
- List<String> ignoreProjects = (List<String>) givenConfig.get("ignore_projects");
- projects.removeAll(ignoreProjects);
- }
+ List<String> addProjects =
+ Arrays.asList(
+ getConfig()
+ .getStringList(
+ "automerger", fromBranch + BRANCH_DELIMITER + toBranch, "addProjects"));
+ projects.addAll(addProjects);
+ List<String> ignoreProjects =
+ Arrays.asList(
+ getConfig()
+ .getStringList(
+ "automerger", fromBranch + BRANCH_DELIMITER + toBranch, "ignoreProjects"));
+ projects.removeAll(ignoreProjects);
+ return projects;
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
index a067416..a2ce9fe 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
@@ -30,7 +30,6 @@
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
-import com.google.gerrit.extensions.events.ChangeMergedListener;
import com.google.gerrit.extensions.events.ChangeRestoredListener;
import com.google.gerrit.extensions.events.CommentAddedListener;
import com.google.gerrit.extensions.events.DraftPublishedListener;
@@ -47,6 +46,7 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,7 +58,6 @@
*/
public class DownstreamCreator
implements ChangeAbandonedListener,
- ChangeMergedListener,
ChangeRestoredListener,
CommentAddedListener,
DraftPublishedListener,
@@ -76,24 +75,6 @@
}
/**
- * Updates the config in memory if the config project is updated.
- *
- * @param event Event we are listening to.
- */
- @Override
- public void onChangeMerged(ChangeMergedListener.Event event) {
- ChangeInfo change = event.getChange();
- try {
- if (change.project.equals(config.configProject)
- && change.branch.equals(config.configProjectBranch)) {
- loadConfig();
- }
- } catch (RestApiException | IOException e) {
- log.error("Failed to reload config at {}", change.id, e);
- }
- }
-
- /**
* Abandons downstream changes if a change is abandoned.
*
* @param event Event we are listening to.
@@ -103,7 +84,11 @@
ChangeInfo change = event.getChange();
String revision = event.getRevision().commit.commit;
log.debug("Detected revision {} abandoned on {}.", revision, change.project);
- abandonDownstream(change, revision);
+ try {
+ abandonDownstream(change, revision);
+ } catch (ConfigInvalidException e) {
+ log.error("Automerger plugin failed onChangeAbandoned for {}", change.id, e);
+ }
}
/**
@@ -119,7 +104,7 @@
Set<String> downstreamBranches;
try {
downstreamBranches = config.getDownstreamBranches(change.branch, change.project);
- } catch (RestApiException | IOException e) {
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
log.error("Failed to edit downstream topics of {}", change.id, e);
return;
}
@@ -161,7 +146,7 @@
Set<String> downstreamBranches;
try {
downstreamBranches = config.getDownstreamBranches(change.branch, change.project);
- } catch (RestApiException | IOException e) {
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
log.error("Failed to update downstream votes of {}", change.id, e);
return;
}
@@ -184,8 +169,8 @@
updateVote(downstreamChange, label.getKey(), label.getValue().value.shortValue());
}
}
- } catch (RestApiException e) {
- log.error("RestApiException when updating downstream votes of {}", change.id, e);
+ } catch (RestApiException | ConfigInvalidException e) {
+ log.error("Exception when updating downstream votes of {}", change.id, e);
}
}
}
@@ -200,8 +185,8 @@
ChangeInfo change = event.getChange();
try {
automergeChanges(change, event.getRevision());
- } catch (RestApiException | IOException e) {
- log.error("Failed to edit downstream topics of {}", change.id, e);
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
+ log.error("Automerger plugin failed onChangeRestored for {}", change.id, e);
}
}
@@ -215,8 +200,8 @@
ChangeInfo change = event.getChange();
try {
automergeChanges(change, event.getRevision());
- } catch (RestApiException | IOException e) {
- log.error("Failed to edit downstream topics of {}", change.id, e);
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
+ log.error("Automerger plugin failed onDraftPublished for {}", change.id, e);
}
}
@@ -230,8 +215,8 @@
ChangeInfo change = event.getChange();
try {
automergeChanges(change, event.getRevision());
- } catch (RestApiException | IOException e) {
- log.error("Failed to edit downstream topics of {}", change.id, e);
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
+ log.error("Automerger plugin failed onRevisionCreated for {}", change.id, e);
}
}
@@ -240,9 +225,10 @@
*
* @param mdsMergeInput Input containing the downstream branch map and source change ID.
* @throws RestApiException Throws if we fail a REST API call.
+ * @throws ConfigInvalidException Throws if we get a malformed configuration
*/
public void createMergesAndHandleConflicts(MultipleDownstreamMergeInput mdsMergeInput)
- throws RestApiException {
+ throws RestApiException, ConfigInvalidException {
ReviewInput reviewInput = new ReviewInput();
Map<String, Short> labels = new HashMap<String, Short>();
short vote = 0;
@@ -274,9 +260,10 @@
* @param mdsMergeInput Input containing the downstream branch map and source change ID.
* @throws RestApiException Throws if we fail a REST API call.
* @throws FailedMergeException Throws if we get a merge conflict when merging downstream.
+ * @throws ConfigInvalidException Throws if we get a malformed config file
*/
public void createDownstreamMerges(MultipleDownstreamMergeInput mdsMergeInput)
- throws RestApiException, FailedMergeException {
+ throws RestApiException, FailedMergeException, ConfigInvalidException {
Map<String, String> failedMerges = new HashMap<String, String>();
List<Integer> existingDownstream;
@@ -406,17 +393,8 @@
gApi.changes().create(downstreamChangeInput);
}
- private void loadConfig() throws IOException, RestApiException {
- try {
- config.loadConfig();
- } catch (IOException | RestApiException e) {
- log.error("Config failed to sync!", e);
- throw e;
- }
- }
-
private void automergeChanges(ChangeInfo change, RevisionInfo revisionInfo)
- throws RestApiException, IOException {
+ throws RestApiException, IOException, ConfigInvalidException {
if (revisionInfo.draft != null && revisionInfo.draft) {
log.debug("Patchset {} is draft change, ignoring.", revisionInfo.commit.commit);
return;
@@ -456,7 +434,7 @@
createMergesAndHandleConflicts(mdsMergeInput);
}
- private void abandonDownstream(ChangeInfo change, String revision) {
+ private void abandonDownstream(ChangeInfo change, String revision) throws ConfigInvalidException {
try {
Set<String> downstreamBranches = config.getDownstreamBranches(change.branch, change.project);
if (downstreamBranches.isEmpty()) {
@@ -477,7 +455,8 @@
}
}
- private void updateVote(ChangeInfo change, String label, short vote) throws RestApiException {
+ private void updateVote(ChangeInfo change, String label, short vote)
+ throws RestApiException, ConfigInvalidException {
if (label.equals(config.getAutomergeLabel())) {
log.debug("Not updating automerge label, as it blocks when there is a merge conflict.");
return;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/LoadedConfig.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/LoadedConfig.java
deleted file mode 100644
index 290b2bf..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/LoadedConfig.java
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (C) 2016 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.googlesource.gerrit.plugins.automerger;
-
-import com.google.common.base.Joiner;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.restapi.BinaryResult;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.re2j.Pattern;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.Yaml;
-
-/** The loaded configuration stored in memory. */
-public class LoadedConfig {
- private static final Logger log = LoggerFactory.getLogger(LoadedConfig.class);
-
- private final Map<String, Object> global;
- private final Map<String, Map<String, ?>> config;
- private final Map<String, String> defaultManifestInfo;
- private final Pattern blankMergePattern;
- private final Pattern alwaysBlankMergePattern;
-
- public LoadedConfig() {
- global = Collections.emptyMap();
- config = Collections.emptyMap();
- defaultManifestInfo = Collections.emptyMap();
- blankMergePattern = null;
- alwaysBlankMergePattern = null;
- }
-
- public LoadedConfig(
- GerritApi gApi,
- String configProject,
- String configProjectBranch,
- String configFilename,
- List<String> configOptionKeys)
- throws IOException, RestApiException {
- log.info(
- "Loading config file from project {} on branch {} and filename {}",
- configProject,
- configProjectBranch,
- configFilename);
- BinaryResult configFile =
- gApi.projects().name(configProject).branch(configProjectBranch).file(configFilename);
- String configFileString = configFile.asString();
- config = (Map<String, Map<String, ?>>) (new Yaml().load(configFileString));
- global = (Map<String, Object>) config.get("global");
- defaultManifestInfo = (Map<String, String>) global.get("manifest");
-
- blankMergePattern = getConfigPattern("blank_merge");
- alwaysBlankMergePattern = getConfigPattern("always_blank_merge");
- log.info("Finished syncing automerger config.");
- }
-
- /**
- * Checks to see if we should skip the change.
- *
- * <p>If the commit message matches the alwaysBlankMergePattern, always return true. If the commit
- * message matches the blankMergePattern and merge_all is false for this pair of branches, return
- * true.
- *
- * @param fromBranch Branch we are merging from.
- * @param toBranch Branch we are merging to.
- * @param commitMessage Commmit message of the original change.
- * @return Whether or not to merge with "-s ours".
- */
- public boolean isSkipMerge(String fromBranch, String toBranch, String commitMessage) {
- // If regex matches always_blank_merge (DO NOT MERGE ANYWHERE), skip.
- if (alwaysBlankMergePattern != null && alwaysBlankMergePattern.matches(commitMessage)) {
- return true;
- }
-
- // If regex matches blank_merge (DO NOT MERGE), skip iff merge_all is false
- if (blankMergePattern != null && blankMergePattern.matches(commitMessage)) {
- Map<String, Object> mergePairConfig = getMergeConfig(fromBranch, toBranch);
- if (mergePairConfig != null) {
- boolean isMergeAll = (boolean) mergePairConfig.getOrDefault("merge_all", false);
- return !isMergeAll;
- }
- return true;
- }
- return false;
- }
-
- /**
- * Gets the merge configuration for this branch.
- *
- * @param fromBranch Branch we are merging from.
- * @return A map of config keys to their values, or a map of "to branches" to a map of config keys
- * to their values.
- */
- public Map<String, Object> getMergeConfig(String fromBranch) {
- return getBranches().get(fromBranch);
- }
-
- /**
- * Gets the merge configuration for a pair of branches.
- *
- * @param fromBranch Branch we are merging from.
- * @param toBranch Branch we are merging to.
- * @return Map of configuration keys to their values.
- */
- public Map<String, Object> getMergeConfig(String fromBranch, String toBranch) {
- Map<String, Object> fromBranchConfig = getBranches().get(fromBranch);
- if (fromBranchConfig == null) {
- return Collections.emptyMap();
- }
- return (Map<String, Object>) fromBranchConfig.get(toBranch);
- }
-
- /**
- * Gets all the branches and their configuration information.
- *
- * @return A map of from branches to their configuration maps.
- */
- public Map<String, Map<String, Object>> getBranches() {
- return (Map<String, Map<String, Object>>)
- config.getOrDefault("branches", Collections.<String, Map<String, Object>>emptyMap());
- }
-
- /**
- * Gets the global config.
- *
- * @return A map of configuration keys to their values.
- */
- public Map<String, Object> getGlobal() {
- return global;
- }
-
- /**
- * Gets the default manifest information.
- *
- * @return A map of configuration keys to their default values.
- */
- public Map<String, String> getDefaultManifestInfo() {
- return defaultManifestInfo;
- }
-
- /**
- * Gets the automerge label (i.e. what to vote -1 on when we hit a merge conflict)
- *
- * @return The automerge label (by default, the String "Verified").
- */
- public String getAutomergeLabel() {
- return (String) global.getOrDefault("automerge_label", "Verified");
- }
-
- /**
- * Gets the value of a global attribute.
- *
- * @param key A configuration key that is defined in the config.
- * @return The value of the global attribute
- */
- public Object getGlobalAttribute(String key) {
- return global.get(key);
- }
-
- /**
- * Gets the value of a global attribute, or the default value if it cannot be found.
- *
- * @param key A configuration key that is defined in the config.
- * @param def The default value if we cannot find it in the config.
- * @return The value of the global attribute, or the default value if it cannot be found.
- */
- public Object getGlobalAttributeOrDefault(String key, Object def) {
- return global.getOrDefault(key, def);
- }
-
- private Pattern getConfigPattern(String key) {
- Object patterns = global.get(key);
- if (patterns != null) {
- Set<String> mergeStrings = new HashSet<>((List<String>) patterns);
- return Pattern.compile(Joiner.on("|").join(mergeStrings), Pattern.DOTALL);
- }
- return null;
- }
-}
diff --git a/src/main/resources/config/config_keys.yaml b/src/main/resources/config/config_keys.yaml
deleted file mode 100644
index 6008124..0000000
--- a/src/main/resources/config/config_keys.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-config_project: tools/automerger
-config_project_branch: master
-config_filename: config.yaml
-global_keys:
-- always_blank_merge
-- blank_merge
-- manifest
-config_option_keys:
-- manifest
-- merge_all
-- merge_manifest
-- set_projects
-- ignore_projects
-- add_projects
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderTest.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderTest.java
index 4bf316a..56348db 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderTest.java
@@ -14,34 +14,46 @@
package com.googlesource.gerrit.plugins.automerger;
+import static com.google.common.truth.Truth.assertThat;
+
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import java.io.IOException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.PluginConfigFactory;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
-import static com.google.common.truth.Truth.assertThat;
-
+@RunWith(MockitoJUnitRunner.class)
public class ConfigLoaderTest {
- protected GerritApi gApiMock;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private GerritApi gApiMock;
+
private ConfigLoader configLoader;
- private String configString;
- private String manifestString;
- private String firstDownstreamManifestString;
- private String secondDownstreamManifestString;
+ private AllProjectsName allProjectsName;
+ @Mock private PluginConfigFactory cfgFactory;
+
+ @Rule public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
- gApiMock = Mockito.mock(GerritApi.class, Mockito.RETURNS_DEEP_STUBS);
- mockFile("config.yaml", "tools/automerger", "master", "config.yaml");
+ allProjectsName = new AllProjectsName("All-Projects");
+ mockFile("automerger.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
mockFile("default.xml", "platform/manifest", "master", "default.xml");
mockFile("ds_one.xml", "platform/manifest", "ds_one", "default.xml");
mockFile("ds_two.xml", "platform/manifest", "ds_two", "default.xml");
@@ -58,7 +70,16 @@
}
private void loadConfig() throws Exception {
- configLoader = new ConfigLoader(gApiMock);
+ Config cfg = new Config();
+ cfg.fromText(
+ gApiMock
+ .projects()
+ .name(allProjectsName.get())
+ .branch(RefNames.REFS_CONFIG)
+ .file("automerger.config")
+ .asString());
+ Mockito.when(cfgFactory.getProjectPluginConfig(allProjectsName, "automerger")).thenReturn(cfg);
+ configLoader = new ConfigLoader(gApiMock, allProjectsName, "automerger", cfgFactory);
}
@Test
@@ -81,6 +102,34 @@
}
@Test
+ public void getProjectsInScope_missingSourceManifest() throws Exception {
+ mockFile("alternate.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
+ Mockito.when(gApiMock.projects().name("platform/manifest").branch("master"))
+ .thenThrow(new ResourceNotFoundException());
+ loadConfig();
+ assertThat(configLoader.getProjectsInScope("master", "ds_one").isEmpty()).isTrue();
+ }
+
+ @Test
+ public void getProjectsInScope_ignoreSourceManifest() throws Exception {
+ mockFile("alternate.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
+ loadConfig();
+ Set<String> expectedProjects = new HashSet<String>();
+ expectedProjects.add("platform/whee");
+ expectedProjects.add("whuu");
+ assertThat(configLoader.getProjectsInScope("master", "ds_two")).isEqualTo(expectedProjects);
+ }
+
+ @Test
+ public void getProjectsInScope_ignoreSourceManifestWithMissingDestManifest() throws Exception {
+ mockFile("alternate.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
+ Mockito.when(gApiMock.projects().name("platform/manifest").branch("ds_four"))
+ .thenThrow(new ResourceNotFoundException());
+ loadConfig();
+ assertThat(configLoader.getProjectsInScope("master", "ds_four").isEmpty()).isTrue();
+ }
+
+ @Test
public void isSkipMergeTest_noSkip() throws Exception {
loadConfig();
assertThat(configLoader.isSkipMerge("ds_two", "ds_three", "bla")).isFalse();
@@ -109,11 +158,26 @@
}
@Test
- public void isSkipMergeTest_alwaysBlankMergeNull() throws Exception {
- mockFile("alternate_config.yaml", "tools/automerger", "master", "config.yaml");
+ public void isSkipMergeTest_alwaysBlankMergeDummy() throws Exception {
+ mockFile("alternate.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
loadConfig();
- assertThat(
- configLoader.isSkipMerge("master", "ds_two", "test test \n \n DO NOT MERGE ANYWHERE"))
+ assertThat(configLoader.isSkipMerge("master", "ds_two", "test test")).isFalse();
+ }
+
+ @Test
+ public void isSkipMergeTest_alwaysBlankMergeNull() throws Exception {
+ mockFile("alternate.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
+ loadConfig();
+ assertThat(configLoader.isSkipMerge("master", "ds_two", "test test \n \n BLANK ANYWHERE"))
+ .isTrue();
+ }
+
+ @Test
+ public void isSkipMergeTest_noBlankMergeSpecified() throws Exception {
+ mockFile(
+ "empty_blank.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
+ loadConfig();
+ assertThat(configLoader.isSkipMerge("master", "ds_one", "test test \n \n DO NOT MERGE"))
.isFalse();
}
@@ -134,27 +198,13 @@
.isEqualTo(expectedBranches);
}
- @Test(expected = IOException.class)
- public void downstreamBranchesTest_IOException() throws Exception {
- Mockito.when(
- gApiMock
- .projects()
- .name("platform/manifest")
- .branch("master")
- .file("default.xml")
- .asString())
- .thenThrow(new IOException("!"));
+ @Test
+ public void downstreamBranchesTest_configException() throws Exception {
+ mockFile("wrong.config", allProjectsName.get(), RefNames.REFS_CONFIG, "automerger.config");
loadConfig();
- Set<String> expectedBranches = new HashSet<String>();
- configLoader.getDownstreamBranches("master", "platform/some/project");
- }
-
- @Test(expected = RestApiException.class)
- public void downstreamBranchesTest_restApiException() throws Exception {
- Mockito.when(gApiMock.projects().name("platform/manifest").branch("master"))
- .thenThrow(new RestApiException("!"));
- loadConfig();
+ thrown.expect(ConfigInvalidException.class);
+ thrown.expectMessage("Automerger config branch pair malformed: master..ds_one");
configLoader.getDownstreamBranches("master", "platform/some/project");
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorTest.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorTest.java
index 5be92c4..54198f8 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorTest.java
@@ -34,24 +34,28 @@
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+@RunWith(MockitoJUnitRunner.class)
public class DownstreamCreatorTest {
private final String changeId = "testid";
private final String changeProject = "testproject";
- private final String changeBranch = "testbranch";
private final String changeTopic = "testtopic";
private final String changeSubject = "testmessage";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private GerritApi gApiMock;
+
private DownstreamCreator ds;
- private ConfigLoader configMock;
@Before
public void setUp() throws Exception {
- gApiMock = Mockito.mock(GerritApi.class, Mockito.RETURNS_DEEP_STUBS);
- configMock = Mockito.mock(ConfigLoader.class);
- ds = new DownstreamCreator(gApiMock, configMock);
+ ds = new DownstreamCreator(gApiMock, Mockito.mock(ConfigLoader.class));
}
private List<ChangeInfo> mockChangeInfoList(String upstreamBranch) {
@@ -70,15 +74,14 @@
ChangeInfo info = Mockito.mock(ChangeInfo.class);
info._number = number;
info.currentRevision = "info" + number;
- info.revisions = Mockito.mock(Map.class);
+ info.revisions = new HashMap<>();
RevisionInfo revisionInfoMock = Mockito.mock(RevisionInfo.class);
CommitInfo commit = Mockito.mock(CommitInfo.class);
commit.parents = ImmutableList.of(parent1, parent2);
revisionInfoMock.commit = commit;
- Mockito.when(info.revisions.get(info.currentRevision)).thenReturn(revisionInfoMock);
-
+ info.revisions.put(info.currentRevision, revisionInfoMock);
return info;
}
@@ -112,9 +115,9 @@
ArgumentCaptor<ChangeInput> changeInputCaptor = ArgumentCaptor.forClass(ChangeInput.class);
Mockito.verify(gApiMock.changes()).create(changeInputCaptor.capture());
ChangeInput changeInput = changeInputCaptor.getValue();
- assertThat(changeProject).isEqualTo(changeInput.project);
- assertThat("testds").isEqualTo(changeInput.branch);
- assertThat(changeTopic).isEqualTo(changeInput.topic);
+ assertThat(changeInput.project).isEqualTo(changeProject);
+ assertThat(changeInput.branch).isEqualTo("testds");
+ assertThat(changeInput.topic).isEqualTo(changeTopic);
assertThat(changeInput.merge.source).isEqualTo(currentRevision);
assertThat(changeInput.merge.strategy).isEqualTo("recursive");
@@ -152,9 +155,9 @@
ArgumentCaptor<ChangeInput> changeInputCaptor = ArgumentCaptor.forClass(ChangeInput.class);
Mockito.verify(gApiMock.changes()).create(changeInputCaptor.capture());
ChangeInput changeInput = changeInputCaptor.getValue();
- assertThat(changeProject).isEqualTo(changeInput.project);
- assertThat("testds").isEqualTo(changeInput.branch);
- assertThat(changeTopic).isEqualTo(changeInput.topic);
+ assertThat(changeInput.project).isEqualTo(changeProject);
+ assertThat(changeInput.branch).isEqualTo("testds");
+ assertThat(changeInput.topic).isEqualTo(changeTopic);
assertThat(changeInput.merge.source).isEqualTo(currentRevision);
// Check that it was actually skipped
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config
new file mode 100644
index 0000000..eaaf49f
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate.config
@@ -0,0 +1,15 @@
+[automerger "master:ds_one"]
+[automerger "master:ds_two"]
+ mergeAll = true
+ ignoreSourceManifest = true
+[automerger "ds_two:ds_three"]
+ setProjects = platform/some/project
+[automerger "master:ds_four"]
+ mergeAll = true
+ ignoreSourceManifest = true
+[global]
+ alwaysBlankMerge = .*BLANK ANYWHERE.*
+ blankMerge = .*DO NOT MERGE.*
+ manifestFile = default.xml
+ manifestProject = platform/manifest
+ ignoreProjects = platform/ignore/me
\ No newline at end of file
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate_config.yaml b/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate_config.yaml
deleted file mode 100644
index 7c8c51a..0000000
--- a/src/test/resources/com/googlesource/gerrit/plugins/automerger/alternate_config.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-branches:
- master:
- ds_one:
- add_projects:
- - platform/added/project
- ignore_projects:
- - whoo
- ds_two:
- merge_all: true
- add_projects:
- - platform/added/project
- ignore_projects:
- - whoo
- set_projects:
- - platform/some/project
- - platform/other/project
- ds_two:
- ds_three:
- set_projects:
- - platform/some/project
-global:
- blank_merge:
- - .*DO NOT MERGE.*
- manifest:
- file: default.xml
- project: platform/manifest
- ignore_projects:
- - platform/ignore/me
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/automerger.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/automerger.config
new file mode 100644
index 0000000..ea54637
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/automerger.config
@@ -0,0 +1,18 @@
+[automerger "master:ds_one"]
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+[automerger "master:ds_two"]
+ mergeAll = true
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+ setProjects = platform/some/project
+ setProjects = platform/other/project
+[automerger "ds_two:ds_three"]
+ setProjects = platform/some/project
+[global]
+ alwaysBlankMerge = .*Import translations. DO NOT MERGE.*
+ alwaysBlankMerge = .*DO NOT MERGE ANYWHERE.*
+ blankMerge = .*DO NOT MERGE.*
+ manifestFile = default.xml
+ manifestProject = platform/manifest
+ ignoreProjects = platform/ignore/me
\ No newline at end of file
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/config.yaml b/src/test/resources/com/googlesource/gerrit/plugins/automerger/config.yaml
deleted file mode 100644
index 271d875..0000000
--- a/src/test/resources/com/googlesource/gerrit/plugins/automerger/config.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-branches:
- master:
- ds_one:
- add_projects:
- - platform/added/project
- ignore_projects:
- - whoo
- ds_two:
- merge_all: true
- add_projects:
- - platform/added/project
- ignore_projects:
- - whoo
- set_projects:
- - platform/some/project
- - platform/other/project
- ds_two:
- ds_three:
- set_projects:
- - platform/some/project
-global:
- always_blank_merge:
- - .*Import translations\.\sDO NOT MERGE.*
- - .*DO NOT MERGE ANYWHERE.*
- blank_merge:
- - .*DO NOT MERGE.*
- manifest:
- file: default.xml
- project: platform/manifest
- ignore_projects:
- - platform/ignore/me
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/empty_blank.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/empty_blank.config
new file mode 100644
index 0000000..41efd40
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/empty_blank.config
@@ -0,0 +1,15 @@
+[automerger "master:ds_one"]
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+[automerger "master:ds_two"]
+ mergeAll = true
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+ setProjects = platform/some/project
+ setProjects = platform/other/project
+[automerger "ds_two:ds_three"]
+ setProjects = platform/some/project
+[global]
+ manifestFile = default.xml
+ manifestProject = platform/manifest
+ ignoreProjects = platform/ignore/me
\ No newline at end of file
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/wrong.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/wrong.config
new file mode 100644
index 0000000..dfd87b2
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/wrong.config
@@ -0,0 +1,17 @@
+[automerger "master..ds_one"]
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+[automerger "master:ds_two"]
+ mergeAll = true
+ addProjects = platform/added/project
+ ignoreProjects = whoo
+ setProjects = platform/some/project
+ setProjects = platform/other/project
+[automerger "ds_two:ds_three"]
+ setProjects = platform/some/project
+[global]
+ alwaysBlankMerge = .*BLANK ANYWHERE.*
+ blankMerge = .*DO NOT MERGE.*
+ manifestFile = default.xml
+ manifestProject = platform/manifest
+ ignoreProjects = platform/ignore/me
\ No newline at end of file