From 46c52cdb99eae298ed7285c00f4bcf21b5a2666a Mon Sep 17 00:00:00 2001 From: Laura Gorzitze Date: Tue, 13 Feb 2024 18:02:54 +0100 Subject: [PATCH] Fix uninstallable flag after discarding changes After uninstalling a plugin (without restart) and then discarding the changes, plugins which are required for the plugin can be uninstalled even if the uninstallation was reverted. This was caused by not updating the dependency relations between the plugins when an uninstallation has been canceled. Therefore, the updating step is added. --- .../dependent_plugin_uninstallation.yaml | 2 ++ .../scm/plugin/DefaultPluginManager.java | 1 + .../plugin/PendingPluginUninstallation.java | 4 +++ .../scm/plugin/DefaultPluginManagerTest.java | 28 ++++++++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 gradle/changelog/dependent_plugin_uninstallation.yaml diff --git a/gradle/changelog/dependent_plugin_uninstallation.yaml b/gradle/changelog/dependent_plugin_uninstallation.yaml new file mode 100644 index 0000000000..e8af43f1df --- /dev/null +++ b/gradle/changelog/dependent_plugin_uninstallation.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Required plugins can be uninstalled even when dependent plugins are still installed diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 00bab2569b..a08cccbc38 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -390,6 +390,7 @@ public class DefaultPluginManager implements PluginManager { PluginPermissions.write().check(); pendingUninstallQueue.forEach(PendingPluginUninstallation::cancel); pendingInstallQueue.forEach(PendingPluginInstallation::cancel); + pendingUninstallQueue.forEach(pendingPluginUninstallation -> dependencyTracker.addInstalled(pendingPluginUninstallation.getPlugin().getDescriptor())); pendingUninstallQueue.clear(); pendingInstallQueue.clear(); updateMayUninstallFlag(); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginUninstallation.java b/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginUninstallation.java index 76b96ac17f..277b5834d8 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginUninstallation.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginUninstallation.java @@ -53,4 +53,8 @@ class PendingPluginUninstallation { throw new PluginFailedToCancelInstallationException("failed to cancel uninstallation", name, ex); } } + + InstalledPlugin getPlugin() { + return plugin; + } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index e4459d11f6..20f4ed63d7 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -57,8 +57,10 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.anyOf; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -99,6 +101,9 @@ class DefaultPluginManagerTest { @Captor private ArgumentCaptor contextCaptor; + @Captor + private ArgumentCaptor booleanCaptor; + private DefaultPluginManager manager; @Mock @@ -458,7 +463,7 @@ class DefaultPluginManagerTest { } @Test - void shouldUseDependencyTrackerForUninstall() { + void shouldPreventUninstallationOfRequiredPlugin() { InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin"); InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin"); when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); @@ -553,6 +558,27 @@ class DefaultPluginManagerTest { verify(mailPlugin).setUninstallable(true); } + @Test + void shouldUpdateMayUninstallFlagAfterCancelingUninstallationOfDependentPlugin() { + InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin"); + InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin"); + when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin)); + + doNothing().when(mailPlugin).setUninstallable(booleanCaptor.capture()); + + manager.computeInstallationDependencies(); + + manager.uninstall("scm-review-plugin", false); + + manager.cancelPending(); + + List values = booleanCaptor.getAllValues(); + + assertThat(values).containsExactly(false, true, false); + } + @Test void shouldUpdateMayUninstallFlagAfterDependencyIsInstalled() { InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");