diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 2ff190fb72..7ffbbf40d9 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -706,13 +706,14 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas // If this was a plugin that was detached some time in the past i.e. not just one of the // plugins that was bundled "for fun". if (ClassicPluginStrategy.isDetachedPlugin(name)) { - // If it's already installed and the installed version is older - // than the bundled version, then we upgrade. The bundled version is the min required version - // for "this" version of Jenkins, so we must upgrade. VersionNumber installedVersion = getPluginVersion(rootDir, name); VersionNumber bundledVersion = getPluginVersion(dir, name); - if (installedVersion != null && bundledVersion != null && installedVersion.isOlderThan(bundledVersion)) { - return true; + // If the plugin is already installed, we need to decide whether to replace it with the bundled version. + if (installedVersion != null && bundledVersion != null) { + // If the installed version is older than the bundled version, then it MUST be upgraded. + // If the installed version is newer than the bundled version, then it MUST NOT be upgraded. + // If the versions are equal we just keep the installed version. + return installedVersion.isOlderThan(bundledVersion); } } diff --git a/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java index 856e5263ba..c8cc60bce5 100644 --- a/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java +++ b/test/src/test/java/jenkins/install/LoadDetachedPluginsTest.java @@ -120,6 +120,25 @@ public class LoadDetachedPluginsTest { }); } + @Issue("JENKINS-48899") + @Test + @LocalData + public void upgradeFromJenkins2WithNewerPlugin() { + // @LocalData has command-launcher 1.2 installed, which should not be downgraded to the detached version: 1.0. + VersionNumber since = new VersionNumber("2.0"); + rr.then(r -> { + List detachedPlugins = ClassicPluginStrategy.getDetachedPlugins(since); + assertThat("Plugins have been detached since the pre-upgrade version", + detachedPlugins.size(), greaterThan(1)); + assertThat("Plugins detached between the pre-upgrade version and the current version should be installed", + getInstalledDetachedPlugins(r, detachedPlugins).size(), equalTo(detachedPlugins.size())); + Plugin commandLauncher = r.jenkins.getPlugin("command-launcher"); + assertThat("Installed detached plugins should not be overwritten by older versions", + commandLauncher.getWrapper().getVersionNumber(), equalTo(new VersionNumber("1.2"))); + assertNoFailedPlugins(r); + }); + } + @Test public void newInstallation() { rr.then(r -> { diff --git a/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip new file mode 100644 index 0000000000..2c18d023bd Binary files /dev/null and b/test/src/test/resources/jenkins/install/LoadDetachedPluginsTest/upgradeFromJenkins2WithNewerPlugin.zip differ