Adds list of extra plugins available from CI
Display the new plugins available for installation under the
list of the ones currently installed.
Plugins repository is injectable and configurable; at the moment only
the Gerrit CI (gerrit-ci.gerritforge.com) is implemented.
Jenkins CI URL is configured under gerri.config:
[plugin "plugin-manager"]
jenkinsUrl = https://myjenkins.myserver.com
Change-Id: I7fc5140363700c9edc5c28da460ed8606ed73d78
diff --git a/BUCK b/BUCK
index de8caf8..8499ba4 100644
--- a/BUCK
+++ b/BUCK
@@ -1,3 +1,5 @@
+include_defs('//bucklets/gerrit_plugin.bucklet')
+
gerrit_plugin(
name = 'plugin-manager',
srcs = glob(['src/main/java/**/*.java']),
@@ -9,4 +11,9 @@
'Implementation-Title: Plugin manager',
'Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/plugin-manager',
],
+ provided_deps = [
+ '//lib:gson',
+ '//lib/log:log4j'
+ ],
)
+
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java b/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java
new file mode 100644
index 0000000..6c0629c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager;
+
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AcceptsCreate;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestCollection;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.server.plugins.PluginResource;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+@Singleton
+public class AvailablePluginsCollection implements
+ RestCollection<TopLevelResource, PluginResource>,
+ AcceptsCreate<TopLevelResource> {
+
+ private final DynamicMap<RestView<PluginResource>> views;
+ private final Provider<ListAvailablePlugins> list;
+
+ @Inject
+ AvailablePluginsCollection(DynamicMap<RestView<PluginResource>> views,
+ Provider<ListAvailablePlugins> list) {
+ this.views = views;
+ this.list = list;
+ }
+
+ @Override
+ public RestView<TopLevelResource> list() throws ResourceNotFoundException {
+ return list.get();
+ }
+
+ @Override
+ public PluginResource parse(TopLevelResource parent, IdString id)
+ throws ResourceNotFoundException {
+ throw new ResourceNotFoundException(id);
+ }
+
+ @Override
+ public DynamicMap<RestView<PluginResource>> views() {
+ return views;
+ }
+
+ @Override
+ public <I> RestModifyView<TopLevelResource, I> create(
+ TopLevelResource parent, IdString id) throws RestApiException {
+ throw new IllegalArgumentException("Operation not supported");
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java b/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java
new file mode 100644
index 0000000..75256b2
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager;
+
+public class GerritVersionBranch {
+ private static final String GERRIT_NEXT_VERSION = "2.13";
+
+ public static String getBranch(String gerritVersion) {
+ if (gerritVersion == null || gerritVersion.trim().isEmpty()
+ || !Character.isDigit(gerritVersion.trim().charAt(0))
+ || gerritVersion.startsWith(GERRIT_NEXT_VERSION)) {
+ return "master";
+ } else {
+ String[] versionNumbers = gerritVersion.split("\\.");
+
+ if (versionNumbers.length > 2) {
+ String fixVersionNumber = versionNumbers[2];
+ if (fixVersionNumber.contains("-")) {
+ String nextVersion =
+ String.format("%s.%d", versionNumbers[0],
+ Integer.parseInt(versionNumbers[1]) + 1);
+ if (nextVersion.equals(GERRIT_NEXT_VERSION)) {
+ return "master";
+ }
+ }
+ }
+ return "stable-" + versionNumbers[0] + "." + versionNumbers[1];
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java
new file mode 100644
index 0000000..d45c16f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager;
+
+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.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.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/** List plugins available for installation. */
+@RequiresCapability(GlobalCapability.VIEW_PLUGINS)
+public class ListAvailablePlugins implements RestReadView<TopLevelResource> {
+ private final PluginsCentralLoader loader;
+
+ @Inject
+ public ListAvailablePlugins(PluginsCentralLoader loader) {
+ this.loader = loader;
+ }
+
+ @Override
+ public Object apply(TopLevelResource resource) throws RestApiException {
+ return display();
+ }
+
+ public JsonElement display() throws RestApiException {
+ Map<String, PluginInfo> output = Maps.newTreeMap();
+ List<PluginInfo> plugins;
+ try {
+ plugins = loader.availablePlugins();
+ } catch (IOException e) {
+ throw new RestApiException(
+ "Unable to load the list of available plugins", e);
+ }
+ Collections.sort(plugins, new Comparator<PluginInfo>() {
+ @Override
+ public int compare(PluginInfo a, PluginInfo b) {
+ return a.name.compareTo(b.name);
+ }
+ });
+
+ for (PluginInfo p : plugins) {
+ output.put(p.name, p);
+ }
+
+ return OutputFormat.JSON.newGson().toJsonTree(output,
+ new TypeToken<Map<String, Object>>() {}.getType());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java
new file mode 100644
index 0000000..bb94616
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager;
+
+import com.google.gerrit.httpd.restapi.RestApiServlet;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.util.Providers;
+
+@Singleton
+public class PluginManagerRestApiServlet extends RestApiServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Inject
+ PluginManagerRestApiServlet(
+ Globals globals,
+ AvailablePluginsCollection members) {
+ super(globals, Providers.of(members));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java
new file mode 100644
index 0000000..33943af
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager;
+
+import com.google.gerrit.common.Version;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import com.googlesource.gerrit.plugins.manager.repository.PluginInfo;
+import com.googlesource.gerrit.plugins.manager.repository.PluginsRepository;
+
+import java.io.IOException;
+import java.util.List;
+
+@Singleton
+public class PluginsCentralLoader {
+
+ private final PluginsRepository repository;
+
+ @Inject
+ public PluginsCentralLoader(PluginsRepository repository) {
+ this.repository = repository;
+ }
+
+ public List<PluginInfo> availablePlugins() throws IOException {
+ return repository.list(Version.getVersion());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java b/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java
index 9810318..2c5976b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java
@@ -30,9 +30,9 @@
private final ByteArrayOutputStream outBuff;
- private HttpServletResponse resp;
+ private final HttpServletResponse resp;
- private int outLen;
+ private final int outLen;
public TokenReplaceOutputStream(HttpServletResponse resp, int contentLength,
byte[] token, byte[] replace) {
@@ -78,7 +78,6 @@
ServletOutputStream out = resp.getOutputStream();
out.write(convertedData.toByteArray());
out.flush();
-
}
@Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java b/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java
index 9a32518..c9994f1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java
@@ -13,18 +13,20 @@
// limitations under the License.
package com.googlesource.gerrit.plugins.manager;
-import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.inject.servlet.ServletModule;
+import com.googlesource.gerrit.plugins.manager.repository.JenkinsCiPluginsRepository;
+import com.googlesource.gerrit.plugins.manager.repository.PluginsRepository;
+
public class WebModule extends ServletModule {
@Override
protected void configureServlets() {
- install(new RestApiModule() {
- @Override
- protected void configure() {
- filterRegex(".*\\.js").through(XAuthFilter.class);
- }
- });
+ bind(AvailablePluginsCollection.class);
+ bind(PluginsRepository.class).to(JenkinsCiPluginsRepository.class);
+
+ serve("/available*").with(PluginManagerRestApiServlet.class);
+
+ filterRegex(".*\\.js").through(XAuthFilter.class);
}
}
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 68874be..33f05b3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java
@@ -59,14 +59,14 @@
final String gerritAuth = webSession.get().getXGerritAuth();
if (gerritAuth != null) {
- log.info("Injecting X-Gerrit-Auth for {}", httpReq.getRequestURI());
+ log.debug("Injecting X-Gerrit-Auth for {}", httpReq.getRequestURI());
httpResp = new HttpServletResponseWrapper(httpResp) {
private int origContentLength;
@Override
public void setHeader(String name, String value) {
- log.info("{}: {}", name, value);
+ log.debug("{}: {}", name, value);
if (name.equalsIgnoreCase("Content-Length")) {
origContentLength = Integer.parseInt(value);
} else {
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
new file mode 100644
index 0000000..400505d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.gson;
+
+import com.google.gerrit.server.OutputFormat;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class SmartGson {
+
+ private final Gson gson;
+
+ public SmartGson() {
+ this.gson = OutputFormat.JSON.newGson();
+ }
+
+ public SmartJson get(String url) throws IOException {
+ return SmartJson.of(gson.fromJson(getReader(url), JsonObject.class));
+ }
+
+ public <T> T get(String url, Class<T> classOfT) throws IOException {
+ try (Reader reader = getReader(url)) {
+ return gson.fromJson(reader, classOfT);
+ }
+ }
+
+ private InputStreamReader getReader(String url) throws IOException {
+ URL ciUrl;
+ try {
+ ciUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(
+ "Internal error: Gerrit CI URL seems to be malformed", e);
+ }
+ return new InputStreamReader(ciUrl.openStream());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java
new file mode 100644
index 0000000..7766796
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.gson;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.gson.JsonElement;
+
+public class SmartJson {
+
+ private final JsonElement jsonElem;
+
+ private SmartJson(JsonElement elem) {
+ this.jsonElem = elem;
+ }
+
+ public static SmartJson of(JsonElement fromJson) {
+ return new SmartJson(fromJson);
+ }
+
+ public Optional<String> getOptionalString(String fieldName) {
+ return getOptional(fieldName).transform(new Function<SmartJson, String>() {
+ @Override
+ public String apply(SmartJson elem) {
+ if (!elem.jsonElem.isJsonPrimitive()) {
+ throw new IllegalArgumentException("cannot convert " + elem.jsonElem
+ + " into a String");
+ }
+ return elem.jsonElem.getAsString();
+ }
+ });
+ }
+
+ public String getString(String fieldName) {
+ return getOptionalString(fieldName).or("");
+ }
+
+ public Optional<SmartJson> getOptional(String fieldName) {
+ if (jsonElem != null && jsonElem.getAsJsonObject().get(fieldName) != null) {
+ return Optional.of(SmartJson
+ .of(jsonElem.getAsJsonObject().get(fieldName)));
+ } else {
+ return Optional.absent();
+ }
+ }
+
+ public SmartJson get(String fieldName) {
+ return getOptional(fieldName).get();
+ }
+
+ public JsonElement get() {
+ return jsonElem;
+ }
+}
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
new file mode 100644
index 0000000..d1620b3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java
@@ -0,0 +1,150 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import com.googlesource.gerrit.plugins.manager.GerritVersionBranch;
+import com.googlesource.gerrit.plugins.manager.gson.SmartGson;
+import com.googlesource.gerrit.plugins.manager.gson.SmartJson;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@Singleton
+public class JenkinsCiPluginsRepository implements PluginsRepository {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(JenkinsCiPluginsRepository.class);
+
+ private static final String DEFAULT_GERRIT_CI_URL =
+ "https://gerrit-ci.gerritforge.com";
+
+ private static final Optional<PluginInfo> noPluginInfo = Optional.absent();
+
+ private final PluginConfig config;
+
+ static class View {
+ String name;
+ Job[] jobs;
+ }
+
+ static class Job {
+ String name;
+ String url;
+ String color;
+ }
+
+ private final Provider<SmartGson> gsonProvider;
+
+ @Inject
+ public JenkinsCiPluginsRepository(Provider<SmartGson> gsonProvider,
+ PluginConfigFactory configFactory, @PluginName String pluginName) {
+ this.gsonProvider = gsonProvider;
+ this.config = configFactory.getFromGerritConfig(pluginName);
+ }
+
+ @Override
+ public List<PluginInfo> list(String gerritVersion) throws IOException {
+ SmartGson gson = gsonProvider.get();
+ String viewName = "Plugins-" + GerritVersionBranch.getBranch(gerritVersion);
+ List<PluginInfo> plugins = new ArrayList<>();
+
+ try {
+ Job[] jobs =
+ gson.get(getJenkinsUrl() + "/view/" + viewName + "/api/json",
+ View.class).jobs;
+
+ for (Job job : jobs) {
+ if (job.color.equals("blue")) {
+ Optional<PluginInfo> pluginInfo = getPluginInfo(gson, job.url);
+ if (pluginInfo.isPresent()) {
+ plugins.add(pluginInfo.get());
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ log.warn("No plugins available for Gerrit version " + gerritVersion, e);
+ }
+
+ return plugins;
+ }
+
+ private String getJenkinsUrl() {
+ return config.getString("jenkinsUrl", DEFAULT_GERRIT_CI_URL);
+ }
+
+ private Optional<PluginInfo> getPluginInfo(final SmartGson gson, String url)
+ throws IOException {
+ SmartJson jobDetails = gson.get(url + "/api/json");
+ Optional<SmartJson> lastSuccessfulBuild =
+ jobDetails.getOptional("lastSuccessfulBuild");
+
+ return lastSuccessfulBuild.transform(
+ new Function<SmartJson, Optional<PluginInfo>>() {
+ @Override
+ public Optional<PluginInfo> apply(SmartJson build) {
+ String buildUrl = build.getString("url");
+ try {
+ return getPluginArtifactInfo(gson, buildUrl);
+ } catch (IOException e) {
+ log.warn("Cannot retrieve plugin artifact info from {}", buildUrl);
+ return noPluginInfo;
+ }
+ }
+ }).or(noPluginInfo);
+ }
+
+ private Optional<PluginInfo> getPluginArtifactInfo(SmartGson gson, String url)
+ throws IOException {
+ SmartJson buildExecution = gson.get(url + "/api/json");
+ JsonArray artifacts =
+ buildExecution.get("artifacts").get().getAsJsonArray();
+ if (artifacts.size() == 0) {
+ return Optional.absent();
+ }
+
+ SmartJson artifactJson = SmartJson.of(artifacts.get(0));
+ String pluginFileName = artifactJson.getString("fileName");
+
+ String pluginVersion = "";
+ for (JsonElement elem : buildExecution.get("actions").get()
+ .getAsJsonArray()) {
+ SmartJson elemJson = SmartJson.of(elem);
+ Optional<SmartJson> lastBuildRevision =
+ elemJson.getOptional("lastBuiltRevision");
+
+ if (lastBuildRevision.isPresent()) {
+ pluginVersion = lastBuildRevision.get().getString("SHA1");
+ }
+ }
+
+ return Optional.of(new PluginInfo(pluginFileName, pluginVersion));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java
new file mode 100644
index 0000000..03e4d8a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository;
+
+import com.google.gerrit.extensions.restapi.Url;
+
+public class PluginInfo {
+ public final String id;
+ public final String name;
+ public final String version;
+
+ PluginInfo(String name, String version) {
+ this.id = Url.encode(name);
+ this.name = name;
+ this.version = version;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java
new file mode 100644
index 0000000..236fc28
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface PluginsRepository {
+
+ List<PluginInfo> list(String gerritVersion) throws IOException;
+}
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
index 9d16466..40d710d 100644
--- a/src/main/resources/static/index.html
+++ b/src/main/resources/static/index.html
@@ -11,7 +11,13 @@
<h1>Installed plugins</h1>
<ul>
<li ng-repeat="(key, prop) in plugins.list">
- {{key}}
+ {{key}} - {{prop.version}}
+ </li>
+ </ul>
+ <h1>Available plugins</h1>
+ <ul>
+ <li ng-repeat="(key, prop) in plugins.available">
+ {{key}} - {{prop.version}}
</li>
</ul>
</div>
diff --git a/src/main/resources/static/js/plugin-manager.js b/src/main/resources/static/js/plugin-manager.js
index ad01b06..d3e455a 100644
--- a/src/main/resources/static/js/plugin-manager.js
+++ b/src/main/resources/static/js/plugin-manager.js
@@ -19,12 +19,18 @@
plugins.list = {};
+ plugins.available = {};
+
$http.get('/plugins/?all', plugins.httpConfig).then(
function successCallback(response) {
plugins.list = response.data;
}, function errorCallback(response) {
- // called asynchronously if an error occurs
- // or server returns response with an error status.
+ });
+
+ $http.get('/plugins/plugin-manager/available', plugins.httpConfig).then(
+ function successCallback(response) {
+ plugins.available = response.data;
+ }, function errorCallback(response) {
});
});