diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt index 34f39c8ca7..0cafc13ea7 100644 --- a/Documentation/config-project-config.txt +++ b/Documentation/config-project-config.txt @@ -291,6 +291,18 @@ Branches not listed in this section will not be included in the mergeability check. If the `branchOrder` section is not defined then the mergeability of a change into other branches will not be done. +[[reviewer-section]] +=== reviewer section + +Defines config options to adjust a project's reviewer workflow such as enabling +reviewers and CCs by email. + +[[reviewer.enableByEmail]]reviewer.enableByEmail:: ++ +A boolean indicating if reviewers and CCs that do not currently have a Gerrit +account can be added to a change by providing their email address. + +Defaults to `false`. [[file-groups]] == The file +groups+ diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java index cc91a4afe5..f6f9811370 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java @@ -31,6 +31,7 @@ public class ConfigInfo { public InheritedBooleanInfo enableSignedPush; public InheritedBooleanInfo requireSignedPush; public InheritedBooleanInfo rejectImplicitMerges; + public InheritedBooleanInfo enableReviewerByEmail; public MaxObjectSizeLimitInfo maxObjectSizeLimit; public SubmitType submitType; public ProjectState state; diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java index 3645fb9249..783069660a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java @@ -86,6 +86,7 @@ public class ProjectInfoScreen extends ProjectScreen { private ListBox enableSignedPush; private ListBox requireSignedPush; private ListBox rejectImplicitMerges; + private ListBox enableReviewerByEmail; private NpTextBox maxObjectSizeLimit; private Label effectiveMaxObjectSizeLimit; private Map> pluginConfigWidgets; @@ -191,6 +192,7 @@ public class ProjectInfoScreen extends ProjectScreen { requireChangeID.setEnabled(isOwner); rejectImplicitMerges.setEnabled(isOwner); maxObjectSizeLimit.setEnabled(isOwner); + enableReviewerByEmail.setEnabled(isOwner); if (pluginConfigWidgets != null) { for (Map widgetMap : pluginConfigWidgets.values()) { @@ -264,6 +266,10 @@ public class ProjectInfoScreen extends ProjectScreen { saveEnabler.listenTo(rejectImplicitMerges); grid.addHtml(AdminConstants.I.rejectImplicitMerges(), rejectImplicitMerges); + enableReviewerByEmail = newInheritedBooleanBox(); + saveEnabler.listenTo(enableReviewerByEmail); + grid.addHtml(AdminConstants.I.rejectImplicitMerges(), enableReviewerByEmail); + maxObjectSizeLimit = new NpTextBox(); saveEnabler.listenTo(maxObjectSizeLimit); effectiveMaxObjectSizeLimit = new Label(); @@ -395,6 +401,7 @@ public class ProjectInfoScreen extends ProjectScreen { setBool(requireSignedPush, result.requireSignedPush()); } setBool(rejectImplicitMerges, result.rejectImplicitMerges()); + setBool(enableReviewerByEmail, result.enableReviewerByEmail()); setSubmitType(result.submitType()); setState(result.state()); maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue()); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java index 738319d410..4eda46be30 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java @@ -57,6 +57,9 @@ public class ConfigInfo extends JavaScriptObject { public final native InheritedBooleanInfo rejectImplicitMerges() /*-{ return this.reject_implicit_merges; }-*/ ; + public final native InheritedBooleanInfo enableReviewerByEmail() + /*-{ return this.enable_reviewer_by_email; }-*/ ; + public final SubmitType submitType() { return SubmitType.valueOf(submitTypeRaw()); } diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java index ba83c5848b..0706032777 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java @@ -99,6 +99,8 @@ public final class Project { protected InheritableBoolean rejectImplicitMerges; + protected InheritableBoolean enableReviewerByEmail; + protected Project() {} public Project(Project.NameKey nameKey) { @@ -112,6 +114,7 @@ public final class Project { createNewChangeForAllNotInTarget = InheritableBoolean.INHERIT; enableSignedPush = InheritableBoolean.INHERIT; requireSignedPush = InheritableBoolean.INHERIT; + enableReviewerByEmail = InheritableBoolean.INHERIT; } public Project.NameKey getNameKey() { @@ -154,6 +157,10 @@ public final class Project { return rejectImplicitMerges; } + public InheritableBoolean getEnableReviewerByEmail() { + return enableReviewerByEmail; + } + public void setUseContributorAgreements(final InheritableBoolean u) { useContributorAgreements = u; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java index 61d8cfecf8..e3d45ac734 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java @@ -155,6 +155,9 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. ImmutableSet.of( "MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp", "PatchSetLock"); + private static final String REVIEWER = "reviewer"; + private static final String KEY_ENABLE_REVIEWER_BY_EMAIL = "enableByEmail"; + private static final String LEGACY_PERMISSION_PUSH_TAG = "pushTag"; private static final String LEGACY_PERMISSION_PUSH_SIGNED_TAG = "pushSignedTag"; @@ -182,6 +185,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. private boolean checkReceivedObjects; private Set sectionsWithUnknownPermissions; private boolean hasLegacyPermissions; + private boolean enableReviewerByEmail; public static ProjectConfig read(MetaDataUpdate update) throws IOException, ConfigInvalidException { @@ -435,6 +439,16 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. return checkReceivedObjects; } + /** @return the enableReviewerByEmail for this project, default is false. */ + public boolean getEnableReviewerByEmail() { + return enableReviewerByEmail; + } + + /** Set enableReviewerByEmail for this project, default is false. */ + public void setEnableReviewerByEmail(boolean val) { + enableReviewerByEmail = val; + } + /** * Check all GroupReferences use current group name, repairing stale ones. * @@ -526,6 +540,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. mimeTypes = new ConfiguredMimeTypes(projectName.get(), rc); loadPluginSections(rc); loadReceiveSection(rc); + loadReviewerSection(rc); } private void loadAccountsSection(Config rc, Map groupsByName) { @@ -933,6 +948,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. maxObjectSizeLimit = rc.getLong(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT, 0); } + private void loadReviewerSection(Config rc) { + enableReviewerByEmail = rc.getBoolean(REVIEWER, null, KEY_ENABLE_REVIEWER_BY_EMAIL, false); + } + private void loadPluginSections(Config rc) { pluginConfigs = new HashMap<>(); for (String plugin : rc.getSubsections(PLUGIN)) { @@ -1067,6 +1086,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. saveAccessSections(rc, keepGroups); saveNotifySections(rc, keepGroups); savePluginSections(rc, keepGroups); + saveReviewerSection(rc); groupList.retainUUIDs(keepGroups); saveLabelSections(rc); saveSubscribeSections(rc); @@ -1288,40 +1308,55 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. setBooleanConfigKey( rc, + LABEL, name, KEY_ALLOW_POST_SUBMIT, label.allowPostSubmit(), LabelType.DEF_ALLOW_POST_SUBMIT); setBooleanConfigKey( - rc, name, KEY_COPY_MIN_SCORE, label.isCopyMinScore(), LabelType.DEF_COPY_MIN_SCORE); - setBooleanConfigKey( - rc, name, KEY_COPY_MAX_SCORE, label.isCopyMaxScore(), LabelType.DEF_COPY_MAX_SCORE); + rc, + LABEL, + name, + KEY_COPY_MIN_SCORE, + label.isCopyMinScore(), + LabelType.DEF_COPY_MIN_SCORE); setBooleanConfigKey( rc, + LABEL, + name, + KEY_COPY_MAX_SCORE, + label.isCopyMaxScore(), + LabelType.DEF_COPY_MAX_SCORE); + setBooleanConfigKey( + rc, + LABEL, name, KEY_COPY_ALL_SCORES_ON_TRIVIAL_REBASE, label.isCopyAllScoresOnTrivialRebase(), LabelType.DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE); setBooleanConfigKey( rc, + LABEL, name, KEY_COPY_ALL_SCORES_IF_NO_CODE_CHANGE, label.isCopyAllScoresIfNoCodeChange(), LabelType.DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE); setBooleanConfigKey( rc, + LABEL, name, KEY_COPY_ALL_SCORES_IF_NO_CHANGE, label.isCopyAllScoresIfNoChange(), LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE); setBooleanConfigKey( rc, + LABEL, name, KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE, label.isCopyAllScoresOnMergeFirstParentUpdate(), LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE); setBooleanConfigKey( - rc, name, KEY_CAN_OVERRIDE, label.canOverride(), LabelType.DEF_CAN_OVERRIDE); + rc, LABEL, name, KEY_CAN_OVERRIDE, label.canOverride(), LabelType.DEF_CAN_OVERRIDE); List values = Lists.newArrayListWithCapacity(label.getValues().size()); for (LabelValue value : label.getValues()) { values.add(value.format()); @@ -1335,11 +1370,11 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. } private static void setBooleanConfigKey( - Config rc, String name, String key, boolean value, boolean defaultValue) { + Config rc, String section, String name, String key, boolean value, boolean defaultValue) { if (value == defaultValue) { - rc.unset(LABEL, name, key); + rc.unset(section, name, key); } else { - rc.setBoolean(LABEL, name, key, value); + rc.setBoolean(section, name, key, value); } } @@ -1367,6 +1402,11 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. } } + private void saveReviewerSection(Config rc) { + setBooleanConfigKey( + rc, REVIEWER, null, KEY_ENABLE_REVIEWER_BY_EMAIL, enableReviewerByEmail, false); + } + private void saveGroupList() throws IOException { saveUTF8(GroupList.FILE_NAME, groupList.asText()); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java index 2f02728fbc..ce2141350d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java @@ -58,6 +58,7 @@ public class ConfigInfoImpl extends ConfigInfo { InheritedBooleanInfo enableSignedPush = new InheritedBooleanInfo(); InheritedBooleanInfo requireSignedPush = new InheritedBooleanInfo(); InheritedBooleanInfo rejectImplicitMerges = new InheritedBooleanInfo(); + InheritedBooleanInfo enableReviewerByEmail = new InheritedBooleanInfo(); useContributorAgreements.value = projectState.isUseContributorAgreements(); useSignedOffBy.value = projectState.isUseSignedOffBy(); @@ -73,6 +74,7 @@ public class ConfigInfoImpl extends ConfigInfo { enableSignedPush.configuredValue = p.getEnableSignedPush(); requireSignedPush.configuredValue = p.getRequireSignedPush(); rejectImplicitMerges.configuredValue = p.getRejectImplicitMerges(); + enableReviewerByEmail.configuredValue = p.getEnableReviewerByEmail(); ProjectState parentState = Iterables.getFirst(projectState.parents(), null); if (parentState != null) { @@ -85,6 +87,7 @@ public class ConfigInfoImpl extends ConfigInfo { enableSignedPush.inheritedValue = projectState.isEnableSignedPush(); requireSignedPush.inheritedValue = projectState.isRequireSignedPush(); rejectImplicitMerges.inheritedValue = projectState.isRejectImplicitMerges(); + enableReviewerByEmail.inheritedValue = projectState.isEnableReviewerByEmail(); } this.useContributorAgreements = useContributorAgreements; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 8b8745e19f..32dc41f4eb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java @@ -394,6 +394,10 @@ public class ProjectState { return getInheritableBoolean(Project::getRejectImplicitMerges); } + public boolean isEnableReviewerByEmail() { + return getInheritableBoolean(Project::getEnableReviewerByEmail); + } + public LabelTypes getLabelTypes() { Map types = new LinkedHashMap<>(); for (ProjectState s : treeInOrder()) { diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java index ab68c10a92..2b7b78d70d 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java @@ -105,7 +105,9 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase { + " accepted = group Developers\n" // + " accepted = group Staff\n" // + " autoVerify = group Developers\n" // - + " agreementUrl = http://www.example.com/agree\n")) // + + " agreementUrl = http://www.example.com/agree\n" // + + "[reviewer]\n" // + + " enableByEmail = true\n")) // )); ProjectConfig cfg = read(rev); @@ -132,6 +134,8 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase { assertThat(submit.getExclusiveGroup()).isTrue(); assertThat(read.getExclusiveGroup()).isTrue(); assertThat(push.getExclusiveGroup()).isFalse(); + + assertThat(cfg.getEnableReviewerByEmail()).isTrue(); } @Test