@@ -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' } | |||
} | |||
# |
@@ -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 |
@@ -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); | |||
@@ -1195,6 +1201,36 @@ public class GitblitManager implements IGitblit { | |||
return pluginManager.deletePlugin(wrapper); | |||
} | |||
@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(); |
@@ -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); | |||
} |
@@ -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 ""; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} | |||
} | |||
} |