Allow to protect specific projects against deletion Some sites don't expose All-Projects and instead have custom top level projects that general projects inherit from. Allow to specifiy custom projects that may not be deleted, in addition to the default All-Projects and All-Users. Change-Id: Ie5227b9650e974571e539eaea73cd15606b239e7
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Configuration.java index be7186d..cd11508 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Configuration.java +++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Configuration.java
@@ -14,11 +14,16 @@ package com.googlesource.gerrit.plugins.deleteproject; +import static java.util.stream.Collectors.toList; + import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.server.config.PluginConfig; import com.google.gerrit.server.config.PluginConfigFactory; import com.google.inject.Inject; import com.google.inject.Singleton; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; @Singleton public class Configuration { @@ -27,6 +32,7 @@ private final boolean allowDeletionWithTags; private final boolean hideProjectOnPreserve; private final String deletedProjectsParent; + private final List<Pattern> protectedProjects; @Inject public Configuration(PluginConfigFactory pluginConfigFactory, @PluginName String pluginName) { @@ -34,6 +40,11 @@ allowDeletionWithTags = cfg.getBoolean("allowDeletionOfReposWithTags", true); hideProjectOnPreserve = cfg.getBoolean("hideProjectOnPreserve", false); deletedProjectsParent = cfg.getString("parentForDeletedProjects", DELETED_PROJECTS_PARENT); + protectedProjects = + Arrays.asList(cfg.getStringList("protectedProject")) + .stream() + .map(Pattern::compile) + .collect(toList()); } public boolean deletionWithTagsAllowed() { @@ -47,4 +58,8 @@ public String getDeletedProjectsParent() { return deletedProjectsParent; } + + public List<Pattern> protectedProjects() { + return protectedProjects; + } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjects.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjects.java index 4157dad..c1e697d 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjects.java +++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjects.java
@@ -28,17 +28,29 @@ public class ProtectedProjects { private final AllProjectsName allProjectsName; private final AllUsersName allUsersName; + private final Configuration config; @Inject ProtectedProjects( - AllProjectsNameProvider allProjectsNameProvider, AllUsersNameProvider allUsersNameProvider) { + AllProjectsNameProvider allProjectsNameProvider, + AllUsersNameProvider allUsersNameProvider, + Configuration config) { this.allProjectsName = allProjectsNameProvider.get(); this.allUsersName = allUsersNameProvider.get(); + this.config = config; + } + + private boolean isDefaultProtected(Project.NameKey name) { + return name.equals(allProjectsName) || name.equals(allUsersName); + } + + private boolean isCustomProtected(Project.NameKey name) { + return config.protectedProjects().stream().anyMatch(p -> p.matcher(name.get()).matches()); } @VisibleForTesting public boolean isProtected(Project.NameKey name) { - return name.equals(allProjectsName) || name.equals(allUsersName); + return isDefaultProtected(name) || isCustomProtected(name); } public boolean isProtected(ProjectResource rsrc) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index cbb1719..65f996d 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -43,3 +43,13 @@ is set to true. By default `Deleted-Projects`. + +<a id="protectedProject"> +`plugin.@PLUGIN@.protectedProject` +: The name of a project that is protected against deletion. May be an exact + name or a regular expression. + + May be specified more than once to specify multiple project names or + patterns. + + By default not set.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java index 5b11a7b..9133f64 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java
@@ -17,6 +17,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; +import com.google.common.collect.ImmutableList; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AllProjectsNameProvider; @@ -24,6 +25,7 @@ import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.PluginConfig; import com.google.gerrit.server.config.PluginConfigFactory; +import java.util.List; import org.eclipse.jgit.lib.Config; import org.junit.Before; import org.junit.Test; @@ -39,15 +41,18 @@ @Mock private AllUsersNameProvider allUsersMock; @Mock private PluginConfigFactory pluginConfigFactoryMock; + private PluginConfig pluginConfig; + private Configuration deleteConfig; private ProtectedProjects protectedProjects; @Before public void setup() throws Exception { when(allProjectsMock.get()).thenReturn(new AllProjectsName("All-Projects")); when(allUsersMock.get()).thenReturn(new AllUsersName("All-Users")); - PluginConfig pluginConfig = new PluginConfig(PLUGIN_NAME, new Config()); + pluginConfig = new PluginConfig(PLUGIN_NAME, new Config()); when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig); - protectedProjects = new ProtectedProjects(allProjectsMock, allUsersMock); + deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME); + protectedProjects = new ProtectedProjects(allProjectsMock, allUsersMock, deleteConfig); } @Test @@ -65,6 +70,25 @@ assertNotProtected("test-project"); } + @Test + public void customProjectIsProtected() throws Exception { + List<String> projects = ImmutableList.of("Custom-Parent", "^protected-.*"); + pluginConfig.setStringList("protectedProject", projects); + when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig); + deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME); + assertThat(deleteConfig.protectedProjects()).hasSize(projects.size()); + protectedProjects = new ProtectedProjects(allProjectsMock, allUsersMock, deleteConfig); + + assertProtected("protected-project-1"); + assertProtected("protected-project-2"); + assertProtected("Custom-Parent"); + assertNotProtected("test-project"); + assertNotProtected("protected"); + assertNotProtected("my-protected-project"); + assertNotProtected("Another-Custom-Parent"); + assertNotProtected("Custom-Parent-2"); + } + private void assertProtected(String name) { assertThat(protectedProjects.isProtected(new Project.NameKey(name))).isTrue(); }