Merge branch 'stable-2.16' into stable-3.0 * stable-2.16: Remove plugin-manager from the list of core plugins Remove BUCK build Remove check with hardcoded GERRIT_NEXT_VERSION Fix core plugin name extractions without MANIFEST.MF Backport tests from master to stable-2.16 Change-Id: I2c2aaab77090b7b7198746664542122b03fbde0e
diff --git a/BUILD b/BUILD index 064033f..b047e31 100644 --- a/BUILD +++ b/BUILD
@@ -1,15 +1,9 @@ load("//tools/bzl:junit.bzl", "junit_tests") -load( - "//tools/bzl:plugin.bzl", - "PLUGIN_DEPS", - "PLUGIN_TEST_DEPS", - "gerrit_plugin", -) +load("//tools/bzl:plugin.bzl", "PLUGIN_TEST_DEPS", "gerrit_plugin") gerrit_plugin( name = "plugin-manager", srcs = glob(["src/main/java/**/*.java"]), - resources = glob(["src/main/resources/**/*"]), manifest_entries = [ "Gerrit-PluginName: plugin-manager", "Gerrit-HttpModule: com.googlesource.gerrit.plugins.manager.WebModule", @@ -18,6 +12,7 @@ "Implementation-Title: Plugin manager", "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/plugin-manager", ], + resources = glob(["src/main/resources/**/*"]), ) junit_tests(
diff --git a/README.md b/README.md index 6405b5c..4bbd207 100644 --- a/README.md +++ b/README.md
@@ -1,2 +1,4 @@ # plugin-manager -Gerrit web-based plugin manager +Gerrit web-based plugin manager. + +To enable this plugin, please look at the [configuration guide](src/main/resources/Documentation/config.md) \ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/FirstWebLoginListener.java b/src/main/java/com/googlesource/gerrit/plugins/manager/FirstWebLoginListener.java index 722d406..f0c934c 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/FirstWebLoginListener.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/FirstWebLoginListener.java
@@ -14,6 +14,8 @@ package com.googlesource.gerrit.plugins.manager; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.gerrit.extensions.annotations.PluginData; import com.google.gerrit.httpd.WebLoginListener; import com.google.gerrit.server.IdentifiedUser; @@ -53,7 +55,8 @@ if (!firstLoginFile.toFile().exists()) { response.sendRedirect(pluginUrlPath + "static/intro.html"); - Files.write(firstLoginFile, new Date().toString().getBytes(), StandardOpenOption.CREATE); + Files.write( + firstLoginFile, new Date().toString().getBytes(UTF_8), StandardOpenOption.CREATE); } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java index 76e7669..ceeda09 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java
@@ -17,12 +17,10 @@ import com.google.common.collect.Maps; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.TopLevelResource; -import com.google.gerrit.server.OutputFormat; -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; import com.google.inject.Inject; import com.googlesource.gerrit.plugins.manager.repository.PluginInfo; import java.util.ArrayList; @@ -43,11 +41,8 @@ } @Override - public Object apply(TopLevelResource resource) throws RestApiException { - return display(); - } - - public JsonElement display() throws RestApiException { + public Response<Map<String, PluginInfo>> apply(TopLevelResource resource) + throws RestApiException { Map<String, PluginInfo> output = Maps.newTreeMap(); List<PluginInfo> plugins; try { @@ -68,8 +63,6 @@ output.put(p.name, p); } - return OutputFormat.JSON - .newGson() - .toJsonTree(output, new TypeToken<Map<String, Object>>() {}.getType()); + return Response.ok(output); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPath.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPath.java index 3f17da8..2005bfa 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPath.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPath.java
@@ -16,12 +16,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - /** * Annotation applied to a String containing the plugin canonical web URL path. *
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPathProvider.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPathProvider.java index 6581f87..d5ece22 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPathProvider.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginCanonicalWebUrlPathProvider.java
@@ -14,12 +14,11 @@ package com.googlesource.gerrit.plugins.manager; -import java.net.URI; - import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import java.net.URI; @Singleton public class PluginCanonicalWebUrlPathProvider implements Provider<String> {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java b/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java index de1eb10..616b9a4 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java
@@ -14,6 +14,8 @@ package com.googlesource.gerrit.plugins.manager; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.httpd.WebSession; import com.google.gerrit.server.AccessPath; @@ -85,8 +87,8 @@ return new TokenReplaceOutputStream( (HttpServletResponse) getResponse(), origContentLength, - "@X-Gerrit-Auth".getBytes(), - gerritAuth.getBytes()); + "@X-Gerrit-Auth".getBytes(UTF_8), + gerritAuth.getBytes(UTF_8)); } };
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java index f8516e2..ae339de 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java
@@ -14,7 +14,9 @@ package com.googlesource.gerrit.plugins.manager.gson; -import com.google.gerrit.server.OutputFormat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.gerrit.json.OutputFormat; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.IOException; @@ -52,6 +54,6 @@ } catch (MalformedURLException e) { throw new IllegalArgumentException("Internal error: Gerrit CI URL seems to be malformed", e); } - return new InputStreamReader(ciUrl.openStream()); + return new InputStreamReader(ciUrl.openStream(), UTF_8); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsDescriptions.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsDescriptions.java index 7e8c422..efb0e10 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsDescriptions.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsDescriptions.java
@@ -14,11 +14,10 @@ package com.googlesource.gerrit.plugins.manager.repository; -import java.util.HashMap; -import java.util.Optional; - import com.google.inject.Inject; import com.google.inject.Singleton; +import java.util.HashMap; +import java.util.Optional; @Singleton public class CorePluginsDescriptions { @@ -28,13 +27,22 @@ public CorePluginsDescriptions() { pluginsDescriptions = new HashMap<>(); pluginsDescriptions.put("codemirror-editor", "CodeMirror plugin for polygerrit"); - pluginsDescriptions.put("commit-message-length-validator", + pluginsDescriptions.put( + "commit-message-length-validator", "Plugin to validate that commit messages conform to length limits"); + pluginsDescriptions.put("delete-project", "Provides the ability to delete a project"); pluginsDescriptions.put("download-commands", "Adds the standard download schemes and commands"); + pluginsDescriptions.put("gitiles", "Plugin running Gitiles alongside a Gerrit server"); pluginsDescriptions.put("hooks", "Old-style fork+exec hooks"); + pluginsDescriptions.put( + "plugin-manager", "Adds support for discovering and installing other plugins"); pluginsDescriptions.put("replication", "Copies to other servers using the Git protocol"); - pluginsDescriptions.put("reviewnotes", "Annotates merged commits using notes on refs/notes/review"); - pluginsDescriptions.put("singleusergroup", "GroupBackend enabling users to be directly added to access rules"); + pluginsDescriptions.put( + "reviewnotes", "Annotates merged commits using notes on refs/notes/review"); + pluginsDescriptions.put( + "singleusergroup", "GroupBackend enabling users to be directly added to access rules"); + pluginsDescriptions.put( + "webhooks", "Allows to propagate Gerrit events to remote http endpoints"); } public Optional<String> get(String plugin) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java index 6e18bee..96947aa 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java
@@ -14,10 +14,12 @@ package com.googlesource.gerrit.plugins.manager.repository; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.io.Files; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.Comparator.comparing; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Version; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; @@ -26,9 +28,8 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -50,106 +51,82 @@ this.pluginsDescriptions = pd; } - static class SelectPluginsFromJar implements Predicate<JarEntry> { - @Override - public boolean apply(JarEntry entry) { - String entryName = entry.getName(); - return (entryName.startsWith("WEB-INF/plugins") && entryName.endsWith(".jar")); - } - } - - class ExtractPluginInfoFromJarEntry implements Function<JarEntry, PluginInfo> { - private String gerritWarFilename; - - public ExtractPluginInfoFromJarEntry(String gerritWarFilename) { - this.gerritWarFilename = gerritWarFilename; - } - - @Override - public PluginInfo apply(JarEntry entry) { - try { - Path entryName = Paths.get(entry.getName()); - URI pluginUrl = new URI("jar:file:" + gerritWarFilename + "!/" + entry.getName()); - try (JarInputStream pluginJar = new JarInputStream(pluginUrl.toURL().openStream())) { - Manifest manifestJarEntry = getManifestEntry(pluginJar); - if (manifestJarEntry != null) { - Attributes pluginAttributes = manifestJarEntry.getMainAttributes(); - String pluginName = pluginAttributes.getValue("Gerrit-PluginName"); - return new PluginInfo( - pluginName, - pluginsDescriptions.get(pluginName).orElse(""), - pluginAttributes.getValue("Implementation-Version"), - "", - pluginUrl.toString()); - } - return new PluginInfo( - dropFileExtension(entryName.getFileName().toString()), - "", - "", - "", - pluginUrl.toString()); - } catch (IOException e) { - log.error("Unable to open plugin " + pluginUrl, e); - return null; - } - } catch (URISyntaxException e) { - log.error("Invalid plugin filename", e); + @Nullable + private PluginInfo extractPluginInfoFromJarEntry(JarEntry entry) { + try { + Path entryName = Paths.get(entry.getName()); + URI pluginUrl = + new URI("jar:file:" + requireNonNull(site.gerrit_war) + "!/" + entry.getName()); + try (JarInputStream pluginJar = new JarInputStream(pluginUrl.toURL().openStream())) { + return getManifestEntry(pluginJar) + .map( + m -> { + Attributes pluginAttributes = m.getMainAttributes(); + String pluginName = pluginAttributes.getValue("Gerrit-PluginName"); + return new PluginInfo( + pluginName, + pluginsDescriptions.get(pluginName).orElse(""), + pluginAttributes.getValue("Implementation-Version"), + "", + pluginUrl.toString()); + }) + .orElse( + new PluginInfo( + dropSuffix(entryName.getFileName().toString(), ".jar"), + "", + "", + "", + pluginUrl.toString())); + } catch (IOException e) { + log.error("Unable to open plugin " + pluginUrl, e); return null; } - } - - private String dropFileExtension(String fileName) { - String extension = Files.getFileExtension(fileName); - return fileName.substring(0, fileName.length() - extension.length() - 1); - } - - private Manifest getManifestEntry(JarInputStream pluginJar) throws IOException { - for (JarEntry entry = pluginJar.getNextJarEntry(); - entry != null; - entry = pluginJar.getNextJarEntry()) { - if (entry.getName().equals("META-INF/MANIFEST.MF")) { - return new Manifest(pluginJar); - } - } + } catch (URISyntaxException e) { + log.error("Invalid plugin filename", e); return null; } } + private String dropSuffix(String string, String suffix) { + return string.endsWith(suffix) + ? string.substring(0, string.length() - suffix.length()) + : string; + } + + @Nullable + private static Optional<Manifest> getManifestEntry(JarInputStream pluginJar) throws IOException { + for (JarEntry entry = pluginJar.getNextJarEntry(); + entry != null; + entry = pluginJar.getNextJarEntry()) { + if (entry.getName().equals(JarFile.MANIFEST_NAME)) { + return Optional.of(new Manifest(pluginJar)); + } + } + return Optional.empty(); + } + @Override - public Collection<PluginInfo> list(String gerritVersion) throws IOException { + public ImmutableList<PluginInfo> list(String gerritVersion) throws IOException { if (!gerritVersion.equals(GERRIT_VERSION)) { log.warn( "No core plugins available for version {} which is different than " + "the current running Gerrit", gerritVersion); - return Collections.emptyList(); + return ImmutableList.of(); } - final Path gerritWarPath = site.gerrit_war; - if (gerritWarPath == null) { + if (site.gerrit_war == null) { log.warn("Core plugins not available on non-war Gerrit distributions"); - return Collections.emptyList(); + return ImmutableList.of(); } - try (JarFile gerritWar = new JarFile(gerritWarPath.toFile())) { - - return FluentIterable.from(Collections.list(gerritWar.entries())) - .filter(new SelectPluginsFromJar()) - .transform(new ExtractPluginInfoFromJarEntry(gerritWarPath.toString())) - .filter( - new Predicate<PluginInfo>() { - @Override - public boolean apply(PluginInfo pluginInfo) { - return pluginInfo != null; - } - }) - .toSortedList( - new Comparator<PluginInfo>() { - @Override - public int compare(PluginInfo a, PluginInfo b) { - return a.name.compareTo(b.name); - } - }); + try (JarFile gerritWar = new JarFile(site.gerrit_war.toFile())) { + return gerritWar.stream() + .filter(e -> e.getName().startsWith("WEB-INF/plugins") && e.getName().endsWith(".jar")) + .map(this::extractPluginInfoFromJarEntry) + .filter(Objects::nonNull) + .sorted(comparing(p -> p.name)) + .collect(toImmutableList()); } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java index 31f77e7..66c4c8d 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java
@@ -14,6 +14,8 @@ package com.googlesource.gerrit.plugins.manager.repository; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.inject.Inject; @@ -168,7 +170,8 @@ "%s/artifact/%s", buildExecution.getString("url"), verArtifactJson.get().getString("relativePath")); try (BufferedReader reader = - new BufferedReader(new InputStreamReader(new URL(versionUrl).openStream()), 4096)) { + new BufferedReader( + new InputStreamReader(new URL(versionUrl).openStream(), UTF_8), 4096)) { String line; while ((line = reader.readLine()) != null) { if (artifactBody.length() > 0) {
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md new file mode 100644 index 0000000..bab92d3 --- /dev/null +++ b/src/main/resources/Documentation/about.md
@@ -0,0 +1,12 @@ +This plugin adds support for discovering and installing other plugins +to Gerrit. + +The list of plugins are taken from the following sources: + +- Internal core plugins contained in the gerrit.war +- Plugins built on the [Gerrit CI][1] or another configurable location + for the stable branch that Gerrit is built + +**NOTE**: Management of plugins is restricted to Gerrit Administrators. + +[1]: https://gerrit-ci.gerritforge.com \ No newline at end of file
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md new file mode 100644 index 0000000..ed9f9f0 --- /dev/null +++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,40 @@ +How to enable +------------- + +The plugin-manager requires the ability to administer the plugins in Gerrit, +using the [Gerrit's `plugins.allowRemoteAdmin = true`][1] setting. + +Configuration +------------- + +The other plugin-specific settings are defined in the `[plugin-manager]` section +in the gerrit.config. + +jenkinsUrl +: URL of the Jenkins CI responsible for building and validating the plugins for + the current stable branch of Gerrit. + Default value: https://gerrit-ci.gerritforge.com + + +Plugin discovery +---------------- + +The compatible plugins are retrieved from a site of build artifacts that are +following the view setup of the gerrit-ci-scripts project. There is one view +per Gerrit stable branch (e.g. `Plugins-stable-3.0` contains all the artifacts +of the plugins built against the Gerrit stable-3.0 branch). + +Only the plugins with a job in the corresponding view and having at least one +successful build will be shown in the list and be discoverable. + +It is possible to control the list of plugins discoverable by editing the +corresponding view. + +*DISCLAIMER*: The plugin-manager aims at allowing the discovery and easy +download and setup of plugins into Gerrit. It is the plugin's maintainer +responsibility to maintain the build an end-to-end test of the plugin itself. +One plugin that is building and passes the tests on the CI may well not work +on Gerrit: testing and validation are always recommended. + +[1]: https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#plugins.allowRemoteAdmin +[2]: https://gerrit.googlesource.com/gerrit-ci-scripts \ No newline at end of file
diff --git a/src/test/java/com/google/gerrit/server/restapi/config/PluginManagerTopMenuIT.java b/src/test/java/com/google/gerrit/server/restapi/config/PluginManagerTopMenuIT.java index 2723c0d..7730ef8 100644 --- a/src/test/java/com/google/gerrit/server/restapi/config/PluginManagerTopMenuIT.java +++ b/src/test/java/com/google/gerrit/server/restapi/config/PluginManagerTopMenuIT.java
@@ -20,6 +20,7 @@ import com.google.gerrit.acceptance.LightweightPluginDaemonTest; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.TestPlugin; +import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.extensions.client.MenuItem; import com.google.gerrit.extensions.webui.TopMenu.MenuEntry; import com.google.gerrit.server.config.ConfigResource; @@ -37,6 +38,8 @@ @Inject ListTopMenus topMenus; + @Inject private RequestScopeOperations requestScopeOperations; + @Test @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true") public void showTopMenuForGerritAdministratorsWhenAllowRemoteAdmin() throws Exception { @@ -56,7 +59,7 @@ @Test @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true") public void hideTopMenuForRegularUsersEvenWhenAllowRemoteAdmin() throws Exception { - atrScope.set(atrScope.newContext(null, null, identifiedUserFactory.create(user.getId()))); + requestScopeOperations.setApiUser(user.id()); assertThat(pluginTopMenuEntries()).isEmpty(); } @@ -66,7 +69,7 @@ } private List<MenuEntry> pluginTopMenuEntries() throws Exception { - List<MenuEntry> topMenuItems = topMenus.apply(new ConfigResource()); + List<MenuEntry> topMenuItems = topMenus.apply(new ConfigResource()).value(); return topMenuItems; } }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranchTest.java b/src/test/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranchTest.java index 2c91b6a..be2d9e4 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranchTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranchTest.java
@@ -34,12 +34,18 @@ public void getBranchReturnsCorrectBranchForThreeDigitsVersions() throws Exception { // 2.x.y version assertBranch("2.16.10", "stable-2.16"); + + // 3.0.0 version + assertBranch("3.0.0", "stable-3.0"); } @Test public void getBranchReturnsCorrectBranchForReleaseCandidates() throws Exception { // 2.x-rcx version assertBranch("2.16-rc1", "stable-2.16"); + + // 3.0.0-rcx version + assertBranch("3.0.0-rc3", "stable-3.0"); } @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java b/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java index 7d43f0a..d01d287 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java
@@ -34,11 +34,15 @@ ImmutableList.of( "codemirror-editor", "commit-message-length-validator", + "delete-project", "download-commands", + "gitiles", "hooks", + "plugin-manager", "replication", "reviewnotes", - "singleusergroup"); + "singleusergroup", + "webhooks"); @Test public void corePluginsRepositoryShouldReturnCorePluginsFromReleaseWar() throws IOException {