Upstream +2 should not override merge conflict -2.
Change-Id: Id870fe4706b1231befed1dea34a7b618e2ecc0c1
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 e6e298d..618385b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/ConfigLoader.java
@@ -256,6 +256,11 @@
return getConfig().getBoolean("global", "disableMinAutomergeVote", false);
}
+ public boolean contextUserIdIsSet() throws ConfigInvalidException {
+ int contextUserId = getConfig().getInt("global", "contextUserId", -1);
+ return contextUserId > 0;
+ }
+
public Account.Id getContextUserId() throws ConfigInvalidException {
int contextUserId = getConfig().getInt("global", "contextUserId", -1);
if (contextUserId > 0) {
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 5a8e380..0fbde41 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
@@ -82,6 +82,7 @@
protected GerritApi gApi;
protected ConfigLoader config;
protected CurrentUser user;
+ protected MergeValidator mergeValidator;
private final OneOffRequestContext oneOffRequestContext;
@@ -91,6 +92,7 @@
this.gApi = gApi;
this.config = config;
this.oneOffRequestContext = oneOffRequestContext;
+ mergeValidator = new MergeValidator(gApi, config);
}
/**
@@ -215,21 +217,34 @@
for (Integer changeNumber : existingDownstream) {
ChangeInfo downstreamChange =
gApi.changes().id(changeNumber).get(EnumSet.of(ListChangesOption.CURRENT_REVISION));
- for (Map.Entry<String, LabelInfo> labelEntry : labels.entrySet()) {
- if (labelEntry.getValue().all.size() > 0) {
- OptionalInt maxVote =
- labelEntry
- .getValue()
- .all
- .stream()
- .filter(o -> o.value != null)
- .mapToInt(i -> i.value)
- .max();
+ if (shouldReceivePropagatedVote(downstreamChange)) {
+ for (Map.Entry<String, LabelInfo> labelEntry : labels.entrySet()) {
+ if (labelEntry.getValue().all.size() > 0) {
+ OptionalInt maxVote =
+ labelEntry
+ .getValue()
+ .all
+ .stream()
+ .filter(o -> o.value != null)
+ .mapToInt(i -> i.value)
+ .max();
- if (maxVote.isPresent()) {
- updateVote(downstreamChange, labelEntry.getKey(), (short) maxVote.getAsInt());
+ if (maxVote.isPresent()) {
+ updateVote(downstreamChange, labelEntry.getKey(), (short) maxVote.getAsInt());
+ }
}
}
+ } else {
+ try {
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.message(
+ "Downstream change is missing. Vote will not be propagated to this change "
+ + "until all merge conflicts are resolved and added to the topic.");
+ reviewInput.notify = NotifyHandling.NONE;
+ gApi.changes().id(downstreamChange._number).revision(CURRENT).review(reviewInput);
+ } catch (RestApiException e) {
+ log.error("Failed to comment on change for automerger plugin.", e);
+ }
}
}
} catch (RestApiException | InvalidQueryParameterException e) {
@@ -333,16 +348,10 @@
reviewInput.labels = labels;
// if this fails, i.e. -2 is restricted, catch it and still post message without a vote.
try {
- gApi.changes()
- .id(mdsMergeInput.changeNumber)
- .revision(CURRENT)
- .review(reviewInput);
+ gApi.changes().id(mdsMergeInput.changeNumber).revision(CURRENT).review(reviewInput);
} catch (AuthException e) {
reviewInput.labels = null;
- gApi.changes()
- .id(mdsMergeInput.changeNumber)
- .revision(CURRENT)
- .review(reviewInput);
+ gApi.changes().id(mdsMergeInput.changeNumber).revision(CURRENT).review(reviewInput);
}
}
}
@@ -578,6 +587,21 @@
return null;
}
+ private boolean shouldReceivePropagatedVote(ChangeInfo change)
+ throws RestApiException, IOException, ConfigInvalidException, InvalidQueryParameterException {
+ boolean shouldPropagate = true;
+ // Check change is created by contextUser if contextUser is set
+ if (config.contextUserIdIsSet()) {
+ Integer accountId = change.revisions.get(change.currentRevision).uploader._accountId;
+
+ shouldPropagate = accountId == config.getContextUserId().get();
+ }
+ // Check that the change does not have any downstream conflicts
+ shouldPropagate =
+ shouldPropagate && mergeValidator.getMissingDownstreamMerges(change).isEmpty();
+ return shouldPropagate;
+ }
+
private void automergeChanges(ChangeInfo change, RevisionInfo revisionInfo)
throws RestApiException, IOException, ConfigInvalidException, InvalidQueryParameterException,
OrmException {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
index 1a041b7..62e0019 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/ConfigLoaderIT.java
@@ -245,6 +245,18 @@
assertThat(configLoader.getContextUserId()).isEqualTo(user.get().getAccountId());
}
+ @Test
+ public void contextUserIdIsSetTest() throws Exception {
+ defaultSetup("context_user.config");
+ assertThat(configLoader.contextUserIdIsSet()).isEqualTo(true);
+ }
+
+ @Test
+ public void contextUserIdIsSetTest_noContextUser() throws Exception {
+ defaultSetup("automerger.config");
+ assertThat(configLoader.contextUserIdIsSet()).isEqualTo(false);
+ }
+
private void setupTestRepo(
String resourceName, Project.NameKey projectNameKey, String branchName, String filename)
throws Exception {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
index 552683d..89bbad0 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
@@ -912,6 +912,81 @@
assertThat(getVote(dsTwoChange, "Code-Review").value).isEqualTo(1);
}
+ @Test
+ public void testContextUser_conflict() throws Exception {
+ Project.NameKey manifestNameKey = defaultSetup();
+ // Create initial change
+ PushOneCommit.Result initialResult = createChange("subject", "filename", "echo Hello");
+ // Project name is scoped by test, so we need to get it from our initial change
+ Project.NameKey projectNameKey = initialResult.getChange().project();
+ String projectName = projectNameKey.get();
+ createBranch(new Branch.NameKey(projectName, "ds_one"));
+ createBranch(new Branch.NameKey(projectName, "ds_two"));
+ initialResult.assertOkStatus();
+ merge(initialResult);
+ // Reset to create a sibling
+ ObjectId initial = repo().exactRef("HEAD").getLeaf().getObjectId();
+ testRepo.reset(initial);
+ // Set up a merge conflict between master and ds_one
+ PushOneCommit.Result ds2Result =
+ createChange(
+ testRepo, "ds_two", "subject", "filename", "echo \"Hello asdfsd World\"", "randtopic");
+ ds2Result.assertOkStatus();
+ merge(ds2Result);
+ // Reset to allow our merge conflict to come
+ testRepo.reset(initial);
+
+ // Create normalUserGroup, containing current user, and contextUserGroup, containing contextUser
+ String normalUserGroup = createGroup("normalUserGroup");
+ gApi.groups().id(normalUserGroup).addMembers(user.get().getAccountId().toString());
+ AccountApi contextUserApi = gApi.accounts().create("aContextUser");
+ String contextUserGroup = createGroup("contextUserGroup");
+ gApi.groups().id(contextUserGroup).addMembers(contextUserApi.get().name);
+
+ // Grant +2 to context user, since it doesn't have it by default
+ grantLabel(
+ "Code-Review",
+ -2,
+ 2,
+ projectNameKey,
+ "refs/heads/*",
+ false,
+ AccountGroup.UUID.parse(gApi.groups().id(contextUserGroup).get().id),
+ false);
+ pushContextUserConfig(manifestNameKey.get(), projectName, contextUserApi.get()._accountId);
+
+ // After we upload our config, we upload a new patchset to create the downstreams
+ PushOneCommit.Result result =
+ createChange("subject", "filename", "echo 'Hello World!'", "sometopic");
+ result.assertOkStatus();
+ // Check that there are the correct number of changes in the topic
+ List<ChangeInfo> changesInTopic =
+ gApi.changes()
+ .query("topic:" + gApi.changes().id(result.getChangeId()).topic())
+ .withOptions(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT)
+ .get();
+ assertThat(changesInTopic).hasSize(2);
+
+ List<ChangeInfo> sortedChanges = sortedChanges(changesInTopic);
+
+ ChangeInfo dsOneChangeInfo = sortedChanges.get(0);
+ assertThat(dsOneChangeInfo.branch).isEqualTo("ds_one");
+ ChangeApi dsOneChange = gApi.changes().id(dsOneChangeInfo._number);
+ // This is -2 because there is a merge conflict downstream
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(-2);
+ assertThat(getVote(dsOneChange, "Code-Review").tag).isEqualTo("autogenerated:MergeConflict");
+
+ ChangeInfo masterChangeInfo = sortedChanges.get(1);
+ ChangeApi masterChange = gApi.changes().id(masterChangeInfo._number);
+ assertThat(getVote(masterChange, "Code-Review").value).isEqualTo(0);
+ assertThat(masterChangeInfo.branch).isEqualTo("master");
+
+ // Test that +2 on master won't propagate to ds_one, as conflict is not resolved.
+ approve(masterChangeInfo.id);
+ assertThat(getVote(masterChange, "Code-Review").value).isEqualTo(2);
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(-2);
+ }
+
private Project.NameKey defaultSetup() throws Exception {
Project.NameKey manifestNameKey = createProject("platform/manifest");
setupTestRepo("default.xml", manifestNameKey, "master", "default.xml");
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
index bb2d262..aeb71d9 100644
--- a/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
@@ -14,4 +14,5 @@
manifestFile = default.xml
manifestProject = platform/manifest
ignoreProjects = platform/ignore/me
- contextUserId = 102304
\ No newline at end of file
+ contextUserId = 102304
+ disableMaxAutomergeVote = true
\ No newline at end of file