Merge branch 'stable-3.0'

* stable-3.0:
  ReceiveCommits: Don't instantiate BranchCommitValidator repeatedly
  ElasticVersionTest: Add missing version 7.1 asserts
  ElasticVersionTest: Improve test method ordering
  ElasticVersionTest: Make test method names accurate
  Upgrade gitiles to 0.2-10
  PermissionRange: Interpret allowMin > allowMax as disallow
  Fix formatting issue in project config documentation
  ReceiveCommits: Validate ref operation before commits for non-ff case
  Add test coverage for ref operation validation extension point
  Allow CommitValidationListener to ignore 'skip validation' push option
  Upgrade JGit to 4.11.8.201904181247-r
  CreateProject: Expose createProject method
  ElasticContainer: Switch to Alpine-based images from blacktop
  DeleteVote.soy: Make wording consistent with DeleteVoteHtml.soy
  DeleteVote.soy add missing review URL
  Update git submodules
  NewChange.soy: add missing closing parenthesis
  Remove use of "NoteDB" config in PolyGerrit
  Always show "NoteDB" config under gr-repo
  Fix hiding "enable signed push" and "require signed push" under gr-repo
  Update git submodules
  gerrit.sh: Fix message about JRE
  Allow CommitValidationListener to ignore 'skip validation' push option
  ChangeEmail: Stop using deprecated SoyListData and SoyMapData
  IndexServlet: Stop using deprecated SoyMapData
  Bazel: Fix lint warning flagged by buildifier

Change-Id: Icadfa2bb77929e0aa5118ea8492fdb24e0e9aa7e
This commit is contained in:
Marco Miller
2019-06-27 12:51:40 -04:00
35 changed files with 476 additions and 258 deletions

View File

@@ -100,7 +100,7 @@ These are the keys:
+ +
A description for the project. A description for the project.
[[state]]state: [[state]]state::
+ +
This setting defines the state of the project. A project can have the This setting defines the state of the project. A project can have the
following states: following states:

View File

@@ -74,8 +74,8 @@ public class PermissionRange implements Comparable<PermissionRange> {
this.min = min; this.min = min;
this.max = max; this.max = max;
} else { } else {
this.min = max; this.min = 0;
this.max = min; this.max = 0;
} }
} }

View File

@@ -36,6 +36,8 @@ import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/** /**
* A set of members that can be modified as plugins reload. * A set of members that can be modified as plugins reload.
@@ -284,6 +286,10 @@ public class DynamicSet<T> implements Iterable<T> {
return new ReloadableHandle(ref, key, ref.get()); return new ReloadableHandle(ref, key, ref.get());
} }
public Stream<T> stream() {
return StreamSupport.stream(spliterator(), false);
}
private class ReloadableHandle implements ReloadableRegistrationHandle<T> { private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
private final AtomicReference<Extension<T>> ref; private final AtomicReference<Extension<T>> ref;
private final Key<T> key; private final Key<T> key;

View File

@@ -107,6 +107,29 @@ public class BranchCommitValidator {
NoteMap rejectCommits, NoteMap rejectCommits,
@Nullable Change change) @Nullable Change change)
throws IOException { throws IOException {
return validateCommit(objectReader, cmd, commit, isMerged, rejectCommits, change, false);
}
/**
* Validates a single commit. If the commit does not validate, the command is rejected.
*
* @param objectReader the object reader to use.
* @param cmd the ReceiveCommand executing the push.
* @param commit the commit being validated.
* @param isMerged whether this is a merge commit created by magicBranch --merge option
* @param change the change for which this is a new patchset.
* @param skipValidation whether 'skip-validation' was requested.
* @return The validation {@link Result}.
*/
Result validateCommit(
ObjectReader objectReader,
ReceiveCommand cmd,
RevCommit commit,
boolean isMerged,
NoteMap rejectCommits,
@Nullable Change change,
boolean skipValidation)
throws IOException {
ImmutableList.Builder<CommitValidationMessage> messages = new ImmutableList.Builder<>(); ImmutableList.Builder<CommitValidationMessage> messages = new ImmutableList.Builder<>();
try (CommitReceivedEvent receiveEvent = try (CommitReceivedEvent receiveEvent =
new CommitReceivedEvent(cmd, project, branch.branch(), objectReader, commit, user)) { new CommitReceivedEvent(cmd, project, branch.branch(), objectReader, commit, user)) {
@@ -123,7 +146,8 @@ public class BranchCommitValidator {
sshInfo, sshInfo,
rejectCommits, rejectCommits,
receiveEvent.revWalk, receiveEvent.revWalk,
change); change,
skipValidation);
} }
for (CommitValidationMessage m : validators.validate(receiveEvent)) { for (CommitValidationMessage m : validators.validate(receiveEvent)) {

View File

@@ -1355,11 +1355,8 @@ class ReceiveCommits {
private void parseRewind(ReceiveCommand cmd) throws PermissionBackendException { private void parseRewind(ReceiveCommand cmd) throws PermissionBackendException {
try (TraceTimer traceTimer = newTimer("parseRewind")) { try (TraceTimer traceTimer = newTimer("parseRewind")) {
RevCommit newObject;
try { try {
newObject = receivePack.getRevWalk().parseCommit(cmd.getNewId()); receivePack.getRevWalk().parseCommit(cmd.getNewId());
} catch (IncorrectObjectTypeException notCommit) {
newObject = null;
} catch (IOException err) { } catch (IOException err) {
logger.atSevere().withCause(err).log( logger.atSevere().withCause(err).log(
"Invalid object %s for %s forced update", cmd.getNewId().name(), cmd.getRefName()); "Invalid object %s for %s forced update", cmd.getNewId().name(), cmd.getRefName());
@@ -1368,18 +1365,16 @@ class ReceiveCommits {
} }
logger.atFine().log("Rewinding %s", cmd); logger.atFine().log("Rewinding %s", cmd);
if (newObject != null) { if (!validRefOperation(cmd)) {
validateRegularPushCommits( return;
BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd); }
if (cmd.getResult() != NOT_ATTEMPTED) { validateRegularPushCommits(BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd);
return; if (cmd.getResult() != NOT_ATTEMPTED) {
} return;
} }
Optional<AuthException> err = checkRefPermission(cmd, RefPermission.FORCE_UPDATE); Optional<AuthException> err = checkRefPermission(cmd, RefPermission.FORCE_UPDATE);
if (!err.isPresent()) { if (err.isPresent()) {
validRefOperation(cmd);
} else {
rejectProhibited(cmd, err.get()); rejectProhibited(cmd, err.get());
} }
} }
@@ -3218,10 +3213,12 @@ class ReceiveCommits {
throws PermissionBackendException { throws PermissionBackendException {
try (TraceTimer traceTimer = try (TraceTimer traceTimer =
newTimer("validateRegularPushCommits", "branch", branch.branch())) { newTimer("validateRegularPushCommits", "branch", branch.branch())) {
if (!RefNames.REFS_CONFIG.equals(cmd.getRefName()) boolean skipValidation =
&& !(MagicBranch.isMagicBranch(cmd.getRefName()) !RefNames.REFS_CONFIG.equals(cmd.getRefName())
|| NEW_PATCHSET_PATTERN.matcher(cmd.getRefName()).matches()) && !(MagicBranch.isMagicBranch(cmd.getRefName())
&& pushOptions.containsKey(PUSH_OPTION_SKIP_VALIDATION)) { || NEW_PATCHSET_PATTERN.matcher(cmd.getRefName()).matches())
&& pushOptions.containsKey(PUSH_OPTION_SKIP_VALIDATION);
if (skipValidation) {
if (projectState.is(BooleanProjectConfig.USE_SIGNED_OFF_BY)) { if (projectState.is(BooleanProjectConfig.USE_SIGNED_OFF_BY)) {
reject(cmd, "requireSignedOffBy prevents option " + PUSH_OPTION_SKIP_VALIDATION); reject(cmd, "requireSignedOffBy prevents option " + PUSH_OPTION_SKIP_VALIDATION);
return; return;
@@ -3236,8 +3233,6 @@ class ReceiveCommits {
if (!Iterables.isEmpty(rejectCommits)) { if (!Iterables.isEmpty(rejectCommits)) {
reject(cmd, "reject-commits prevents " + PUSH_OPTION_SKIP_VALIDATION); reject(cmd, "reject-commits prevents " + PUSH_OPTION_SKIP_VALIDATION);
} }
logger.atFine().log("Short-circuiting new commit validation");
return;
} }
BranchCommitValidator validator = commitValidatorFactory.create(projectState, branch, user); BranchCommitValidator validator = commitValidatorFactory.create(projectState, branch, user);
@@ -3255,7 +3250,7 @@ class ReceiveCommits {
int limit = receiveConfig.maxBatchCommits; int limit = receiveConfig.maxBatchCommits;
int n = 0; int n = 0;
for (RevCommit c; (c = walk.next()) != null; ) { for (RevCommit c; (c = walk.next()) != null; ) {
if (++n > limit) { if (++n > limit && !skipValidation) {
logger.atFine().log("Number of new commits exceeds limit of %d", limit); logger.atFine().log("Number of new commits exceeds limit of %d", limit);
reject( reject(
cmd, cmd,
@@ -3268,7 +3263,8 @@ class ReceiveCommits {
} }
BranchCommitValidator.Result validationResult = BranchCommitValidator.Result validationResult =
validator.validateCommit(walk.getObjectReader(), cmd, c, false, rejectCommits, null); validator.validateCommit(
walk.getObjectReader(), cmd, c, false, rejectCommits, null, skipValidation);
messages.addAll(validationResult.messages()); messages.addAll(validationResult.messages());
if (!validationResult.isValid()) { if (!validationResult.isValid()) {
break; break;

View File

@@ -35,4 +35,14 @@ public interface CommitValidationListener {
*/ */
List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent) List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException; throws CommitValidationException;
/**
* Whether this validator should validate all commits.
*
* @return {@code true} if this validator should validate all commits, even when the {@code
* skip-validation} push option was specified.
*/
default boolean shouldValidateAllCommits() {
return false;
}
} }

View File

@@ -132,7 +132,8 @@ public class CommitValidators {
SshInfo sshInfo, SshInfo sshInfo,
NoteMap rejectCommits, NoteMap rejectCommits,
RevWalk rw, RevWalk rw,
@Nullable Change change) @Nullable Change change,
boolean skipValidation)
throws IOException { throws IOException {
PermissionBackend.ForRef perm = forProject.ref(branch.branch()); PermissionBackend.ForRef perm = forProject.ref(branch.branch());
ProjectState projectState = projectCache.checkedGet(branch.project()); ProjectState projectState = projectCache.checkedGet(branch.project());
@@ -153,7 +154,7 @@ public class CommitValidators {
change), change),
new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects), new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects),
new BannedCommitsValidator(rejectCommits), new BannedCommitsValidator(rejectCommits),
new PluginCommitValidationListener(pluginValidators), new PluginCommitValidationListener(pluginValidators, skipValidation),
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker), new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
new AccountCommitValidator(repoManager, allUsers, accountValidator), new AccountCommitValidator(repoManager, allUsers, accountValidator),
new GroupCommitValidator(allUsers))); new GroupCommitValidator(allUsers)));
@@ -476,27 +477,50 @@ public class CommitValidators {
/** Execute commit validation plug-ins */ /** Execute commit validation plug-ins */
public static class PluginCommitValidationListener implements CommitValidationListener { public static class PluginCommitValidationListener implements CommitValidationListener {
private boolean skipValidation;
private final PluginSetContext<CommitValidationListener> commitValidationListeners; private final PluginSetContext<CommitValidationListener> commitValidationListeners;
public PluginCommitValidationListener( public PluginCommitValidationListener(
final PluginSetContext<CommitValidationListener> commitValidationListeners) { final PluginSetContext<CommitValidationListener> commitValidationListeners) {
this(commitValidationListeners, false);
}
public PluginCommitValidationListener(
final PluginSetContext<CommitValidationListener> commitValidationListeners,
boolean skipValidation) {
this.skipValidation = skipValidation;
this.commitValidationListeners = commitValidationListeners; this.commitValidationListeners = commitValidationListeners;
} }
private void runValidator(
CommitValidationListener validator,
List<CommitValidationMessage> messages,
CommitReceivedEvent receiveEvent)
throws CommitValidationException {
if (skipValidation && !validator.shouldValidateAllCommits()) {
return;
}
messages.addAll(validator.onCommitReceived(receiveEvent));
}
@Override @Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent) public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException { throws CommitValidationException {
List<CommitValidationMessage> messages = new ArrayList<>(); List<CommitValidationMessage> messages = new ArrayList<>();
try { try {
commitValidationListeners.runEach( commitValidationListeners.runEach(
l -> messages.addAll(l.onCommitReceived(receiveEvent)), l -> runValidator(l, messages, receiveEvent), CommitValidationException.class);
CommitValidationException.class);
} catch (CommitValidationException e) { } catch (CommitValidationException e) {
messages.addAll(e.getMessages()); messages.addAll(e.getMessages());
throw new CommitValidationException(e.getMessage(), messages); throw new CommitValidationException(e.getMessage(), messages);
} }
return messages; return messages;
} }
@Override
public boolean shouldValidateAllCommits() {
return commitValidationListeners.stream().anyMatch(v -> v.shouldValidateAllCommits());
}
} }
public static class SignedOffByValidator implements CommitValidationListener { public static class SignedOffByValidator implements CommitValidationListener {

View File

@@ -15,6 +15,8 @@
package com.google.gerrit.server.mail.send; package com.google.gerrit.server.mail.send;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
@@ -44,8 +46,6 @@ import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.template.soy.data.SoyListData;
import com.google.template.soy.data.SoyMapData;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.MessageFormat; import java.text.MessageFormat;
@@ -556,15 +556,15 @@ public abstract class ChangeEmail extends NotificationEmail {
} }
/** /**
* Generate a Soy list of maps representing each line of the unified diff. The line maps will have * Generate a list of maps representing each line of the unified diff. The line maps will have a
* a 'type' key which maps to one of 'common', 'add' or 'remove' and a 'text' key which maps to * 'type' key which maps to one of 'common', 'add' or 'remove' and a 'text' key which maps to the
* the line's content. * line's content.
*/ */
private SoyListData getDiffTemplateData() { private ImmutableList<ImmutableMap<String, String>> getDiffTemplateData() {
SoyListData result = new SoyListData(); ImmutableList.Builder<ImmutableMap<String, String>> result = ImmutableList.builder();
Splitter lineSplitter = Splitter.on(System.getProperty("line.separator")); Splitter lineSplitter = Splitter.on(System.getProperty("line.separator"));
for (String diffLine : lineSplitter.split(getUnifiedDiff())) { for (String diffLine : lineSplitter.split(getUnifiedDiff())) {
SoyMapData lineData = new SoyMapData(); ImmutableMap.Builder<String, String> lineData = ImmutableMap.builder();
lineData.put("text", diffLine); lineData.put("text", diffLine);
// Skip empty lines and lines that look like diff headers. // Skip empty lines and lines that look like diff headers.
@@ -583,8 +583,8 @@ public abstract class ChangeEmail extends NotificationEmail {
break; break;
} }
} }
result.add(lineData); result.add(lineData.build());
} }
return result; return result.build();
} }
} }

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Iterator; import java.util.Iterator;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.stream.Stream;
/** /**
* Context to invoke extensions from a {@link DynamicSet}. * Context to invoke extensions from a {@link DynamicSet}.
@@ -147,6 +148,10 @@ public class PluginSetContext<T> implements Iterable<PluginSetEntryContext<T>> {
.forEach(p -> PluginContext.runLogExceptions(pluginMetrics, p, extensionImplConsumer)); .forEach(p -> PluginContext.runLogExceptions(pluginMetrics, p, extensionImplConsumer));
} }
public Stream<T> stream() {
return dynamicSet.stream();
}
/** /**
* Invokes each extension in the set. All exceptions from the plugin extensions except exceptions * Invokes each extension in the set. All exceptions from the plugin extensions except exceptions
* of the specified type are caught and logged. * of the specified type are caught and logged.

View File

@@ -2338,7 +2338,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(messages).hasSize(1); assertThat(messages).hasSize(1);
Message msg = messages.get(0); Message msg = messages.get(0);
assertThat(msg.rcpt()).containsExactly(user.getEmailAddress()); assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
assertThat(msg.body()).contains(admin.fullName() + " has removed a vote on this change.\n"); assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body()) assertThat(msg.body())
.contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n"); .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");

View File

@@ -99,7 +99,6 @@ import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.events.CommitReceivedEvent; import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.receive.NoteDbPushOption; import com.google.gerrit.server.git.receive.NoteDbPushOption;
import com.google.gerrit.server.git.receive.ReceiveConstants; import com.google.gerrit.server.git.receive.ReceiveConstants;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener; import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage; import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.git.validators.CommitValidators.ChangeIdValidator; import com.google.gerrit.server.git.validators.CommitValidators.ChangeIdValidator;
@@ -2342,14 +2341,27 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
private static class TestValidator implements CommitValidationListener { private static class TestValidator implements CommitValidationListener {
private final AtomicInteger count = new AtomicInteger(); private final AtomicInteger count = new AtomicInteger();
private final boolean validateAll;
TestValidator(boolean validateAll) {
this.validateAll = validateAll;
}
TestValidator() {
this(false);
}
@Override @Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent) public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent) {
throws CommitValidationException {
count.incrementAndGet(); count.incrementAndGet();
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public boolean shouldValidateAllCommits() {
return validateAll;
}
public int count() { public int count() {
return count.get(); return count.get();
} }
@@ -2360,6 +2372,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
String master = "refs/heads/master"; String master = "refs/heads/master";
TestValidator validator = new TestValidator(); TestValidator validator = new TestValidator();
RegistrationHandle handle = commitValidators.add("test-validator", validator); RegistrationHandle handle = commitValidators.add("test-validator", validator);
RegistrationHandle handle2 = null;
try { try {
// Validation listener is called on normal push // Validation listener is called on normal push
@@ -2386,8 +2399,25 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r = push3.to(master); r = push3.to(master);
r.assertOkStatus(); r.assertOkStatus();
assertThat(validator.count()).isEqualTo(1); assertThat(validator.count()).isEqualTo(1);
// Validation listener that needs to validate all commits gets called even
// when the skip option is used.
TestValidator validator2 = new TestValidator(true);
handle2 = commitValidators.add("test-validator-2", validator2);
PushOneCommit push4 =
pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push4.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
r = push4.to(master);
r.assertOkStatus();
// First listener was not called; its count remains the same.
assertThat(validator.count()).isEqualTo(1);
// Second listener was called.
assertThat(validator2.count()).isEqualTo(1);
} finally { } finally {
handle.remove(); handle.remove();
if (handle2 != null) {
handle2.remove();
}
} }
} }

View File

@@ -0,0 +1,210 @@
// Copyright (C) 2019 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.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.events.RefReceivedEvent;
import com.google.gerrit.server.git.validators.RefOperationValidationListener;
import com.google.gerrit.server.git.validators.ValidationMessage;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.Test;
public class RefOperationValidationIT extends AbstractDaemonTest {
private static final String TEST_REF = "refs/heads/protected";
@Inject DynamicSet<RefOperationValidationListener> validators;
@Inject private ProjectOperations projectOperations;
private class TestRefValidator implements RefOperationValidationListener, AutoCloseable {
private final ReceiveCommand.Type rejectType;
private final String rejectRef;
private final RegistrationHandle handle;
public TestRefValidator(ReceiveCommand.Type rejectType) {
this.rejectType = rejectType;
this.rejectRef = TEST_REF;
this.handle = validators.add("test-" + rejectType.name(), this);
}
@Override
public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
throws ValidationException {
if (refEvent.getRefName().equals(rejectRef)
&& refEvent.command.getType().equals(rejectType)) {
throw new ValidationException(rejectType.name());
}
return Collections.emptyList();
}
@Override
public void close() throws Exception {
handle.remove();
}
}
@Test
public void rejectRefCreation() throws Exception {
try (TestRefValidator validator = new TestRefValidator(CREATE)) {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
assert_().fail("expected exception");
} catch (RestApiException expected) {
assertThat(expected).hasMessageThat().contains(CREATE.name());
}
}
private void grant(String permission) {
projectOperations
.project(project)
.forUpdate()
.add(allow(permission).ref("refs/*").group(REGISTERED_USERS).force(true))
.update();
}
@Test
public void rejectRefCreationByPush() throws Exception {
try (TestRefValidator validator = new TestRefValidator(CREATE)) {
grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
PushOneCommit.Result r2 = push1.to(TEST_REF);
r2.assertErrorStatus(CREATE.name());
}
}
@Test
public void rejectRefDeletion() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
try (TestRefValidator validator = new TestRefValidator(DELETE)) {
gApi.projects().name(project.get()).branch(TEST_REF).delete();
assert_().fail("expected exception");
} catch (RestApiException expected) {
assertThat(expected).hasMessageThat().contains(DELETE.name());
}
}
@Test
public void rejectRefDeletionByPush() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
grant(Permission.DELETE);
try (TestRefValidator validator = new TestRefValidator(DELETE)) {
PushResult result = deleteRef(testRepo, TEST_REF);
RemoteRefUpdate refUpdate = result.getRemoteUpdate(TEST_REF);
assertThat(refUpdate.getMessage()).contains(DELETE.name());
}
}
@Test
public void rejectRefUpdateFastForward() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
try (TestRefValidator validator = new TestRefValidator(UPDATE)) {
grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
r1.assertErrorStatus(UPDATE.name());
}
}
@Test
public void rejectRefUpdateNonFastForward() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
try (TestRefValidator validator = new TestRefValidator(UPDATE_NONFASTFORWARD)) {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
r1.assertOkStatus();
// Reset HEAD to initial so the new change is a non-fast forward
RefUpdate ru = repo().updateRef(HEAD);
ru.setNewObjectId(initial);
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push2 =
pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push2.setForce(true);
PushOneCommit.Result r2 = push2.to(TEST_REF);
r2.assertErrorStatus(UPDATE_NONFASTFORWARD.name());
}
}
@Test
public void rejectRefUpdateNonFastForwardToExistingCommit() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
try (TestRefValidator validator = new TestRefValidator(UPDATE_NONFASTFORWARD)) {
grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
ObjectId push1Id = r1.getCommit();
PushOneCommit push2 =
pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
PushOneCommit.Result r2 = push2.to("refs/heads/master");
r2.assertOkStatus();
ObjectId push2Id = r2.getCommit();
RefUpdate ru = repo().updateRef(HEAD);
ru.setNewObjectId(push1Id);
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push3 =
pushFactory.create(admin.newIdent(), testRepo, "change3", "c.txt", "content");
PushOneCommit.Result r3 = push3.to(TEST_REF);
r3.assertOkStatus();
ru = repo().updateRef(HEAD);
ru.setNewObjectId(push2Id);
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push4 =
pushFactory.create(admin.newIdent(), testRepo, "change4", "d.txt", "content");
push4.setForce(true);
PushOneCommit.Result r4 = push4.to(TEST_REF);
r4.assertErrorStatus(UPDATE_NONFASTFORWARD.name());
}
}
}

View File

@@ -74,7 +74,7 @@ public class DeleteVoteIT extends AbstractDaemonTest {
assertThat(messages).hasSize(1); assertThat(messages).hasSize(1);
FakeEmailSender.Message msg = messages.get(0); FakeEmailSender.Message msg = messages.get(0);
assertThat(msg.rcpt()).containsExactly(user.getEmailAddress()); assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
assertThat(msg.body()).contains(admin.fullName() + " has removed a vote on this change.\n"); assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body()) assertThat(msg.body())
.contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n"); .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");

View File

@@ -37,23 +37,23 @@ public class ElasticContainer extends ElasticsearchContainer {
private static String getImageName(ElasticVersion version) { private static String getImageName(ElasticVersion version) {
switch (version) { switch (version) {
case V5_6: case V5_6:
return "docker.elastic.co/elasticsearch/elasticsearch:5.6.16"; return "blacktop/elasticsearch:5.6.16";
case V6_2: case V6_2:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.2.4"; return "blacktop/elasticsearch:6.2.4";
case V6_3: case V6_3:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.3.2"; return "blacktop/elasticsearch:6.3.2";
case V6_4: case V6_4:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3"; return "blacktop/elasticsearch:6.4.3";
case V6_5: case V6_5:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.5.4"; return "blacktop/elasticsearch:6.5.4";
case V6_6: case V6_6:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.6.2"; return "blacktop/elasticsearch:6.6.2";
case V6_7: case V6_7:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.7.2"; return "blacktop/elasticsearch:6.7.2";
case V7_0: case V7_0:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.1"; return "blacktop/elasticsearch:7.0.1";
case V7_1: case V7_1:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:7.1.1"; return "blacktop/elasticsearch:7.1.1";
} }
throw new IllegalStateException("No tests for version: " + version.name()); throw new IllegalStateException("No tests for version: " + version.name());
} }

View File

@@ -45,6 +45,9 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0); assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0);
assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0); assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0);
assertThat(ElasticVersion.forVersion("7.1.0")).isEqualTo(ElasticVersion.V7_1);
assertThat(ElasticVersion.forVersion("7.1.1")).isEqualTo(ElasticVersion.V7_1);
} }
@Test @Test
@@ -59,18 +62,6 @@ public class ElasticVersionTest {
+ ElasticVersion.supportedVersions()); + ElasticVersion.supportedVersions());
} }
@Test
public void version6() throws Exception {
assertThat(ElasticVersion.V5_6.isV6OrLater()).isFalse();
assertThat(ElasticVersion.V6_2.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_3.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_4.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
}
@Test @Test
public void atLeastMinorVersion() throws Exception { public void atLeastMinorVersion() throws Exception {
assertThat(ElasticVersion.V5_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V5_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
@@ -81,10 +72,24 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue(); assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse(); assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
} }
@Test @Test
public void version7() throws Exception { public void version6OrLater() throws Exception {
assertThat(ElasticVersion.V5_6.isV6OrLater()).isFalse();
assertThat(ElasticVersion.V6_2.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_3.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_4.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
}
@Test
public void version7OrLater() throws Exception {
assertThat(ElasticVersion.V5_6.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V5_6.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_2.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_2.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_3.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_3.isV7OrLater()).isFalse();
@@ -93,5 +98,6 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse(); assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue(); assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
} }
} }

View File

@@ -615,6 +615,20 @@ public class RefControlTest {
assertCannotUpdate("refs/tags/V10", u); assertCannotUpdate("refs/tags/V10", u);
} }
@Test
public void blockPartialRangeLocally() throws Exception {
projectOperations
.project(localKey)
.forUpdate()
.add(blockLabel("Code-Review").ref("refs/heads/master").group(DEVS).range(+1, +2))
.update();
ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(2, range);
}
@Test @Test
public void blockLabelRange_ParentBlocksChild() throws Exception { public void blockLabelRange_ParentBlocksChild() throws Exception {
projectOperations projectOperations

View File

@@ -46,10 +46,10 @@ limitations under the License.
#loading:not(.loading) { #loading:not(.loading) {
display: none; display: none;
} }
.repositorySettings { #options .repositorySettings {
display: none; display: none;
} }
.repositorySettings.showConfig { #options .repositorySettings.showConfig {
display: block; display: block;
} }
</style> </style>
@@ -219,7 +219,7 @@ limitations under the License.
</gr-select> </gr-select>
</span> </span>
</section> </section>
<section id="noteDbSettings" class$="repositorySettings [[_computeRepositoriesClass(_noteDbEnabled)]]"> <section>
<span class="title"> <span class="title">
Enable adding unregistered users as reviewers and CCs on changes</span> Enable adding unregistered users as reviewers and CCs on changes</span>
<span class="value"> <span class="value">

View File

@@ -107,10 +107,6 @@
}, },
_selectedScheme: String, _selectedScheme: String,
_schemesObj: Object, _schemesObj: Object,
_noteDbEnabled: {
type: Boolean,
value: false,
},
}, },
observers: [ observers: [
@@ -177,7 +173,6 @@
if (!config) { return Promise.resolve(); } if (!config) { return Promise.resolve(); }
this._schemesObj = config.download.schemes; this._schemesObj = config.download.schemes;
this._noteDbEnabled = !!config.note_db_enabled;
})); }));
return Promise.all(promises); return Promise.all(promises);

View File

@@ -316,17 +316,6 @@ limitations under the License.
}); });
test('fields update and save correctly', () => { test('fields update and save correctly', () => {
// test notedb
element._noteDbEnabled = false;
assert.equal(
element._computeRepositoriesClass(element._noteDbEnabled), '');
element._noteDbEnabled = true;
assert.equal(element._computeRepositoriesClass(
element._noteDbEnabled), 'showConfig');
const configInputObj = { const configInputObj = {
description: 'new description', description: 'new description',
use_contributor_agreements: 'TRUE', use_contributor_agreements: 'TRUE',

View File

@@ -85,9 +85,6 @@ limitations under the License.
getLoggedIn() { return Promise.resolve(false); }, getLoggedIn() { return Promise.resolve(false); },
deleteVote() { return Promise.resolve({ok: true}); }, deleteVote() { return Promise.resolve({ok: true}); },
}); });
stub('gr-change-metadata', {
_computeShowReviewersByState() { return true; },
});
}); });
teardown(() => { teardown(() => {

View File

@@ -200,38 +200,26 @@ limitations under the License.
allow-any-user></gr-account-list> allow-any-user></gr-account-list>
</span> </span>
</section> </section>
<template is="dom-if" if="[[_showReviewersByState]]"> <section>
<section> <span class="title">Reviewers</span>
<span class="title">Reviewers</span> <span class="value">
<span class="value"> <gr-reviewer-list
<gr-reviewer-list change="{{change}}"
change="{{change}}" mutable="[[_mutable]]"
mutable="[[_mutable]]" reviewers-only
reviewers-only max-reviewers-displayed="3"></gr-reviewer-list>
max-reviewers-displayed="3"></gr-reviewer-list> </span>
</span> </section>
</section> <section>
<section> <span class="title">CC</span>
<span class="title">CC</span> <span class="value">
<span class="value"> <gr-reviewer-list
<gr-reviewer-list change="{{change}}"
change="{{change}}" mutable="[[_mutable]]"
mutable="[[_mutable]]" ccs-only
ccs-only max-reviewers-displayed="3"></gr-reviewer-list>
max-reviewers-displayed="3"></gr-reviewer-list> </span>
</span> </section>
</section>
</template>
<template is="dom-if" if="[[!_showReviewersByState]]">
<section>
<span class="title">Reviewers</span>
<span class="value">
<gr-reviewer-list
change="{{change}}"
mutable="[[_mutable]]"></gr-reviewer-list>
</span>
</section>
</template>
<section> <section>
<span class="title">Repo</span> <span class="title">Repo</span>
<span class="value"> <span class="value">
@@ -299,31 +287,29 @@ limitations under the License.
<span class="title">Strategy</span> <span class="title">Strategy</span>
<span class="value">[[_computeStrategy(change)]]</span> <span class="value">[[_computeStrategy(change)]]</span>
</section> </section>
<template is="dom-if" if="[[serverConfig.note_db_enabled]]"> <section class="hashtag">
<section class="hashtag"> <span class="title">Hashtags</span>
<span class="title">Hashtags</span> <span class="value">
<span class="value"> <template is="dom-repeat" items="[[change.hashtags]]">
<template is="dom-repeat" items="[[change.hashtags]]"> <gr-linked-chip
<gr-linked-chip class="hashtagChip"
class="hashtagChip" text="[[item]]"
text="[[item]]" href="[[_computeHashtagURL(item)]]"
href="[[_computeHashtagURL(item)]]" removable="[[!_hashtagReadOnly]]"
removable="[[!_hashtagReadOnly]]" on-remove="_handleHashtagRemoved">
on-remove="_handleHashtagRemoved"> </gr-linked-chip>
</gr-linked-chip> </template>
</template> <template is="dom-if" if="[[!_hashtagReadOnly]]">
<template is="dom-if" if="[[!_hashtagReadOnly]]"> <gr-editable-label
<gr-editable-label uppercase
uppercase label-text="Add a hashtag"
label-text="Add a hashtag" value="{{_newHashtag}}"
value="{{_newHashtag}}" placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]"
placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]" read-only="[[_hashtagReadOnly]]"
read-only="[[_hashtagReadOnly]]" on-changed="_handleHashtagChanged"></gr-editable-label>
on-changed="_handleHashtagChanged"></gr-editable-label> </template>
</template> </span>
</span> </section>
</section>
</template>
<div class="separatedSection"> <div class="separatedSection">
<gr-change-requirements <gr-change-requirements
change="{{change}}" change="{{change}}"

View File

@@ -84,9 +84,7 @@
type: Boolean, type: Boolean,
computed: '_computeIsMutable(account)', computed: '_computeIsMutable(account)',
}, },
/** /** @type {?} */
* @type {{ note_db_enabled: string }}
*/
serverConfig: Object, serverConfig: Object,
parentIsCurrent: Boolean, parentIsCurrent: Boolean,
_notCurrentMessage: { _notCurrentMessage: {
@@ -102,10 +100,6 @@
type: Boolean, type: Boolean,
computed: '_computeHashtagReadOnly(_mutable, change)', computed: '_computeHashtagReadOnly(_mutable, change)',
}, },
_showReviewersByState: {
type: Boolean,
computed: '_computeShowReviewersByState(serverConfig)',
},
/** /**
* @type {Defs.PushCertificateValidation} * @type {Defs.PushCertificateValidation}
*/ */
@@ -283,10 +277,6 @@
return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE; return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE;
}, },
_computeShowReviewersByState(serverConfig) {
return !!serverConfig.note_db_enabled;
},
_computeShowRequirements(change) { _computeShowRequirements(change) {
if (change.status !== this.ChangeStatus.NEW) { if (change.status !== this.ChangeStatus.NEW) {
// TODO(maximeg) change this to display the stored // TODO(maximeg) change this to display the stored

View File

@@ -119,18 +119,6 @@ limitations under the License.
assert.isTrue(element.$$('.strategy').hasAttribute('hidden')); assert.isTrue(element.$$('.strategy').hasAttribute('hidden'));
}); });
test('show CC section when NoteDb enabled', () => {
function hasCc() {
return element._showReviewersByState;
}
element.serverConfig = {};
assert.isFalse(hasCc());
element.serverConfig = {note_db_enabled: true};
assert.isTrue(hasCc());
});
test('weblinks use Gerrit.Nav interface', () => { test('weblinks use Gerrit.Nav interface', () => {
const weblinksStub = sandbox.stub(Gerrit.Nav, '_generateWeblinks') const weblinksStub = sandbox.stub(Gerrit.Nav, '_generateWeblinks')
.returns([{name: 'stubb', url: '#s'}]); .returns([{name: 'stubb', url: '#s'}]);
@@ -552,9 +540,6 @@ limitations under the License.
}); });
test('_computeHashtagReadOnly', () => { test('_computeHashtagReadOnly', () => {
element.serverConfig = {
note_db_enabled: true,
};
flushAsynchronousOperations(); flushAsynchronousOperations();
let mutable = false; let mutable = false;
assert.isTrue(element._computeHashtagReadOnly(mutable, change)); assert.isTrue(element._computeHashtagReadOnly(mutable, change));
@@ -567,9 +552,6 @@ limitations under the License.
}); });
test('hashtag read only hides delete button', () => { test('hashtag read only hides delete button', () => {
element.serverConfig = {
note_db_enabled: true,
};
flushAsynchronousOperations(); flushAsynchronousOperations();
element.account = {}; element.account = {};
element.change = change; element.change = change;
@@ -579,9 +561,6 @@ limitations under the License.
}); });
test('hashtag not read only does not hide delete button', () => { test('hashtag not read only does not hide delete button', () => {
element.serverConfig = {
note_db_enabled: true,
};
flushAsynchronousOperations(); flushAsynchronousOperations();
element.account = {test: true}; element.account = {test: true};
change.actions.hashtags.enabled = true; change.actions.hashtags.enabled = true;
@@ -703,9 +682,6 @@ limitations under the License.
}); });
test('changing hashtag', () => { test('changing hashtag', () => {
element.serverConfig = {
note_db_enabled: true,
};
flushAsynchronousOperations(); flushAsynchronousOperations();
element._newHashtag = 'new hashtag'; element._newHashtag = 'new hashtag';
const newHashtag = ['new hashtag']; const newHashtag = ['new hashtag'];

View File

@@ -673,7 +673,6 @@ limitations under the License.
patch-num="[[computeLatestPatchNum(_allPatchSets)]]" patch-num="[[computeLatestPatchNum(_allPatchSets)]]"
permitted-labels="[[_change.permitted_labels]]" permitted-labels="[[_change.permitted_labels]]"
diff-drafts="[[_diffDrafts]]" diff-drafts="[[_diffDrafts]]"
server-config="[[_serverConfig]]"
project-config="[[_projectConfig]]" project-config="[[_projectConfig]]"
can-be-started="[[_canStartReview]]" can-be-started="[[_canStartReview]]"
on-send="_handleReplySent" on-send="_handleReplySent"

View File

@@ -88,7 +88,6 @@ limitations under the License.
'+1', '+1',
], ],
}; };
element.serverConfig = {note_db_enabled: true};
sandbox.stub(element, 'fetchChangeUpdates') sandbox.stub(element, 'fetchChangeUpdates')
.returns(Promise.resolve({isLatest: true})); .returns(Promise.resolve({isLatest: true}));
}; };

View File

@@ -171,21 +171,19 @@ limitations under the License.
on-account-text-changed="_handleAccountTextEntry"> on-account-text-changed="_handleAccountTextEntry">
</gr-account-list> </gr-account-list>
</div> </div>
<template is="dom-if" if="[[serverConfig.note_db_enabled]]"> <div class="peopleList">
<div class="peopleList"> <div class="peopleListLabel">CC</div>
<div class="peopleListLabel">CC</div> <gr-account-list
<gr-account-list id="ccs"
id="ccs" accounts="{{_ccs}}"
accounts="{{_ccs}}" change="[[change]]"
change="[[change]]" filter="[[filterCCSuggestion]]"
filter="[[filterCCSuggestion]]" pending-confirmation="{{_ccPendingConfirmation}}"
pending-confirmation="{{_ccPendingConfirmation}}" allow-any-input
allow-any-input placeholder="Add CC..."
placeholder="Add CC..." on-account-text-changed="_handleAccountTextEntry">
on-account-text-changed="_handleAccountTextEntry"> </gr-account-list>
</gr-account-list> </div>
</div>
</template>
<gr-overlay <gr-overlay
id="reviewerConfirmationOverlay" id="reviewerConfirmationOverlay"
on-iron-overlay-canceled="_cancelPendingReviewer"> on-iron-overlay-canceled="_cancelPendingReviewer">

View File

@@ -141,10 +141,6 @@
}, },
}, },
permittedLabels: Object, permittedLabels: Object,
/**
* @type {{ note_db_enabled: boolean }}
*/
serverConfig: Object,
/** /**
* @type {{ commentlinks: Array }} * @type {{ commentlinks: Array }}
*/ */
@@ -195,10 +191,6 @@
type: String, type: String,
computed: '_computeSendButtonLabel(canBeStarted)', computed: '_computeSendButtonLabel(canBeStarted)',
}, },
_ccsEnabled: {
type: Boolean,
computed: '_computeCCsEnabled(serverConfig)',
},
_savingComments: Boolean, _savingComments: Boolean,
_reviewersMutated: { _reviewersMutated: {
type: Boolean, type: Boolean,
@@ -245,7 +237,7 @@
}, },
observers: [ observers: [
'_changeUpdated(change.reviewers.*, change.owner, serverConfig)', '_changeUpdated(change.reviewers.*, change.owner)',
'_ccsChanged(_ccs.splices)', '_ccsChanged(_ccs.splices)',
'_reviewersChanged(_reviewers.splices)', '_reviewersChanged(_reviewers.splices)',
], ],
@@ -629,14 +621,14 @@
'Say something nice...'; 'Say something nice...';
}, },
_changeUpdated(changeRecord, owner, serverConfig) { _changeUpdated(changeRecord, owner) {
this._rebuildReviewerArrays(changeRecord.base, owner, serverConfig); this._rebuildReviewerArrays(changeRecord.base, owner);
}, },
_rebuildReviewerArrays(change, owner, serverConfig) { _rebuildReviewerArrays(change, owner) {
this._owner = owner; this._owner = owner;
let reviewers = []; const reviewers = [];
const ccs = []; const ccs = [];
for (const key in change) { for (const key in change) {
@@ -661,12 +653,7 @@
} }
} }
if (this._ccsEnabled) { this._ccs = ccs;
this._ccs = ccs;
} else {
this._ccs = [];
reviewers = reviewers.concat(ccs);
}
this._reviewers = reviewers; this._reviewers = reviewers;
}, },
@@ -719,13 +706,12 @@
this.fire('cancel', null, {bubbles: false}); this.fire('cancel', null, {bubbles: false});
this.$.textarea.closeDropdown(); this.$.textarea.closeDropdown();
this._purgeReviewersPendingRemove(true); this._purgeReviewersPendingRemove(true);
this._rebuildReviewerArrays(this.change.reviewers, this._owner, this._rebuildReviewerArrays(this.change.reviewers, this._owner);
this.serverConfig);
}, },
_saveTapHandler(e) { _saveTapHandler(e) {
e.preventDefault(); e.preventDefault();
if (this._ccsEnabled && !this.$$('#ccs').submitEntryText()) { if (!this.$$('#ccs').submitEntryText()) {
// Do not proceed with the save if there is an invalid email entry in // Do not proceed with the save if there is an invalid email entry in
// the text field of the CC entry. // the text field of the CC entry.
return; return;
@@ -741,7 +727,7 @@
}, },
_submit() { _submit() {
if (this._ccsEnabled && !this.$$('#ccs').submitEntryText()) { if (!this.$$('#ccs').submitEntryText()) {
// Do not proceed with the send if there is an invalid email entry in // Do not proceed with the send if there is an invalid email entry in
// the text field of the CC entry. // the text field of the CC entry.
return; return;
@@ -863,10 +849,6 @@
return canBeStarted ? ButtonTooltips.START_REVIEW : ButtonTooltips.SEND; return canBeStarted ? ButtonTooltips.START_REVIEW : ButtonTooltips.SEND;
}, },
_computeCCsEnabled(serverConfig) {
return serverConfig && serverConfig.note_db_enabled;
},
_computeSavingLabelClass(savingComments) { _computeSavingLabelClass(savingComments) {
return savingComments ? 'saving' : ''; return savingComments ? 'saving' : '';
}, },

View File

@@ -99,7 +99,6 @@ limitations under the License.
'+1', '+1',
], ],
}; };
element.serverConfig = {};
getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment'); getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment');
setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment'); setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment');
@@ -298,7 +297,6 @@ limitations under the License.
const noButton = const noButton =
element.$$('.reviewerConfirmationButtons gr-button:last-child'); element.$$('.reviewerConfirmationButtons gr-button:last-child');
element.serverConfig = {note_db_enabled: true};
element._ccPendingConfirmation = null; element._ccPendingConfirmation = null;
element._reviewerPendingConfirmation = null; element._reviewerPendingConfirmation = null;
flushAsynchronousOperations(); flushAsynchronousOperations();
@@ -411,7 +409,6 @@ limitations under the License.
}); });
test('_reviewersMutated when account-text-change is fired from ccs', () => { test('_reviewersMutated when account-text-change is fired from ccs', () => {
element.serverConfig = {note_db_enabled: true};
flushAsynchronousOperations(); flushAsynchronousOperations();
assert.isFalse(element._reviewersMutated); assert.isFalse(element._reviewersMutated);
assert.isTrue(element.$$('#ccs').allowAnyInput); assert.isTrue(element.$$('#ccs').allowAnyInput);
@@ -498,19 +495,6 @@ limitations under the License.
flush(() => { element.send(); }); flush(() => { element.send(); });
}); });
test('ccs are displayed if NoteDb is enabled', () => {
function hasCc() {
flushAsynchronousOperations();
return !!element.$$('#ccs');
}
element.serverConfig = {};
assert.isFalse(hasCc());
element.serverConfig = {note_db_enabled: true};
assert.isTrue(hasCc());
});
test('filterReviewerSuggestion', () => { test('filterReviewerSuggestion', () => {
const owner = makeAccount(); const owner = makeAccount();
const reviewer1 = makeAccount(); const reviewer1 = makeAccount();
@@ -542,7 +526,6 @@ limitations under the License.
test('_focusOn', () => { test('_focusOn', () => {
sandbox.spy(element, '_chooseFocusTarget'); sandbox.spy(element, '_chooseFocusTarget');
element.serverConfig = {note_db_enabled: true};
flushAsynchronousOperations(); flushAsynchronousOperations();
const textareaStub = sandbox.stub(element.$.textarea, 'async'); const textareaStub = sandbox.stub(element.$.textarea, 'async');
const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart, const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart,
@@ -682,7 +665,6 @@ limitations under the License.
}); });
test('moving from cc to reviewer', () => { test('moving from cc to reviewer', () => {
element.serverConfig = {note_db_enabled: true};
element._reviewersPendingRemove = { element._reviewersPendingRemove = {
CC: [], CC: [],
REVIEWER: [], REVIEWER: [],
@@ -716,7 +698,6 @@ limitations under the License.
}); });
test('migrate reviewers between states', done => { test('migrate reviewers between states', done => {
element.serverConfig = {note_db_enabled: true};
element._reviewersPendingRemove = { element._reviewersPendingRemove = {
CC: [], CC: [],
REVIEWER: [], REVIEWER: [],

View File

@@ -265,7 +265,7 @@ fi
if test -z "$JAVA" ; then if test -z "$JAVA" ; then
echo >&2 "Cannot find a JRE or JDK. Please ensure that the JAVA_HOME environment" echo >&2 "Cannot find a JRE or JDK. Please ensure that the JAVA_HOME environment"
echo >&2 "variable or container.javaHome in $GERRIT_SITE/etc/gerrit.config is" echo >&2 "variable or container.javaHome in $GERRIT_SITE/etc/gerrit.config is"
echo >&2 "set to a valid >=1.7 JRE location" echo >&2 "set to a valid >=1.8 JRE location"
exit 1 exit 1
fi fi

View File

@@ -23,8 +23,9 @@
{template .DeleteVote kind="text"} {template .DeleteVote kind="text"}
{@param change: ?} {@param change: ?}
{@param coverLetter: ?} {@param coverLetter: ?}
{@param email: ?}
{@param fromName: ?} {@param fromName: ?}
{$fromName} has removed a vote on this change.{\n} {$fromName} has removed a vote from this change.{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}{\n}
{\n} {\n}
Change subject: {$change.subject}{\n} Change subject: {$change.subject}{\n}
......................................................................{\n} ......................................................................{\n}

View File

@@ -53,7 +53,7 @@
{/if} {/if}
{else} {else}
{$ownerName} has uploaded this change for review. {$ownerName} has uploaded this change for review.
{if $email.changeUrl} ( {$email.changeUrl}{/if} {if $email.changeUrl} ( {$email.changeUrl} ){/if}
{/if}{\n} {/if}{\n}
{\n} {\n}

View File

@@ -17,8 +17,8 @@
def _impl(ctx): def _impl(ctx):
zip_output = ctx.outputs.zip zip_output = ctx.outputs.zip
transitive_jars = depset(transitive = [l.java.transitive_deps for l in ctx.attr.libs]) transitive_jars = depset(transitive = [j.java.transitive_deps for j in ctx.attr.libs])
source_jars = depset(transitive = [l.java.source_jars for l in ctx.attr.libs]) source_jars = depset(transitive = [j.java.source_jars for j in ctx.attr.libs])
transitive_jar_paths = [j.path for j in transitive_jars.to_list()] transitive_jar_paths = [j.path for j in transitive_jars.to_list()]
dir = ctx.outputs.zip.path + ".dir" dir = ctx.outputs.zip.path + ".dir"

View File

@@ -28,8 +28,8 @@ public class %s {}
_PREFIXES = ("org", "com", "edu") _PREFIXES = ("org", "com", "edu")
def _SafeIndex(l, val): def _SafeIndex(j, val):
for i, v in enumerate(l): for i, v in enumerate(j):
if val == v: if val == v:
return i return i
return -1 return -1

View File

@@ -76,11 +76,11 @@ def _war_impl(ctx):
# Add lib # Add lib
transitive_libs = [] transitive_libs = []
for l in ctx.attr.libs: for j in ctx.attr.libs:
if hasattr(l, "java"): if hasattr(j, "java"):
transitive_libs.append(l.java.transitive_runtime_deps) transitive_libs.append(j.java.transitive_runtime_deps)
elif hasattr(l, "files"): elif hasattr(j, "files"):
transitive_libs.append(l.files) transitive_libs.append(j.files)
transitive_lib_deps = depset(transitive = transitive_libs) transitive_lib_deps = depset(transitive = transitive_libs)
for dep in transitive_lib_deps.to_list(): for dep in transitive_lib_deps.to_list():
@@ -89,8 +89,8 @@ def _war_impl(ctx):
# Add pgm lib # Add pgm lib
transitive_pgmlibs = [] transitive_pgmlibs = []
for l in ctx.attr.pgmlibs: for j in ctx.attr.pgmlibs:
transitive_pgmlibs.append(l.java.transitive_runtime_deps) transitive_pgmlibs.append(j.java.transitive_runtime_deps)
transitive_pgmlib_deps = depset(transitive = transitive_pgmlibs) transitive_pgmlib_deps = depset(transitive = transitive_pgmlibs)
for dep in transitive_pgmlib_deps.to_list(): for dep in transitive_pgmlib_deps.to_list():