Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: java
dist: trusty
jdk:
- oraclejdk8
dist: trusty
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,18 @@ private void runMirrorCommand(MirrorSettings settings, Repository repository) {

// Call push command with the prune flag and refspecs for heads and tags
// Do not use the mirror flag as pull-request refs are included
ScmCommandBuilder<?> builder = scmService.createBuilder(repository)
.command("push")
.argument("--prune") // this deletes locally deleted branches
.argument(authenticatedUrl)
.argument("--force");
ScmCommandBuilder<?> builder = scmService.createBuilder(repository).command("push");

// this deletes locally deleted branches
if (settings.prune) {
builder.argument("--prune");
}

builder.argument(authenticatedUrl);

if (settings.force) {
builder.argument("--force");
}

// Use an atomic transaction to have a consistent state
if (settings.atomic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class MirrorRepositoryHook implements PostRepositoryHook<RepositoryHookRe
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";

/**
* Trigger types that don't cause a mirror to happen
Expand Down Expand Up @@ -125,7 +127,8 @@ public Repository visit(@Nonnull RepositoryScope scope) {
boolean ok = true;
logger.debug("MirrorRepositoryHook: validate started.");

List<MirrorSettings> mirrorSettings = getMirrorSettings(settings, false, false, false);
List<MirrorSettings> mirrorSettings = getMirrorSettings(settings, false, false, false, false, false);

for (MirrorSettings ms : mirrorSettings) {
if (!validate(ms, errors)) {
ok = false;
Expand All @@ -144,10 +147,10 @@ public Repository visit(@Nonnull RepositoryScope scope) {
}

private List<MirrorSettings> getMirrorSettings(Settings settings) {
return getMirrorSettings(settings, true, true, true);
return getMirrorSettings(settings, true, true, true, true, true);
}

private List<MirrorSettings> getMirrorSettings(Settings settings, boolean defTags, boolean defNotes, boolean defAtomic) {
private List<MirrorSettings> getMirrorSettings(Settings settings, boolean defTags, boolean defNotes, boolean defAtomic, boolean defPrune, boolean defForce) {
Map<String, Object> allSettings = settings.asMap();
int count = 0;

Expand All @@ -164,6 +167,8 @@ private List<MirrorSettings> getMirrorSettings(Settings settings, boolean defTag
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);
Expand Down Expand Up @@ -239,6 +244,8 @@ private void updateSettings(List<MirrorSettings> mirrorSettings, Settings settin
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ class MirrorSettings implements Serializable {
boolean tags;
boolean notes;
boolean atomic;
boolean prune;
boolean force;
}
2 changes: 2 additions & 0 deletions src/main/resources/i18n/stash-hook-mirror.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions src/main/resources/static/mirror-repository-hook.soy
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.englishtown.bitbucket.hook;

import com.atlassian.bitbucket.hook.repository.PostRepositoryHookContext;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.bitbucket.repository.Repository;
Expand All @@ -8,6 +9,7 @@
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.setting.Settings;
import com.atlassian.bitbucket.user.DummySecurityService;
import com.atlassian.bitbucket.user.SecurityService;
import org.junit.Before;
Expand All @@ -20,7 +22,9 @@

import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.atlassian.bitbucket.mockito.MockitoUtils.returnArg;
import static com.atlassian.bitbucket.mockito.MockitoUtils.returnFirst;
Expand All @@ -37,8 +41,10 @@ public class MirrorBucketProcessorTest {
private static final String URL_HTTP = "https://bitbucket-mirror.englishtown.com/scm/test/test.git";
private static final String URL_SSH = "ssh://git@bitbucket-mirror.englishtown.com/scm/test/test.git";

private static final MirrorSettings SETTINGS = new MirrorSettings() {
{
private static final MirrorSettings SETTINGS = ownSettings(true, true);

private static MirrorSettings ownSettings(boolean pruneFlag, boolean forceFlag) {
return new MirrorSettings() {{
mirrorRepoUrl = URL_SSH;
password = "test-password";
refspec = "+refs/heads/master:refs/heads/master +refs/heads/develop:refs/heads/develop";
Expand All @@ -47,8 +53,11 @@ public class MirrorBucketProcessorTest {
atomic = true;
notes = true;
tags = true;
}
};
prune = pruneFlag;
force = forceFlag;
}};
}

private static final MirrorRequest REQUEST = new MirrorRequest(1, SETTINGS);
private static final List<MirrorRequest> REQUESTS = Collections.singletonList(REQUEST);

Expand Down Expand Up @@ -113,6 +122,53 @@ public void testProcess() {
verify(scmService).createBuilder(same(repository));
}


@Test
public void testPruneFlag() throws Exception{
when(repositoryService.getById(eq(1))).thenReturn(repository);

MirrorSettings settings = ownSettings(false, true);
MirrorRequest request = new MirrorRequest(1, settings);

processor.process("ignored", Collections.singletonList(request));

verify(builder).command(eq("push"));
verify(builder).argument(eq("--force"));
verify(builder).argument(eq(URL_SSH));
verify(builder).argument(eq("--atomic"));
verify(builder).argument(eq("+refs/heads/master:refs/heads/master"));
verify(builder).argument(eq("+refs/heads/develop:refs/heads/develop"));
verify(builder).argument(eq("+refs/tags/*:refs/tags/*"));
verify(builder).argument(eq("+refs/notes/*:refs/notes/*"));
verify(command).call();
verify(command).setTimeout(eq(Duration.ofSeconds(120L)));
verify(passwordEncryptor).decrypt(eq(SETTINGS.password));
verify(scmService).createBuilder(same(repository));
}

@Test
public void testForceFlag() throws Exception{
when(repositoryService.getById(eq(1))).thenReturn(repository);

MirrorSettings settings = ownSettings(true, false);
MirrorRequest request = new MirrorRequest(1, settings);

processor.process("ignored", Collections.singletonList(request));

verify(builder).command(eq("push"));
verify(builder).argument(eq("--prune"));
verify(builder).argument(eq(URL_SSH));
verify(builder).argument(eq("--atomic"));
verify(builder).argument(eq("+refs/heads/master:refs/heads/master"));
verify(builder).argument(eq("+refs/heads/develop:refs/heads/develop"));
verify(builder).argument(eq("+refs/tags/*:refs/tags/*"));
verify(builder).argument(eq("+refs/notes/*:refs/notes/*"));
verify(command).call();
verify(command).setTimeout(eq(Duration.ofSeconds(120L)));
verify(passwordEncryptor).decrypt(eq(SETTINGS.password));
verify(scmService).createBuilder(same(repository));
}

@Test
public void testProcessWithDeletedRepository() {
processor.process("ignored", REQUESTS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import com.atlassian.bitbucket.hook.repository.*;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.CommandErrorHandler;
import com.atlassian.bitbucket.scm.CommandExitHandler;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.git.GitScm;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.scope.Scope;
import com.atlassian.bitbucket.scope.Scopes;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
Expand All @@ -29,6 +33,7 @@
import static com.englishtown.bitbucket.hook.MirrorRepositoryHook.PROP_ATTEMPTS;
import static com.englishtown.bitbucket.hook.MirrorRepositoryHook.PROP_THREADS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.mockito.Mockito.*;

/**
Expand Down Expand Up @@ -63,7 +68,7 @@ public class MirrorRepositoryHookTest {

@Before
public void setup() {
doReturn(bucketedExecutor).when(concurrencyService).getBucketedExecutor(anyString(), any());
doReturn(bucketedExecutor).when(concurrencyService).getBucketedExecutor(anyString(), any());

when(propertiesService.getPluginProperty(eq(PROP_ATTEMPTS), anyInt())).thenAnswer(returnArg(1));
when(propertiesService.getPluginProperty(eq(PROP_THREADS), anyInt())).thenAnswer(returnArg(1));
Expand Down Expand Up @@ -237,6 +242,7 @@ public void testValidateForProject() {
verifyZeroInteractions(bucketedExecutor, errors, settings);
}


private PostRepositoryHookContext buildContext() {
Settings settings = defaultSettings();

Expand Down Expand Up @@ -266,7 +272,8 @@ private Settings defaultSettings() {
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;
}
}