Merge branch 'stable-3.7'
* stable-3.7:
Allow to hide download schemes from the REST API and UI
Remove documentation of removed option download.maxBundleSize
TaskThunk#run: ignore SshChannelClosedException
SSH BaseCommand: handle Throwable instead of Exception
Always track master on download-commands plugin
Mark setFunction as deprecated
Make it possible to run BatchUpdate with non-identified user
Don't limit PerThreadCache rather limit PerThreadProjectCache
SSH BaseCommand: handle Throwable instead of Exception
Set version to 3.7.2-SNAPSHOT
Set version to 3.7.1
Set version to 3.6.5-SNAPSHOT
Set version to 3.6.4
Make a viewState a state in gr-editor-view
Fix “Old Patchset” being displayed on current edits
Fix e2e test compilation failure
Release-Notes: skip
Change-Id: I047edcfb75d2203b0c7fd5a6d26aab1a115b51fe
diff --git a/.gitmodules b/.gitmodules
index e5eef1e..6217b4d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -20,7 +20,7 @@
[submodule "plugins/download-commands"]
path = plugins/download-commands
url = ../plugins/download-commands
- branch = .
+ branch = master
[submodule "plugins/gitiles"]
path = plugins/gitiles
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 19445b8..295bd5a 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2074,6 +2074,7 @@
scheme = anon_http
scheme = anon_git
scheme = repo
+ hide = ssh
----
The download section configures the allowed download methods.
@@ -2142,6 +2143,17 @@
If `download.scheme` is not specified, SSH, HTTP and Anonymous HTTP
downloads are allowed.
+[[download.hide]]download.hide::
++
+Schemes that can be used to download changes, but will not be advertised
+in the UI. This can be any scheme that can be configured in <<download.scheme>>.
++
+This is mostly useful in a deprecation scenario during a time where using
+a scheme is discouraged, but has to be supported until all clients have
+migrated to use a different scheme.
++
+By default, no scheme will be hidden in the UI.
+
[[download.checkForHiddenChangeRefs]]download.checkForHiddenChangeRefs::
+
Whether the download commands should be adapted when the change refs
@@ -2189,15 +2201,6 @@
For this reason `zip` format is always excluded from formats offered
through the `Download` drop down or accessible in the REST API.
-[[download.maxBundleSize]]download.maxBundleSize::
-+
-Specifies the maximum size of a bundle in bytes that can be downloaded.
-As bundles are kept in memory this setting is to protect the server
-from a single request consuming too much heap when generating
-a bundle and thereby impacting other users.
-+
-Defaults to 100MB.
-
[[gc]]
=== Section gc
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
index 53f942d..5885fb0 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
@@ -14,7 +14,7 @@
package com.google.gerrit.scenarios
-import static java.nio.charset.StandardCharsets.UTF_8
+import java.nio.charset.StandardCharsets.UTF_8
import java.io.{File, IOException}
import java.net.URLEncoder
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
index f0c6f68..3802cea 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
@@ -14,13 +14,13 @@
package com.google.gerrit.scenarios
-import static java.nio.charset.StandardCharsets.UTF_8
+import java.nio.charset.StandardCharsets.UTF_8
import java.net.URLEncoder
class ProjectSimulation extends GerritSimulation {
projectName = "defaultTestProject"
override def replaceOverride(in: String): String = {
- replaceProperty("project", URLEncoder.encode(getFullProjectName(projectName), UTF_8, in)
+ replaceProperty("project", URLEncoder.encode(getFullProjectName(projectName), UTF_8), in)
}
}
diff --git a/java/com/google/gerrit/entities/LabelType.java b/java/com/google/gerrit/entities/LabelType.java
index f009872..7a3266ecc 100644
--- a/java/com/google/gerrit/entities/LabelType.java
+++ b/java/com/google/gerrit/entities/LabelType.java
@@ -203,6 +203,11 @@
public abstract Builder setDescription(Optional<String> description);
+ /**
+ * @deprecated in favour of using submit requirements, except if it’s needed to set the value to
+ * PatchSetLock
+ */
+ @Deprecated
public abstract Builder setFunction(LabelFunction function);
public abstract Builder setCanOverride(boolean canOverride);
diff --git a/java/com/google/gerrit/extensions/config/DownloadScheme.java b/java/com/google/gerrit/extensions/config/DownloadScheme.java
index 96b5878..15801d4 100644
--- a/java/com/google/gerrit/extensions/config/DownloadScheme.java
+++ b/java/com/google/gerrit/extensions/config/DownloadScheme.java
@@ -34,4 +34,7 @@
/** Returns whether the download scheme is enabled */
public abstract boolean isEnabled();
+
+ /** Returns whether the download scheme is hidden in the UI */
+ public abstract boolean isHidden();
}
diff --git a/java/com/google/gerrit/server/cache/PerThreadCache.java b/java/com/google/gerrit/server/cache/PerThreadCache.java
index ef00b80..8ae9710 100644
--- a/java/com/google/gerrit/server/cache/PerThreadCache.java
+++ b/java/com/google/gerrit/server/cache/PerThreadCache.java
@@ -43,19 +43,9 @@
* <p>Lastly, this class offers a cache, that requires callers to also provide a {@code Supplier} in
* case the object is not present in the cache, while {@code CurrentUser} provides a storage where
* just retrieving stored values is a valid operation.
- *
- * <p>To prevent OOM errors on requests that would cache a lot of objects, this class enforces an
- * internal limit after which no new elements are cached. All {@code get} calls are served by
- * invoking the {@code Supplier} after that.
*/
public class PerThreadCache implements AutoCloseable {
private static final ThreadLocal<PerThreadCache> CACHE = new ThreadLocal<>();
- /**
- * Cache at maximum 25 values per thread. This value was chosen arbitrarily. Some endpoints (like
- * ListProjects) break the assumption that the data cached in a request is limited. To prevent
- * this class from accumulating an unbound number of objects, we enforce this limit.
- */
- private static final int PER_THREAD_CACHE_SIZE = 25;
/**
* Unique key for key-value mappings stored in PerThreadCache. The key is based on the value's
@@ -119,7 +109,7 @@
return cache != null ? cache.get(key, loader) : loader.get();
}
- private final Map<Key<?>, Object> cache = Maps.newHashMapWithExpectedSize(PER_THREAD_CACHE_SIZE);
+ private final Map<Key<?>, Object> cache = Maps.newHashMap();
private PerThreadCache() {}
@@ -132,9 +122,7 @@
T value = (T) cache.get(key);
if (value == null) {
value = loader.get();
- if (cache.size() < PER_THREAD_CACHE_SIZE) {
- cache.put(key, value);
- }
+ cache.put(key, value);
}
return value;
}
diff --git a/java/com/google/gerrit/server/cache/PerThreadProjectCache.java b/java/com/google/gerrit/server/cache/PerThreadProjectCache.java
new file mode 100644
index 0000000..86f1d2d
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/PerThreadProjectCache.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 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.server.cache;
+
+import com.google.common.collect.Maps;
+import com.google.gerrit.entities.Project;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * To prevent OOM errors on requests that would cache a lot of objects, this class enforces an
+ * internal limit after which no new elements are cached. All {@code computeIfAbsentWithinLimit}
+ * calls are served by invoking the {@code Supplier} after that.
+ */
+public class PerThreadProjectCache {
+ private static final PerThreadCache.Key<PerThreadProjectCache> PER_THREAD_PROJECT_CACHE_KEY =
+ PerThreadCache.Key.create(PerThreadProjectCache.class);
+ /**
+ * Cache at maximum 25 values per thread. This value was chosen arbitrarily. Some endpoints (like
+ * ListProjects) break the assumption that the data cached in a request is limited. To prevent
+ * this class from accumulating an unbound number of objects, we enforce this limit.
+ */
+ private static final int PER_THREAD_PROJECT_CACHE_SIZE = 25;
+
+ private final Map<PerThreadCache.Key<Project.NameKey>, Object> valueByNameKey =
+ Maps.newHashMapWithExpectedSize(PER_THREAD_PROJECT_CACHE_SIZE);
+
+ private PerThreadProjectCache() {}
+
+ public static <T> T getOrCompute(PerThreadCache.Key<Project.NameKey> key, Supplier<T> loader) {
+ PerThreadCache perThreadCache = PerThreadCache.get();
+ if (perThreadCache != null) {
+ PerThreadProjectCache perThreadProjectCache =
+ perThreadCache.get(PER_THREAD_PROJECT_CACHE_KEY, PerThreadProjectCache::new);
+ return perThreadProjectCache.computeIfAbsentWithinLimit(key, loader);
+ }
+ return loader.get();
+ }
+
+ protected <T> T computeIfAbsentWithinLimit(
+ PerThreadCache.Key<Project.NameKey> key, Supplier<T> loader) {
+ @SuppressWarnings("unchecked")
+ T value = (T) valueByNameKey.get(key);
+ if (value == null) {
+ value = loader.get();
+ if (valueByNameKey.size() < PER_THREAD_PROJECT_CACHE_SIZE) {
+ valueByNameKey.put(key, value);
+ }
+ }
+ return value;
+ }
+}
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index ce63c7e..c4fd5be 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -250,6 +250,7 @@
String schemeName = e.getExportName();
DownloadScheme scheme = e.getProvider().get();
if (!scheme.isEnabled()
+ || scheme.isHidden()
|| (scheme.isAuthRequired() && !userProvider.get().isIdentifiedUser())) {
continue;
}
diff --git a/java/com/google/gerrit/server/config/DownloadConfig.java b/java/com/google/gerrit/server/config/DownloadConfig.java
index d581675..8ac858c 100644
--- a/java/com/google/gerrit/server/config/DownloadConfig.java
+++ b/java/com/google/gerrit/server/config/DownloadConfig.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.config;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
@@ -24,8 +25,11 @@
import com.google.inject.Singleton;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.Arrays;
import java.util.EnumSet;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.lib.Config;
/**
@@ -39,10 +43,12 @@
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final ImmutableSet<String> downloadSchemes;
+ private final ImmutableSet<String> hiddenSchemes;
private final ImmutableSet<DownloadCommand> downloadCommands;
private final ImmutableSet<ArchiveFormatInternal> archiveFormats;
@Inject
+ @VisibleForTesting
public DownloadConfig(@GerritServerConfig Config cfg) {
String[] allSchemes = cfg.getStringList("download", null, "scheme");
if (allSchemes.length == 0) {
@@ -63,6 +69,10 @@
downloadSchemes = normalized.build();
}
+ Set<String> hidden = new HashSet<>(Arrays.asList(cfg.getStringList("download", null, "hide")));
+ hidden.retainAll(downloadSchemes);
+ hiddenSchemes = ImmutableSet.copyOf(hidden);
+
DownloadCommand[] downloadCommandValues = DownloadCommand.values();
List<DownloadCommand> allCommands =
ConfigUtil.getEnumList(cfg, "download", null, "command", downloadCommandValues, null);
@@ -110,6 +120,11 @@
return downloadSchemes;
}
+ /** Scheme hidden in the UI. */
+ public ImmutableSet<String> getHiddenSchemes() {
+ return hiddenSchemes;
+ }
+
/** Command used to download. */
public ImmutableSet<DownloadCommand> getDownloadCommands() {
return downloadCommands;
diff --git a/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java b/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
index fde4088..6a73348 100644
--- a/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/AbstractChangeEvent.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.extensions.events;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -28,7 +29,7 @@
private final NotifyHandling notify;
protected AbstractChangeEvent(
- ChangeInfo change, AccountInfo who, Instant when, NotifyHandling notify) {
+ ChangeInfo change, @Nullable AccountInfo who, Instant when, NotifyHandling notify) {
this.changeInfo = change;
this.who = who;
this.when = when;
diff --git a/java/com/google/gerrit/server/extensions/events/AttentionSetObserver.java b/java/com/google/gerrit/server/extensions/events/AttentionSetObserver.java
index 27e0a5e..04ffcc1 100644
--- a/java/com/google/gerrit/server/extensions/events/AttentionSetObserver.java
+++ b/java/com/google/gerrit/server/extensions/events/AttentionSetObserver.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.AttentionSetUpdate;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
@@ -73,7 +74,10 @@
* @param when is the time of the event
*/
public void fire(
- ChangeData changeData, AccountState accountState, AttentionSetUpdate update, Instant when) {
+ ChangeData changeData,
+ @Nullable AccountState accountState,
+ AttentionSetUpdate update,
+ Instant when) {
if (listeners.isEmpty()) {
return;
}
@@ -107,7 +111,7 @@
public Event(
ChangeInfo change,
- AccountInfo editor,
+ @Nullable AccountInfo editor,
Set<Integer> added,
Set<Integer> removed,
Instant when) {
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index aa49852..bf4d05a 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -35,6 +35,7 @@
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.cache.PerThreadCache;
+import com.google.gerrit.server.cache.PerThreadProjectCache;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -124,8 +125,8 @@
public ForProject project(Project.NameKey project) {
try {
ProjectControl control =
- PerThreadCache.getOrCompute(
- PerThreadCache.Key.create(ProjectControl.class, project, user.getCacheKey()),
+ PerThreadProjectCache.getOrCompute(
+ PerThreadCache.Key.create(Project.NameKey.class, project, user.getCacheKey()),
() ->
projectControlFactory.create(
user, projectCache.get(project).orElseThrow(illegalState(project))));
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index e8a6671..977c737 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -231,7 +231,7 @@
downloadSchemes.runEach(
extension -> {
DownloadScheme scheme = extension.get();
- if (scheme.isEnabled() && scheme.getUrl("${project}") != null) {
+ if (scheme.isEnabled() && !scheme.isHidden() && scheme.getUrl("${project}") != null) {
info.schemes.put(extension.getExportName(), getDownloadSchemeInfo(scheme));
}
});
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index c1c58c8..547aff3 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -55,6 +55,7 @@
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.channel.exception.SshChannelClosedException;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.channel.ChannelSession;
@@ -499,19 +500,11 @@
throw new UnloggedFailure(1, e.getMessage() + " no such change");
}
- out.flush();
- err.flush();
- } catch (Exception e) {
- try {
- out.flush();
- } catch (Exception e2) {
- // Ignored
- }
- try {
- err.flush();
- } catch (Exception e2) {
- // Ignored
- }
+ flushIgnoreSCCE(out);
+ flushIgnoreSCCE(err);
+ } catch (Throwable e) {
+ flushIgnoreException(out);
+ flushIgnoreException(err);
rc = handleError(e);
} finally {
try {
@@ -524,6 +517,22 @@
}
}
+ private void flushIgnoreSCCE(OutputStream os) throws IOException {
+ try {
+ os.flush();
+ } catch (SshChannelClosedException e) {
+ // Ignore - command implementation flushed stream already
+ }
+ }
+
+ private void flushIgnoreException(OutputStream os) {
+ try {
+ os.flush();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
@Override
public String toString() {
return taskName;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 5745a4e..59ba00b 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -247,5 +247,10 @@
public boolean isEnabled() {
return true;
}
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
}
}
diff --git a/javatests/com/google/gerrit/server/cache/BUILD b/javatests/com/google/gerrit/server/cache/BUILD
index 275f2ec..cd6a6b4 100644
--- a/javatests/com/google/gerrit/server/cache/BUILD
+++ b/javatests/com/google/gerrit/server/cache/BUILD
@@ -5,6 +5,7 @@
name = "tests",
srcs = glob(["*Test.java"]),
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:junit",
diff --git a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
index 5d420d3..6966302 100644
--- a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
+++ b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
@@ -84,20 +84,4 @@
assertThat(thrown).hasMessageThat().contains("called create() twice on the same request");
}
}
-
- @Test
- public void enforceMaxSize() {
- try (PerThreadCache cache = PerThreadCache.create()) {
- // Fill the cache
- for (int i = 0; i < 50; i++) {
- PerThreadCache.Key<String> key = PerThreadCache.Key.create(String.class, i);
- cache.get(key, () -> "cached value");
- }
- // Assert that the value was not persisted
- PerThreadCache.Key<String> key = PerThreadCache.Key.create(String.class, 1000);
- cache.get(key, () -> "new value");
- String value = cache.get(key, () -> "directly served");
- assertThat(value).isEqualTo("directly served");
- }
- }
}
diff --git a/javatests/com/google/gerrit/server/cache/PerThreadProjectCacheTest.java b/javatests/com/google/gerrit/server/cache/PerThreadProjectCacheTest.java
new file mode 100644
index 0000000..055b95d
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/PerThreadProjectCacheTest.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 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.server.cache;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.entities.Project;
+import org.junit.Test;
+
+public class PerThreadProjectCacheTest {
+ @Test
+ public void testValueIsCachedWithinSizeLimit() {
+ try (PerThreadCache cache = PerThreadCache.create()) {
+ PerThreadCache.Key<Project.NameKey> key =
+ PerThreadCache.Key.create(Project.NameKey.class, Project.nameKey("test-project"));
+ PerThreadProjectCache.getOrCompute(key, () -> "cached");
+ String value = PerThreadProjectCache.getOrCompute(key, () -> "directly served");
+ assertThat(value).isEqualTo("cached");
+ }
+ }
+
+ @Test
+ public void testEnforceMaxSize() {
+ try (PerThreadCache cache = PerThreadCache.create()) {
+ // Fill the cache
+ for (int i = 0; i < 50; i++) {
+ PerThreadCache.Key<Project.NameKey> key =
+ PerThreadCache.Key.create(Project.NameKey.class, Project.nameKey("test-project" + i));
+ PerThreadProjectCache.getOrCompute(key, () -> "cached");
+ }
+ // Assert that the value was not persisted
+ PerThreadCache.Key<Project.NameKey> key =
+ PerThreadCache.Key.create(Project.NameKey.class, "Project" + 1000);
+ PerThreadProjectCache.getOrCompute(key, () -> "new value");
+ String value = PerThreadProjectCache.getOrCompute(key, () -> "directly served");
+ assertThat(value).isEqualTo("directly served");
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index a49f156..ff1f6a3 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -39,8 +39,10 @@
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.change.AbandonOp;
import com.google.gerrit.server.change.AddReviewersOp;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.PatchSetInserter;
@@ -103,6 +105,8 @@
@Inject private AccountManager accountManager;
@Inject private AuthRequest.Factory authRequestFactory;
@Inject private IdentifiedUser.GenericFactory userFactory;
+ @Inject private InternalUser.Factory internalUserFactory;
+ @Inject private AbandonOp.Factory abandonOpFactory;
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@@ -152,6 +156,40 @@
}
@Test
+ public void batchUpdateThatChangeAttentionSetAsInternalUser() throws Exception {
+ Change.Id id = createChangeWithUpdates(1);
+ attentionSetListeners.add("test", attentionSetListener);
+
+ Account.Id reviewer =
+ accountManager.authenticate(authRequestFactory.createForUser("user")).getAccountId();
+
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.now())) {
+ bu.addOp(
+ id,
+ addReviewersOpFactory.create(
+ ImmutableSet.of(reviewer), ImmutableList.of(), ReviewerState.REVIEWER, false));
+ bu.execute();
+ }
+
+ try (BatchUpdate bu =
+ batchUpdateFactory.create(project, internalUserFactory.create(), TimeUtil.now())) {
+ bu.addOp(id, abandonOpFactory.create(null, "test abandon"));
+ bu.execute();
+ }
+
+ verify(attentionSetListener, times(2)).onAttentionSetChanged(attentionSetEventCaptor.capture());
+ AttentionSetListener.Event event = attentionSetEventCaptor.getAllValues().get(0);
+ assertThat(event.getChange()._number).isEqualTo(id.get());
+ assertThat(event.usersAdded()).containsExactly(reviewer.get());
+ assertThat(event.usersRemoved()).isEmpty();
+
+ event = attentionSetEventCaptor.getAllValues().get(1);
+ assertThat(event.getChange()._number).isEqualTo(id.get());
+ assertThat(event.usersAdded()).isEmpty();
+ assertThat(event.usersRemoved()).containsExactly(reviewer.get());
+ }
+
+ @Test
public void cannotExceedMaxUpdates() throws Exception {
Change.Id id = createChangeWithUpdates(MAX_UPDATES);
ObjectId oldMetaId = getMetaId(id);
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 1ebf3d0..b923e44 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-acceptance-framework</artifactId>
- <version>3.7.1-SNAPSHOT</version>
+ <version>3.7.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 5d817af..b3110c3 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
- <version>3.7.1-SNAPSHOT</version>
+ <version>3.7.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index bc61907c..345d80a 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-plugin-api</artifactId>
- <version>3.7.1-SNAPSHOT</version>
+ <version>3.7.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index 77c1134..9e30306 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-war</artifactId>
- <version>3.7.1-SNAPSHOT</version>
+ <version>3.7.2-SNAPSHOT</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
diff --git a/version.bzl b/version.bzl
index 569943f..120e839 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
# Used by :api_install and :api_deploy targets
# when talking to the destination repository.
#
-GERRIT_VERSION = "3.7.1-SNAPSHOT"
+GERRIT_VERSION = "3.7.2-SNAPSHOT"