diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index e217789166b9..f0e51e8aeb93 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -521,20 +521,30 @@ public String getShortName() { */ @Exported public String getUrl() { - // first look for the manifest entry. This is new in maven-hpi-plugin 1.30 - String url = manifest.getMainAttributes().getValue("Url"); - if(url!=null) return url; + // first look in update center metadata + List siteMetadataList = getInfoFromAllSites(); + String firstSiteUrl = null; + if (!siteMetadataList.isEmpty()) { + firstSiteUrl = siteMetadataList.get(0).wiki; + if (allUrlsMatch(firstSiteUrl, siteMetadataList)) { + return firstSiteUrl; + } + } - // fallback to update center metadata - UpdateSite.Plugin ui = getInfo(); - if(ui!=null) return ui.wiki; + // if update sites give different / empty results, + // use manifest (since maven-hpi-plugin 1.30) + String url = manifest.getMainAttributes().getValue("Url"); + if (url != null) { + return url; + } + return firstSiteUrl; + } - return null; + private boolean allUrlsMatch(String url, List uiList) { + return uiList.stream().allMatch(k -> k.wiki != null && k.wiki.equals(url)); } - - - @Override + @Override public String toString() { return "Plugin:" + getShortName(); } @@ -972,6 +982,11 @@ public UpdateSite.Plugin getInfo() { return uc.getPlugin(getShortName()); } + private List getInfoFromAllSites() { + UpdateCenter uc = Jenkins.get().getUpdateCenter(); + return uc.getPluginFromAllSites(getShortName(), getVersionNumber()); + } + /** * Returns true if this plugin has update in the update center. * diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 9bcb13bf5f15..a6440b566915 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -640,8 +640,7 @@ public String getDefaultBaseUrl() { } for (UpdateSite s : sites) { Plugin p = s.getPlugin(artifactId); - if (p!=null) { - if (minVersion.isNewerThan(new VersionNumber(p.version))) continue; + if (checkMinVersion(p, minVersion)) { return p; } } @@ -649,6 +648,28 @@ public String getDefaultBaseUrl() { } /** + * Gets plugin info from all available sites + * @return list of plugins + */ + @Restricted(NoExternalUse.class) + public @Nonnull List getPluginFromAllSites(String artifactId, + @CheckForNull VersionNumber minVersion) { + ArrayList result = new ArrayList<>(); + for (UpdateSite s : sites) { + Plugin p = s.getPlugin(artifactId); + if (checkMinVersion(p, minVersion)) { + result.add(p); + } + } + return result; + } + + private boolean checkMinVersion(@CheckForNull Plugin p, @CheckForNull VersionNumber minVersion) { + return p != null + && (minVersion == null || !minVersion.isNewerThan(new VersionNumber(p.version))); + } + + /** * Schedules a Jenkins upgrade. */ @RequirePOST diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java index 2bc8f754bbd5..2b332d174fbb 100644 --- a/core/src/test/java/hudson/PluginWrapperTest.java +++ b/core/src/test/java/hudson/PluginWrapperTest.java @@ -161,12 +161,11 @@ private PluginWrapper buildFailed() { } private PluginWrapper build() { - Manifest manifest = mock(Manifest.class); - Attributes attributes = new Attributes(); + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); attributes.put(new Attributes.Name("Short-Name"), name); attributes.put(new Attributes.Name("Jenkins-Version"), requiredCoreVersion); attributes.put(new Attributes.Name("Plugin-Version"), version); - when(manifest.getMainAttributes()).thenReturn(attributes); return new PluginWrapper( pm, new File("/tmp/" + name + ".jpi"), diff --git a/test/src/test/java/hudson/model/UpdateSiteTest.java b/test/src/test/java/hudson/model/UpdateSiteTest.java index 559c75be59aa..d5c7f824d7bf 100644 --- a/test/src/test/java/hudson/model/UpdateSiteTest.java +++ b/test/src/test/java/hudson/model/UpdateSiteTest.java @@ -24,6 +24,8 @@ package hudson.model; +import hudson.PluginWrapper; +import hudson.PluginWrapper.Dependency; import hudson.model.UpdateSite.Data; import hudson.util.FormValidation; import hudson.util.PersistedList; @@ -32,9 +34,12 @@ import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -42,6 +47,7 @@ import static org.junit.Assert.*; +import jenkins.model.Jenkins; import jenkins.security.UpdateSiteWarningsConfiguration; import jenkins.security.UpdateSiteWarningsMonitor; import org.apache.commons.io.FileUtils; @@ -107,11 +113,9 @@ public void shutdownWebserver() throws Exception { } @Test public void relativeURLs() throws Exception { - PersistedList sites = j.jenkins.getUpdateCenter().getSites(); - sites.clear(); URL url = new URL(baseUrl, "/plugins/tasks-update-center.json"); UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString()); - sites.add(site); + overrideUpdateSite(site); assertEquals(FormValidation.ok(), site.updateDirectly(false).get()); Data data = site.getData(); assertNotNull(data); @@ -124,6 +128,28 @@ public void shutdownWebserver() throws Exception { assertEquals("Wrong name of plugin found", "Task Scanner Plug-in", tasksPlugin.getDisplayName()); } + @Test public void wikiUrlFromSingleSite() throws Exception { + UpdateSite site = getUpdateSite("/plugins/tasks-update-center.json"); + overrideUpdateSite(site); + PluginWrapper wrapper = buildPluginWrapper("dummy", "https://wiki.jenkins.io/display/JENKINS/dummy"); + assertEquals("https://plugins.jenkins.io/dummy", wrapper.getUrl()); + } + + @Test public void wikiUrlFromMoreSites() throws Exception { + UpdateSite site = getUpdateSite("/plugins/tasks-update-center.json"); + UpdateSite alternativeSite = getUpdateSite("/plugins/alternative-update-center.json", "alternative"); + overrideUpdateSite(site, alternativeSite); + // sites use different Wiki URL for dummy -> use URL from manifest + PluginWrapper wrapper = buildPluginWrapper("dummy", "https://wiki.jenkins.io/display/JENKINS/dummy"); + assertEquals("https://wiki.jenkins.io/display/JENKINS/dummy", wrapper.getUrl()); + // sites use the same Wiki URL for tasks -> use it + wrapper = buildPluginWrapper("tasks", "https://wiki.jenkins.io/display/JENKINS/tasks"); + assertEquals("https://plugins.jenkins.io/tasks", wrapper.getUrl()); + // only one site has it + wrapper = buildPluginWrapper("foo", "https://wiki.jenkins.io/display/JENKINS/foo"); + assertEquals("https://plugins.jenkins.io/foo", wrapper.getUrl()); + } + @Test public void updateDirectlyWithJson() throws Exception { UpdateSite us = new UpdateSite("default", new URL(baseUrl, "update-center.json").toExternalForm()); assertNull(us.getPlugin("AdaptivePlugin")); @@ -141,12 +167,8 @@ public void shutdownWebserver() throws Exception { } @Test public void incompleteWarningsJson() throws Exception { - PersistedList sites = j.jenkins.getUpdateCenter().getSites(); - sites.clear(); - URL url = new URL(baseUrl, "/plugins/warnings-update-center-malformed.json"); - UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString()); - sites.add(site); - assertEquals(FormValidation.ok(), site.updateDirectly(false).get()); + UpdateSite site = getUpdateSite("/plugins/warnings-update-center-malformed.json"); + overrideUpdateSite(site); assertEquals("number of warnings", 7, site.getData().getWarnings().size()); assertNotEquals("plugin data is present", Collections.emptyMap(), site.getData().plugins); } @@ -190,11 +212,38 @@ public void isPluginUpdateCompatible() throws Exception { assertFalse("isLegacyDefault should be false with null url",new UpdateSite(null,null).isLegacyDefault()); } - private UpdateSite getUpdateSite(String path) throws Exception { + return getUpdateSite(path, UpdateCenter.ID_DEFAULT); + } + + private UpdateSite getUpdateSite(String path, String id) throws Exception { URL url = new URL(baseUrl, path); - UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString()); + UpdateSite site = new UpdateSite(id, url.toString()); assertEquals(FormValidation.ok(), site.updateDirectly(false).get()); return site; } + + private void overrideUpdateSite(UpdateSite... overrideSites) { + PersistedList sites = j.jenkins.getUpdateCenter().getSites(); + sites.clear(); + sites.addAll(Arrays.asList(overrideSites)); + } + + private PluginWrapper buildPluginWrapper(String name, String wikiUrl) { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.put(new Attributes.Name("Short-Name"), name); + attributes.put(new Attributes.Name("Plugin-Version"), "1.0.0"); + attributes.put(new Attributes.Name("Url"), wikiUrl); + return new PluginWrapper( + Jenkins.get().getPluginManager(), + new File("/tmp/" + name + ".jpi"), + manifest, + null, + null, + new File("/tmp/" + name + ".jpi.disabled"), + null, + new ArrayList() + ); + } } diff --git a/test/src/test/resources/plugins/alternative-update-center.json b/test/src/test/resources/plugins/alternative-update-center.json new file mode 100644 index 000000000000..4277698cb0c2 --- /dev/null +++ b/test/src/test/resources/plugins/alternative-update-center.json @@ -0,0 +1,47 @@ +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": "alternative", + "plugins": { + "tasks": { + "buildDate": "Dec 17, 2008", + "dependencies": [], + "developers": [{"name": "lokadm"}], + "excerpt": " This plug-in scans for open tasks in a specified set of files in the project modules and visualizes the results. ", + "name": "tasks", + "wiki": "https://plugins.jenkins.io/tasks", + "requiredCore": "1.264", + "sha1": "wtzlciUKiMcg90H5CTYkGX6+r8Y=", + "title": "Jenkins Task Scanner Plug-in", + "url": "tasks.jpi", + "version": "2.23" + }, + "dummy": { + "buildDate": "Dec 17, 2008", + "dependencies": [], + "developers": [], + "excerpt": "…", + "name": "dummy", + "wiki": "https://plugins.example.com/dummy", + "requiredCore": "1.100", + "sha1": "wtzlcjUKiMcg90H5CTYkGX6+r8Y=", + "title": "Dummy", + "url": "http://nowhere.net/dummy.hpi", + "version": "1.0" + }, + "foo": { + "dependencies": [], + "name": "foo", + "wiki": "https://plugins.jenkins.io/foo", + "version": "1.0", + "url": "http://nowhere.net/dummy.hpi", + } + }, + "updateCenterVersion": 1 +} +); diff --git a/test/src/test/resources/plugins/tasks-update-center.json b/test/src/test/resources/plugins/tasks-update-center.json index 092d90d382f6..b70301c79701 100644 --- a/test/src/test/resources/plugins/tasks-update-center.json +++ b/test/src/test/resources/plugins/tasks-update-center.json @@ -14,6 +14,7 @@ updateCenter.post( "developers": [{"name": "lokadm"}], "excerpt": " This plug-in scans for open tasks in a specified set of files in the project modules and visualizes the results. ", "name": "tasks", + "wiki": "https://plugins.jenkins.io/tasks", "requiredCore": "1.264", "sha1": "wtzlciUKiMcg90H5CTYkGX6+r8Y=", "title": "Jenkins Task Scanner Plug-in", @@ -26,6 +27,7 @@ updateCenter.post( "developers": [], "excerpt": "…", "name": "dummy", + "wiki": "https://plugins.jenkins.io/dummy", "requiredCore": "1.100", "sha1": "wtzlcjUKiMcg90H5CTYkGX6+r8Y=", "title": "Dummy",