diff options
70 files changed, 599 insertions, 366 deletions
diff --git a/Documentation/concept-changes.txt b/Documentation/concept-changes.txt index 7320a508f6..1d275b419a 100644 --- a/Documentation/concept-changes.txt +++ b/Documentation/concept-changes.txt @@ -55,7 +55,7 @@ are not required to review it. |An optional topic. |Strategy -|The <<submit-strategy>> for the change. +|The <<submit-strategies,submit strategy>> for the change. |Code Review |Displays the Code Review status for the change. @@ -84,10 +84,10 @@ listed next to the change message. These related changes are grouped together in several categories, including: * Relation Chain. These changes are related by parent-child relationships, - regardless of <<topics>>. + regardless of <<topic,topic>>. * Merge Conflicts. These are changes in which there is a merge conflict with the current change. -* Submitted Together. These are changes that share the same <<topics>>. +* Submitted Together. These are changes that share the same <<topic,topic>>. An arrow indicates the change you are currently viewing. diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index f27434efa1..9f0bf8650e 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -690,7 +690,7 @@ H2 uses memory to cache its database content. The parameter `h2CacheSize` allows to limit the memory used by H2 and thus prevent out-of-memory caused by the H2 database using too much memory. + -See <<database.h2.cachesize,database.h2.cachesize>> for a detailed discussion. +See <<database.h2.cacheSize,database.h2.cacheSize>> for a detailed discussion. + Default is unset, using up to half of the available memory. + diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index b517d3c0d4..2a9dcee018 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt @@ -3570,6 +3570,12 @@ Whether the usage of Change-Ids is required for the project (`TRUE`, `FALSE`, `INHERIT`). This property is deprecated and will be removed in a future release. +|`enable_signed_push` |`INHERIT` if not set| +Whether signed push validation is enabled on the project (`TRUE`, +`FALSE`, `INHERIT`). +|`require_signed_push` |`INHERIT` if not set| +Whether signed push validation is required on the project (`TRUE`, +`FALSE`, `INHERIT`). |`max_object_size_limit` |optional| Max allowed Git object size for this project. Common unit suffixes of 'k', 'm', or 'g' are supported. @@ -3707,8 +3713,6 @@ The path to the `GerritSiteHeader.html` file. The path to the `GerritSiteFooter.html` file. |============================= ----- - GERRIT ------ Part of link:index.html[Gerrit Code Review] diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt index 41cb380908..7c904f5332 100644 --- a/Documentation/user-search.txt +++ b/Documentation/user-search.txt @@ -166,7 +166,7 @@ Changes occurring in 'PROJECT' or in one of the child projects of 'PROJECT'. [[repository]] -repository:'REPOSITORY':: +repository:'REPOSITORY', repo:'REPOSITORY':: + Changes occurring in 'REPOSITORY'. If 'REPOSITORY' starts with `^` it matches repository names by regular expression. The @@ -174,12 +174,12 @@ link:http://www.brics.dk/automaton/[dk.brics.automaton library] is used for evaluation of such patterns. [[repositories]] -repositories:'PREFIX':: +repositories:'PREFIX', repos:'PREFIX':: + Changes occurring in repositories starting with 'PREFIX'. [[parentrepository]] -parentrepository:'REPOSITORY':: +parentrepository:'REPOSITORY', parentrepo:'REPOSITORY':: + Changes occurring in 'REPOSITORY' or in one of the child repositories of 'REPOSITORY'. @@ -25,6 +25,7 @@ http_archive( # https://github.com/google/closure-compiler/blob/master/contrib/externs/polymer-1.0.js http_file( name = "polymer_closure", + downloaded_file_path = "polymer_closure.js", sha256 = "5a589bdba674e1fec7188e9251c8624ebf2d4d969beb6635f9148f420d1e08b1", urls = ["https://raw.githubusercontent.com/google/closure-compiler/775609aad61e14aef289ebec4bfc09ad88877f9e/contrib/externs/polymer-1.0.js"], ) @@ -1066,14 +1067,14 @@ maven_jar( maven_jar( name = "asciidoctor", - artifact = "org.asciidoctor:asciidoctorj:1.5.6", - sha1 = "bb757d4b8b0f8438ce2ed781f6688cc6c01d9237", + artifact = "org.asciidoctor:asciidoctorj:1.5.7", + sha1 = "8e8c1d8fc6144405700dd8df3b177f2801ac5987", ) maven_jar( name = "jruby", - artifact = "org.jruby:jruby-complete:9.1.13.0", - sha1 = "8903bf42272062e87a7cbc1d98919e0729a9939f", + artifact = "org.jruby:jruby-complete:9.1.17.0", + sha1 = "76716d529710fc03d1d429b43e3cedd4419f78d4", ) maven_jar( diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java index bd7d65d6a5..030ec2a528 100644 --- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java +++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java @@ -502,6 +502,8 @@ public abstract class AbstractDaemonTest { in.useSignedOffBy = ann.useSignedOffBy(); in.useContentMerge = ann.useContentMerge(); in.rejectEmptyCommit = ann.rejectEmptyCommit(); + in.enableSignedPush = ann.enableSignedPush(); + in.requireSignedPush = ann.requireSignedPush(); } else { // Defaults should match TestProjectConfig, omitting nullable values. in.createEmptyCommit = true; diff --git a/java/com/google/gerrit/acceptance/TestProjectInput.java b/java/com/google/gerrit/acceptance/TestProjectInput.java index eada6434c2..0a3686b543 100644 --- a/java/com/google/gerrit/acceptance/TestProjectInput.java +++ b/java/com/google/gerrit/acceptance/TestProjectInput.java @@ -47,6 +47,10 @@ public @interface TestProjectInput { InheritableBoolean rejectEmptyCommit() default InheritableBoolean.INHERIT; + InheritableBoolean enableSignedPush() default InheritableBoolean.INHERIT; + + InheritableBoolean requireSignedPush() default InheritableBoolean.INHERIT; + // Fields specific to acceptance test behavior. /** Username to use for initial clone, passed to {@link AccountCreator}. */ diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java index 2c1c93a1ba..fec7137094 100644 --- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java +++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java @@ -26,6 +26,7 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.flogger.FluentLogger; import com.google.common.io.CharStreams; +import com.google.gerrit.common.Nullable; import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; import com.google.gerrit.elasticsearch.builders.QueryBuilder; import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; @@ -160,7 +161,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { @Override public void delete(K id) throws IOException { String uri = getURI(type, BULK); - Response response = postRequest(getDeleteActions(id), uri, getRefreshParam()); + Response response = postRequest(uri, getDeleteActions(id), getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( @@ -172,10 +173,10 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { public void deleteAll() throws IOException { // Delete the index, if it exists. String endpoint = indexName + client.adapter().indicesExistParam(); - Response response = client.get().performRequest(new Request("HEAD", endpoint)); + Response response = performRequest("HEAD", endpoint); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { - response = client.get().performRequest(new Request("DELETE", indexName)); + response = performRequest("DELETE", indexName); statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( @@ -185,7 +186,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { // Recreate the index. String indexCreationFields = concatJsonString(getSettings(), getMappings()); - response = performRequest("PUT", indexCreationFields, indexName, Collections.emptyMap()); + response = performRequest("PUT", indexName, indexCreationFields); statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { String error = String.format("Failed to create index %s: %s", indexName, statusCode); @@ -297,20 +298,36 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> { return encodedIndexName + "/" + encodedType + "/" + request; } - protected Response postRequest(Object payload, String uri, Map<String, String> params) + protected Response postRequest(String uri, Object payload) throws IOException { + return performRequest("POST", uri, payload); + } + + protected Response postRequest(String uri, Object payload, Map<String, String> params) throws IOException { - return performRequest("POST", payload, uri, params); + return performRequest("POST", uri, payload, params); } private String concatJsonString(String target, String addition) { return target.substring(0, target.length() - 1) + "," + addition.substring(1); } + private Response performRequest(String method, String uri) throws IOException { + return performRequest(method, uri, null); + } + + private Response performRequest(String method, String uri, @Nullable Object payload) + throws IOException { + return performRequest(method, uri, payload, Collections.emptyMap()); + } + private Response performRequest( - String method, Object payload, String uri, Map<String, String> params) throws IOException { - Request request = new Request(method, uri); - String payloadStr = payload instanceof String ? (String) payload : payload.toString(); - request.setEntity(new NStringEntity(payloadStr, ContentType.APPLICATION_JSON)); + String method, String uri, @Nullable Object payload, Map<String, String> params) + throws IOException { + Request request = new Request(method, uri.startsWith("/") ? uri : "/" + uri); + if (payload != null) { + String payloadStr = payload instanceof String ? (String) payload : payload.toString(); + request.setEntity(new NStringEntity(payloadStr, ContentType.APPLICATION_JSON)); + } for (Map.Entry<String, String> entry : params.entrySet()) { request.addParameter(entry.getKey(), entry.getValue()); } diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD index 31ede79f16..8d23051517 100644 --- a/java/com/google/gerrit/elasticsearch/BUILD +++ b/java/com/google/gerrit/elasticsearch/BUILD @@ -3,6 +3,7 @@ java_library( srcs = glob(["**/*.java"]), visibility = ["//visibility:public"], deps = [ + "//java/com/google/gerrit/common:annotations", "//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/index", "//java/com/google/gerrit/index:query_exception", diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java index d18af423f5..1b69b6d295 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java @@ -79,7 +79,7 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun .add(new UpdateRequest<>(schema, as)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java index f6af79f228..d7c8b00039 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java @@ -138,7 +138,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData> } String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java index bf6b962304..f694a05482 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java @@ -77,7 +77,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I .add(new UpdateRequest<>(schema, group)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java index 623f62c439..8510559791 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java +++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java @@ -77,7 +77,7 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P .add(new UpdateRequest<>(schema, projectState)); String uri = getURI(type, BULK); - Response response = postRequest(bulk, uri, getRefreshParam()); + Response response = postRequest(uri, bulk, getRefreshParam()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new IOException( diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java index 8cb69e0179..05fd7a7afc 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java +++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java @@ -33,7 +33,7 @@ public class ElasticQueryAdapter { ElasticQueryAdapter(ElasticVersion version) { this.ignoreUnmapped = version == ElasticVersion.V2_4; this.usePostV5Type = version.isV6(); - this.versionDiscoveryUrl = version.isV6() ? "%s*" : "%s*/_aliases"; + this.versionDiscoveryUrl = version.isV6() ? "/%s*" : "/%s*/_aliases"; switch (version) { case V5_6: diff --git a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java index 337f2ca882..e9839b7978 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java +++ b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java @@ -106,7 +106,7 @@ class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListen private ElasticVersion getVersion() throws ElasticException { try { - Response response = client.performRequest(new Request("GET", "")); + Response response = client.performRequest(new Request("GET", "/")); StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() != HttpStatus.SC_OK) { throw new FailedToGetVersion(statusLine); diff --git a/java/com/google/gerrit/extensions/api/projects/ProjectInput.java b/java/com/google/gerrit/extensions/api/projects/ProjectInput.java index b7079ae1ee..e61d316a0a 100644 --- a/java/com/google/gerrit/extensions/api/projects/ProjectInput.java +++ b/java/com/google/gerrit/extensions/api/projects/ProjectInput.java @@ -34,6 +34,8 @@ public class ProjectInput { public InheritableBoolean requireChangeId; public InheritableBoolean createNewChangeForAllNotInTarget; public InheritableBoolean rejectEmptyCommit; + public InheritableBoolean enableSignedPush; + public InheritableBoolean requireSignedPush; public String maxObjectSizeLimit; public Map<String, Map<String, ConfigValue>> pluginConfigValues; } diff --git a/java/com/google/gerrit/extensions/registration/DynamicItem.java b/java/com/google/gerrit/extensions/registration/DynamicItem.java index 4f36ab4817..67982d9040 100644 --- a/java/com/google/gerrit/extensions/registration/DynamicItem.java +++ b/java/com/google/gerrit/extensions/registration/DynamicItem.java @@ -111,17 +111,22 @@ public class DynamicItem<T> { } private final Key<DynamicItem<T>> key; - private final AtomicReference<NamedProvider<T>> ref; + private final AtomicReference<Extension<T>> ref; DynamicItem(Key<DynamicItem<T>> key, Provider<T> provider, String pluginName) { - NamedProvider<T> in = null; + Extension<T> in = null; if (provider != null) { - in = new NamedProvider<>(provider, pluginName); + in = new Extension<>(pluginName, provider); } this.key = key; this.ref = new AtomicReference<>(in); } + @Nullable + public Extension<T> getEntry() { + return ref.get(); + } + /** * Get the configured item, or null. * @@ -130,8 +135,8 @@ public class DynamicItem<T> { */ @Nullable public T get() { - NamedProvider<T> item = ref.get(); - return item != null ? item.impl.get() : null; + Extension<T> item = ref.get(); + return item != null ? item.get() : null; } /** @@ -143,8 +148,8 @@ public class DynamicItem<T> { */ @Nullable public String getPluginName() { - NamedProvider<T> item = ref.get(); - return item != null ? item.pluginName : null; + Extension<T> item = ref.get(); + return item != null ? item.getPluginName() : null; } /** @@ -166,19 +171,19 @@ public class DynamicItem<T> { * @return handle to remove the item at a later point in time. */ public RegistrationHandle set(Provider<T> impl, String pluginName) { - final NamedProvider<T> item = new NamedProvider<>(impl, pluginName); - NamedProvider<T> old = null; + final Extension<T> item = new Extension<>(pluginName, impl); + Extension<T> old = null; while (!ref.compareAndSet(old, item)) { old = ref.get(); - if (old != null && !PluginName.GERRIT.equals(old.pluginName)) { + if (old != null && !PluginName.GERRIT.equals(old.getPluginName())) { throw new ProvisionException( String.format( "%s already provided by %s, ignoring plugin %s", - key.getTypeLiteral(), old.pluginName, pluginName)); + key.getTypeLiteral(), old.getPluginName(), pluginName)); } } - final NamedProvider<T> defaultItem = old; + final Extension<T> defaultItem = old; return new RegistrationHandle() { @Override public void remove() { @@ -198,13 +203,13 @@ public class DynamicItem<T> { * @return a handle that can remove this item later, or hot-swap the item. */ public ReloadableRegistrationHandle<T> set(Key<T> key, Provider<T> impl, String pluginName) { - final NamedProvider<T> item = new NamedProvider<>(impl, pluginName); - NamedProvider<T> old = null; + final Extension<T> item = new Extension<>(pluginName, impl); + Extension<T> old = null; while (!ref.compareAndSet(old, item)) { old = ref.get(); if (old != null - && !PluginName.GERRIT.equals(old.pluginName) - && !pluginName.equals(old.pluginName)) { + && !PluginName.GERRIT.equals(old.getPluginName()) + && !pluginName.equals(old.getPluginName())) { // We allow to replace: // 1. Gerrit core items, e.g. websession cache // can be replaced by plugin implementation @@ -212,7 +217,7 @@ public class DynamicItem<T> { throw new ProvisionException( String.format( "%s already provided by %s, ignoring plugin %s", - this.key.getTypeLiteral(), old.pluginName, pluginName)); + this.key.getTypeLiteral(), old.getPluginName(), pluginName)); } } return new ReloadableHandle(key, item, old); @@ -220,10 +225,10 @@ public class DynamicItem<T> { private class ReloadableHandle implements ReloadableRegistrationHandle<T> { private final Key<T> handleKey; - private final NamedProvider<T> item; - private final NamedProvider<T> defaultItem; + private final Extension<T> item; + private final Extension<T> defaultItem; - ReloadableHandle(Key<T> handleKey, NamedProvider<T> item, NamedProvider<T> defaultItem) { + ReloadableHandle(Key<T> handleKey, Extension<T> item, Extension<T> defaultItem) { this.handleKey = handleKey; this.item = item; this.defaultItem = defaultItem; @@ -242,7 +247,7 @@ public class DynamicItem<T> { @Override @Nullable public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) { - NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName); + Extension<T> n = new Extension<>(item.getPluginName(), newItem); if (ref.compareAndSet(item, n)) { return new ReloadableHandle(newKey, n, defaultItem); } diff --git a/java/com/google/gerrit/extensions/registration/DynamicMap.java b/java/com/google/gerrit/extensions/registration/DynamicMap.java index 96d19b2090..48b1279489 100644 --- a/java/com/google/gerrit/extensions/registration/DynamicMap.java +++ b/java/com/google/gerrit/extensions/registration/DynamicMap.java @@ -40,29 +40,7 @@ import java.util.concurrent.ConcurrentMap; * resolve the provider to an instance on demand. This enables registrations to decide between * singleton and non-singleton members. */ -public abstract class DynamicMap<T> implements Iterable<DynamicMap.Entry<T>> { - public static class Entry<T> { - private final NamePair namePair; - private final Provider<T> provider; - - private Entry(NamePair namePair, Provider<T> provider) { - this.namePair = namePair; - this.provider = provider; - } - - public String getPluginName() { - return namePair.pluginName; - } - - public String getExportName() { - return namePair.exportName; - } - - public Provider<T> getProvider() { - return provider; - } - } - +public abstract class DynamicMap<T> implements Iterable<Extension<T>> { /** * Declare a singleton {@code DynamicMap<T>} with a binder. * @@ -166,18 +144,18 @@ public abstract class DynamicMap<T> implements Iterable<DynamicMap.Entry<T>> { /** Iterate through all entries in an undefined order. */ @Override - public Iterator<Entry<T>> iterator() { + public Iterator<Extension<T>> iterator() { final Iterator<Map.Entry<NamePair, Provider<T>>> i = items.entrySet().iterator(); - return new Iterator<Entry<T>>() { + return new Iterator<Extension<T>>() { @Override public boolean hasNext() { return i.hasNext(); } @Override - public Entry<T> next() { + public Extension<T> next() { Map.Entry<NamePair, Provider<T>> e = i.next(); - return new Entry<>(e.getKey(), e.getValue()); + return new Extension<>(e.getKey().pluginName, e.getKey().exportName, e.getValue()); } @Override diff --git a/java/com/google/gerrit/extensions/registration/DynamicSet.java b/java/com/google/gerrit/extensions/registration/DynamicSet.java index 6b3a49bffe..dcd0d8f1b0 100644 --- a/java/com/google/gerrit/extensions/registration/DynamicSet.java +++ b/java/com/google/gerrit/extensions/registration/DynamicSet.java @@ -45,24 +45,6 @@ import java.util.concurrent.atomic.AtomicReference; * singleton and non-singleton members. */ public class DynamicSet<T> implements Iterable<T> { - public static class Entry<T> { - private final String pluginName; - private final Provider<T> provider; - - private Entry(String pluginName, Provider<T> provider) { - this.pluginName = pluginName; - this.provider = provider; - } - - public String getPluginName() { - return pluginName; - } - - public Provider<T> getProvider() { - return provider; - } - } - /** * Declare a singleton {@code DynamicSet<T>} with a binder. * @@ -153,12 +135,12 @@ public class DynamicSet<T> implements Iterable<T> { } public static <T> DynamicSet<T> emptySet() { - return new DynamicSet<>(Collections.<AtomicReference<NamedProvider<T>>>emptySet()); + return new DynamicSet<>(Collections.<AtomicReference<Extension<T>>>emptySet()); } - private final CopyOnWriteArrayList<AtomicReference<NamedProvider<T>>> items; + private final CopyOnWriteArrayList<AtomicReference<Extension<T>>> items; - DynamicSet(Collection<AtomicReference<NamedProvider<T>>> base) { + DynamicSet(Collection<AtomicReference<Extension<T>>> base) { items = new CopyOnWriteArrayList<>(base); } @@ -168,7 +150,7 @@ public class DynamicSet<T> implements Iterable<T> { @Override public Iterator<T> iterator() { - Iterator<Entry<T>> entryIterator = entries().iterator(); + Iterator<Extension<T>> entryIterator = entries().iterator(); return new Iterator<T>() { @Override public boolean hasNext() { @@ -177,39 +159,35 @@ public class DynamicSet<T> implements Iterable<T> { @Override public T next() { - Entry<T> next = entryIterator.next(); + Extension<T> next = entryIterator.next(); return next != null ? next.getProvider().get() : null; } }; } - public Iterable<Entry<T>> entries() { - final Iterator<AtomicReference<NamedProvider<T>>> itr = items.iterator(); - return new Iterable<Entry<T>>() { + public Iterable<Extension<T>> entries() { + final Iterator<AtomicReference<Extension<T>>> itr = items.iterator(); + return new Iterable<Extension<T>>() { @Override - public Iterator<Entry<T>> iterator() { - return new Iterator<Entry<T>>() { - private Entry<T> next; + public Iterator<Extension<T>> iterator() { + return new Iterator<Extension<T>>() { + private Extension<T> next; @Override public boolean hasNext() { while (next == null && itr.hasNext()) { - NamedProvider<T> p = itr.next().get(); + Extension<T> p = itr.next().get(); if (p != null) { - try { - next = new Entry<>(p.pluginName, p.impl); - } catch (RuntimeException e) { - // TODO Log failed member of DynamicSet. - } + next = p; } } return next != null; } @Override - public Entry<T> next() { + public Extension<T> next() { if (hasNext()) { - Entry<T> result = next; + Extension<T> result = next; next = null; return result; } @@ -250,7 +228,7 @@ public class DynamicSet<T> implements Iterable<T> { public ImmutableSortedSet<String> plugins() { return items .stream() - .map(i -> i.get().pluginName) + .map(i -> i.get().getPluginName()) .collect(toImmutableSortedSet(naturalOrder())); } @@ -263,8 +241,8 @@ public class DynamicSet<T> implements Iterable<T> { public ImmutableSet<Provider<T>> byPlugin(String pluginName) { return items .stream() - .filter(i -> i.get().pluginName.equals(pluginName)) - .map(i -> i.get().impl) + .filter(i -> i.get().getPluginName().equals(pluginName)) + .map(i -> i.get().getProvider()) .collect(toImmutableSet()); } @@ -285,8 +263,8 @@ public class DynamicSet<T> implements Iterable<T> { * @return handle to remove the item at a later point in time. */ public RegistrationHandle add(String pluginName, Provider<T> item) { - final AtomicReference<NamedProvider<T>> ref = - new AtomicReference<>(new NamedProvider<>(item, pluginName)); + final AtomicReference<Extension<T>> ref = + new AtomicReference<>(new Extension<>(pluginName, item)); items.add(ref); return new RegistrationHandle() { @Override @@ -310,18 +288,17 @@ public class DynamicSet<T> implements Iterable<T> { * the collection. */ public ReloadableRegistrationHandle<T> add(String pluginName, Key<T> key, Provider<T> item) { - AtomicReference<NamedProvider<T>> ref = - new AtomicReference<>(new NamedProvider<>(item, pluginName)); + AtomicReference<Extension<T>> ref = new AtomicReference<>(new Extension<>(pluginName, item)); items.add(ref); return new ReloadableHandle(ref, key, ref.get()); } private class ReloadableHandle implements ReloadableRegistrationHandle<T> { - private final AtomicReference<NamedProvider<T>> ref; + private final AtomicReference<Extension<T>> ref; private final Key<T> key; - private final NamedProvider<T> item; + private final Extension<T> item; - ReloadableHandle(AtomicReference<NamedProvider<T>> ref, Key<T> key, NamedProvider<T> item) { + ReloadableHandle(AtomicReference<Extension<T>> ref, Key<T> key, Extension<T> item) { this.ref = ref; this.key = key; this.item = item; @@ -341,7 +318,7 @@ public class DynamicSet<T> implements Iterable<T> { @Override public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) { - NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName); + Extension<T> n = new Extension<>(item.getPluginName(), newItem); if (ref.compareAndSet(item, n)) { return new ReloadableHandle(ref, newKey, n); } diff --git a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java index 6d36f54bb3..832933b31c 100644 --- a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java +++ b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java @@ -38,17 +38,16 @@ class DynamicSetProvider<T> implements Provider<DynamicSet<T>> { return new DynamicSet<>(find(injector, type)); } - private static <T> List<AtomicReference<NamedProvider<T>>> find( - Injector src, TypeLiteral<T> type) { + private static <T> List<AtomicReference<Extension<T>>> find(Injector src, TypeLiteral<T> type) { List<Binding<T>> bindings = src.findBindingsByType(type); int cnt = bindings != null ? bindings.size() : 0; if (cnt == 0) { return Collections.emptyList(); } - List<AtomicReference<NamedProvider<T>>> r = new ArrayList<>(cnt); + List<AtomicReference<Extension<T>>> r = new ArrayList<>(cnt); for (Binding<T> b : bindings) { if (b.getKey().getAnnotation() != null) { - r.add(new AtomicReference<>(new NamedProvider<>(b.getProvider(), PluginName.GERRIT))); + r.add(new AtomicReference<>(new Extension<>(PluginName.GERRIT, b.getProvider()))); } } return r; diff --git a/java/com/google/gerrit/extensions/registration/Extension.java b/java/com/google/gerrit/extensions/registration/Extension.java new file mode 100644 index 0000000000..aaec201bd7 --- /dev/null +++ b/java/com/google/gerrit/extensions/registration/Extension.java @@ -0,0 +1,61 @@ +// 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.extensions.registration; + +import com.google.gerrit.common.Nullable; +import com.google.inject.Provider; + +/** + * An extension that is provided by a plugin. + * + * <p>Contains the name of the plugin that provides the extension, the extension point + * implementation and optionally the export name under which the extension was exported. + * + * <p>An export name is only available if this extension is an entry in a {@link DynamicMap}. + * + * @param <T> Type of extension point that this extension implements + */ +public class Extension<T> { + private final String pluginName; + private final @Nullable String exportName; + private final Provider<T> provider; + + protected Extension(String pluginName, Provider<T> provider) { + this(pluginName, null, provider); + } + + protected Extension(String pluginName, @Nullable String exportName, Provider<T> provider) { + this.pluginName = pluginName; + this.exportName = exportName; + this.provider = provider; + } + + public String getPluginName() { + return pluginName; + } + + @Nullable + public String getExportName() { + return exportName; + } + + public Provider<T> getProvider() { + return provider; + } + + public T get() { + return provider.get(); + } +} diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java index bc0cd899f8..b6fb46e60d 100644 --- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java +++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java @@ -22,12 +22,11 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.flogger.FluentLogger; import com.google.common.io.BaseEncoding; -import com.google.gerrit.common.PageLinks; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.externalids.ExternalId; -import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.query.account.InternalAccountQuery; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; @@ -38,6 +37,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; @@ -58,7 +58,7 @@ public class GerritPublicKeyChecker extends PublicKeyChecker { @Singleton public static class Factory { private final Provider<InternalAccountQuery> accountQueryProvider; - private final String webUrl; + private final UrlFormatter urlFormatter; private final IdentifiedUser.GenericFactory userFactory; private final int maxTrustDepth; private final ImmutableMap<Long, Fingerprint> trusted; @@ -68,9 +68,9 @@ public class GerritPublicKeyChecker extends PublicKeyChecker { @GerritServerConfig Config cfg, Provider<InternalAccountQuery> accountQueryProvider, IdentifiedUser.GenericFactory userFactory, - @CanonicalWebUrl String webUrl) { + UrlFormatter urlFormatter) { this.accountQueryProvider = accountQueryProvider; - this.webUrl = webUrl; + this.urlFormatter = urlFormatter; this.userFactory = userFactory; this.maxTrustDepth = cfg.getInt("receive", null, "maxTrustDepth", 0); @@ -101,14 +101,14 @@ public class GerritPublicKeyChecker extends PublicKeyChecker { } private final Provider<InternalAccountQuery> accountQueryProvider; - private final String webUrl; + private final UrlFormatter urlFormatter; private final IdentifiedUser.GenericFactory userFactory; private IdentifiedUser expectedUser; private GerritPublicKeyChecker(Factory factory) { this.accountQueryProvider = factory.accountQueryProvider; - this.webUrl = factory.webUrl; + this.urlFormatter = factory.urlFormatter; this.userFactory = factory.userFactory; if (factory.trusted != null) { enableTrust(factory.maxTrustDepth, factory.trusted); @@ -144,8 +144,10 @@ public class GerritPublicKeyChecker extends PublicKeyChecker { private CheckResult checkIdsForExpectedUser(PGPPublicKey key) throws PGPException { Set<String> allowedUserIds = getAllowedUserIds(expectedUser); if (allowedUserIds.isEmpty()) { + Optional<String> settings = urlFormatter.getSettingsUrl("Identities"); return CheckResult.bad( - "No identities found for user; check " + webUrl + "#" + PageLinks.SETTINGS_WEBIDENT); + "No identities found for user" + + (settings.isPresent() ? "; check " + settings.get() : "")); } if (hasAllowedUserId(key, allowedUserIds)) { return CheckResult.trusted(); diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java index 5e234d2e38..30ebe6e7cb 100644 --- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java +++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java @@ -25,7 +25,7 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.registration.DynamicMap.Entry; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.account.AccountCache; @@ -191,7 +191,7 @@ class ProjectOAuthFilter implements Filter { */ private void pickOnlyProvider() throws ServletException { try { - Entry<OAuthLoginProvider> loginProvider = Iterables.getOnlyElement(loginProviders); + Extension<OAuthLoginProvider> loginProvider = Iterables.getOnlyElement(loginProviders); defaultAuthPlugin = loginProvider.getPluginName(); defaultAuthProvider = loginProvider.getExportName(); } catch (NoSuchElementException e) { diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java index 683a205646..b316d3a7a8 100644 --- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java +++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java @@ -47,12 +47,14 @@ import com.google.gerrit.server.change.RebaseChangeOp; import com.google.gerrit.server.config.AdministrateServerGroups; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.CanonicalWebUrlProvider; +import com.google.gerrit.server.config.DefaultBrowseUrls; import com.google.gerrit.server.config.DisableReverseDnsLookup; import com.google.gerrit.server.config.DisableReverseDnsLookupProvider; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GitReceivePackGroups; import com.google.gerrit.server.config.GitUploadPackGroups; import com.google.gerrit.server.config.SysExecutorModule; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.extensions.events.EventUtil; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.RevisionCreated; @@ -138,6 +140,7 @@ public class BatchProgramModule extends FactoryModule { bind(String.class) .annotatedWith(CanonicalWebUrl.class) .toProvider(CanonicalWebUrlProvider.class); + bind(UrlFormatter.class).to(DefaultBrowseUrls.class); bind(Boolean.class) .annotatedWith(DisableReverseDnsLookup.class) .toProvider(DisableReverseDnsLookupProvider.class) diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java index fa6cd6c61a..29974e9941 100644 --- a/java/com/google/gerrit/server/StarredChangesUtil.java +++ b/java/com/google/gerrit/server/StarredChangesUtil.java @@ -42,6 +42,8 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.index.change.ChangeField; import com.google.gerrit.server.index.change.ChangeIndexer; +import com.google.gerrit.server.logging.TraceContext; +import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.InternalChangeQuery; @@ -376,20 +378,20 @@ public class StarredChangesUtil { } public static StarRef readLabels(Repository repo, String refName) throws IOException { - logger.atFine().log("Read star labels from %s", refName); - - Ref ref = repo.exactRef(refName); - if (ref == null) { - return StarRef.MISSING; - } + try (TraceTimer traceTimer = TraceContext.newTimer("Read star labels from %s", refName)) { + Ref ref = repo.exactRef(refName); + if (ref == null) { + return StarRef.MISSING; + } - try (ObjectReader reader = repo.newObjectReader()) { - ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB); - return StarRef.create( - ref, - Splitter.on(CharMatcher.whitespace()) - .omitEmptyStrings() - .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8))); + try (ObjectReader reader = repo.newObjectReader()) { + ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB); + return StarRef.create( + ref, + Splitter.on(CharMatcher.whitespace()) + .omitEmptyStrings() + .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8))); + } } } @@ -450,8 +452,9 @@ public class StarredChangesUtil { private void updateLabels( Repository repo, String refName, ObjectId oldObjectId, Collection<String> labels) throws IOException, OrmException, InvalidLabelsException { - logger.atFine().log("Update star labels in %s (labels=%s)", refName, labels); - try (RevWalk rw = new RevWalk(repo)) { + try (TraceTimer traceTimer = + TraceContext.newTimer("Update star labels in %s (labels=%s)", refName, labels); + RevWalk rw = new RevWalk(repo)) { RefUpdate u = repo.updateRef(refName); u.setExpectedOldObjectId(oldObjectId); u.setForceUpdate(true); @@ -488,31 +491,32 @@ public class StarredChangesUtil { return; } - logger.atFine().log("Delete star labels in %s", refName); - RefUpdate u = repo.updateRef(refName); - u.setForceUpdate(true); - u.setExpectedOldObjectId(oldObjectId); - u.setRefLogIdent(serverIdent.get()); - u.setRefLogMessage("Unstar change", true); - RefUpdate.Result result = u.delete(); - switch (result) { - case FORCED: - gitRefUpdated.fire(allUsers, u, null); - return; - case NEW: - case NO_CHANGE: - case FAST_FORWARD: - case IO_FAILURE: - case LOCK_FAILURE: - case NOT_ATTEMPTED: - case REJECTED: - case REJECTED_CURRENT_BRANCH: - case RENAMED: - case REJECTED_MISSING_OBJECT: - case REJECTED_OTHER_REASON: - default: - throw new OrmException( - String.format("Delete star ref %s failed: %s", refName, result.name())); + try (TraceTimer traceTimer = TraceContext.newTimer("Delete star labels in %s", refName)) { + RefUpdate u = repo.updateRef(refName); + u.setForceUpdate(true); + u.setExpectedOldObjectId(oldObjectId); + u.setRefLogIdent(serverIdent.get()); + u.setRefLogMessage("Unstar change", true); + RefUpdate.Result result = u.delete(); + switch (result) { + case FORCED: + gitRefUpdated.fire(allUsers, u, null); + return; + case NEW: + case NO_CHANGE: + case FAST_FORWARD: + case IO_FAILURE: + case LOCK_FAILURE: + case NOT_ATTEMPTED: + case REJECTED: + case REJECTED_CURRENT_BRANCH: + case RENAMED: + case REJECTED_MISSING_OBJECT: + case REJECTED_OTHER_REASON: + default: + throw new OrmException( + String.format("Delete star ref %s failed: %s", refName, result.name())); + } } } } diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java index b117888892..9dd9bd89f8 100644 --- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java +++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java @@ -656,7 +656,7 @@ public class ExternalIdNotes extends VersionedMetaData { @Override protected void onLoad() throws IOException, ConfigInvalidException { - logger.atFine().log("Reading external IDs"); + logger.atFine().log("Reading external ID note map"); noteMap = revision != null ? NoteMap.read(reader, revision) : NoteMap.newEmptyMap(); diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java index 343565266f..c652d50d73 100644 --- a/java/com/google/gerrit/server/cache/CacheMetrics.java +++ b/java/com/google/gerrit/server/cache/CacheMetrics.java @@ -18,6 +18,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheStats; import com.google.common.collect.ImmutableSet; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.registration.PluginName; import com.google.gerrit.metrics.CallbackMetric; import com.google.gerrit.metrics.CallbackMetric1; @@ -71,7 +72,7 @@ public class CacheMetrics { metrics.newTrigger( cacheMetrics, () -> { - for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) { + for (Extension<Cache<?, ?>> e : cacheMap) { Cache<?, ?> c = e.getProvider().get(); String name = metricNameOf(e); CacheStats cstats = c.stats(); @@ -95,7 +96,7 @@ public class CacheMetrics { return ((double) d.hitCount() / d.requestCount() * 100); } - private static String metricNameOf(DynamicMap.Entry<Cache<?, ?>> e) { + private static String metricNameOf(Extension<Cache<?, ?>> e) { if (PluginName.GERRIT.equals(e.getPluginName())) { return e.getExportName(); } diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java index 43f7b2f742..5de50f680f 100644 --- a/java/com/google/gerrit/server/change/ChangeJson.java +++ b/java/com/google/gerrit/server/change/ChangeJson.java @@ -88,6 +88,7 @@ import com.google.gerrit.extensions.common.WebLinkInfo; import com.google.gerrit.extensions.config.DownloadCommand; import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.index.query.QueryResult; @@ -340,6 +341,7 @@ public class ChangeJson { this.enableParallelFormatting = config.getBoolean("change", "enableParallelFormatting", false); this.fanOutExecutor = fanOutExecutor; this.options = Sets.immutableEnumSet(options); + logger.atFine().log("options = %s", options); } /** @@ -1471,7 +1473,7 @@ public class ChangeJson { private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in) throws PermissionBackendException, OrmException, IOException { Map<String, FetchInfo> r = new LinkedHashMap<>(); - for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + for (Extension<DownloadScheme> e : downloadSchemes) { String schemeName = e.getExportName(); DownloadScheme scheme = e.getProvider().get(); if (!scheme.isEnabled() @@ -1502,7 +1504,7 @@ public class ChangeJson { String projectName, String refName, FetchInfo fetchInfo) { - for (DynamicMap.Entry<DownloadCommand> e2 : commands) { + for (Extension<DownloadCommand> e2 : commands) { String commandName = e2.getExportName(); DownloadCommand command = e2.getProvider().get(); String c = command.getCommand(scheme, projectName, refName); diff --git a/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java b/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java index 646d395225..14c93dc0d9 100644 --- a/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java +++ b/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java @@ -30,6 +30,7 @@ public abstract class CanonicalWebUrlModule extends AbstractModule { // final Class<? extends Provider<String>> provider = provider(); bind(String.class).annotatedWith(CanonicalWebUrl.class).toProvider(provider); + bind(UrlFormatter.class).to(DefaultBrowseUrls.class); } protected abstract Class<? extends Provider<String>> provider(); diff --git a/java/com/google/gerrit/server/config/ChangeCleanupConfig.java b/java/com/google/gerrit/server/config/ChangeCleanupConfig.java index 632293ef25..f49224780f 100644 --- a/java/com/google/gerrit/server/config/ChangeCleanupConfig.java +++ b/java/com/google/gerrit/server/config/ChangeCleanupConfig.java @@ -15,7 +15,6 @@ package com.google.gerrit.server.config; import com.google.common.base.Strings; -import com.google.gerrit.common.Nullable; import com.google.gerrit.server.config.ScheduleConfig.Schedule; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -31,7 +30,7 @@ public class ChangeCleanupConfig { private static String KEY_ABANDON_MESSAGE = "abandonMessage"; private static String DEFAULT_ABANDON_MESSAGE = "Auto-Abandoned due to inactivity, see " - + "${URL}Documentation/user-change-cleanup.html#auto-abandon\n" + + "${URL}\n" + "\n" + "If this change is still wanted it should be restored."; @@ -41,12 +40,11 @@ public class ChangeCleanupConfig { private final String abandonMessage; @Inject - ChangeCleanupConfig( - @GerritServerConfig Config cfg, @CanonicalWebUrl @Nullable String canonicalWebUrl) { + ChangeCleanupConfig(@GerritServerConfig Config cfg, UrlFormatter urlFormatter) { schedule = ScheduleConfig.createSchedule(cfg, SECTION); abandonAfter = readAbandonAfter(cfg); abandonIfMergeable = cfg.getBoolean(SECTION, null, KEY_ABANDON_IF_MERGEABLE, true); - abandonMessage = readAbandonMessage(cfg, canonicalWebUrl); + abandonMessage = readAbandonMessage(cfg, urlFormatter); } private long readAbandonAfter(Config cfg) { @@ -55,14 +53,17 @@ public class ChangeCleanupConfig { return abandonAfter >= 0 ? abandonAfter : 0; } - private String readAbandonMessage(Config cfg, String webUrl) { + private String readAbandonMessage(Config cfg, UrlFormatter urlFormatter) { String abandonMessage = cfg.getString(SECTION, null, KEY_ABANDON_MESSAGE); if (Strings.isNullOrEmpty(abandonMessage)) { abandonMessage = DEFAULT_ABANDON_MESSAGE; } - if (!Strings.isNullOrEmpty(webUrl)) { - abandonMessage = abandonMessage.replaceAll("\\$\\{URL\\}", webUrl); + + String docUrl = urlFormatter.getDocUrl("user-change-cleanup.html", "auto-abandon").orElse(""); + if (!docUrl.isEmpty()) { + abandonMessage = abandonMessage.replaceAll("\\$\\{URL\\}", docUrl); } + return abandonMessage; } diff --git a/java/com/google/gerrit/extensions/registration/NamedProvider.java b/java/com/google/gerrit/server/config/DefaultBrowseUrls.java index aca651b5f8..988cb0551b 100644 --- a/java/com/google/gerrit/extensions/registration/NamedProvider.java +++ b/java/com/google/gerrit/server/config/DefaultBrowseUrls.java @@ -12,17 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.extensions.registration; +package com.google.gerrit.server.config; -import com.google.inject.Provider; +import java.util.Optional; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; -/** Pair of provider implementation and plugin providing it. */ -class NamedProvider<T> { - final Provider<T> impl; - final String pluginName; +@Singleton +public class DefaultBrowseUrls implements UrlFormatter { + private final Provider<String> canonicalWebUrlProvider; - NamedProvider(Provider<T> provider, String pluginName) { - this.impl = provider; - this.pluginName = pluginName; + @Inject + DefaultBrowseUrls(@CanonicalWebUrl Provider<String> canonicalWebUrlProvider) { + this.canonicalWebUrlProvider = canonicalWebUrlProvider; + } + + @Override + public Optional<String> getWebUrl() { + return Optional.ofNullable(canonicalWebUrlProvider.get()); } } diff --git a/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/java/com/google/gerrit/server/config/ProjectConfigEntry.java index d30e080aef..5515f0ebc3 100644 --- a/java/com/google/gerrit/server/config/ProjectConfigEntry.java +++ b/java/com/google/gerrit/server/config/ProjectConfigEntry.java @@ -22,7 +22,7 @@ import com.google.gerrit.extensions.api.projects.ConfigValue; import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.registration.DynamicMap.Entry; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.git.GitRepositoryManager; @@ -321,7 +321,7 @@ public class ProjectConfigEntry { ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId()); ProjectConfig newCfg = parseConfig(p, event.getNewObjectId()); if (oldCfg != null && newCfg != null) { - for (Entry<ProjectConfigEntry> e : pluginConfigEntries) { + for (Extension<ProjectConfigEntry> e : pluginConfigEntries) { ProjectConfigEntry configEntry = e.getProvider().get(); String newValue = getValue(newCfg, e); String oldValue = getValue(oldCfg, e); @@ -367,7 +367,7 @@ public class ProjectConfigEntry { } } - private static String getValue(ProjectConfig cfg, Entry<ProjectConfigEntry> e) { + private static String getValue(ProjectConfig cfg, Extension<ProjectConfigEntry> e) { String value = cfg.getPluginConfig(e.getPluginName()).getString(e.getExportName()); if (value == null) { value = e.getProvider().get().getDefaultValue(); diff --git a/java/com/google/gerrit/server/config/UrlFormatter.java b/java/com/google/gerrit/server/config/UrlFormatter.java new file mode 100644 index 0000000000..5cec1ac2c4 --- /dev/null +++ b/java/com/google/gerrit/server/config/UrlFormatter.java @@ -0,0 +1,66 @@ +// 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.server.config; + +import com.google.common.base.Strings; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.Project; +import java.util.Optional; + +/** + * Formats URLs to different parts of the Gerrit API and UI. + * + * <p>By default, these gerrit URLs are formed by adding suffixes to the web URL. The interface + * centralizes these conventions, and also allows introducing different, custom URL schemes. + * + * <p>Unfortunately, Gerrit operates in modes for which there is no canonical URL. This can be in + * standalone utilities that have no HTTP server (eg. index upgrade commands), in servers that run + * SSH only, or in a HTTP/SSH server that is accessed over SSH without canonical web URL configured. + */ +public interface UrlFormatter { + + /** + * The canonical base URL where this Gerrit installation can be reached. + * + * <p>For the default implementations below to work, it must end in "/". + */ + Optional<String> getWebUrl(); + + /** Returns the URL for viewing a change. */ + default Optional<String> getChangeViewUrl(@Nullable Project.NameKey project, Change.Id id) { + + // In the PolyGerrit URL (contrary to REST URLs) there is no need to URL-escape strings, since + // the /+/ separator unambiguously defines how to parse the path. + return getWebUrl() + .map(url -> url + "c/" + (project != null ? project.get() + "/+/" : "") + id.get()); + } + + /** Returns a URL pointing to a section of the settings page. */ + default Optional<String> getSettingsUrl(String section) { + return getWebUrl() + .map(url -> url + "settings" + (Strings.isNullOrEmpty(section) ? "" : "#" + section)); + } + + /** Returns a URL pointing to a documentation page, at a given named anchor. */ + default Optional<String> getDocUrl(String page, String anchor) { + return getWebUrl().map(url -> url + "Documentation/" + page + "#" + anchor); + } + + /** Returns a REST API URL for a given suffix (eg. "accounts/self/details") */ + default Optional<String> getRestUrl(String suffix) { + return getWebUrl().map(url -> url + suffix); + } +} diff --git a/java/com/google/gerrit/server/edit/ChangeEditJson.java b/java/com/google/gerrit/server/edit/ChangeEditJson.java index 78baef7195..bd9c3a616b 100644 --- a/java/com/google/gerrit/server/edit/ChangeEditJson.java +++ b/java/com/google/gerrit/server/edit/ChangeEditJson.java @@ -20,6 +20,7 @@ import com.google.gerrit.extensions.common.FetchInfo; import com.google.gerrit.extensions.config.DownloadCommand; import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.server.CommonConverters; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.change.ChangeJson; @@ -78,7 +79,7 @@ public class ChangeEditJson { private Map<String, FetchInfo> fillFetchMap(ChangeEdit edit) { Map<String, FetchInfo> r = new LinkedHashMap<>(); - for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + for (Extension<DownloadScheme> e : downloadSchemes) { String schemeName = e.getExportName(); DownloadScheme scheme = e.getProvider().get(); if (!scheme.isEnabled() diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java index fbce4b273f..62dc511b68 100644 --- a/java/com/google/gerrit/server/events/EventFactory.java +++ b/java/com/google/gerrit/server/events/EventFactory.java @@ -20,7 +20,6 @@ import static java.util.Comparator.comparing; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.flogger.FluentLogger; -import com.google.gerrit.common.Nullable; import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.common.data.SubmitRecord; @@ -42,7 +41,7 @@ import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.Emails; import com.google.gerrit.server.change.ChangeKindCache; -import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.ApprovalAttribute; import com.google.gerrit.server.data.ChangeAttribute; @@ -86,8 +85,8 @@ public class EventFactory { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final AccountCache accountCache; + private final UrlFormatter urlFormatter; private final Emails emails; - private final Provider<String> urlProvider; private final PatchListCache patchListCache; private final Provider<PersonIdent> myIdent; private final ChangeData.Factory changeDataFactory; @@ -101,7 +100,7 @@ public class EventFactory { EventFactory( AccountCache accountCache, Emails emails, - @CanonicalWebUrl @Nullable Provider<String> urlProvider, + UrlFormatter urlFormatter, PatchListCache patchListCache, @GerritPersonIdent Provider<PersonIdent> myIdent, ChangeData.Factory changeDataFactory, @@ -111,8 +110,8 @@ public class EventFactory { SchemaFactory<ReviewDb> schema, IndexConfig indexConfig) { this.accountCache = accountCache; + this.urlFormatter = urlFormatter; this.emails = emails; - this.urlProvider = urlProvider; this.patchListCache = patchListCache; this.myIdent = myIdent; this.changeDataFactory = changeDataFactory; @@ -678,11 +677,8 @@ public class EventFactory { /** Get a link to the change; null if the server doesn't know its own address. */ private String getChangeUrl(Change change) { - if (change != null && urlProvider.get() != null) { - StringBuilder r = new StringBuilder(); - r.append(urlProvider.get()); - r.append(change.getChangeId()); - return r.toString(); + if (change != null) { + return urlFormatter.getChangeViewUrl(change.getProject(), change.getId()).orElse(null); } return null; } diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java index af28bed3fe..3ca2bdb93d 100644 --- a/java/com/google/gerrit/server/extensions/webui/UiActions.java +++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java @@ -26,6 +26,7 @@ import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission; import com.google.gerrit.extensions.conditions.BooleanCondition; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.registration.PluginName; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestResource; @@ -121,7 +122,7 @@ public class UiActions { @Nullable private <R extends RestResource> UiAction.Description describe( - DynamicMap.Entry<RestView<R>> e, R resource) { + Extension<RestView<R>> e, R resource) { int d = e.getExportName().indexOf('.'); if (d < 0) { return null; diff --git a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java index 513d909551..7cf97ac779 100644 --- a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java +++ b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java @@ -14,9 +14,11 @@ package com.google.gerrit.server.git; +import com.google.common.base.Preconditions; import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.UrlFormatter; import com.google.inject.Inject; +import java.util.Optional; /** Print a change description for use in git command-line progress. */ public class DefaultChangeReportFormatter implements ChangeReportFormatter { @@ -24,30 +26,29 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter { private static final String SUBJECT_CROP_APPENDIX = "..."; private static final int SUBJECT_CROP_RANGE = 10; - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; @Inject - DefaultChangeReportFormatter(@CanonicalWebUrl String canonicalWebUrl) { - this.canonicalWebUrl = canonicalWebUrl; + DefaultChangeReportFormatter(UrlFormatter urlFormatter) { + this.urlFormatter = urlFormatter; } @Override public String newChange(ChangeReportFormatter.Input input) { - return formatChangeUrl(canonicalWebUrl, input); + return formatChangeUrl(input); } @Override public String changeUpdated(ChangeReportFormatter.Input input) { - return formatChangeUrl(canonicalWebUrl, input); - } - - public static String formatChangeUrl(String canonicalWebUrl, Change change) { - return canonicalWebUrl + "c/" + change.getProject().get() + "/+/" + change.getChangeId(); + return formatChangeUrl(input); } @Override public String changeClosed(ChangeReportFormatter.Input input) { - return String.format("change %s closed", formatChangeUrl(canonicalWebUrl, input.change())); + Change c = input.change(); + return String.format( + "change %s closed", + urlFormatter.getChangeViewUrl(c.getProject(), c.getId()).orElse(c.getId().toString())); } protected String cropSubject(String subject) { @@ -65,11 +66,15 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter { return subject; } - protected String formatChangeUrl(String url, Input input) { + protected String formatChangeUrl(Input input) { + Change c = input.change(); + Optional<String> changeUrl = urlFormatter.getChangeViewUrl(c.getProject(), c.getId()); + Preconditions.checkState(changeUrl.isPresent()); + StringBuilder m = new StringBuilder() .append(" ") - .append(formatChangeUrl(url, input.change())) + .append(changeUrl.get()) .append(" ") .append(cropSubject(input.subject())); if (input.isEdit()) { diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java index c035269536..c2e147b317 100644 --- a/java/com/google/gerrit/server/git/MergeUtil.java +++ b/java/com/google/gerrit/server/git/MergeUtil.java @@ -25,7 +25,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; import com.google.gerrit.common.FooterConstants; -import com.google.gerrit.common.Nullable; import com.google.gerrit.common.data.LabelType; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.BadRequestException; @@ -42,8 +41,8 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.project.ProjectState; @@ -65,6 +64,7 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -147,7 +147,7 @@ public class MergeUtil { private final Provider<ReviewDb> db; private final IdentifiedUser.GenericFactory identifiedUserFactory; - private final Provider<String> urlProvider; + private final UrlFormatter urlFormatter; private final ApprovalsUtil approvalsUtil; private final ProjectState project; private final boolean useContentMerge; @@ -159,7 +159,7 @@ public class MergeUtil { @GerritServerConfig Config serverConfig, Provider<ReviewDb> db, IdentifiedUser.GenericFactory identifiedUserFactory, - @CanonicalWebUrl @Nullable Provider<String> urlProvider, + UrlFormatter urlFormatter, ApprovalsUtil approvalsUtil, PluggableCommitMessageGenerator commitMessageGenerator, @Assisted ProjectState project) { @@ -167,7 +167,7 @@ public class MergeUtil { serverConfig, db, identifiedUserFactory, - urlProvider, + urlFormatter, approvalsUtil, project, commitMessageGenerator, @@ -179,14 +179,14 @@ public class MergeUtil { @GerritServerConfig Config serverConfig, Provider<ReviewDb> db, IdentifiedUser.GenericFactory identifiedUserFactory, - @CanonicalWebUrl @Nullable Provider<String> urlProvider, + UrlFormatter urlFormatter, ApprovalsUtil approvalsUtil, @Assisted ProjectState project, PluggableCommitMessageGenerator commitMessageGenerator, @Assisted boolean useContentMerge) { this.db = db; this.identifiedUserFactory = identifiedUserFactory; - this.urlProvider = urlProvider; + this.urlFormatter = urlFormatter; this.approvalsUtil = approvalsUtil; this.project = project; this.useContentMerge = useContentMerge; @@ -345,17 +345,16 @@ public class MergeUtil { msgbuf.append('\n'); } - final String siteUrl = urlProvider.get(); - if (siteUrl != null) { - final String url = siteUrl + c.getId().get(); - if (!contains(footers, FooterConstants.REVIEWED_ON, url)) { - msgbuf.append(FooterConstants.REVIEWED_ON.getName()); - msgbuf.append(": "); - msgbuf.append(url); - msgbuf.append('\n'); + Optional<String> url = urlFormatter.getChangeViewUrl(null, c.getId()); + if (url.isPresent()) { + if (!contains(footers, FooterConstants.REVIEWED_ON, url.get())) { + msgbuf + .append(FooterConstants.REVIEWED_ON.getName()) + .append(": ") + .append(url.get()) + .append('\n'); } } - PatchSetApproval submitAudit = null; for (PatchSetApproval a : safeGetApprovals(notes, psId)) { diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java index eb62d547aa..b9ab6fda46 100644 --- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java +++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java @@ -110,14 +110,9 @@ public class AsyncReceiveCommits implements PreReceiveHook { final MultiProgressMonitor progress; private final Collection<ReceiveCommand> commands; - private final ReceiveCommits receiveCommits; private Worker(Collection<ReceiveCommand> commands) { this.commands = commands; - receiveCommits = - factory.create( - projectState, user, receivePack, allRefsWatcher, extraReviewers, messageSender); - receiveCommits.init(); progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes"); } @@ -173,7 +168,7 @@ public class AsyncReceiveCommits implements PreReceiveHook { } } - private final ReceiveCommits.Factory factory; + private final ReceiveCommits receiveCommits; private final PermissionBackend.ForProject perm; private final ReceivePack receivePack; private final ExecutorService executor; @@ -184,8 +179,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { private final ProjectState projectState; private final IdentifiedUser user; private final Repository repo; - private final MessageSender messageSender; - private final SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers; private final AllRefsWatcher allRefsWatcher; @Inject @@ -206,7 +199,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { @Assisted @Nullable MessageSender messageSender, @Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers) throws PermissionBackendException { - this.factory = factory; this.executor = executor; this.scopePropagator = scopePropagator; this.receiveConfig = receiveConfig; @@ -215,8 +207,6 @@ public class AsyncReceiveCommits implements PreReceiveHook { this.projectState = projectState; this.user = user; this.repo = repo; - this.messageSender = messageSender; - this.extraReviewers = extraReviewers; Project.NameKey projectName = projectState.getNameKey(); receivePack = new ReceivePack(repo); @@ -251,6 +241,11 @@ public class AsyncReceiveCommits implements PreReceiveHook { advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName)); advHooks.add(new HackPushNegotiateHook()); receivePack.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks)); + + receiveCommits = + factory.create( + projectState, user, receivePack, allRefsWatcher, extraReviewers, messageSender); + receiveCommits.init(); } /** Determine if the user can upload commits. */ @@ -275,6 +270,11 @@ public class AsyncReceiveCommits implements PreReceiveHook { @Override public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { + if (commands.stream().anyMatch(c -> c.getResult() != Result.NOT_ATTEMPTED)) { + // Stop processing when command was already processed by previously invoked + // pre-receive hooks + return; + } Worker w = new Worker(commands); try { w.progress.waitFor( diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java index 7566b5593c..ef699b32a4 100644 --- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java +++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java @@ -70,8 +70,8 @@ import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType; import com.google.gerrit.extensions.client.GeneralPreferencesInfo; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.registration.DynamicMap.Entry; import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.ResourceConflictException; @@ -1131,7 +1131,7 @@ class ReceiveCommits { * fails. */ private void validatePluginConfig(ReceiveCommand cmd, ProjectConfig cfg) { - for (Entry<ProjectConfigEntry> e : pluginConfigEntries) { + for (Extension<ProjectConfigEntry> e : pluginConfigEntries) { PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName()); ProjectConfigEntry configEntry = e.getProvider().get(); String value = pluginCfg.getString(e.getExportName()); diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java index 7930fe89e2..8af04e0f4b 100644 --- a/java/com/google/gerrit/server/git/validators/CommitValidators.java +++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java @@ -20,13 +20,13 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG; import static java.util.stream.Collectors.toList; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.flogger.FluentLogger; import com.google.gerrit.common.FooterConstants; import com.google.gerrit.common.Nullable; -import com.google.gerrit.common.PageLinks; import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.AuthException; @@ -41,8 +41,8 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AllUsersName; -import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.events.CommitReceivedEvent; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.ValidationError; @@ -63,6 +63,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.regex.Pattern; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; @@ -87,7 +88,7 @@ public class CommitValidators { @Singleton public static class Factory { private final PersonIdent gerritIdent; - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; private final DynamicSet<CommitValidationListener> pluginValidators; private final GitRepositoryManager repoManager; private final AllUsersName allUsers; @@ -100,7 +101,7 @@ public class CommitValidators { @Inject Factory( @GerritPersonIdent PersonIdent gerritIdent, - @CanonicalWebUrl @Nullable String canonicalWebUrl, + UrlFormatter urlFormatter, @GerritServerConfig Config cfg, DynamicSet<CommitValidationListener> pluginValidators, GitRepositoryManager repoManager, @@ -110,7 +111,7 @@ public class CommitValidators { AccountValidator accountValidator, ProjectCache projectCache) { this.gerritIdent = gerritIdent; - this.canonicalWebUrl = canonicalWebUrl; + this.urlFormatter = urlFormatter; this.pluginValidators = pluginValidators; this.repoManager = repoManager; this.allUsers = allUsers; @@ -138,16 +139,11 @@ public class CommitValidators { new UploadMergesPermissionValidator(perm), new ProjectStateValidationListener(projectState), new AmendedGerritMergeCommitValidationListener(perm, gerritIdent), - new AuthorUploaderValidator(user, perm, canonicalWebUrl), - new CommitterUploaderValidator(user, perm, canonicalWebUrl), + new AuthorUploaderValidator(user, perm, urlFormatter), + new CommitterUploaderValidator(user, perm, urlFormatter), new SignedOffByValidator(user, perm, projectState), new ChangeIdValidator( - projectState, - user, - canonicalWebUrl, - installCommitMsgHookCommand, - sshInfo, - change), + projectState, user, urlFormatter, installCommitMsgHookCommand, sshInfo, change), new ConfigValidator(branch, user, rw, allUsers, allProjects), new BannedCommitsValidator(rejectCommits), new PluginCommitValidationListener(pluginValidators), @@ -171,15 +167,10 @@ public class CommitValidators { new UploadMergesPermissionValidator(perm), new ProjectStateValidationListener(projectState), new AmendedGerritMergeCommitValidationListener(perm, gerritIdent), - new AuthorUploaderValidator(user, perm, canonicalWebUrl), + new AuthorUploaderValidator(user, perm, urlFormatter), new SignedOffByValidator(user, perm, projectCache.checkedGet(branch.getParentKey())), new ChangeIdValidator( - projectState, - user, - canonicalWebUrl, - installCommitMsgHookCommand, - sshInfo, - change), + projectState, user, urlFormatter, installCommitMsgHookCommand, sshInfo, change), new ConfigValidator(branch, user, rw, allUsers, allProjects), new PluginCommitValidationListener(pluginValidators), new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker), @@ -208,8 +199,8 @@ public class CommitValidators { ImmutableList.of( new UploadMergesPermissionValidator(perm), new ProjectStateValidationListener(projectCache.checkedGet(branch.getParentKey())), - new AuthorUploaderValidator(user, perm, canonicalWebUrl), - new CommitterUploaderValidator(user, perm, canonicalWebUrl))); + new AuthorUploaderValidator(user, perm, urlFormatter), + new CommitterUploaderValidator(user, perm, urlFormatter))); } } @@ -253,7 +244,7 @@ public class CommitValidators { private static final Pattern CHANGE_ID = Pattern.compile(CHANGE_ID_PATTERN); private final ProjectState projectState; - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; private final String installCommitMsgHookCommand; private final SshInfo sshInfo; private final IdentifiedUser user; @@ -262,12 +253,12 @@ public class CommitValidators { public ChangeIdValidator( ProjectState projectState, IdentifiedUser user, - String canonicalWebUrl, + UrlFormatter urlFormatter, String installCommitMsgHookCommand, SshInfo sshInfo, Change change) { this.projectState = projectState; - this.canonicalWebUrl = canonicalWebUrl; + this.urlFormatter = urlFormatter; this.installCommitMsgHookCommand = installCommitMsgHookCommand; this.sshInfo = sshInfo; this.user = user; @@ -352,10 +343,12 @@ public class CommitValidators { // If there are no SSH keys, the commit-msg hook must be installed via // HTTP(S) + Optional<String> webUrl = urlFormatter.getWebUrl(); if (hostKeys.isEmpty()) { + Preconditions.checkState(webUrl.isPresent()); return String.format( " f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\" %stools/hooks/commit-msg ; chmod +x \"$f\"", - canonicalWebUrl); + webUrl.get()); } // SSH keys exist, so the hook can be installed with scp. @@ -365,7 +358,8 @@ public class CommitValidators { int c = host.lastIndexOf(':'); if (0 <= c) { if (host.startsWith("*:")) { - sshHost = getGerritHost(canonicalWebUrl); + Preconditions.checkState(webUrl.isPresent()); + sshHost = getGerritHost(webUrl.get()); } else { sshHost = host.substring(0, c); } @@ -547,13 +541,13 @@ public class CommitValidators { public static class AuthorUploaderValidator implements CommitValidationListener { private final IdentifiedUser user; private final PermissionBackend.ForRef perm; - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; public AuthorUploaderValidator( - IdentifiedUser user, PermissionBackend.ForRef perm, String canonicalWebUrl) { + IdentifiedUser user, PermissionBackend.ForRef perm, UrlFormatter urlFormatter) { this.user = user; this.perm = perm; - this.canonicalWebUrl = canonicalWebUrl; + this.urlFormatter = urlFormatter; } @Override @@ -568,7 +562,7 @@ public class CommitValidators { return Collections.emptyList(); } catch (AuthException e) { throw new CommitValidationException( - "invalid author", invalidEmail("author", author, user, canonicalWebUrl)); + "invalid author", invalidEmail("author", author, user, urlFormatter)); } catch (PermissionBackendException e) { logger.atSevere().withCause(e).log("cannot check FORGE_AUTHOR"); throw new CommitValidationException("internal auth error"); @@ -580,13 +574,13 @@ public class CommitValidators { public static class CommitterUploaderValidator implements CommitValidationListener { private final IdentifiedUser user; private final PermissionBackend.ForRef perm; - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; public CommitterUploaderValidator( - IdentifiedUser user, PermissionBackend.ForRef perm, String canonicalWebUrl) { + IdentifiedUser user, PermissionBackend.ForRef perm, UrlFormatter urlFormatter) { this.user = user; this.perm = perm; - this.canonicalWebUrl = canonicalWebUrl; + this.urlFormatter = urlFormatter; } @Override @@ -601,7 +595,7 @@ public class CommitValidators { return Collections.emptyList(); } catch (AuthException e) { throw new CommitValidationException( - "invalid committer", invalidEmail("committer", committer, user, canonicalWebUrl)); + "invalid committer", invalidEmail("committer", committer, user, urlFormatter)); } catch (PermissionBackendException e) { logger.atSevere().withCause(e).log("cannot check FORGE_COMMITTER"); throw new CommitValidationException("internal auth error"); @@ -821,7 +815,7 @@ public class CommitValidators { } private static CommitValidationMessage invalidEmail( - String type, PersonIdent who, IdentifiedUser currentUser, String canonicalWebUrl) { + String type, PersonIdent who, IdentifiedUser currentUser, UrlFormatter urlFormatter) { StringBuilder sb = new StringBuilder(); sb.append("email address ") @@ -839,11 +833,11 @@ public class CommitValidators { } } - if (canonicalWebUrl != null) { - sb.append("To register an email address, visit:\n"); - sb.append(canonicalWebUrl).append("#").append(PageLinks.SETTINGS_CONTACT).append("\n"); + if (urlFormatter.getSettingsUrl("").isPresent()) { + sb.append("To register an email address, visit:\n") + .append(urlFormatter.getSettingsUrl("EmailAddresses").get()) + .append("\n\n"); } - sb.append("\n"); return new CommitValidationMessage(sb.toString(), true); } diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java index c4df4dd11c..f755aab162 100644 --- a/java/com/google/gerrit/server/git/validators/MergeValidators.java +++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java @@ -19,8 +19,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.registration.DynamicMap.Entry; import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Branch; @@ -207,7 +207,7 @@ public class MergeValidators { } } - for (Entry<ProjectConfigEntry> e : pluginConfigEntries) { + for (Extension<ProjectConfigEntry> e : pluginConfigEntries) { PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName()); ProjectConfigEntry configEntry = e.getProvider().get(); diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java index 977baa585d..d68874039b 100644 --- a/java/com/google/gerrit/server/logging/TraceContext.java +++ b/java/com/google/gerrit/server/logging/TraceContext.java @@ -192,6 +192,21 @@ public class TraceContext implements AutoCloseable { return new TraceTimer(format, arg1, arg2); } + /** + * Opens a new timer that logs the time for an operation if request tracing is enabled. + * + * <p>If request tracing is not enabled this is a no-op. + * + * @param format the message format string + * @param arg1 first argument for the message + * @param arg2 second argument for the message + * @param arg3 third argument for the message + * @return the trace timer + */ + public static TraceTimer newTimer(String format, Object arg1, Object arg2, Object arg3) { + return new TraceTimer(format, arg1, arg2, arg3); + } + public static class TraceTimer implements AutoCloseable { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @@ -210,6 +225,11 @@ public class TraceContext implements AutoCloseable { this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, elapsedMs)); } + private TraceTimer( + String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) { + this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, elapsedMs)); + } + private TraceTimer(Consumer<Long> logFn) { this.logFn = logFn; this.stopwatch = Stopwatch.createStarted(); diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java index ec0c1f2e23..262e82bc5c 100644 --- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java +++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java @@ -23,6 +23,7 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.client.Side; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.mail.HtmlParser; @@ -46,7 +47,7 @@ import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.Emails; import com.google.gerrit.server.change.EmailReviewComments; -import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.extensions.events.CommentAdded; import com.google.gerrit.server.mail.MailFilter; import com.google.gerrit.server.mail.send.InboundEmailRejectionSender; @@ -97,7 +98,7 @@ public class MailProcessor { private final CommentAdded commentAdded; private final ApprovalsUtil approvalsUtil; private final AccountCache accountCache; - private final Provider<String> canonicalUrl; + private final UrlFormatter urlFormatter; @Inject public MailProcessor( @@ -115,7 +116,7 @@ public class MailProcessor { ApprovalsUtil approvalsUtil, CommentAdded commentAdded, AccountCache accountCache, - @CanonicalWebUrl Provider<String> canonicalUrl) { + UrlFormatter urlFormatter) { this.emails = emails; this.emailRejectionSender = emailRejectionSender; this.retryHelper = retryHelper; @@ -130,7 +131,7 @@ public class MailProcessor { this.commentAdded = commentAdded; this.approvalsUtil = approvalsUtil; this.accountCache = accountCache; - this.canonicalUrl = canonicalUrl; + this.urlFormatter = urlFormatter; } /** @@ -148,7 +149,7 @@ public class MailProcessor { private void processImpl(BatchUpdate.Factory buf, MailMessage message) throws OrmException, UpdateException, RestApiException, IOException { - for (DynamicMap.Entry<MailFilter> filter : mailFilters) { + for (Extension<MailFilter> filter : mailFilters) { if (!filter.getProvider().get().shouldProcessMessage(message)) { logger.atWarning().log( "Message %s filtered by plugin %s %s. Will delete message.", @@ -237,7 +238,11 @@ public class MailProcessor { .sorted(CommentsUtil.COMMENT_ORDER) .collect(toList()); Project.NameKey project = cd.project(); - String changeUrl = canonicalUrl.get() + "c/" + cd.project().get() + "/+/" + cd.getId().get(); + + // If URL is not defined, we won't be able to parse line comments. We still attempt to get the + // other ones. + String changeUrl = + urlFormatter.getChangeViewUrl(cd.project(), cd.getId()).orElse("http://gerrit.invalid/"); List<MailComment> parsedComments; if (useHtmlParser(message)) { diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java index bfb210ec9c..0b8a3c1192 100644 --- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java +++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.mail.send; import com.google.common.base.Splitter; import com.google.common.collect.ListMultimap; import com.google.common.flogger.FluentLogger; +import com.google.gerrit.common.Nullable; import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.api.changes.RecipientType; @@ -217,11 +218,9 @@ public abstract class ChangeEmail extends NotificationEmail { } /** Get a link to the change; null if the server doesn't know its own address. */ + @Nullable public String getChangeUrl() { - if (getGerritUrl() != null) { - return getGerritUrl() + "c/" + change.getProject().get() + "/+/" + change.getChangeId(); - } - return null; + return args.urlFormatter.getChangeViewUrl(change.getProject(), change.getId()).orElse(null); } public String getChangeMessageThreadId() { diff --git a/java/com/google/gerrit/server/mail/send/EmailArguments.java b/java/com/google/gerrit/server/mail/send/EmailArguments.java index f49951fd4b..42d1b32d89 100644 --- a/java/com/google/gerrit/server/mail/send/EmailArguments.java +++ b/java/com/google/gerrit/server/mail/send/EmailArguments.java @@ -14,7 +14,6 @@ package com.google.gerrit.server.mail.send; -import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.AnonymousUser; @@ -27,10 +26,10 @@ import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.GroupBackend; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AnonymousCowardName; -import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritInstanceName; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.mail.EmailSettings; import com.google.gerrit.server.notedb.ChangeNotes; @@ -67,7 +66,7 @@ public class EmailArguments { final AnonymousUser anonymousUser; final String anonymousCowardName; final PersonIdent gerritPersonIdent; - final Provider<String> urlProvider; + final UrlFormatter urlFormatter; final AllProjectsName allProjectsName; final List<String> sshAddresses; final SitePaths site; @@ -100,7 +99,7 @@ public class EmailArguments { AnonymousUser anonymousUser, @AnonymousCowardName String anonymousCowardName, GerritPersonIdentProvider gerritPersonIdentProvider, - @CanonicalWebUrl @Nullable Provider<String> urlProvider, + UrlFormatter urlFormatter, AllProjectsName allProjectsName, ChangeQueryBuilder queryBuilder, Provider<ReviewDb> db, @@ -129,7 +128,7 @@ public class EmailArguments { this.anonymousUser = anonymousUser; this.anonymousCowardName = anonymousCowardName; this.gerritPersonIdent = gerritPersonIdentProvider.get(); - this.urlProvider = urlProvider; + this.urlFormatter = urlFormatter; this.allProjectsName = allProjectsName; this.queryBuilder = queryBuilder; this.db = db; diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java index fe9b44626a..6182826c66 100644 --- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java +++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java @@ -299,7 +299,7 @@ public abstract class OutgoingEmail { } public String getGerritUrl() { - return args.urlProvider.get(); + return args.urlFormatter.getWebUrl().orElse(null); } /** Set a header in the outgoing message. */ diff --git a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java index c3c76de228..fc342db677 100644 --- a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java +++ b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java @@ -14,8 +14,6 @@ package com.google.gerrit.server.project; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.data.ContributorAgreement; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule.Action; @@ -29,7 +27,7 @@ import com.google.gerrit.reviewdb.client.BooleanProjectConfig; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.UrlFormatter; import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.IOException; @@ -40,7 +38,7 @@ import java.util.List; @Singleton public class ContributorAgreementsChecker { - private final String canonicalWebUrl; + private final UrlFormatter urlFormatter; private final ProjectCache projectCache; private final Metrics metrics; @@ -59,10 +57,8 @@ public class ContributorAgreementsChecker { @Inject ContributorAgreementsChecker( - @CanonicalWebUrl @Nullable String canonicalWebUrl, - ProjectCache projectCache, - Metrics metrics) { - this.canonicalWebUrl = canonicalWebUrl; + UrlFormatter urlFormatter, ProjectCache projectCache, Metrics metrics) { + this.urlFormatter = urlFormatter; this.projectCache = projectCache; this.metrics = metrics; } @@ -113,15 +109,8 @@ public class ContributorAgreementsChecker { .append(" (id=") .append(iUser.getAccountId()) .append(")"); - if (canonicalWebUrl != null) { - msg.append(":\n\n "); - msg.append(canonicalWebUrl); - msg.append("#"); - msg.append(PageLinks.SETTINGS_AGREEMENTS); - msg.append("\n"); - } else { - msg.append("."); - } + + msg.append(urlFormatter.getSettingsUrl("Agreements").orElse("")); throw new AuthException(msg.toString()); } } diff --git a/java/com/google/gerrit/server/project/CreateProjectArgs.java b/java/com/google/gerrit/server/project/CreateProjectArgs.java index e4623b227f..a68bd84860 100644 --- a/java/com/google/gerrit/server/project/CreateProjectArgs.java +++ b/java/com/google/gerrit/server/project/CreateProjectArgs.java @@ -35,6 +35,8 @@ public class CreateProjectArgs { public InheritableBoolean newChangeForAllNotInTarget; public InheritableBoolean changeIdRequired; public InheritableBoolean rejectEmptyCommit; + public InheritableBoolean enableSignedPush; + public InheritableBoolean requireSignedPush; public boolean createEmptyCommit; public String maxObjectSizeLimit; @@ -44,6 +46,8 @@ public class CreateProjectArgs { contentMerge = InheritableBoolean.INHERIT; changeIdRequired = InheritableBoolean.INHERIT; newChangeForAllNotInTarget = InheritableBoolean.INHERIT; + enableSignedPush = InheritableBoolean.INHERIT; + requireSignedPush = InheritableBoolean.INHERIT; submitType = SubmitType.MERGE_IF_NECESSARY; } diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index 566786984b..2ec2e7ccb3 100644 --- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java @@ -31,6 +31,7 @@ import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.errors.NotSignedInException; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.index.IndexConfig; import com.google.gerrit.index.Schema; import com.google.gerrit.index.SchemaUtil; @@ -427,7 +428,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> { } private void setupDynamicOperators() { - for (DynamicMap.Entry<ChangeOperatorFactory> e : args.opFactories) { + for (Extension<ChangeOperatorFactory> e : args.opFactories) { String name = e.getExportName() + "_" + e.getPluginName(); opFactories.put(name, e.getProvider().get()); } @@ -673,6 +674,21 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> { } @Operator + public Predicate<ChangeData> repo(String name) { + return project(name); + } + + @Operator + public Predicate<ChangeData> repos(String name) { + return projects(name); + } + + @Operator + public Predicate<ChangeData> parentrepo(String name) { + return parentproject(name); + } + + @Operator public Predicate<ChangeData> branch(String name) { if (name.startsWith("^")) { return ref("^" + RefNames.fullName(name.substring(1))); diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java index fccdabe984..2471689644 100644 --- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java +++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java @@ -18,6 +18,7 @@ import com.google.common.base.Strings; import com.google.gerrit.extensions.client.GeneralPreferencesInfo; import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; @@ -83,7 +84,7 @@ public class SetPreferences implements RestModifyView<AccountResource, GeneralPr return; } - for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + for (Extension<DownloadScheme> e : downloadSchemes) { if (e.getExportName().equals(downloadScheme) && e.getProvider().get().isEnabled()) { return; } diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java index 6b7a708c1f..ae6e5d1c6d 100644 --- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java +++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java @@ -24,6 +24,7 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.common.Nullable; import com.google.gerrit.common.data.LabelType; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.index.query.Predicate; import com.google.gerrit.index.query.QueryParseException; import com.google.gerrit.reviewdb.client.Account; @@ -130,7 +131,7 @@ public class ReviewerRecommender { new ArrayList<>(reviewerSuggestionPluginMap.plugins().size()); List<Double> weights = new ArrayList<>(reviewerSuggestionPluginMap.plugins().size()); - for (DynamicMap.Entry<ReviewerSuggestion> plugin : reviewerSuggestionPluginMap) { + for (Extension<ReviewerSuggestion> plugin : reviewerSuggestionPluginMap) { tasks.add( () -> plugin diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java index d6071d5e93..e1144d5bdd 100644 --- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java +++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java @@ -39,6 +39,7 @@ import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.webui.WebUiPlugin; import com.google.gerrit.server.EnableSignedPush; @@ -252,7 +253,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> { private DownloadInfo getDownloadInfo() { DownloadInfo info = new DownloadInfo(); info.schemes = new HashMap<>(); - for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + for (Extension<DownloadScheme> e : downloadSchemes) { DownloadScheme scheme = e.getProvider().get(); if (scheme.isEnabled() && scheme.getUrl("${project}") != null) { info.schemes.put(e.getExportName(), getDownloadSchemeInfo(scheme)); @@ -270,7 +271,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> { info.isAuthSupported = toBoolean(scheme.isAuthSupported()); info.commands = new HashMap<>(); - for (DynamicMap.Entry<DownloadCommand> e : downloadCommands) { + for (Extension<DownloadCommand> e : downloadCommands) { String commandName = e.getExportName(); DownloadCommand command = e.getProvider().get(); String c = command.getCommand(scheme, "${project}", "${ref}"); @@ -280,7 +281,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> { } info.cloneCommands = new HashMap<>(); - for (DynamicMap.Entry<CloneCommand> e : cloneCommands) { + for (Extension<CloneCommand> e : cloneCommands) { String commandName = e.getExportName(); CloneCommand command = e.getProvider().get(); String c = command.getCommand(scheme, "${project-path}/${project-base-name}"); diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java index 38664fbc61..f310ed7e71 100644 --- a/java/com/google/gerrit/server/restapi/config/ListCaches.java +++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java @@ -26,6 +26,7 @@ import com.google.common.cache.CacheStats; import com.google.common.collect.Streams; import com.google.gerrit.extensions.annotations.RequiresAnyCapability; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.BinaryResult; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.server.cache.PersistentCache; @@ -60,7 +61,7 @@ public class ListCaches implements RestReadView<ConfigResource> { public Map<String, CacheInfo> getCacheInfos() { Map<String, CacheInfo> cacheInfos = new TreeMap<>(); - for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) { + for (Extension<Cache<?, ?>> e : cacheMap) { cacheInfos.put( cacheNameOf(e.getPluginName(), e.getExportName()), new CacheInfo(e.getProvider().get())); } diff --git a/java/com/google/gerrit/server/restapi/config/PostCaches.java b/java/com/google/gerrit/server/restapi/config/PostCaches.java index 57ba097f9f..c633af0ce5 100644 --- a/java/com/google/gerrit/server/restapi/config/PostCaches.java +++ b/java/com/google/gerrit/server/restapi/config/PostCaches.java @@ -20,6 +20,7 @@ import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER; import com.google.common.cache.Cache; import com.google.gerrit.extensions.annotations.RequiresAnyCapability; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.registration.PluginName; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; @@ -96,7 +97,7 @@ public class PostCaches implements RestCollectionModifyView<ConfigResource, Cach } private void flushAll() throws AuthException, PermissionBackendException { - for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) { + for (Extension<Cache<?, ?>> e : cacheMap) { CacheResource cacheResource = new CacheResource(e.getPluginName(), e.getExportName(), e.getProvider()); if (FlushCache.WEB_SESSIONS.equals(cacheResource.getName())) { diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java index 60b5dee4ea..e1798962fb 100644 --- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java +++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java @@ -22,7 +22,7 @@ import com.google.gerrit.extensions.api.projects.ConfigInfo; import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType; import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.registration.DynamicMap.Entry; +import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.reviewdb.client.BooleanProjectConfig; @@ -122,7 +122,7 @@ public class ConfigInfoImpl extends ConfigInfo { PluginConfigFactory cfgFactory, AllProjectsName allProjects) { TreeMap<String, Map<String, ConfigParameterInfo>> pluginConfig = new TreeMap<>(); - for (Entry<ProjectConfigEntry> e : pluginConfigEntries) { + for (Extension<ProjectConfigEntry> e : pluginConfigEntries) { ProjectConfigEntry configEntry = e.getProvider().get(); PluginConfig cfg = cfgFactory.getFromProjectConfig(project, e.getPluginName()); String configuredValue = cfg.getString(e.getExportName()); @@ -165,7 +165,7 @@ public class ConfigInfoImpl extends ConfigInfo { } private String getInheritedValue( - ProjectState project, PluginConfigFactory cfgFactory, Entry<ProjectConfigEntry> e) { + ProjectState project, PluginConfigFactory cfgFactory, Extension<ProjectConfigEntry> e) { ProjectConfigEntry configEntry = e.getProvider().get(); ProjectState parent = Iterables.getFirst(project.parents(), null); String inheritedValue = configEntry.getDefaultValue(); diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java index 271848b9de..030402e18d 100644 --- a/java/com/google/gerrit/server/restapi/project/CreateProject.java +++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java @@ -203,6 +203,10 @@ public class CreateProject MoreObjects.firstNonNull(input.requireChangeId, InheritableBoolean.INHERIT); args.rejectEmptyCommit = MoreObjects.firstNonNull(input.rejectEmptyCommit, InheritableBoolean.INHERIT); + args.enableSignedPush = + MoreObjects.firstNonNull(input.enableSignedPush, InheritableBoolean.INHERIT); + args.requireSignedPush = + MoreObjects.firstNonNull(input.requireSignedPush, InheritableBoolean.INHERIT); try { args.maxObjectSizeLimit = ProjectConfig.validMaxObjectSizeLimit(input.maxObjectSizeLimit); } catch (ConfigInvalidException e) { @@ -297,6 +301,8 @@ public class CreateProject newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, args.changeIdRequired); newProject.setBooleanConfig(BooleanProjectConfig.REJECT_EMPTY_COMMIT, args.rejectEmptyCommit); newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit); + newProject.setBooleanConfig(BooleanProjectConfig.ENABLE_SIGNED_PUSH, args.enableSignedPush); + newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_SIGNED_PUSH, args.requireSignedPush); if (args.newParent != null) { newProject.setParentName(args.newParent); } diff --git a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java index 3b7f3e3d74..e0b2d93f7e 100644 --- a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java +++ b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java @@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.common.base.Preconditions; import com.google.gerrit.common.data.GarbageCollectionResult; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; @@ -24,7 +25,7 @@ import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.git.GarbageCollection; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.LocalDiskRepositoryManager; @@ -33,13 +34,13 @@ import com.google.gerrit.server.ioutil.HexFormat; import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.restapi.project.GarbageCollect.Input; import com.google.inject.Inject; -import com.google.inject.Provider; import com.google.inject.Singleton; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Collections; +import java.util.Optional; @RequiresCapability(GlobalCapability.RUN_GC) @Singleton @@ -54,16 +55,16 @@ public class GarbageCollect private final boolean canGC; private final GarbageCollection.Factory garbageCollectionFactory; private final WorkQueue workQueue; - private final Provider<String> canonicalUrl; + private final UrlFormatter urlFormatter; @Inject GarbageCollect( GitRepositoryManager repoManager, GarbageCollection.Factory garbageCollectionFactory, WorkQueue workQueue, - @CanonicalWebUrl Provider<String> canonicalUrl) { + UrlFormatter urlFormatter) { this.workQueue = workQueue; - this.canonicalUrl = canonicalUrl; + this.urlFormatter = urlFormatter; this.canGC = repoManager instanceof LocalDiskRepositoryManager; this.garbageCollectionFactory = garbageCollectionFactory; } @@ -97,10 +98,11 @@ public class GarbageCollect @SuppressWarnings("unchecked") WorkQueue.Task<Void> task = (WorkQueue.Task<Void>) workQueue.getDefaultQueue().submit(job); - String location = - canonicalUrl.get() + "a/config/server/tasks/" + HexFormat.fromInt(task.getTaskId()); - - return Response.accepted(location); + Optional<String> url = + urlFormatter.getRestUrl("a/config/server/tasks/" + HexFormat.fromInt(task.getTaskId())); + // We're in a HTTP handler, so must be present. + Preconditions.checkState(url.isPresent()); + return Response.accepted(url.get()); } @SuppressWarnings("resource") diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java index 8e1ba6bf34..6abf102d65 100644 --- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java +++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java @@ -15,6 +15,7 @@ package com.google.gerrit.server.restapi.project; import com.google.common.collect.ListMultimap; +import com.google.common.flogger.FluentLogger; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AuthException; @@ -46,6 +47,8 @@ import org.eclipse.jgit.lib.Constants; @Singleton public class ProjectsCollection implements RestCollection<TopLevelResource, ProjectResource>, NeedsParams { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private final DynamicMap<RestView<ProjectResource>> views; private final Provider<ListProjects> list; private final Provider<QueryProjects> queryProjects; @@ -143,6 +146,8 @@ public class ProjectsCollection return null; } + logger.atFine().log("Project %s has state %s", nameKey, state.getProject().getState()); + if (checkAccess) { // Hidden projects(permitsRead = false) should only be accessible by the project owners. // WRITE_CONFIG is checked here because it's only allowed to project owners (ACCESS may also diff --git a/java/com/google/gerrit/server/submit/MergeSorter.java b/java/com/google/gerrit/server/submit/MergeSorter.java index 8b9b1cc89a..14a2913865 100644 --- a/java/com/google/gerrit/server/submit/MergeSorter.java +++ b/java/com/google/gerrit/server/submit/MergeSorter.java @@ -14,6 +14,7 @@ package com.google.gerrit.server.submit; +import com.google.common.flogger.FluentLogger; import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; import java.io.IOException; @@ -26,6 +27,8 @@ import org.eclipse.jgit.revwalk.RevCommitList; import org.eclipse.jgit.revwalk.RevFlag; public class MergeSorter { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private final CodeReviewRevWalk rw; private final RevFlag canMergeFlag; private final Set<RevCommit> accepted; @@ -62,6 +65,8 @@ public class MergeSorter { // We cannot merge n as it would bring something we // aren't permitted to merge at this time. Drop n. // + logger.atFine().log( + "commit %s depends on commit %s which cannot be merged", n.name(), c.name()); n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); break; } diff --git a/java/com/google/gerrit/server/submit/RebaseSorter.java b/java/com/google/gerrit/server/submit/RebaseSorter.java index 7ec8b0e139..a2d07fa274 100644 --- a/java/com/google/gerrit/server/submit/RebaseSorter.java +++ b/java/com/google/gerrit/server/submit/RebaseSorter.java @@ -81,6 +81,8 @@ public class RebaseSorter { // We cannot merge n as it would bring something we // aren't permitted to merge at this time. Drop n. // + logger.atFine().log( + "commit %s depends on commit %s which cannot be merged", n.name(), c.name()); n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); } // Stop RevWalk because c is either a merged commit or a missing diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java index 395a797895..72878eea67 100644 --- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java +++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java @@ -2585,7 +2585,7 @@ public class ChangeIT extends AbstractDaemonTest { List<String> expectedFooters = Arrays.asList( "Change-Id: " + r2.getChangeId(), - "Reviewed-on: " + canonicalWebUrl.get() + r2.getChange().getId(), + "Reviewed-on: " + canonicalWebUrl.get() + "c/" + r2.getChange().getId(), "Reviewed-by: Administrator <admin@example.com>", "Custom2: Administrator <admin@example.com>", "Tested-by: Administrator <admin@example.com>"); @@ -2627,7 +2627,7 @@ public class ChangeIT extends AbstractDaemonTest { List<String> expectedFooters = Arrays.asList( "Change-Id: " + change.getChangeId(), - "Reviewed-on: " + canonicalWebUrl.get() + change.getChange().getId(), + "Reviewed-on: " + canonicalWebUrl.get() + "c/" + change.getChange().getId(), "Custom: refs/heads/master"); assertThat(footers).containsExactlyElementsIn(expectedFooters); } diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java index d581c1e7a1..8891deef08 100644 --- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java +++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java @@ -231,6 +231,15 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest { } @Test + @GerritConfig(name = "receive.enableSignedPush", value = "true") + @TestProjectInput( + enableSignedPush = InheritableBoolean.TRUE, + requireSignedPush = InheritableBoolean.TRUE) + public void nonSignedPushRejectedWhenSignPushRequired() throws Exception { + pushTo("refs/for/master").assertErrorStatus("push cert error"); + } + + @Test public void pushInitialCommitForRefsMetaConfigBranch() throws Exception { // delete refs/meta/config try (Repository repo = repoManager.openRepository(project); diff --git a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java index c86160f9bb..0542c35d1c 100644 --- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java +++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java @@ -123,8 +123,8 @@ public class DynamicSetTest { ds.add("bar", 2); ds.add("bar", 3); - Iterator<DynamicSet.Entry<Integer>> entryIterator = ds.entries().iterator(); - DynamicSet.Entry<Integer> next = entryIterator.next(); + Iterator<Extension<Integer>> entryIterator = ds.entries().iterator(); + Extension<Integer> next = entryIterator.next(); assertThat(next.getPluginName()).isEqualTo("foo"); assertThat(next.getProvider().get()).isEqualTo(1); diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java index f8ab417435..685f42de42 100644 --- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java +++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java @@ -232,7 +232,7 @@ public class GerritPublicKeyCheckerTest { assertProblems( checker.check(key.getPublicKey()), Status.BAD, - "No identities found for user; check http://test/#/settings/web-identities"); + "No identities found for user; check http://test/settings#Identities"); checker = checkerFactory.create().setStore(store).disableTrust(); assertProblems( diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java index b93e9c13eb..0c4a47f0dd 100644 --- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java +++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java @@ -42,9 +42,11 @@ import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AnonymousCowardNameProvider; import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.DefaultBrowseUrls; import com.google.gerrit.server.config.DisableReverseDnsLookup; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerId; +import com.google.gerrit.server.config.UrlFormatter; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.GitModule; import com.google.gerrit.server.git.GitRepositoryManager; @@ -162,6 +164,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests { migration.setFrom(NotesMigrationState.FINAL); bind(MutableNotesMigration.class).toInstance(migration); bind(NotesMigration.class).to(MutableNotesMigration.class); + bind(UrlFormatter.class).to(DefaultBrowseUrls.class); // Tests don't support ReviewDb at all, but bindings are required via NoteDbModule. bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}) diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java index f3275d15f9..b9973e906c 100644 --- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java +++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java @@ -841,6 +841,43 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests { } @Test + public void byRepo() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("repo:foo"); + assertQuery("repo:repo"); + assertQuery("repo:repo1", change1); + assertQuery("repo:repo2", change2); + } + + @Test + public void byParentRepo() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2", "repo1"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("parentrepo:repo1", change2, change1); + assertQuery("parentrepo:repo2", change2); + } + + @Test + public void byRepoPrefix() throws Exception { + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); + Change change1 = insert(repo1, newChange(repo1)); + Change change2 = insert(repo2, newChange(repo2)); + + assertQuery("repos:foo"); + assertQuery("repos:repo1", change1); + assertQuery("repos:repo2", change2); + assertQuery("repos:repo", change2, change1); + } + + @Test public void byBranchAndRef() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = insert(repo, newChangeForBranch(repo, "master")); diff --git a/lib/polymer_externs/BUILD b/lib/polymer_externs/BUILD index ae8f9c0ffe..2f1bdbded2 100644 --- a/lib/polymer_externs/BUILD +++ b/lib/polymer_externs/BUILD @@ -18,16 +18,9 @@ package( load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library") -genrule( - name = "polymer_closure_renamed", - srcs = ["@polymer_closure//file"], - outs = ["polymer_closure_renamed.js"], - cmd = "cp $< $@", -) - closure_js_library( name = "polymer_closure", - srcs = [":polymer_closure_renamed"], + srcs = ["@polymer_closure//file"], data = ["//lib:LICENSE-Apache2.0"], no_closure_library = True, ) diff --git a/plugins/hooks b/plugins/hooks -Subproject 7185e5ce46646e952071befd9cf8f4267560b51 +Subproject de469e8e2598779773652abb43a0356650e257b diff --git a/plugins/reviewnotes b/plugins/reviewnotes -Subproject 54525ffaed5e8925d97657a622532a00a000634 +Subproject 543ae1f0a24dda61d4f36b173b9bddfd52ead3b |