Merge branch 'stable-2.15'

* stable-2.15:
  Polygerrit: Always create new changes as WIP
  ElasticIndexIT: replace member with local variable
  Elasticsearch tests: remove password duplication
  Fix call to the fail skylark global in junit.bzl
  Set version to 2.14.11-SNAPSHOT
  Set version to 2.14.10
  AbstractQueryChangesTest: Add byMessageSubstring test
  AbstractQueryChangesTest: Expand byTopic with more 'intopic' tests
  Elasticsearch: Add char analyzer to ensure consistency of query results
  Elasticsearch: remove overridden build assignment
  Elasticsearch: run no other test at the same time
  ElasticVersionTest: run it through bazel as well
  Elasticsearch: cover V5 and flaky V6 tests -for CI
  Split Elasticsearch query tests into separate rules
  dev-contributing: Document that we format .bzl files with buildifier
  Add account setting for defaulting new changes to WIP
  Add project setting for defaulting new changes to WIP
  Apply buildifier to .bzl files.
  Update Bower to 1.8.2
  Bump commons-io version to 2.2
  Add missing elasticsearch dependency in pgm tests
  Highlight.js: style gr-syntax-name as gr-syntax-keyword
  Elasticsearch: remove unnecessary test build deps
  Clarify behavior of "Ignore" feature in REST API documentation
  Expose commons-compress in plugin API
  Bump commons-io version to 2.2

Change-Id: I99edb118d193ebe9150f89a902a1407e69711cfc
This commit is contained in:
David Pursehouse 2018-07-17 12:56:33 +09:00
commit 4610fcad4e
48 changed files with 554 additions and 32 deletions

View File

@ -217,6 +217,19 @@ the Git push.
Default is `INHERIT`, which means that this property is inherited from
the parent project.
[[change.workInProgressByDefault]]change.workInProgressByDefault::
+
Controls whether all new changes in the project are set as WIP by default.
+
Note that a new change will be ready if the `workInProgress` field in
link:rest-api-changes.html#change-input[ChangeInput] is set to `false` explicitly
when calling the link:rest-api-changes.html#create-change[CreateChange] REST API
or the `ready` link:user-upload.html#wip[PushOption] is used during
the Git push.
+
Default is `INHERIT`, which means that this property is inherited from
the parent project.
[[submit-section]]
=== Submit section

View File

@ -164,7 +164,7 @@ Guide].
To format Java source code, Gerrit uses the
link:https://github.com/google/google-java-format[`google-java-format`]
tool (version 1.5), and to format Bazel BUILD and WORKSPACE files the
tool (version 1.5), and to format Bazel BUILD, WORKSPACE and .bzl files the
link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`]
tool (version 0.12.0).
These tools automatically apply format according to the style guides; this

View File

@ -827,6 +827,12 @@ commands that need to be copied frequently, such as the Change-Ids, commit IDs
and download commands. Note that this option is only shown if the Flash plugin
is available and the JavaScript Clipboard API is unavailable.
- [[work-in-progress-by-default]]`Set new changes work-in-progress`:
+
Whether new changes are uploaded as work-in-progress per default. This
preference just sets the default; the behavior can still be overridden using a
link:user-upload.html#wip[push option].
[[my-menu]]
In addition it is possible to customize the menu entries of the `My`
menu. This can be used to make the navigation to frequently used

View File

@ -1255,6 +1255,7 @@ any account.
"review_category_strategy": "ABBREV",
"mute_common_path_prefixes": true,
"publish_comments_on_push": true,
"work_in_progress_by_default": true,
"default_base_for_merges": "FIRST_PARENT",
"my": [
{
@ -1361,6 +1362,7 @@ link:#preferences-info[PreferencesInfo] entity.
"review_category_strategy": "NAME",
"diff_view": "SIDE_BY_SIDE",
"publish_comments_on_push": true,
"work_in_progress_by_default": true,
"mute_common_path_prefixes": true,
"my": [
{
@ -2654,6 +2656,9 @@ Allowed values are `AUTO_MERGE` and `FIRST_PARENT`.
|`publish_comments_on_push` |not set if `false`|
Whether to link:user-upload.html#publish-comments[publish draft comments] on
push by default.
|`work_in_progress_by_default` |not set if `false`|
Whether to link:user-upload.html#wip[set work-in-progress] on
push or on create changes online by default.
|============================================
[[preferences-input]]

View File

@ -2277,7 +2277,9 @@ POST request:
--
Marks a change as ignored. The change will not be shown in the incoming
reviews dashboard, and email notifications will be suppressed.
reviews dashboard, and email notifications will be suppressed. Ignoring
a change does not cause the change's "updated" timestamp to be modified,
and the owner is not notified.
.Request
----

View File

@ -2890,10 +2890,13 @@ signed push validation is required on the project.
|`reject_implicit_merges`|optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
implicit merges should be rejected on changes pushed to the project.
|`private_by_default` ||
|`private_by_default` ||
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
all new changes are set as private by default.
|`max_object_size_limit` ||
|`work_in_progress_by_default`||
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
all new changes are set as work-in-progress by default.
|`max_object_size_limit` ||
The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
limit] of this project as a link:#max-object-size-limit-info[
MaxObjectSizeLimitInfo] entity.

View File

@ -309,6 +309,11 @@ flag from a change on push, explicitly specify the `ready` option:
Only change owners, project owners and site administrators can specify
`work-in-progress` and `ready` options on push.
The default for this option can be set as a
link:intro-user.html#work-in-progress-by-default[user preference]. If the
preference is set so the default behavior is to create `work-in-progress`
changes, this can be overridden with the `ready` option.
[[message]]
==== Message

View File

@ -878,8 +878,8 @@ maven_jar(
maven_jar(
name = "commons-io",
artifact = "commons-io:commons-io:1.4",
sha1 = "a8762d07e76cfde2395257a5da47ba7c1dbd3dce",
artifact = "commons-io:commons-io:2.2",
sha1 = "83b5b8a7ba1c08f9e8c8ff2373724e33d3c1e22a",
)
maven_jar(

View File

@ -0,0 +1,21 @@
load("//tools/bzl:junit.bzl", "junit_tests")
def acceptance_tests(
group,
deps = [],
labels = [],
vm_args = ["-Xmx256m"],
**kwargs):
junit_tests(
name = group,
deps = deps + [
"//gerrit-acceptance-tests:lib",
],
tags = labels + [
"acceptance",
"slow",
],
size = "large",
jvm_flags = vm_args,
**kwargs
)

View File

@ -151,6 +151,9 @@ public class GeneralPreferences extends JavaScriptObject {
public final native boolean
publishCommentsOnPush() /*-{ return this.publish_comments_on_push || false }-*/;
public final native boolean
workInProgressByDefault() /*-{ return this.work_in_progress_by_default || false }-*/;
public final native JsArray<TopMenuItem> my() /*-{ return this.my; }-*/;
public final native void changesPerPage(int n) /*-{ this.changes_per_page = n }-*/;
@ -230,6 +233,9 @@ public class GeneralPreferences extends JavaScriptObject {
public final native void publishCommentsOnPush(
boolean p) /*-{ this.publish_comments_on_push = p }-*/;
public final native void workInProgressByDefault(
boolean p) /*-{ this.work_in_progress_by_default = p }-*/;
public final void setMyMenus(List<TopMenuItem> myMenus) {
initMy();
for (TopMenuItem n : myMenus) {

View File

@ -69,6 +69,8 @@ public interface AccountConstants extends Constants {
String publishCommentsOnPush();
String workInProgressByDefault();
String myMenu();
String myMenuInfo();

View File

@ -39,6 +39,7 @@ showLegacycidInChangeTable = Show Change Number In Changes Table
muteCommonPathPrefixes = Mute Common Path Prefixes In File List
signedOffBy = Insert Signed-off-by Footer For Inline Edit Changes
publishCommentsOnPush = Publish Comments On Push
workInProgressByDefault = Set all new changes work-in-progress by default
myMenu = My Menu
myMenuInfo = \
Menu items for the 'My' top level menu. \

View File

@ -56,6 +56,7 @@ public class MyPreferencesScreen extends SettingsScreen {
private CheckBox muteCommonPathPrefixes;
private CheckBox signedOffBy;
private CheckBox publishCommentsOnPush;
private CheckBox workInProgressByDefault;
private ListBox maximumPageSize;
private ListBox dateFormat;
private ListBox timeFormat;
@ -163,9 +164,10 @@ public class MyPreferencesScreen extends SettingsScreen {
muteCommonPathPrefixes = new CheckBox(Util.C.muteCommonPathPrefixes());
signedOffBy = new CheckBox(Util.C.signedOffBy());
publishCommentsOnPush = new CheckBox(Util.C.publishCommentsOnPush());
workInProgressByDefault = new CheckBox(Util.C.workInProgressByDefault());
boolean flashClippy = !UserAgent.hasJavaScriptClipboard() && UserAgent.Flash.isInstalled();
final Grid formGrid = new Grid(15 + (flashClippy ? 1 : 0), 2);
final Grid formGrid = new Grid(16 + (flashClippy ? 1 : 0), 2);
int row = 0;
@ -229,6 +231,10 @@ public class MyPreferencesScreen extends SettingsScreen {
formGrid.setWidget(row, fieldIdx, publishCommentsOnPush);
row++;
formGrid.setText(row, labelIdx, "");
formGrid.setWidget(row, fieldIdx, workInProgressByDefault);
row++;
if (flashClippy) {
formGrid.setText(row, labelIdx, "");
formGrid.setWidget(row, fieldIdx, useFlashClipboard);
@ -264,6 +270,7 @@ public class MyPreferencesScreen extends SettingsScreen {
e.listenTo(muteCommonPathPrefixes);
e.listenTo(signedOffBy);
e.listenTo(publishCommentsOnPush);
e.listenTo(workInProgressByDefault);
e.listenTo(diffView);
e.listenTo(reviewCategoryStrategy);
e.listenTo(emailStrategy);
@ -303,6 +310,7 @@ public class MyPreferencesScreen extends SettingsScreen {
muteCommonPathPrefixes.setEnabled(on);
signedOffBy.setEnabled(on);
publishCommentsOnPush.setEnabled(on);
workInProgressByDefault.setEnabled(on);
reviewCategoryStrategy.setEnabled(on);
diffView.setEnabled(on);
emailStrategy.setEnabled(on);
@ -329,6 +337,7 @@ public class MyPreferencesScreen extends SettingsScreen {
muteCommonPathPrefixes.setValue(p.muteCommonPathPrefixes());
signedOffBy.setValue(p.signedOffBy());
publishCommentsOnPush.setValue(p.publishCommentsOnPush());
workInProgressByDefault.setValue(p.workInProgressByDefault());
setListBox(
reviewCategoryStrategy,
GeneralPreferencesInfo.ReviewCategoryStrategy.NONE,
@ -421,6 +430,7 @@ public class MyPreferencesScreen extends SettingsScreen {
p.muteCommonPathPrefixes(muteCommonPathPrefixes.getValue());
p.signedOffBy(signedOffBy.getValue());
p.publishCommentsOnPush(publishCommentsOnPush.getValue());
p.workInProgressByDefault(workInProgressByDefault.getValue());
p.reviewCategoryStrategy(
getListBox(
reviewCategoryStrategy, ReviewCategoryStrategy.NONE, ReviewCategoryStrategy.values()));

View File

@ -79,6 +79,8 @@ public interface AdminConstants extends Constants {
String privateByDefault();
String workInProgressByDefault();
String enableReviewerByEmail();
String matchAuthorToCommitterDate();

View File

@ -31,6 +31,7 @@ requireSignedPush = Require signed push
requireChangeID = Require <code>Change-Id</code> in commit message
rejectImplicitMerges = Reject implicit merges when changes are pushed for review
privateByDefault = Set all new changes private by default
workInProgressByDefault = Set all new changes work-in-progress by default
headingMaxObjectSizeLimit = Maximum Git object size limit
headingGroupOptions = Group Options
isVisibleToAll = Make group visible to all registered users.

View File

@ -88,6 +88,7 @@ public class ProjectInfoScreen extends ProjectScreen {
private ListBox requireSignedPush;
private ListBox rejectImplicitMerges;
private ListBox privateByDefault;
private ListBox workInProgressByDefault;
private ListBox enableReviewerByEmail;
private ListBox matchAuthorToCommitterDate;
private NpTextBox maxObjectSizeLimit;
@ -198,6 +199,7 @@ public class ProjectInfoScreen extends ProjectScreen {
requireChangeID.setEnabled(isOwner);
rejectImplicitMerges.setEnabled(isOwner);
privateByDefault.setEnabled(isOwner);
workInProgressByDefault.setEnabled(isOwner);
maxObjectSizeLimit.setEnabled(isOwner);
enableReviewerByEmail.setEnabled(isOwner);
matchAuthorToCommitterDate.setEnabled(isOwner);
@ -278,6 +280,10 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(privateByDefault);
grid.addHtml(AdminConstants.I.privateByDefault(), privateByDefault);
workInProgressByDefault = newInheritedBooleanBox();
saveEnabler.listenTo(workInProgressByDefault);
grid.addHtml(AdminConstants.I.workInProgressByDefault(), workInProgressByDefault);
enableReviewerByEmail = newInheritedBooleanBox();
saveEnabler.listenTo(enableReviewerByEmail);
grid.addHtml(AdminConstants.I.enableReviewerByEmail(), enableReviewerByEmail);
@ -427,6 +433,7 @@ public class ProjectInfoScreen extends ProjectScreen {
}
setBool(rejectImplicitMerges, result.rejectImplicitMerges());
setBool(privateByDefault, result.privateByDefault());
setBool(workInProgressByDefault, result.workInProgressByDefault());
setBool(enableReviewerByEmail, result.enableReviewerByEmail());
setBool(matchAuthorToCommitterDate, result.matchAuthorToCommitterDate());
setSubmitType(result.defaultSubmitType());
@ -700,6 +707,7 @@ public class ProjectInfoScreen extends ProjectScreen {
rsp,
getBool(rejectImplicitMerges),
getBool(privateByDefault),
getBool(workInProgressByDefault),
getBool(enableReviewerByEmail),
getBool(matchAuthorToCommitterDate),
maxObjectSizeLimit.getText().trim(),

View File

@ -60,6 +60,9 @@ public class ConfigInfo extends JavaScriptObject {
public final native InheritedBooleanInfo privateByDefault()
/*-{ return this.private_by_default; }-*/ ;
public final native InheritedBooleanInfo workInProgressByDefault()
/*-{ return this.work_in_progress_by_default; }-*/ ;
public final native InheritedBooleanInfo enableReviewerByEmail()
/*-{ return this.enable_reviewer_by_email; }-*/ ;

View File

@ -153,6 +153,7 @@ public class ProjectApi {
InheritableBoolean requireSignedPush,
InheritableBoolean rejectImplicitMerges,
InheritableBoolean privateByDefault,
InheritableBoolean workInProgressByDefault,
InheritableBoolean enableReviewerByEmail,
InheritableBoolean matchAuthorToCommitterDate,
String maxObjectSizeLimit,
@ -175,6 +176,7 @@ public class ProjectApi {
}
in.setRejectImplicitMerges(rejectImplicitMerges);
in.setPrivateByDefault(privateByDefault);
in.setWorkInProgressByDefault(workInProgressByDefault);
in.setMaxObjectSizeLimit(maxObjectSizeLimit);
if (submitType != null) {
in.setSubmitType(submitType);
@ -313,6 +315,13 @@ public class ProjectApi {
private native void setPrivateByDefault(String v) /*-{ if(v)this.private_by_default=v; }-*/;
final void setWorkInProgressByDefault(InheritableBoolean v) {
setWorkInProgressByDefault(v.name());
}
private native void setWorkInProgressByDefault(
String v) /*-{ if(v)this.work_in_progress_by_default=v; }-*/;
final void setEnableReviewerByEmail(InheritableBoolean v) {
setEnableReviewerByEmailRaw(v.name());
}

View File

@ -21,6 +21,7 @@ import static org.apache.commons.codec.binary.Base64.decodeBase64;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
@ -79,6 +80,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
protected static final String MAPPINGS = "mappings";
protected static final String ORDER = "order";
protected static final String SEARCH = "_search";
protected static final String SETTINGS = "settings";
protected static <T> List<T> decodeProtos(
JsonObject doc, String fieldName, ProtobufCodec<T> codec) {
@ -181,7 +183,8 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
}
// Recreate the index.
response = performRequest("PUT", getMappings(), indexName, Collections.emptyMap());
String indexCreationFields = concatJsonString(getSettings(), getMappings());
response = performRequest("PUT", indexCreationFields, indexName, Collections.emptyMap());
statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
String error = String.format("Failed to create index %s: %s", indexName, statusCode);
@ -193,6 +196,10 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
protected abstract String getMappings();
private String getSettings() {
return gson.toJson(ImmutableMap.of(SETTINGS, ElasticSetting.createSetting()));
}
protected abstract String getId(V v);
protected String getMappingsForSingleType(String candidateType, MappingProperties properties) {
@ -294,6 +301,10 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
return performRequest("POST", payload, uri, params);
}
private String concatJsonString(String target, String addition) {
return target.substring(0, target.length() - 1) + "," + addition.substring(1);
}
private Response performRequest(
String method, Object payload, String uri, Map<String, String> params) throws IOException {
String payloadStr = payload instanceof String ? (String) payload : payload.toString();

View File

@ -34,9 +34,9 @@ class ElasticMapping {
|| fieldType == FieldType.INTEGER_RANGE
|| fieldType == FieldType.LONG) {
mapping.addNumber(name);
} else if (fieldType == FieldType.PREFIX
|| fieldType == FieldType.FULL_TEXT
|| fieldType == FieldType.STORED_ONLY) {
} else if (fieldType == FieldType.FULL_TEXT) {
mapping.addStringWithAnalyzer(name);
} else if (fieldType == FieldType.PREFIX || fieldType == FieldType.STORED_ONLY) {
mapping.addString(name);
} else {
throw new IllegalStateException("Unsupported field type: " + fieldType.getName());
@ -88,6 +88,13 @@ class ElasticMapping {
return this;
}
Builder addStringWithAnalyzer(String name) {
FieldProperties key = new FieldProperties(adapter.stringFieldType());
key.analyzer = "custom_with_char_filter";
fields.put(name, key);
return this;
}
Builder add(String name, String type) {
fields.put(name, new FieldProperties(type));
return this;
@ -102,6 +109,7 @@ class ElasticMapping {
String type;
String index;
String format;
String analyzer;
Map<String, FieldProperties> fields;
FieldProperties(String type) {

View File

@ -0,0 +1,92 @@
// Copyright (C) 2018 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.google.gerrit.elasticsearch;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
class ElasticSetting {
/** The custom char mappings of "." to " " and "_" to " " in the form of UTF-8 */
private static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
ImmutableMap.of("\\u002E", "\\u0020", "\\u005F", "\\u0020");
static SettingProperties createSetting() {
ElasticSetting.Builder settings = new ElasticSetting.Builder();
settings.addCharFilter();
settings.addAnalyzer();
return settings.build();
}
static class Builder {
private final ImmutableMap.Builder<String, FieldProperties> fields =
new ImmutableMap.Builder<>();
SettingProperties build() {
SettingProperties properties = new SettingProperties();
properties.analysis = fields.build();
return properties;
}
void addCharFilter() {
FieldProperties charMapping = new FieldProperties("mapping");
charMapping.mappings = getCustomCharMappings(CUSTOM_CHAR_MAPPING);
FieldProperties charFilter = new FieldProperties();
charFilter.customMapping = charMapping;
fields.put("char_filter", charFilter);
}
void addAnalyzer() {
FieldProperties customAnalyzer = new FieldProperties("custom");
customAnalyzer.tokenizer = "standard";
customAnalyzer.charFilter = new String[] {"custom_mapping"};
customAnalyzer.filter = new String[] {"lowercase"};
FieldProperties analyzer = new FieldProperties();
analyzer.customWithCharFilter = customAnalyzer;
fields.put("analyzer", analyzer);
}
private static String[] getCustomCharMappings(ImmutableMap<String, String> map) {
int mappingIndex = 0;
int numOfMappings = map.size();
String[] mapping = new String[numOfMappings];
for (Map.Entry<String, String> e : map.entrySet()) {
mapping[mappingIndex++] = e.getKey() + "=>" + e.getValue();
}
return mapping;
}
}
static class SettingProperties {
Map<String, FieldProperties> analysis;
}
static class FieldProperties {
String tokenizer;
String type;
String[] charFilter;
String[] filter;
String[] mappings;
FieldProperties customMapping;
FieldProperties customWithCharFilter;
FieldProperties() {}
FieldProperties(String type) {
this.type = type;
}
}
}

View File

@ -33,6 +33,7 @@ public class ConfigInfo {
public InheritedBooleanInfo requireSignedPush;
public InheritedBooleanInfo rejectImplicitMerges;
public InheritedBooleanInfo privateByDefault;
public InheritedBooleanInfo workInProgressByDefault;
public InheritedBooleanInfo enableReviewerByEmail;
public InheritedBooleanInfo matchAuthorToCommitterDate;
public InheritedBooleanInfo rejectEmptyCommit;

View File

@ -30,6 +30,7 @@ public class ConfigInput {
public InheritableBoolean requireSignedPush;
public InheritableBoolean rejectImplicitMerges;
public InheritableBoolean privateByDefault;
public InheritableBoolean workInProgressByDefault;
public InheritableBoolean enableReviewerByEmail;
public InheritableBoolean matchAuthorToCommitterDate;
public InheritableBoolean rejectEmptyCommit;

View File

@ -158,6 +158,7 @@ public class GeneralPreferencesInfo {
public EmailFormat emailFormat;
public DefaultBase defaultBaseForMerges;
public Boolean publishCommentsOnPush;
public Boolean workInProgressByDefault;
public boolean isShowInfoInReviewCategory() {
return getReviewCategoryStrategy() != ReviewCategoryStrategy.NONE;
@ -227,6 +228,7 @@ public class GeneralPreferencesInfo {
p.signedOffBy = false;
p.defaultBaseForMerges = DefaultBase.FIRST_PARENT;
p.publishCommentsOnPush = false;
p.workInProgressByDefault = false;
return p;
}
}

View File

@ -40,7 +40,8 @@ public enum BooleanProjectConfig {
PRIVATE_BY_DEFAULT("change", "privateByDefault"),
ENABLE_REVIEWER_BY_EMAIL("reviewer", "enableByEmail"),
MATCH_AUTHOR_TO_COMMITTER_DATE("submit", "matchAuthorToCommitterDate"),
REJECT_EMPTY_COMMIT("submit", "rejectEmptyCommit");
REJECT_EMPTY_COMMIT("submit", "rejectEmptyCommit"),
WORK_IN_PROGRESS_BY_DEFAULT("change", "workInProgressByDefault");
// Git config
private final String section;

View File

@ -2204,6 +2204,7 @@ class ReceiveCommits {
}
private void setChangeId(int id) {
possiblyOverrideWorkInProgress();
changeId = new Change.Id(id);
ins =
@ -2224,6 +2225,16 @@ class ReceiveCommits {
}
}
private void possiblyOverrideWorkInProgress() {
// When wip or ready explicitly provided, leave it as is.
if (magicBranch.workInProgress || magicBranch.ready) {
return;
}
magicBranch.workInProgress =
projectState.is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
|| firstNonNull(user.state().getGeneralPreferences().workInProgressByDefault, false);
}
private void addOps(BatchUpdate bu) throws RestApiException {
checkState(changeId != null, "must call setChangeId before addOps");
try {

View File

@ -68,6 +68,9 @@ public class BooleanProjectConfigTransformations {
.put(
BooleanProjectConfig.REJECT_EMPTY_COMMIT,
new Mapper(i -> i.rejectEmptyCommit, (i, v) -> i.rejectEmptyCommit = v))
.put(
BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT,
new Mapper(i -> i.workInProgressByDefault, (i, v) -> i.workInProgressByDefault = v))
.build();
static {

View File

@ -266,6 +266,12 @@ public class CreateChange
AccountState accountState = me.state();
GeneralPreferencesInfo info = accountState.getGeneralPreferences();
boolean isWorkInProgress =
input.workInProgress == null
? rsrc.getProjectState().is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
|| MoreObjects.firstNonNull(info.workInProgressByDefault, false)
: input.workInProgress;
// Add a Change-Id line if there isn't already one
String commitMessage = subject;
if (ChangeIdUtil.indexOfChangeId(commitMessage, "\n") == -1) {
@ -309,7 +315,7 @@ public class CreateChange
}
ins.setTopic(topic);
ins.setPrivate(isPrivate);
ins.setWorkInProgress(input.workInProgress != null && input.workInProgress);
ins.setWorkInProgress(isWorkInProgress);
ins.setGroups(groups);
ins.setNotify(input.notify);
ins.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));

View File

@ -448,6 +448,16 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
}
@Test
public void createWipChangeWithWorkInProgressByDefaultForProject() throws Exception {
ConfigInput input = new ConfigInput();
input.workInProgressByDefault = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(input);
String changeId =
gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
}
@Test
public void setReadyForReviewNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result rready = createChange();

View File

@ -22,6 +22,7 @@ acceptance_tests(
group = "elastic",
labels = [
"elastic",
"exclusive",
"flaky",
"pgm",
"no_windows",

View File

@ -0,0 +1,158 @@
// Copyright (C) 2018 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.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.reviewdb.client.Project;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class WorkInProgressByDefaultIT extends AbstractDaemonTest {
private Project.NameKey project1;
private Project.NameKey project2;
@Before
public void setUp() throws Exception {
project1 = createProject("project-1");
project2 = createProject("project-2", project1);
}
@After
public void tearDown() throws Exception {
setApiUser(admin);
GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
prefs.workInProgressByDefault = false;
gApi.accounts().id(admin.id.get()).setPreferences(prefs);
}
@Test
public void createChangeWithWorkInProgressByDefaultForProjectDisabled() throws Exception {
ChangeInfo info =
gApi.changes().create(new ChangeInput(project2.get(), "master", "empty change")).get();
assertThat(info.workInProgress).isNull();
}
@Test
public void createChangeWithWorkInProgressByDefaultForProjectEnabled() throws Exception {
setWorkInProgressByDefaultForProject(project2);
ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
assertThat(gApi.changes().create(input).get().workInProgress).isTrue();
}
@Test
public void createChangeWithWorkInProgressByDefaultForUserEnabled() throws Exception {
setWorkInProgressByDefaultForUser();
ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
assertThat(gApi.changes().create(input).get().workInProgress).isTrue();
}
@Test
public void createChangeBypassWorkInProgressByDefaultForProjectEnabled() throws Exception {
setWorkInProgressByDefaultForProject(project2);
ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
input.workInProgress = false;
assertThat(gApi.changes().create(input).get().workInProgress).isNull();
}
@Test
public void createChangeBypassWorkInProgressByDefaultForUserEnabled() throws Exception {
setWorkInProgressByDefaultForUser();
ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
input.workInProgress = false;
assertThat(gApi.changes().create(input).get().workInProgress).isNull();
}
@Test
public void createChangeWithWorkInProgressByDefaultForProjectInherited() throws Exception {
setWorkInProgressByDefaultForProject(project1);
ChangeInfo info =
gApi.changes().create(new ChangeInput(project2.get(), "master", "empty change")).get();
assertThat(info.workInProgress).isTrue();
}
@Test
public void pushWithWorkInProgressByDefaultForProjectEnabled() throws Exception {
setWorkInProgressByDefaultForProject(project2);
assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
}
@Test
public void pushWithWorkInProgressByDefaultForUserEnabled() throws Exception {
setWorkInProgressByDefaultForUser();
assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
}
@Test
public void pushBypassWorkInProgressByDefaultForProjectEnabled() throws Exception {
setWorkInProgressByDefaultForProject(project2);
assertThat(
createChange(project2, "refs/for/master%ready").getChange().change().isWorkInProgress())
.isFalse();
}
@Test
public void pushBypassWorkInProgressByDefaultForUserEnabled() throws Exception {
setWorkInProgressByDefaultForUser();
assertThat(
createChange(project2, "refs/for/master%ready").getChange().change().isWorkInProgress())
.isFalse();
}
@Test
public void pushWithWorkInProgressByDefaultForProjectDisabled() throws Exception {
assertThat(createChange(project2).getChange().change().isWorkInProgress()).isFalse();
}
@Test
public void pushWorkInProgressByDefaultForProjectInherited() throws Exception {
setWorkInProgressByDefaultForProject(project1);
assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
}
private void setWorkInProgressByDefaultForProject(Project.NameKey p) throws Exception {
ConfigInput input = new ConfigInput();
input.workInProgressByDefault = InheritableBoolean.TRUE;
gApi.projects().name(p.get()).config(input);
}
private void setWorkInProgressByDefaultForUser() throws Exception {
GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
prefs.workInProgressByDefault = true;
gApi.accounts().id(admin.id.get()).setPreferences(prefs);
}
private PushOneCommit.Result createChange(Project.NameKey p) throws Exception {
return createChange(p, "refs/for/master");
}
private PushOneCommit.Result createChange(Project.NameKey p, String r) throws Exception {
TestRepository<InMemoryRepository> testRepo = cloneProject(p);
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
PushOneCommit.Result result = push.to(r);
result.assertOkStatus();
return result;
}
}

View File

@ -42,10 +42,14 @@ import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.CommitMessageInput;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.change.PostReview;
@ -969,6 +973,38 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
assertThat(sender).notSent();
}
@Test
public void createWipChangeWithWorkInProgressByDefaultForProject() throws Exception {
setWorkInProgressByDefault(project, InheritableBoolean.TRUE);
StagedPreChange spc = stagePreChange("refs/for/master");
Truth.assertThat(gApi.changes().id(spc.changeId).get().workInProgress).isTrue();
assertThat(sender).notSent();
}
@Test
public void createWipChangeWithWorkInProgressByDefaultForUser() throws Exception {
// Make sure owner user is created
StagedChange sc = stageReviewableChange();
// All was cleaned already
assertThat(sender).notSent();
// Toggle workInProgress flag for owner
GeneralPreferencesInfo prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
prefs.workInProgressByDefault = true;
gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
// Create another change without notification that should be wip
StagedPreChange spc = stagePreChange("refs/for/master");
Truth.assertThat(gApi.changes().id(spc.changeId).get().workInProgress).isTrue();
assertThat(sender).notSent();
// Clean up workInProgressByDefault by owner
prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
Truth.assertThat(prefs.workInProgressByDefault).isTrue();
prefs.workInProgressByDefault = false;
gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
}
@Test
public void createReviewableChangeWithNotifyOwnerReviewers() throws Exception {
stagePreChange("refs/for/master%notify=OWNER_REVIEWERS");
@ -2646,4 +2682,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
// PolyGerrit current immediately follows up with a review.
gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.noScore());
}
private void setWorkInProgressByDefault(Project.NameKey p, InheritableBoolean v)
throws Exception {
ConfigInput input = new ConfigInput();
input.workInProgressByDefault = v;
gApi.projects().name(p.get()).config(input);
}
}

View File

@ -25,8 +25,9 @@ acceptance_tests(
srcs = ["ElasticIndexIT.java"],
group = "elastic",
labels = [
"elastic",
"docker",
"elastic",
"exclusive",
"ssh",
],
deps = [

View File

@ -24,16 +24,14 @@ import java.util.UUID;
import org.eclipse.jgit.lib.Config;
public class ElasticIndexIT extends AbstractIndexTests {
private static ElasticContainer<?> container;
private static Config getConfig(ElasticVersion version) {
ElasticNodeInfo elasticNodeInfo;
container = ElasticContainer.createAndStart(version);
ElasticContainer<?> container = ElasticContainer.createAndStart(version);
elasticNodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
String indicesPrefix = UUID.randomUUID().toString();
Config cfg = new Config();
String password = version == ElasticVersion.V5_6 ? "changeme" : null;
ElasticTestUtils.configure(cfg, elasticNodeInfo.port, indicesPrefix, password);
ElasticTestUtils.configure(cfg, elasticNodeInfo.port, indicesPrefix, version);
return cfg;
}

View File

@ -49,6 +49,7 @@ ELASTICSEARCH_TESTS_V6 = {i: "ElasticV6Query" + i.capitalize() + SUFFIX for i in
ELASTICSEARCH_TAGS = [
"docker",
"elastic",
"exclusive",
]
[junit_tests(

View File

@ -32,11 +32,12 @@ public final class ElasticTestUtils {
}
}
public static void configure(Config config, int port, String prefix, String password) {
public static void configure(Config config, int port, String prefix, ElasticVersion version) {
config.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
config.setString("elasticsearch", null, "server", "http://localhost:" + port);
config.setString("elasticsearch", null, "prefix", prefix);
config.setInt("index", null, "maxLimit", 10000);
String password = version == ElasticVersion.V5_6 ? "changeme" : null;
if (password != null) {
config.setString("elasticsearch", null, "password", password);
}

View File

@ -67,7 +67,8 @@ public class ElasticV5QueryAccountsTest extends AbstractQueryAccountsTest {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix, "changeme");
ElasticTestUtils.configure(
elasticsearchConfig, nodeInfo.port, indicesPrefix, ElasticVersion.V5_6);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@ -67,7 +67,8 @@ public class ElasticV5QueryChangesTest extends AbstractQueryChangesTest {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix, "changeme");
ElasticTestUtils.configure(
elasticsearchConfig, nodeInfo.port, indicesPrefix, ElasticVersion.V5_6);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@ -67,7 +67,8 @@ public class ElasticV5QueryGroupsTest extends AbstractQueryGroupsTest {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix, "changeme");
ElasticTestUtils.configure(
elasticsearchConfig, nodeInfo.port, indicesPrefix, ElasticVersion.V5_6);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@ -67,7 +67,8 @@ public class ElasticV5QueryProjectsTest extends AbstractQueryProjectsTest {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix, "changeme");
ElasticTestUtils.configure(
elasticsearchConfig, nodeInfo.port, indicesPrefix, ElasticVersion.V5_6);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@ -831,7 +831,13 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
ChangeInserter ins4 = newChangeWithTopic(repo, "feature2-fixup");
Change change4 = insert(repo, ins4);
Change change5 = insert(repo, newChange(repo));
ChangeInserter ins5 = newChangeWithTopic(repo, "https://gerrit.local");
Change change5 = insert(repo, ins5);
ChangeInserter ins6 = newChangeWithTopic(repo, "git_gerrit_training");
Change change6 = insert(repo, ins6);
Change change_no_topic = insert(repo, newChange(repo));
assertQuery("intopic:foo");
assertQuery("intopic:feature1", change1);
@ -839,8 +845,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("topic:feature2", change2);
assertQuery("intopic:feature2", change4, change3, change2);
assertQuery("intopic:fixup", change4);
assertQuery("topic:\"\"", change5);
assertQuery("intopic:\"\"", change5);
assertQuery("intopic:gerrit", change6, change5);
assertQuery("topic:\"\"", change_no_topic);
assertQuery("intopic:\"\"", change_no_topic);
}
@Test
@ -898,6 +905,14 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("message:Gerrit", change2, change1);
}
@Test
public void byMessageSubstring() throws Exception {
TestRepository<Repo> repo = createProject("repo");
RevCommit commit1 = repo.parseBody(repo.commit().message("https://gerrit.local").create());
Change change1 = insert(repo, newChangeForCommit(repo, commit1));
assertQuery("message:gerrit", change1);
}
@Test
public void byLabel() throws Exception {
accountManager.authenticate(AuthRequest.forUser("anotheruser"));

View File

@ -37,6 +37,7 @@ EXPORTS = [
"//java/com/google/gerrit/metrics/dropwizard",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/util/http",
"//lib/commons:compress",
"//lib/commons:dbcp",
"//lib/commons:lang",
"//lib/dropwizard:dropwizard-core",

View File

@ -242,6 +242,22 @@ limitations under the License.
</gr-select>
</span>
</section>
<section>
<span class="title">
Set new changes to "work in progress" by default</span>
<span class="value">
<gr-select
id="setAllNewChangesWorkInProgressByDefaultSelect"
bind-value="{{_repoConfig.work_in_progress_by_default.configured_value}}">
<select disabled$="[[_readOnly]]">
<template is="dom-repeat"
items="[[_formatBooleanSelect(_repoConfig.work_in_progress_by_default)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</gr-select>
</span>
</section>
<section>
<span class="title">Maximum Git object size limit</span>
<span class="value">

View File

@ -32,7 +32,8 @@ limitations under the License.
.gr-syntax-meta {
color: var(--syntax-meta-color);
}
.gr-syntax-keyword {
.gr-syntax-keyword,
.gr-syntax-name {
color: var(--syntax-keyword-color);
line-height: 1;
}

View File

@ -226,6 +226,16 @@ limitations under the License.
on-change="_handlePublishCommentsOnPushChanged">
</span>
</section>
<section>
<span class="title">Set new changes to "work in progress" by default</span>
<span class="value">
<input
id="workInProgressByDefault"
type="checkbox"
checked$="[[_localPrefs.work_in_progress_by_default]]"
on-change="_handleWorkInProgressByDefault">
</span>
</section>
<section>
<span class="title">
Insert Signed-off-by Footer For Inline Edit Changes

View File

@ -24,6 +24,7 @@
'email_strategy',
'diff_view',
'publish_comments_on_push',
'work_in_progress_by_default',
'signed_off_by',
'email_format',
'size_bar_in_change_table',
@ -291,6 +292,11 @@
this.$.publishCommentsOnPush.checked);
},
_handleWorkInProgressByDefault() {
this.set('_localPrefs.work_in_progress_by_default',
this.$.workInProgressByDefault.checked);
},
_handleInsertSignedOff() {
this.set('_localPrefs.signed_off_by', this.$.insertSignedOff.checked);
},

View File

@ -174,6 +174,9 @@ limitations under the License.
.firstElementChild.checked, true);
assert.equal(valueOf('Publish comments on push', 'preferences')
.firstElementChild.checked, false);
assert.equal(valueOf(
'Set new changes to "work in progress" by default', 'preferences')
.firstElementChild.checked, false);
assert.equal(valueOf(
'Insert Signed-off-by Footer For Inline Edit Changes', 'preferences')
.firstElementChild.checked, false);
@ -234,6 +237,30 @@ limitations under the License.
});
});
test('set new changes work-in-progress', done => {
const newChangesWorkInProgress =
valueOf('Set new changes to "work in progress" by default',
'preferences').firstElementChild;
MockInteractions.tap(newChangesWorkInProgress);
assert.isFalse(element._menuChanged);
assert.isTrue(element._prefsChanged);
stub('gr-rest-api-interface', {
savePreferences(prefs) {
assert.equal(prefs.work_in_progress_by_default, true);
return Promise.resolve();
},
});
// Save the change.
element._handleSavePreferences().then(() => {
assert.isFalse(element._prefsChanged);
assert.isFalse(element._menuChanged);
done();
});
});
test('diff preferences', done => {
// Rendered with the expected preferences selected.
assert.equal(valueOf('Context', 'diffPreferences')

View File

@ -43,11 +43,7 @@ def _AsClassName(fname):
if findex != -1:
break
if findex == -1:
fail(
"%s does not contain any of %s",
fname,
_PREFIXES,
)
fail("%s does not contain any of %s" % (fname, _PREFIXES))
return ".".join(toks[findex:]) + ".class"
def _impl(ctx):