diff --git a/src/main/java/com/englishtown/bitbucket/hook/MirrorRepositoryHook.java b/src/main/java/com/englishtown/bitbucket/hook/MirrorRepositoryHook.java index e6c8a7d..a066acf 100644 --- a/src/main/java/com/englishtown/bitbucket/hook/MirrorRepositoryHook.java +++ b/src/main/java/com/englishtown/bitbucket/hook/MirrorRepositoryHook.java @@ -39,6 +39,8 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep boolean tags; boolean notes; boolean atomic; + boolean prune; + boolean force; } public static final String PLUGIN_SETTINGS_KEY = "com.englishtown.stash.hook.mirror"; @@ -49,6 +51,8 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep static final String SETTING_TAGS = "tags"; static final String SETTING_NOTES = "notes"; static final String SETTING_ATOMIC = "atomic"; + static final String SETTING_PRUNE = "prune"; + static final String SETTING_FORCE = "force"; static final int MAX_ATTEMPTS = 5; static final String DEFAULT_REFSPEC = "+refs/heads/*:refs/heads/*"; @@ -137,18 +141,26 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep GitScmCommandBuilder builder = (GitScmCommandBuilder) obj; PasswordHandler passwordHandler = getPasswordHandler(builder, password); - // Call push command with the prune flag and refspecs for heads and tags + // Call push command with the refspecs for heads and tags // Do not use the mirror flag as pull-request refs are included - builder.command("push") - .argument("--prune") // this deletes locally deleted branches - .argument(authenticatedUrl) - .argument("--force"); + builder.command("push"); + + // this deletes locally deleted branches + if (settings.prune) { + builder.argument("--prune"); + } // Use an atomic transaction to have a consistent state if (settings.atomic) { builder.argument("--atomic"); } + builder.argument(authenticatedUrl); + + if (settings.force) { + builder.argument("--force"); + } + // Add refspec args String refspecs = Strings.isNullOrEmpty(settings.refspec) ? DEFAULT_REFSPEC : settings.refspec; for (String refspec : refspecs.split("\\s|\\n")) { @@ -224,7 +236,7 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep boolean ok = true; logger.debug("MirrorRepositoryHook: validate started."); - List mirrorSettings = getMirrorSettings(settings, false, false, false); + List mirrorSettings = getMirrorSettings(settings, false, false, false, false, false); for (MirrorSettings ms : mirrorSettings) { if (!validate(ms, settings, errors)) { @@ -248,10 +260,10 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep } protected List getMirrorSettings(Settings settings) { - return getMirrorSettings(settings, true, true, true); + return getMirrorSettings(settings, true, true, true, true, true); } - protected List getMirrorSettings(Settings settings, boolean defTags, boolean defNotes, boolean defAtomic) { + protected List getMirrorSettings(Settings settings, boolean defTags, boolean defNotes, boolean defAtomic, boolean defPrune, boolean defForce) { List results = new ArrayList<>(); Map allSettings = settings.asMap(); @@ -269,6 +281,8 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep ms.tags = (settings.getBoolean(SETTING_TAGS + suffix, defTags)); ms.notes = (settings.getBoolean(SETTING_NOTES + suffix, defNotes)); ms.atomic = (settings.getBoolean(SETTING_ATOMIC + suffix, defAtomic)); + ms.prune = (settings.getBoolean(SETTING_PRUNE + suffix, defPrune)); + ms.force = (settings.getBoolean(SETTING_FORCE + suffix, defForce)); ms.suffix = String.valueOf(count++); results.add(ms); @@ -344,6 +358,8 @@ public class MirrorRepositoryHook implements AsyncPostReceiveRepositoryHook, Rep values.put(SETTING_TAGS + ms.suffix, ms.tags); values.put(SETTING_NOTES + ms.suffix, ms.notes); values.put(SETTING_ATOMIC + ms.suffix, ms.atomic); + values.put(SETTING_PRUNE + ms.suffix, ms.prune); + values.put(SETTING_FORCE + ms.suffix, ms.force); } // Unfortunately the settings are stored in an immutable map, so need to cheat with reflection diff --git a/src/main/resources/i18n/stash-hook-mirror.properties b/src/main/resources/i18n/stash-hook-mirror.properties index b256001..2494ed5 100644 --- a/src/main/resources/i18n/stash-hook-mirror.properties +++ b/src/main/resources/i18n/stash-hook-mirror.properties @@ -17,3 +17,5 @@ mirror-repository-hook.refspec.description=The git refspec(s) to mirror (default mirror-repository-hook.tags.label=Tags (ie. +refs/tags/*:refs/tags/*) mirror-repository-hook.notes.label=Notes (ie. +refs/notes/*:refs/notes/*) mirror-repository-hook.atomic.label=Atomic +mirror-repository-hook.prune.label=Prune +mirror-repository-hook.force.label=Force diff --git a/src/main/resources/static/mirror-repository-hook.soy b/src/main/resources/static/mirror-repository-hook.soy index 71026d3..5eeb9b8 100644 --- a/src/main/resources/static/mirror-repository-hook.soy +++ b/src/main/resources/static/mirror-repository-hook.soy @@ -98,6 +98,16 @@ 'id' : 'atomic' + $index, 'labelText': getText('mirror-repository-hook.atomic.label'), 'isChecked' : $config['atomic' + $index] != false + ], + [ + 'id' : 'prune' + $index, + 'labelText': getText('mirror-repository-hook.prune.label'), + 'isChecked' : $config['prune' + $index] != false + ], + [ + 'id' : 'force' + $index, + 'labelText': getText('mirror-repository-hook.force.label'), + 'isChecked' : $config['force' + $index] != false ] ] /} {/call} diff --git a/src/test/java/com/englishtown/bitbucket/hook/MirrorRepositoryHookTest.java b/src/test/java/com/englishtown/bitbucket/hook/MirrorRepositoryHookTest.java index 98a0442..7a1e443 100644 --- a/src/test/java/com/englishtown/bitbucket/hook/MirrorRepositoryHookTest.java +++ b/src/test/java/com/englishtown/bitbucket/hook/MirrorRepositoryHookTest.java @@ -31,6 +31,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; import static org.mockito.Mockito.*; /** @@ -269,6 +270,54 @@ public class MirrorRepositoryHookTest { } + @Test + public void testPruneFlag() throws Exception{ + when(passwordEncryptor.decrypt(anyString())).thenReturn(password); + + Repository repo = mock(Repository.class); + + RepositoryHookContext context = mock(RepositoryHookContext.class); + Settings settings = ownSettings(false, true); + when(context.getSettings()).thenReturn(settings); + when(context.getRepository()).thenReturn(repo); + + hook.postReceive(context, new ArrayList<>()); + + verify(executor).submit(argumentCaptor.capture()); + Runnable runnable = argumentCaptor.getValue(); + runnable.run(); + + verify(builder, times(1)).command(eq("push")); + verify(builder, never()).argument(eq("--prune")); + verify(builder, times(1)).argument(eq("--atomic")); + verify(builder, times(1)).argument(eq(repository)); + verify(builder, times(1)).argument(eq("--force")); + } + + @Test + public void testForceFlag() throws Exception{ + when(passwordEncryptor.decrypt(anyString())).thenReturn(password); + + Repository repo = mock(Repository.class); + + RepositoryHookContext context = mock(RepositoryHookContext.class); + Settings settings = ownSettings(true, false); + when(context.getSettings()).thenReturn(settings); + when(context.getRepository()).thenReturn(repo); + + hook.postReceive(context, new ArrayList<>()); + + verify(executor).submit(argumentCaptor.capture()); + Runnable runnable = argumentCaptor.getValue(); + runnable.run(); + + verify(builder, times(1)).command(eq("push")); + verify(builder, times(1)).argument(eq("--prune")); + verify(builder, times(1)).argument(eq("--atomic")); + verify(builder, times(1)).argument(eq(repository)); + verify(builder, never()).argument(eq("--force")); + } + private RepositoryHookContext buildContext(Repository repo) { RepositoryHookContext context = mock(RepositoryHookContext.class); Settings settings = defaultSettings(); @@ -290,6 +339,26 @@ public class MirrorRepositoryHookTest { when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_TAGS), eq(true))).thenReturn(true); when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_NOTES), eq(true))).thenReturn(true); when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_ATOMIC), eq(true))).thenReturn(true); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_PRUNE), eq(true))).thenReturn(true); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_FORCE), eq(true))).thenReturn(true); + return settings; + } + + private Settings ownSettings(boolean pruneFlag, boolean forceFlag) { + Map map = new HashMap(); + map.put(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL, ""); + + Settings settings = mock(Settings.class); + when(settings.asMap()).thenReturn(map); + when(settings.getString(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL), eq(""))).thenReturn(mirrorRepoUrlHttp); + when(settings.getString(eq(MirrorRepositoryHook.SETTING_USERNAME), eq(""))).thenReturn(username); + when(settings.getString(eq(MirrorRepositoryHook.SETTING_PASSWORD), eq(""))).thenReturn(password); + when(settings.getString(eq(MirrorRepositoryHook.SETTING_REFSPEC), eq(""))).thenReturn(refspec); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_TAGS), eq(true))).thenReturn(true); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_NOTES), eq(true))).thenReturn(true); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_ATOMIC), eq(true))).thenReturn(true); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_PRUNE), eq(pruneFlag))).thenReturn(pruneFlag); + when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_FORCE), eq(forceFlag))).thenReturn(forceFlag); return settings; } }