Skip to content

Commit

Permalink
Merge pull request #2239 from slide/master
Browse files Browse the repository at this point in the history
[FIXED JENKINS-15057] Add dependency resolution to manually uploaded plugins.
  • Loading branch information
daniel-beck committed Apr 8, 2016
2 parents 274183d + 1c60167 commit d5c508d
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 1 deletion.
24 changes: 23 additions & 1 deletion core/src/main/java/hudson/PluginManager.java
Expand Up @@ -62,6 +62,7 @@
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;
Expand Down Expand Up @@ -1298,12 +1299,33 @@ public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, Servl

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) {
Expand Down
49 changes: 49 additions & 0 deletions test/src/test/java/hudson/PluginManagerTest.java
Expand Up @@ -377,4 +377,53 @@ private String callDependerValue() throws Exception {
private void dynamicLoad(String plugin) throws IOException, InterruptedException, RestartRequiredException {
PluginManagerUtil.dynamicLoad(plugin, r.jenkins);
}

@Test public void uploadDependencyResolution() throws Exception {
PersistedList<UpdateSite> 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;
boolean passed = true;
do {
Thread.sleep(100);
done = true;
for(UpdateCenterJob job : r.jenkins.getUpdateCenter().getJobs()) {
if(job instanceof UpdateCenter.DownloadJob) {
UpdateCenter.DownloadJob j = (UpdateCenter.DownloadJob)job;
assertFalse(j.status instanceof UpdateCenter.DownloadJob.Failure);
done &= !(((j.status instanceof UpdateCenter.DownloadJob.Pending) ||
(j.status instanceof UpdateCenter.DownloadJob.Installing)));
}
}
} 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"));
}
}
Binary file not shown.
Binary file added test/src/test/resources/plugins/credentials.hpi
Binary file not shown.
Binary file added test/src/test/resources/plugins/icon-shim.hpi
Binary file not shown.
Binary file added test/src/test/resources/plugins/token-macro.hpi
Binary file not shown.
89 changes: 89 additions & 0 deletions test/src/test/resources/plugins/upload-test-update-center.json
@@ -0,0 +1,89 @@
updateCenter.post(
{"connectionCheckUrl":"http://www.google.com/",
"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": "XWJpfL8B2x7qrISJg7IY2ZTPxFk=",
"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 <l:icon> 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
}
);

0 comments on commit d5c508d

Please sign in to comment.