diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 52e6e5ba13..4e60191e6e 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -62,6 +62,7 @@ import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; import org.jenkinsci.bytecode.Transformer; import org.jvnet.hudson.reactor.Executable; @@ -1298,12 +1299,33 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas pluginUploaded = true; + JSONArray dependencies = new JSONArray(); + try { + Manifest m = new JarFile(t).getManifest(); + String deps = m.getMainAttributes().getValue("Plugin-Dependencies"); + + if (StringUtils.isNotBlank(deps)) { + // now we get to parse it! + String[] plugins = deps.split(","); + for (String p : plugins) { + // should have name:version[;resolution:=optional] + String[] attrs = p.split("[:;]"); + dependencies.add(new JSONObject() + .element("name", attrs[0]) + .element("version", attrs[1]) + .element("optional", p.contains("resolution:=optional"))); + } + } + } catch(IOException e) { + LOGGER.log(WARNING, "Unable to setup dependency list for plugin upload", e); + } + // Now create a dummy plugin that we can dynamically load (the InstallationJob will force a restart if one is needed): JSONObject cfg = new JSONObject(). element("name", baseName). element("version", "0"). // unused but mandatory element("url", t.toURI().toString()). - element("dependencies", new JSONArray()); + element("dependencies", dependencies); new UpdateSite(UpdateCenter.ID_UPLOAD, null).new Plugin(UpdateCenter.ID_UPLOAD, cfg).deploy(true); return new HttpRedirect("../updateCenter"); } catch (IOException e) { diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 913e3f4a15..1507a06756 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -377,4 +377,48 @@ public class PluginManagerTest { private void dynamicLoad(String plugin) throws IOException, InterruptedException, RestartRequiredException { PluginManagerUtil.dynamicLoad(plugin, r.jenkins); } + + @Test public void uploadDependencyResolution() throws Exception { + PersistedList sites = r.jenkins.getUpdateCenter().getSites(); + sites.clear(); + URL url = PluginManagerTest.class.getResource("/plugins/upload-test-update-center.json"); + UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString()); + sites.add(site); + + assertEquals(FormValidation.ok(), site.updateDirectly(false).get()); + assertNotNull(site.getData()); + + // neither of the following plugins should be installed + assertNull(r.jenkins.getPluginManager().getPlugin("Parameterized-Remote-Trigger")); + assertNull(r.jenkins.getPluginManager().getPlugin("token-macro")); + + HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); + HtmlForm f = page.getFormByName("uploadPlugin"); + File dir = tmp.newFolder(); + File plugin = new File(dir, "Parameterized-Remote-Trigger.hpi"); + FileUtils.copyURLToFile(getClass().getClassLoader().getResource("plugins/Parameterized-Remote-Trigger.hpi"),plugin); + f.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); + r.submit(f); + + assertTrue(r.jenkins.getUpdateCenter().getJobs().size() > 0); + + // wait for all the download jobs to complete + boolean done = true; + do { + Thread.sleep(100); + for(UpdateCenterJob job : r.jenkins.getUpdateCenter().getJobs()) { + if(job instanceof UpdateCenter.DownloadJob) { + done &= ((UpdateCenter.DownloadJob)job).status.isSuccess(); + } + } + } while(!done); + + // the files get renamed to .jpi + assertTrue( new File(r.jenkins.getRootDir(),"plugins/Parameterized-Remote-Trigger.jpi").exists() ); + assertTrue( new File(r.jenkins.getRootDir(),"plugins/token-macro.jpi").exists() ); + + // now the other plugins should have been found as dependencies and downloaded + assertNotNull(r.jenkins.getPluginManager().getPlugin("Parameterized-Remote-Trigger")); + assertNotNull(r.jenkins.getPluginManager().getPlugin("token-macro")); + } } diff --git a/test/src/test/resources/plugins/Parameterized-Remote-Trigger.hpi b/test/src/test/resources/plugins/Parameterized-Remote-Trigger.hpi new file mode 100644 index 0000000000..0594e3eabe Binary files /dev/null and b/test/src/test/resources/plugins/Parameterized-Remote-Trigger.hpi differ diff --git a/test/src/test/resources/plugins/credentials.hpi b/test/src/test/resources/plugins/credentials.hpi new file mode 100644 index 0000000000..ba3c386839 Binary files /dev/null and b/test/src/test/resources/plugins/credentials.hpi differ diff --git a/test/src/test/resources/plugins/icon-shim.hpi b/test/src/test/resources/plugins/icon-shim.hpi new file mode 100644 index 0000000000..9edbfe23ff Binary files /dev/null and b/test/src/test/resources/plugins/icon-shim.hpi differ diff --git a/test/src/test/resources/plugins/token-macro.hpi b/test/src/test/resources/plugins/token-macro.hpi new file mode 100644 index 0000000000..5283eb96a7 Binary files /dev/null and b/test/src/test/resources/plugins/token-macro.hpi differ diff --git a/test/src/test/resources/plugins/upload-test-update-center.json b/test/src/test/resources/plugins/upload-test-update-center.json new file mode 100644 index 0000000000..028679fce7 --- /dev/null +++ b/test/src/test/resources/plugins/upload-test-update-center.json @@ -0,0 +1,89 @@ +updateCenter.post( +{"connectionCheckUrl":"http://www.google.com/", // has to be here because updateDirectly sniffs only very limited formats + "core": { + "buildDate": "Dec 31, 1969", + "name": "core", + "url": "jenkins.war", + "version": "23" + }, + "id": "default", + "plugins": { + "credentials": { + "buildDate": "Apr 04, 2016", + "dependencies": [ + { + "name": "icon-shim", + "optional": false, + "version":"2.0.2" + } + ], + "developers": [], + "excerpt": "This plugin allows you to store credentials in Jenkins.", + "gav": "org.jenkins-ci.plugins:credentials:1.27", + "labels": [], + "name": "credentials", + "previousTimestamp": "2016-03-24T01:19:58.00Z", + "previousVersion": "1.26", + "releaseTimestamp": "2016-04-04T16:26:54.00Z", + "requiredCore": "1.565", + "scm": "github.com", + "sha1": "5Ffulp5hSySnUL3gs427m6wVFJ4=", + "title": "Credentials Plugin", + "url": "credentials.hpi", + "version": "1.27", + "wiki": "https://wiki.jenkins-ci.org/display/JENKINS/Credentials+Plugin" + }, + "icon-shim": { + "buildDate": "Feb 23, 2016", + "dependencies": [], + "developers": [], + "excerpt": "This plugin allows other Jenkins plugins to take advantage of the tag. ", + "gav": "org.jenkins-ci.plugins.icon-shim:icon-shim:2.0.3", + "labels": ["ui"], + "name": "icon-shim", + "previousTimestamp": "2016-01-09T20:01:12.00Z", + "previousVersion": "2.0.2", + "releaseTimestamp": "2016-02-23T23:08:18.00Z", + "requiredCore": "1.609", + "scm": "github.com", + "sha1": "umtSUbNwIuqU6JGEQcBxqFt1X24=", + "title": "Icon Shim Plugin", + "url": "icon-shim.hpi", + "version": "2.0.3", + "wiki": "https://wiki.jenkins-ci.org/display/JENKINS/Icon+Shim+Plugin" + }, + "token-macro": { + "buildDate": "Dec 14, 2015", + "dependencies": [], + "developers": [], + "excerpt": "This plugin adds reusable macro expansion capability for other plugins to use", + "gav": "org.jenkins-ci.plugins:token-macro:1.12.1", + "labels": [], + "name": "token-macro", + "previousTimestamp": "2015-12-11T08:27:18.00Z", + "previousVersion": "1.12", + "releaseTimestamp": "2015-12-14T18:07:56.00Z", + "requiredCore": "1.609.2", + "scm": "github.com", + "sha1": "TV61PGo+od75URp3maLGUFJ768k=", + "title": "Token Macro Plugin", + "url": "token-macro.hpi", + "version": "1.9", + "wiki": "https://wiki.jenkins-ci.org/display/JENKINS/Token+Macro+Plugin" + }, + "dummy": { + "buildDate": "Dec 17, 2008", + "dependencies": [], + "developers": [], + "excerpt": "…", + "name": "dummy", + "requiredCore": "1.100", + "sha1": "wtzlcjUKiMcg90H5CTYkGX6+r8Y=", + "title": "Dummy", + "url": "http://nowhere.net/dummy.hpi", + "version": "1.0" + } + }, + "updateCenterVersion": 1 +} +);