diff options
author | James Moger <james.moger@gitblit.com> | 2014-04-01 00:10:43 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2014-04-10 19:00:52 -0400 |
commit | e5d0bacbf746e09a9194822b231cb27090f58973 (patch) | |
tree | e6d7d2f96384aa89a4f965ad1f47fb130ec907a4 | |
parent | ec3b719dab4ae3c51d8bd52ab2b4176f82c5006f (diff) | |
download | gitblit-e5d0bacbf746e09a9194822b231cb27090f58973.tar.gz gitblit-e5d0bacbf746e09a9194822b231cb27090f58973.zip |
Implement simple JSON-based plugin registry and install command
-rw-r--r-- | releases.moxie | 1 | ||||
-rw-r--r-- | src/main/distrib/data/gitblit.properties | 20 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/GitblitManager.java | 36 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/IPluginManager.java | 48 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/PluginManager.java | 250 | ||||
-rw-r--r-- | src/main/java/com/gitblit/models/PluginRegistry.java | 143 | ||||
-rw-r--r-- | src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java | 120 |
7 files changed, 588 insertions, 30 deletions
diff --git a/releases.moxie b/releases.moxie index 0f37bf2b..89a7a5f3 100644 --- a/releases.moxie +++ b/releases.moxie @@ -48,6 +48,7 @@ r22: { - { name: 'git.sshBackend', defaultValue: 'NIO2' } - { name: 'git.sshCommandStartThreads', defaultValue: '2' } - { name: 'plugins.folder', defaultValue: '${baseFolder}/plugins' } + - { name: 'plugins.registry', defaultValue: 'http://gitblit.github.io/gitblit-registry/plugins.json' } } # diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties index 1a613e25..c52423b9 100644 --- a/src/main/distrib/data/gitblit.properties +++ b/src/main/distrib/data/gitblit.properties @@ -548,6 +548,18 @@ tickets.redis.url = # SINCE 1.4.0
tickets.perPage = 25
+# The folder where plugins are loaded from.
+#
+# SINCE 1.5.0
+# RESTART REQUIRED
+# BASEFOLDER
+plugins.folder = ${baseFolder}/plugins
+
+# The registry of available plugins.
+#
+# SINCE 1.5.0
+plugins.registry = http://gitblit.github.io/gitblit-registry/plugins.json
+
#
# Groovy Integration
#
@@ -1850,11 +1862,3 @@ server.requireClientCertificates = false # SINCE 0.5.0
# RESTART REQUIRED
server.shutdownPort = 8081
-
-# Base folder for plugins.
-# This folder may contain Gitblit plugins
-#
-# SINCE 1.6.0
-# RESTART REQUIRED
-# BASEFOLDER
-plugins.folder = ${baseFolder}/plugins
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 6b1cc8a5..5a7d15ae 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -61,6 +61,8 @@ import com.gitblit.models.ForkModel; import com.gitblit.models.GitClientApplication; import com.gitblit.models.Mailing; import com.gitblit.models.Metric; +import com.gitblit.models.PluginRegistry.PluginRegistration; +import com.gitblit.models.PluginRegistry.PluginRelease; import com.gitblit.models.ProjectModel; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.RepositoryModel; @@ -1180,6 +1182,10 @@ public class GitblitManager implements IGitblit { return repositoryManager.isIdle(repository); } + /* + * PLUGIN MANAGER + */ + @Override public <T> List<T> getExtensions(Class<T> clazz) { return pluginManager.getExtensions(clazz); @@ -1196,6 +1202,36 @@ public class GitblitManager implements IGitblit { } @Override + public boolean refreshRegistry() { + return pluginManager.refreshRegistry(); + } + + @Override + public boolean installPlugin(String url) { + return pluginManager.installPlugin(url); + } + + @Override + public boolean installPlugin(PluginRelease pv) { + return pluginManager.installPlugin(pv); + } + + @Override + public List<PluginRegistration> getRegisteredPlugins() { + return pluginManager.getRegisteredPlugins(); + } + + @Override + public PluginRegistration lookupPlugin(String idOrName) { + return pluginManager.lookupPlugin(idOrName); + } + + @Override + public PluginRelease lookupRelease(String idOrName, String version) { + return pluginManager.lookupRelease(idOrName, version); + } + + @Override public List<PluginWrapper> getPlugins() { return pluginManager.getPlugins(); } diff --git a/src/main/java/com/gitblit/manager/IPluginManager.java b/src/main/java/com/gitblit/manager/IPluginManager.java index 11b81ea3..1f7f85ee 100644 --- a/src/main/java/com/gitblit/manager/IPluginManager.java +++ b/src/main/java/com/gitblit/manager/IPluginManager.java @@ -15,9 +15,14 @@ */ package com.gitblit.manager; +import java.util.List; + import ro.fortsoft.pf4j.PluginManager; import ro.fortsoft.pf4j.PluginWrapper; +import com.gitblit.models.PluginRegistry.PluginRegistration; +import com.gitblit.models.PluginRegistry.PluginRelease; + public interface IPluginManager extends IManager, PluginManager { /** @@ -27,12 +32,51 @@ public interface IPluginManager extends IManager, PluginManager { * @return PluginWrapper that loaded the given class */ PluginWrapper whichPlugin(Class<?> clazz); - + /** * Delete the plugin represented by {@link PluginWrapper}. - * + * * @param wrapper * @return true if successful */ boolean deletePlugin(PluginWrapper wrapper); + + /** + * Refresh the plugin registry. + */ + boolean refreshRegistry(); + + /** + * Install the plugin from the specified url. + */ + boolean installPlugin(String url); + + /** + * Install the plugin. + */ + boolean installPlugin(PluginRelease pr); + + /** + * The list of all registered plugins. + * + * @return a list of registered plugins + */ + List<PluginRegistration> getRegisteredPlugins(); + + /** + * Lookup a plugin registration from the plugin registries. + * + * @param idOrName + * @return a plugin registration or null + */ + PluginRegistration lookupPlugin(String idOrName); + + /** + * Lookup a plugin release. + * + * @param idOrName + * @param version (use null for the current version) + * @return the identified plugin version or null + */ + PluginRelease lookupRelease(String idOrName, String version); } diff --git a/src/main/java/com/gitblit/manager/PluginManager.java b/src/main/java/com/gitblit/manager/PluginManager.java index e23aaec0..7b03f50d 100644 --- a/src/main/java/com/gitblit/manager/PluginManager.java +++ b/src/main/java/com/gitblit/manager/PluginManager.java @@ -15,31 +15,57 @@ */ package com.gitblit.manager; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.PluginVersion; import ro.fortsoft.pf4j.PluginWrapper; import com.gitblit.Keys; +import com.gitblit.models.PluginRegistry; +import com.gitblit.models.PluginRegistry.PluginRegistration; +import com.gitblit.models.PluginRegistry.PluginRelease; +import com.gitblit.utils.Base64; import com.gitblit.utils.FileUtils; +import com.gitblit.utils.JsonUtils; +import com.gitblit.utils.StringUtils; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; /** * The plugin manager maintains the lifecycle of plugins. It is exposed as * Dagger bean. The extension consumers supposed to retrieve plugin manager * from the Dagger DI and retrieve extensions provided by active plugins. - * + * * @author David Ostrovsky - * + * */ public class PluginManager extends DefaultPluginManager implements IPluginManager { private final Logger logger = LoggerFactory.getLogger(getClass()); - + private final IRuntimeManager runtimeManager; + // timeout defaults of Maven 3.0.4 in seconds + private int connectTimeout = 20; + + private int readTimeout = 12800; + public PluginManager(IRuntimeManager runtimeManager) { super(runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins")); this.runtimeManager = runtimeManager; @@ -60,13 +86,13 @@ public class PluginManager extends DefaultPluginManager implements IPluginManage stopPlugins(); return null; } - + @Override public boolean deletePlugin(PluginWrapper pw) { File folder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); File pluginFolder = new File(folder, pw.getPluginPath()); File pluginZip = new File(folder, pw.getPluginPath() + ".zip"); - + if (pluginFolder.exists()) { FileUtils.delete(pluginFolder); } @@ -75,4 +101,218 @@ public class PluginManager extends DefaultPluginManager implements IPluginManage } return true; } + + @Override + public boolean refreshRegistry() { + String dr = "http://gitblit.github.io/gitblit-registry/plugins.json"; + String url = runtimeManager.getSettings().getString(Keys.plugins.registry, dr); + try { + return download(url); + } catch (Exception e) { + logger.error(String.format("Failed to retrieve plugins.json from %s", url), e); + } + return false; + } + + protected List<PluginRegistry> getRegistries() { + List<PluginRegistry> list = new ArrayList<PluginRegistry>(); + File folder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); + FileFilter jsonFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return !file.isDirectory() && file.getName().toLowerCase().endsWith(".json"); + } + }; + + File [] files = folder.listFiles(jsonFilter); + if (files == null || files.length == 0) { + // automatically retrieve the registry if we don't have a local copy + refreshRegistry(); + files = folder.listFiles(jsonFilter); + } + + if (files == null || files.length == 0) { + return list; + } + + for (File file : files) { + PluginRegistry registry = null; + try { + String json = FileUtils.readContent(file, "\n"); + registry = JsonUtils.fromJsonString(json, PluginRegistry.class); + } catch (Exception e) { + logger.error("Failed to deserialize " + file, e); + } + if (registry != null) { + list.add(registry); + } + } + return list; + } + + @Override + public List<PluginRegistration> getRegisteredPlugins() { + List<PluginRegistration> list = new ArrayList<PluginRegistration>(); + Map<String, PluginRegistration> map = new TreeMap<String, PluginRegistration>(); + for (PluginRegistry registry : getRegistries()) { + List<PluginRegistration> registrations = registry.registrations; + list.addAll(registrations); + for (PluginRegistration reg : registrations) { + reg.installedRelease = null; + map.put(reg.id, reg); + } + } + for (PluginWrapper pw : getPlugins()) { + String id = pw.getDescriptor().getPluginId(); + PluginVersion pv = pw.getDescriptor().getVersion(); + PluginRegistration reg = map.get(id); + if (reg != null) { + reg.installedRelease = pv.toString(); + } + } + return list; + } + + @Override + public PluginRegistration lookupPlugin(String idOrName) { + for (PluginRegistry registry : getRegistries()) { + PluginRegistration reg = registry.lookup(idOrName); + if (reg != null) { + return reg; + } + } + return null; + } + + @Override + public PluginRelease lookupRelease(String idOrName, String version) { + for (PluginRegistry registry : getRegistries()) { + PluginRegistration reg = registry.lookup(idOrName); + if (reg != null) { + PluginRelease pv; + if (StringUtils.isEmpty(version)) { + pv = reg.getCurrentRelease(); + } else { + pv = reg.getRelease(version); + } + if (pv != null) { + return pv; + } + } + } + return null; + } + + + /** + * Installs the plugin from the plugin version. + * + * @param pv + * @throws IOException + * @return true if successful + */ + @Override + public boolean installPlugin(PluginRelease pv) { + return installPlugin(pv.url); + } + + /** + * Installs the plugin from the url. + * + * @param url + * @return true if successful + */ + @Override + public boolean installPlugin(String url) { + try { + if (!download(url)) { + return false; + } + // TODO stop, unload, load + } catch (IOException e) { + logger.error("Failed to install plugin from " + url, e); + } + return true; + } + + /** + * Download a file to the plugins folder. + * + * @param url + * @return + * @throws IOException + */ + protected boolean download(String url) throws IOException { + File pFolder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); + File tmpFile = new File(pFolder, StringUtils.getSHA1(url) + ".tmp"); + if (tmpFile.exists()) { + tmpFile.delete(); + } + + URL u = new URL(url); + final URLConnection conn = getConnection(u); + + // try to get the server-specified last-modified date of this artifact + long lastModified = conn.getHeaderFieldDate("Last-Modified", System.currentTimeMillis()); + + Files.copy(new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return new BufferedInputStream(conn.getInputStream()); + } + }, tmpFile); + + File destFile = new File(pFolder, StringUtils.getLastPathElement(u.getPath())); + if (destFile.exists()) { + destFile.delete(); + } + tmpFile.renameTo(destFile); + destFile.setLastModified(lastModified); + + return true; + } + + protected URLConnection getConnection(URL url) throws IOException { + java.net.Proxy proxy = getProxy(url); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy); + if (java.net.Proxy.Type.DIRECT != proxy.type()) { + String auth = getProxyAuthorization(url); + conn.setRequestProperty("Proxy-Authorization", auth); + } + + String username = null; + String password = null; + if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) { + // set basic authentication header + String auth = Base64.encodeBytes((username + ":" + password).getBytes()); + conn.setRequestProperty("Authorization", "Basic " + auth); + } + + // configure timeouts + conn.setConnectTimeout(connectTimeout * 1000); + conn.setReadTimeout(readTimeout * 1000); + + switch (conn.getResponseCode()) { + case HttpURLConnection.HTTP_MOVED_TEMP: + case HttpURLConnection.HTTP_MOVED_PERM: + // handle redirects by closing this connection and opening a new + // one to the new location of the requested resource + String newLocation = conn.getHeaderField("Location"); + if (!StringUtils.isEmpty(newLocation)) { + logger.info("following redirect to {0}", newLocation); + conn.disconnect(); + return getConnection(new URL(newLocation)); + } + } + + return conn; + } + + protected Proxy getProxy(URL url) { + return java.net.Proxy.NO_PROXY; + } + + protected String getProxyAuthorization(URL url) { + return ""; + } } diff --git a/src/main/java/com/gitblit/models/PluginRegistry.java b/src/main/java/com/gitblit/models/PluginRegistry.java new file mode 100644 index 00000000..c81a0f23 --- /dev/null +++ b/src/main/java/com/gitblit/models/PluginRegistry.java @@ -0,0 +1,143 @@ +/*
+ * Copyright 2014 gitblit.com.
+ *
+ * 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.gitblit.models;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.parboiled.common.StringUtils;
+
+import ro.fortsoft.pf4j.PluginVersion;
+
+/**
+ * Represents a list of plugin registrations.
+ */
+public class PluginRegistry implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String name;
+
+ public final List<PluginRegistration> registrations;
+
+ public PluginRegistry(String name) {
+ this.name = name;
+ registrations = new ArrayList<PluginRegistration>();
+ }
+
+ public PluginRegistration lookup(String idOrName) {
+ for (PluginRegistration registration : registrations) {
+ if (registration.id.equalsIgnoreCase(idOrName)
+ || registration.name.equalsIgnoreCase(idOrName)) {
+ return registration;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+ public static enum InstallState {
+ NOT_INSTALLED, INSTALLED, CAN_UPDATE, UNKNOWN
+ }
+
+ /**
+ * Represents a plugin registration.
+ */
+ public static class PluginRegistration implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String id;
+
+ public String name;
+
+ public String description;
+
+ public String provider;
+
+ public String projectUrl;
+
+ public String currentRelease;
+
+ public transient String installedRelease;
+
+ public List<PluginRelease> releases;
+
+ public PluginRegistration(String id) {
+ this.id = id;
+ this.releases = new ArrayList<PluginRelease>();
+ }
+
+ public PluginRelease getCurrentRelease() {
+ PluginRelease current = null;
+ if (!StringUtils.isEmpty(currentRelease)) {
+ current = getRelease(currentRelease);
+ }
+
+ if (current == null) {
+ Date date = new Date(0);
+ for (PluginRelease pv : releases) {
+ if (pv.date.after(date)) {
+ current = pv;
+ }
+ }
+ }
+ return current;
+ }
+
+ public PluginRelease getRelease(String version) {
+ for (PluginRelease pv : releases) {
+ if (pv.version.equalsIgnoreCase(version)) {
+ return pv;
+ }
+ }
+ return null;
+ }
+
+ public InstallState getInstallState() {
+ if (StringUtils.isEmpty(installedRelease)) {
+ return InstallState.NOT_INSTALLED;
+ }
+ PluginVersion ir = PluginVersion.createVersion(installedRelease);
+ PluginVersion cr = PluginVersion.createVersion(currentRelease);
+ switch (ir.compareTo(cr)) {
+ case -1:
+ return InstallState.UNKNOWN;
+ case 1:
+ return InstallState.CAN_UPDATE;
+ default:
+ return InstallState.INSTALLED;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+ }
+
+ public static class PluginRelease {
+ public String version;
+ public Date date;
+ public String url;
+ }
+}
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java b/src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java index 5c413db2..ba6f30d6 100644 --- a/src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java +++ b/src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; import ro.fortsoft.pf4j.PluginDependency; import ro.fortsoft.pf4j.PluginDescriptor; @@ -26,6 +27,8 @@ import ro.fortsoft.pf4j.PluginState; import ro.fortsoft.pf4j.PluginWrapper; import com.gitblit.manager.IGitblit; +import com.gitblit.models.PluginRegistry.PluginRegistration; +import com.gitblit.models.PluginRegistry.PluginRelease; import com.gitblit.models.UserModel; import com.gitblit.utils.FlipTable; import com.gitblit.utils.FlipTable.Borders; @@ -46,7 +49,8 @@ public class PluginDispatcher extends DispatchCommand { register(user, StopPlugin.class); register(user, ShowPlugin.class); register(user, RemovePlugin.class); - register(user, UploadPlugin.class); + register(user, InstallPlugin.class); + register(user, AvailablePlugins.class); } @CommandMetaData(name = "list", aliases = { "ls" }, description = "List the loaded plugins") @@ -82,7 +86,7 @@ public class PluginDispatcher extends DispatchCommand { stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS)); } - + @Override protected void asTabbed(List<PluginWrapper> list) { for (PluginWrapper pw : list) { @@ -95,7 +99,7 @@ public class PluginDispatcher extends DispatchCommand { } } } - + @CommandMetaData(name = "start", description = "Start a plugin") public static class StartPlugin extends SshCommand { @@ -128,7 +132,7 @@ public class PluginDispatcher extends DispatchCommand { } } } - + protected void start(PluginWrapper pw) throws UnloggedFailure { String id = pw.getDescriptor().getPluginId(); if (pw.getPluginState() == PluginState.STARTED) { @@ -143,7 +147,7 @@ public class PluginDispatcher extends DispatchCommand { } } } - + @CommandMetaData(name = "stop", description = "Stop a plugin") public static class StopPlugin extends SshCommand { @@ -177,7 +181,7 @@ public class PluginDispatcher extends DispatchCommand { } } } - + protected void stop(PluginWrapper pw) throws UnloggedFailure { String id = pw.getDescriptor().getPluginId(); if (pw.getPluginState() == PluginState.STOPPED) { @@ -192,7 +196,7 @@ public class PluginDispatcher extends DispatchCommand { } } } - + @CommandMetaData(name = "show", description = "Show the details of a plugin") public static class ShowPlugin extends SshCommand { @@ -230,7 +234,7 @@ public class PluginDispatcher extends DispatchCommand { String ext = exts.get(i); data[0] = new Object[] { ext.toString(), ext.toString() }; } - extensions = FlipTable.of(headers, data, Borders.COLS); + extensions = FlipTable.of(headers, data, Borders.COLS); } // DEPENDENCIES @@ -246,9 +250,9 @@ public class PluginDispatcher extends DispatchCommand { PluginDependency dep = deps.get(i); data[0] = new Object[] { dep.getPluginId(), dep.getPluginVersion() }; } - dependencies = FlipTable.of(headers, data, Borders.COLS); + dependencies = FlipTable.of(headers, data, Borders.COLS); } - + String[] headers = { d.getPluginId() }; Object[][] data = new Object[5][]; data[0] = new Object[] { fields }; @@ -256,10 +260,10 @@ public class PluginDispatcher extends DispatchCommand { data[2] = new Object[] { extensions }; data[3] = new Object[] { "DEPENDENCIES" }; data[4] = new Object[] { dependencies }; - stdout.println(FlipTable.of(headers, data)); + stdout.println(FlipTable.of(headers, data)); } } - + @CommandMetaData(name = "remove", aliases= { "rm", "del" }, description = "Remove a plugin", hidden = true) public static class RemovePlugin extends SshCommand { @@ -282,12 +286,98 @@ public class PluginDispatcher extends DispatchCommand { } } } - - @CommandMetaData(name = "receive", aliases= { "upload" }, description = "Upload a plugin to the server", hidden = true) - public static class UploadPlugin extends SshCommand { + + @CommandMetaData(name = "install", description = "Download and installs a plugin", hidden = true) + public static class InstallPlugin extends SshCommand { + + @Argument(index = 0, required = true, metaVar = "<URL>|<ID>|<NAME>", usage = "the id, name, or the url of the plugin to download and install") + protected String urlOrIdOrName; + + @Option(name = "--version", usage = "The specific version to install") + private String version; @Override public void run() throws UnloggedFailure { + IGitblit gitblit = getContext().getGitblit(); + try { + String ulc = urlOrIdOrName.toLowerCase(); + if (ulc.startsWith("http://") || ulc.startsWith("https://")) { + if (gitblit.installPlugin(urlOrIdOrName)) { + stdout.println(String.format("Installed %s", urlOrIdOrName)); + } else { + new UnloggedFailure(1, String.format("Failed to install %s", urlOrIdOrName)); + } + } else { + PluginRelease pv = gitblit.lookupRelease(urlOrIdOrName, version); + if (pv == null) { + throw new UnloggedFailure(1, String.format("Plugin \"%s\" is not in the registry!", urlOrIdOrName)); + } + if (gitblit.installPlugin(pv)) { + stdout.println(String.format("Installed %s", urlOrIdOrName)); + } else { + throw new UnloggedFailure(1, String.format("Failed to install %s", urlOrIdOrName)); + } + } + } catch (Exception e) { + log.error("Failed to install " + urlOrIdOrName, e); + throw new UnloggedFailure(1, String.format("Failed to install %s", urlOrIdOrName), e); + } + } + } + + @CommandMetaData(name = "available", description = "List the available plugins") + public static class AvailablePlugins extends ListFilterCommand<PluginRegistration> { + + @Option(name = "--refresh", aliases = { "-r" }, usage = "refresh the plugin registry") + protected boolean refresh; + + @Override + protected List<PluginRegistration> getItems() throws UnloggedFailure { + IGitblit gitblit = getContext().getGitblit(); + if (refresh) { + gitblit.refreshRegistry(); + } + List<PluginRegistration> list = gitblit.getRegisteredPlugins(); + return list; + } + + @Override + protected boolean matches(String filter, PluginRegistration t) { + return t.id.matches(filter) || t.name.matches(filter); + } + + @Override + protected void asTable(List<PluginRegistration> list) { + String[] headers; + if (verbose) { + String [] h = { "Name", "Description", "Installed", "Release", "State", "Id", "Provider" }; + headers = h; + } else { + String [] h = { "Name", "Description", "Installed", "Release", "State" }; + headers = h; + } + Object[][] data = new Object[list.size()][]; + for (int i = 0; i < list.size(); i++) { + PluginRegistration p = list.get(i); + if (verbose) { + data[i] = new Object[] {p.name, p.description, p.installedRelease, p.currentRelease, p.getInstallState(), p.id, p.provider}; + } else { + data[i] = new Object[] {p.name, p.description, p.installedRelease, p.currentRelease, p.getInstallState()}; + } + } + + stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS)); + } + + @Override + protected void asTabbed(List<PluginRegistration> list) { + for (PluginRegistration p : list) { + if (verbose) { + outTabbed(p.name, p.description, p.currentRelease, p.getInstallState(), p.id, p.provider); + } else { + outTabbed(p.name, p.description, p.currentRelease, p.getInstallState()); + } + } } } } |