Merge "commit-msg: Adapt to awk behavior change on Cygwin/MSYS" into stable-2.14
diff --git a/Documentation/dev-build-plugins.txt b/Documentation/dev-build-plugins.txt
index c777327..072c22c 100644
--- a/Documentation/dev-build-plugins.txt
+++ b/Documentation/dev-build-plugins.txt
@@ -95,6 +95,51 @@
)
----
+=== Bundle custom plugin in release.war ===
+
+To bundle custom plugin(s) in the link:dev-bazel.html#release[release.war] artifact,
+add them to the CUSTOM_PLUGINS list in `tools/bzl/plugins.bzl`.
+
+Example of `tools/bzl/plugins.bzl` with custom plugin `my-plugin`:
+
+----
+CORE_PLUGINS = [
+ "commit-message-length-validator",
+ "download-commands",
+ "hooks",
+ "replication",
+ "reviewnotes",
+ "singleusergroup",
+]
+
+CUSTOM_PLUGINS = [
+ "my-plugin",
+]
+
+CUSTOM_PLUGINS_TEST_DEPS = [
+ # Add custom core plugins with tests deps here
+]
+----
+
+If the plugin(s) being bundled in the release have external dependencies, include them
+in `plugins/external_plugin_deps`. You should alias `external_plugin_deps()` so it
+can be imported for multiple plugins. For example:
+
+----
+load(":my-plugin/external_plugin_deps.bzl", my_plugin="external_plugin_deps")
+load(":my-other-plugin/external_plugin_deps.bzl", my_other_plugin="external_plugin_deps")
+
+def external_plugin_deps():
+ my_plugin()
+ my_other_plugin()
+----
+
+[NOTE]
+Since `tools/bzl/plugins.bzl` and `plugins/external_plugin_deps.bzl` are part of
+Gerrit's source code and the version of the war is based on the state of the git
+repository that is built; you should commit this change before building, otherwise
+the version will be marked as 'dirty'.
+
== Bazel standalone driven
Only few plugins support that mode for now:
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 72452b3..e175a7e 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1028,6 +1028,14 @@
return createGroup(name, "Administrators");
}
+ protected String createGroupWithRealName(String name) throws Exception {
+ GroupInput in = new GroupInput();
+ in.name = name;
+ in.ownerId = "Administrators";
+ gApi.groups().create(in);
+ return name;
+ }
+
protected String createGroup(String name, String owner) throws Exception {
name = name(name);
GroupInput in = new GroupInput();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 5c5d15d..1442bed 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -62,6 +62,7 @@
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
import com.google.gerrit.extensions.api.changes.RevisionApi;
+import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.client.ChangeStatus;
@@ -1017,6 +1018,98 @@
}
@Test
+ public void addReviewerThatIsNotPerfectMatch() throws Exception {
+ TestTimeUtil.resetWithClockStep(1, SECONDS);
+ PushOneCommit.Result r = createChange();
+ ChangeResource rsrc = parseResource(r);
+ String oldETag = rsrc.getETag();
+ Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
+
+ //create a group named "ab" with one user: testUser
+ TestAccount testUser = accounts.create("abcd", "abcd@test.com", "abcd");
+ String testGroup = createGroupWithRealName("ab");
+ GroupApi groupApi = gApi.groups().id(testGroup);
+ groupApi.description("test group");
+ groupApi.addMembers(user.fullName);
+
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = "abc";
+ gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
+
+ List<Message> messages = sender.getMessages();
+ assertThat(messages).hasSize(1);
+ Message m = messages.get(0);
+ assertThat(m.rcpt()).containsExactly(testUser.emailAddress);
+ assertThat(m.body()).contains("Hello " + testUser.fullName + ",\n");
+ assertThat(m.body()).contains("I'd like you to do a code review.");
+ assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
+ assertMailReplyTo(m, testUser.email);
+ ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+
+ // When NoteDb is enabled adding a reviewer records that user as reviewer
+ // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
+ // approval on the change which is treated as CC when the ChangeInfo is
+ // created.
+ Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+ assertThat(reviewers).isNotNull();
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.getId().get());
+
+ // Ensure ETag and lastUpdatedOn are updated.
+ rsrc = parseResource(r);
+ assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
+ assertThat(rsrc.getChange().getLastUpdatedOn()).isNotEqualTo(oldTs);
+ }
+
+ @Test
+ public void addGroupAsReviewersWhenANotPerfectMatchedUserExists() throws Exception {
+ TestTimeUtil.resetWithClockStep(1, SECONDS);
+ PushOneCommit.Result r = createChange();
+ ChangeResource rsrc = parseResource(r);
+ String oldETag = rsrc.getETag();
+ Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
+
+ //create a group named "us" with one user: testUser
+ TestAccount testUser = accounts.create("testUser", "testUser@test.com", "testUser");
+ String testGroup =
+ createGroupWithRealName(user.fullName.substring(0, user.fullName.length() / 2));
+ GroupApi groupApi = gApi.groups().id(testGroup);
+ groupApi.description("test group");
+ groupApi.addMembers(testUser.fullName);
+
+ //ensure that user "user" is not in the group
+ groupApi.removeMembers(user.fullName);
+
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = testGroup;
+ gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
+
+ List<Message> messages = sender.getMessages();
+ assertThat(messages).hasSize(1);
+ Message m = messages.get(0);
+ assertThat(m.rcpt()).containsExactly(testUser.emailAddress);
+ assertThat(m.body()).contains("Hello " + testUser.fullName + ",\n");
+ assertThat(m.body()).contains("I'd like you to do a code review.");
+ assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
+ assertMailReplyTo(m, testUser.email);
+ ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+
+ // When NoteDb is enabled adding a reviewer records that user as reviewer
+ // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
+ // approval on the change which is treated as CC when the ChangeInfo is
+ // created.
+ Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+ assertThat(reviewers).isNotNull();
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.getId().get());
+
+ // Ensure ETag and lastUpdatedOn are updated.
+ rsrc = parseResource(r);
+ assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
+ assertThat(rsrc.getChange().getLastUpdatedOn()).isNotEqualTo(oldTs);
+ }
+
+ @Test
public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists() throws Exception {
assume().that(notesMigration.enabled()).isTrue();
assume().that(notesMigration.changePrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index 7031d51..debd32a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -170,25 +170,37 @@
public Addition prepareApplication(
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup)
throws OrmException, RestApiException, IOException {
- Account.Id accountId;
+ IdentifiedUser user = null;
+ boolean accountFound = true;
+ boolean isExactMatch = false;
try {
- accountId = accounts.parse(input.reviewer).getAccountId();
+ user = accounts.parse(input.reviewer);
+ if (input.reviewer.equalsIgnoreCase(user.getName())
+ || input.reviewer.equals(String.valueOf(user.getAccountId()))) {
+ isExactMatch = true;
+ }
} catch (UnprocessableEntityException e) {
- if (allowGroup) {
- try {
- return putGroup(rsrc, input);
- } catch (UnprocessableEntityException e2) {
+ accountFound = false;
+ }
+
+ if (allowGroup && !isExactMatch) {
+ try {
+ return putGroup(rsrc, input);
+ } catch (UnprocessableEntityException e2) {
+ if (!accountFound) {
throw new UnprocessableEntityException(
MessageFormat.format(
ChangeMessages.get().reviewerNotFoundUserOrGroup, input.reviewer));
}
}
+ }
+ if (!accountFound) {
throw new UnprocessableEntityException(
MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, input.reviewer));
}
return putAccount(
input.reviewer,
- reviewerFactory.create(rsrc, accountId),
+ reviewerFactory.create(rsrc, user.getAccountId()),
input.state(),
input.notify,
notifyUtil.resolveAccounts(input.notifyDetails));