summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2014-04-01 00:10:43 -0400
committerJames Moger <james.moger@gitblit.com>2014-04-10 19:00:52 -0400
commite5d0bacbf746e09a9194822b231cb27090f58973 (patch)
treee6d7d2f96384aa89a4f965ad1f47fb130ec907a4
parentec3b719dab4ae3c51d8bd52ab2b4176f82c5006f (diff)
downloadgitblit-e5d0bacbf746e09a9194822b231cb27090f58973.tar.gz
gitblit-e5d0bacbf746e09a9194822b231cb27090f58973.zip
Implement simple JSON-based plugin registry and install command
-rw-r--r--releases.moxie1
-rw-r--r--src/main/distrib/data/gitblit.properties20
-rw-r--r--src/main/java/com/gitblit/manager/GitblitManager.java36
-rw-r--r--src/main/java/com/gitblit/manager/IPluginManager.java48
-rw-r--r--src/main/java/com/gitblit/manager/PluginManager.java250
-rw-r--r--src/main/java/com/gitblit/models/PluginRegistry.java143
-rw-r--r--src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java120
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());
+ }
+ }
}
}
}