Merge branch 'develop' into feature/rebase

This commit is contained in:
Eduard Heimbuch
2020-09-21 14:01:34 +02:00
45 changed files with 968 additions and 66 deletions

View File

@@ -21,21 +21,51 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.api;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.event.ScmEventBus;
import sonia.scm.repository.Branch;
import sonia.scm.repository.BranchCreatedEvent;
import sonia.scm.repository.Repository;
import sonia.scm.repository.spi.BranchCommand;
import java.io.IOException;
/**
* @since 2.0
*/
public final class BranchCommandBuilder {
private static final Logger LOG = LoggerFactory.getLogger(BranchCommandBuilder.class);
private final Repository repository;
private final BranchCommand command;
private final ScmEventBus eventBus;
private final BranchRequest request = new BranchRequest();
public BranchCommandBuilder(Repository repository, BranchCommand command) {
this(repository, command, ScmEventBus.getInstance());
}
/**
* Creates a new {@link BranchCommandBuilder}.
*
* @param command type specific command implementation
*
* @deprecated use {@link #BranchCommandBuilder(Repository, BranchCommand)} instead.
*/
@Deprecated
public BranchCommandBuilder(BranchCommand command) {
this(null, command, ScmEventBus.getInstance());
}
@VisibleForTesting
BranchCommandBuilder(Repository repository, BranchCommand command, ScmEventBus eventBus) {
this.repository = repository;
this.command = command;
this.eventBus = eventBus;
}
/**
@@ -53,17 +83,23 @@ public final class BranchCommandBuilder {
* Execute the command and create a new branch with the given name.
* @param name The name of the new branch.
* @return The created branch.
* @throws IOException
*/
public Branch branch(String name) {
request.setNewBranch(name);
return command.branch(request);
Branch branch = command.branch(request);
fireCreatedEvent(branch);
return branch;
}
private void fireCreatedEvent(Branch branch) {
if (repository != null) {
eventBus.post(new BranchCreatedEvent(repository, branch.getName()));
} else {
LOG.warn("the branch command was created without a repository, so we are not able to fire a BranchCreatedEvent");
}
}
public void delete(String branchName) {
command.deleteOrClose(branchName);
}
private BranchCommand command;
private BranchRequest request = new BranchRequest();
}

View File

@@ -21,12 +21,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.api;
public enum MergeStrategy {
MERGE_COMMIT,
FAST_FORWARD_IF_POSSIBLE,
SQUASH,
REBASE
MERGE_COMMIT("merge commit", true),
FAST_FORWARD_IF_POSSIBLE("fast forward if possible", false),
SQUASH("squash", true),
REBASE("rebase", false);
private final String name;
private final boolean commitMessageAllowed;
MergeStrategy(String name, boolean commitMessageAllowed) {
this.name = name;
this.commitMessageAllowed = commitMessageAllowed;
}
public String getName() {
return name;
}
public boolean isCommitMessageAllowed() {
return commitMessageAllowed;
}
}

View File

@@ -177,7 +177,7 @@ public final class RepositoryService implements Closeable {
LOG.debug("create branch command for repository {}",
repository.getNamespaceAndName());
return new BranchCommandBuilder(provider.getBranchCommand());
return new BranchCommandBuilder(repository, provider.getBranchCommand());
}
/**

View File

@@ -298,10 +298,12 @@ public final class RepositoryServiceFactory {
/**
* Clear caches on repository push.
* We do this synchronously, because there are often workflows which are creating branches and fetch them straight
* after the creation.
*
* @param event hook event
*/
@Subscribe(referenceType = ReferenceType.STRONG)
@Subscribe(async = false, referenceType = ReferenceType.STRONG)
public void onEvent(PostReceiveRepositoryHookEvent event) {
Repository repository = event.getRepository();
@@ -324,13 +326,6 @@ public final class RepositoryServiceFactory {
}
}
@Subscribe(async = false)
@SuppressWarnings({"unchecked", "java:S3740", "rawtypes"})
public void onEvent(BranchCreatedEvent event) {
RepositoryCacheKeyPredicate predicate = new RepositoryCacheKeyPredicate(event.getRepository().getId());
cacheManager.getCache(BranchesCommandBuilder.CACHE_NAME).removeAll(predicate);
}
@Subscribe
public void onEvent(PublicKeyDeletedEvent event) {
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();

View File

@@ -0,0 +1,105 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.api;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.event.ScmEventBus;
import sonia.scm.repository.Branch;
import sonia.scm.repository.BranchCreatedEvent;
import sonia.scm.repository.Repository;
import sonia.scm.repository.spi.BranchCommand;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class BranchCommandBuilderTest {
@Mock
private BranchCommand command;
@Mock
private ScmEventBus eventBus;
private final Repository repository = new Repository("42", "git", "spaceships", "heart-of-gold");
private final String branchName = "feature/infinite_improbability_drive";
private final Branch branch = Branch.normalBranch(branchName, "42");
@Nested
class Creation {
@BeforeEach
void configureMocks() {
when(command.branch(any())).thenReturn(branch);
}
@Test
void shouldDelegateCreationToCommand() {
BranchCommandBuilder builder = new BranchCommandBuilder(repository, command, eventBus);
Branch returnedBranch = builder.branch(branchName);
assertThat(branch).isSameAs(returnedBranch);
}
@Test
void shouldSendBranchCreatedEvent() {
BranchCommandBuilder builder = new BranchCommandBuilder(repository, command, eventBus);
builder.branch(branchName);
ArgumentCaptor<BranchCreatedEvent> captor = ArgumentCaptor.forClass(BranchCreatedEvent.class);
verify(eventBus).post(captor.capture());
BranchCreatedEvent event = captor.getValue();
assertThat(event.getBranchName()).isEqualTo("feature/infinite_improbability_drive");
}
@Test
void shouldNotSendEventWithoutRepository() {
BranchCommandBuilder builder = new BranchCommandBuilder(null, command, eventBus);
builder.branch(branchName);
verify(eventBus, never()).post(any());
}
}
@Nested
class Deletion {
@Test
void shouldDelegateDeletionToCommand() {
BranchCommandBuilder builder = new BranchCommandBuilder(repository, command, eventBus);
builder.delete(branchName);
verify(command).deleteOrClose(branchName);
}
}
}