Add first integration tests for merge detection

This commit is contained in:
René Pfeuffer
2020-08-06 14:40:43 +02:00
parent 072d8f15c9
commit 6f20781812
21 changed files with 1071 additions and 7 deletions

View File

@@ -192,6 +192,7 @@
<version>2.10</version>
<configuration>
<artifactItems>
<!-- Copy war file -->
<artifactItem>
<groupId>sonia.scm</groupId>
<artifactId>scm-webapp</artifactId>
@@ -200,6 +201,15 @@
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<destFileName>scm-webapp.war</destFileName>
</artifactItem>
<!-- Copy integration test plugin -->
<artifactItem>
<groupId>sonia.scm.plugins</groupId>
<artifactId>scm-integration-test-plugin</artifactId>
<version>${project.version}</version>
<type>smp</type>
<outputDirectory>${scm.home}/plugins</outputDirectory>
<destFileName>scm-integration-test-plugin.smp</destFileName>
</artifactItem>
</artifactItems>
</configuration>
<executions>

View File

@@ -0,0 +1,176 @@
/*
* 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.it;
import io.restassured.RestAssured;
import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.it.utils.RepositoryUtil;
import sonia.scm.it.utils.RestUtil;
import sonia.scm.it.utils.TestData;
import sonia.scm.repository.Person;
import sonia.scm.repository.client.api.RepositoryClient;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static sonia.scm.it.utils.TestData.USER_SCM_ADMIN;
public class MergeDetectionITCase {
private static final Person ARTHUR = new Person("arthur", "arthur@hitchhiker.com");
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
RepositoryClient client;
String masterFile;
String developFile;
@Before
public void createRepository() throws IOException {
TestData.createDefault();
client = RepositoryUtil.createRepositoryClient("git", temporaryFolder.getRoot());
masterFile = createFile("hg2g.md");
developFile = createFile("how_to_make_tea.md");
client.getAddCommand().add(masterFile);
client.getCommitCommand().commit(ARTHUR, "Add base file");
client.getPushCommand().push();
client.getBranchCommand().branch("develop");
client.getCheckoutCommand().checkout("develop");
client.getAddCommand().add(developFile);
client.getCommitCommand().commit(ARTHUR, "add more important things");
client.getPushCommand().push();
}
@After
public void disableMergeDetection() {
RestAssured.given()
.auth().preemptive().basic(USER_SCM_ADMIN, USER_SCM_ADMIN)
.when()
.contentType("application/json")
.accept("application/json")
.body(toJson("{}"))
.post(RestUtil.createResourceUrl("integration-test/merge-detection/"))
.then()
.statusCode(204);
}
@Test
public void shouldDetectSimpleMergeAsMerged() throws IOException {
client.getCheckoutCommand().checkout("master");
client.getMergeCommand().merge("develop");
initializeMergeDetection("master", "develop");
client.getPushCommand().push();
Assertions.assertThat(getMergeDetectionResult("preMergeDetection", 0)).isTrue();
Assertions.assertThat(getMergeDetectionResult("postMergeDetection", 0)).isTrue();
}
@Test
public void shouldDetectMergeWhenBranchHasBeenDeletedAsMerged() throws IOException {
client.getCheckoutCommand().checkout("master");
client.getMergeCommand().merge("develop");
client.getPushCommand().push();
initializeMergeDetection("master", "develop");
client.getDeleteRemoteBranchCommand().delete("develop");
client.getPushCommand().push();
Assertions.assertThat(getMergeDetectionResult("preMergeDetection", 0)).isTrue();
Assertions.assertThat(getMergeDetectionResult("postMergeDetection", 0)).isTrue();
}
@Test
public void shouldDetectNormalPushAsNotMerged() throws IOException {
client.getCheckoutCommand().checkout("develop");
writeFile(developFile, "other content");
client.getAddCommand().add(developFile);
client.getCommitCommand().commit(ARTHUR, "simple commit");
initializeMergeDetection("master", "develop");
client.getPushCommand().push();
Assertions.assertThat(getMergeDetectionResult("preMergeDetection", 0)).isFalse();
Assertions.assertThat(getMergeDetectionResult("postMergeDetection", 0)).isFalse();
}
private boolean getMergeDetectionResult(String type, int n) {
return RestAssured.given()
.auth().preemptive().basic(USER_SCM_ADMIN, USER_SCM_ADMIN)
.when()
.accept("application/json")
.get(RestUtil.createResourceUrl("integration-test/"))
.then()
.statusCode(200)
.extract()
.jsonPath()
.getBoolean("_embedded." +
type +
"[" + n + "].merged");
}
private void initializeMergeDetection(String target, String branch) {
RestAssured.given()
.auth().preemptive().basic(USER_SCM_ADMIN, USER_SCM_ADMIN)
.when()
.contentType("application/json")
.accept("application/json")
.body(toJson(format("{'target': '%s', 'branch': '%s'}", target, branch)))
.post(RestUtil.createResourceUrl("integration-test/merge-detection/"))
.then()
.statusCode(204);
}
private String createFile(String name) throws IOException {
temporaryFolder.newFile(name);
writeFile(name, "Some content");
return name;
}
private void writeFile(String name, String content) throws IOException {
Path file = temporaryFolder.getRoot().toPath().resolve(name);
Files.write(file, singletonList(content));
}
private String toJson(String json) {
return json.replaceAll("'", "\"");
}
}

View File

@@ -45,6 +45,7 @@
<module>scm-git-plugin</module>
<module>scm-svn-plugin</module>
<module>scm-legacy-plugin</module>
<module>scm-integration-test-plugin</module>
</modules>
<dependencies>

View File

@@ -0,0 +1,49 @@
/*
* 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.client.spi;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import sonia.scm.repository.client.api.RepositoryClientException;
import java.io.IOException;
public class GitCheckoutCommand implements CheckoutCommand {
private Git git;
GitCheckoutCommand(Git git) {
this.git = git;
}
@Override
public void checkout(String name) throws IOException {
try {
git.checkout().setName(name).call();
} catch (GitAPIException ex) {
throw new RepositoryClientException("could not checkout branch or revision", ex);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.client.spi;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RefSpec;
import sonia.scm.repository.client.api.RepositoryClientException;
import java.io.IOException;
public class GitDeleteRemoteBranchCommand implements DeleteRemoteBranchCommand {
private final Git git;
private final CredentialsProvider credentialsProvider;
GitDeleteRemoteBranchCommand(Git git, CredentialsProvider credentialsProvider) {
this.git = git;
this.credentialsProvider = credentialsProvider;
}
@Override
public void delete(String name) throws IOException {
try {
git.branchDelete().setBranchNames("refs/heads/" + name).call();
RefSpec refSpec = new RefSpec()
.setSource(null)
.setDestination("refs/heads/" + name);
PushCommand push = git.push();
if (credentialsProvider != null) {
push.setCredentialsProvider(credentialsProvider);
}
push.setRefSpecs(refSpec).call();
// List<String> result = git.branchDelete().setBranchNames(name).call();
} catch (GitAPIException ex) {
throw new RepositoryClientException("could not delete branch", ex);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.client.spi;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.GitChangesetConverter;
import sonia.scm.repository.client.api.RepositoryClientException;
import java.io.IOException;
public class GitMergeCommand implements MergeCommand {
private final Git git;
GitMergeCommand(Git git) {
this.git = git;
}
@Override
public Changeset merge(MergeRequest request) throws IOException {
try (GitChangesetConverter converter = new GitChangesetConverter(git.getRepository())) {
ObjectId resolved = git.getRepository().resolve(request.getBranch());
MergeResult mergeResult = git.merge()
.include(request.getBranch(), resolved)
.setMessage(request.getMessage())
.call();
try (RevWalk revWalk = new RevWalk(git.getRepository())) {
RevCommit commit = revWalk.parseCommit(mergeResult.getNewHead());
return converter.createChangeset(commit);
}
} catch (GitAPIException ex) {
throw new RepositoryClientException("could not commit changes to repository", ex);
}
}
}

View File

@@ -49,7 +49,7 @@ public class GitRepositoryClientProvider extends RepositoryClientProvider
private static final Set<ClientCommand> SUPPORTED_COMMANDS =
ImmutableSet.of(ClientCommand.ADD, ClientCommand.REMOVE,
ClientCommand.COMMIT, ClientCommand.TAG, ClientCommand.BRANCH,
ClientCommand.PUSH);
ClientCommand.DELETE_REMOTE_BRANCH, ClientCommand.MERGE, ClientCommand.PUSH);
//~--- constructors ---------------------------------------------------------
@@ -118,6 +118,16 @@ public class GitRepositoryClientProvider extends RepositoryClientProvider
return new GitBranchCommand(git);
}
@Override
public DeleteRemoteBranchCommand getDeleteRemoteBranchCommand() {
return new GitDeleteRemoteBranchCommand(git, credentialsProvider);
}
@Override
public CheckoutCommand getCheckoutCommand() {
return new GitCheckoutCommand(git);
}
/**
* Method description
*
@@ -178,6 +188,11 @@ public class GitRepositoryClientProvider extends RepositoryClientProvider
return new GitTagCommand(git);
}
@Override
public MergeCommand getMergeCommand() {
return new GitMergeCommand(git);
}
@Override
public File getWorkingCopy() {
return git.getRepository().getWorkTree();

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>sonia.scm.plugins</groupId>
<artifactId>scm-plugins</artifactId>
<version>2.4.0-SNAPSHOT</version>
</parent>
<artifactId>scm-integration-test-plugin</artifactId>
<description>Add functions for integration tests. This is not intended for production systems.</description>
<version>2.4.0-SNAPSHOT</version>
<packaging>smp</packaging>
<dependencies>
<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,113 @@
/*
* 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.it.resource;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.Setter;
import sonia.scm.api.v2.resources.LinkBuilder;
import sonia.scm.api.v2.resources.ScmPathInfoStore;
import sonia.scm.plugin.Extension;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Links.linkingTo;
/**
* Web Service Resource to support integration tests.
*/
@OpenAPIDefinition(tags = {
@Tag(name = "Integration Test", description = "Support for integration tests")
})
@Path(IntegrationTestResource.INTEGRATION_TEST_PATH_V2)
@Extension
public class IntegrationTestResource {
static final String INTEGRATION_TEST_PATH_V2 = "v2/integration-test";
private final ScmPathInfoStore scmPathInfoStore;
private final MergeDetectionHelper mergeDetectionHelper;
@Inject
public IntegrationTestResource(ScmPathInfoStore scmPathInfoStore, MergeDetectionHelper mergeDetectionHelper) {
this.scmPathInfoStore = scmPathInfoStore;
this.mergeDetectionHelper = mergeDetectionHelper;
}
@GET
@Path("")
@Produces("application/json")
public CollectionDto get() {
Links links = linkingTo()
.self(self())
.build();
Embedded embedded = embeddedBuilder()
.with("preMergeDetection", mergeDetectionHelper.getPreMergeDetections())
.with("postMergeDetection", mergeDetectionHelper.getPostMergeDetections())
.build();
return new CollectionDto(links, embedded);
}
@POST
@Path("merge-detection")
@Consumes("application/json")
public void initMergeDetection(MergeDetectionConfigurationDto mergeDetectionConfiguration) {
mergeDetectionHelper.initialize(mergeDetectionConfiguration.getTarget(), mergeDetectionConfiguration.getBranch());
}
private String self() {
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), IntegrationTestResource.class);
return linkBuilder.method("get").parameters().href();
}
static class CollectionDto extends HalRepresentation {
CollectionDto(Links links, Embedded embedded) {
super(links, embedded);
}
@Override
protected HalRepresentation withEmbedded(String rel, HalRepresentation embeddedItem) {
return super.withEmbedded(rel, embeddedItem);
}
}
@Getter
@Setter
static class MergeDetectionConfigurationDto {
private String target;
private String branch;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.it.resource;
import com.github.legman.Subscribe;
import de.otto.edison.hal.HalRepresentation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import sonia.scm.EagerSingleton;
import sonia.scm.plugin.Extension;
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
import sonia.scm.repository.RepositoryHookEvent;
import sonia.scm.repository.spi.HookMergeDetectionProvider;
import java.util.ArrayList;
import java.util.List;
@EagerSingleton
@Extension
public class MergeDetectionHelper {
private final List<ResultDto> preMergeDetections = new ArrayList<>();
private final List<ResultDto> postMergeDetections = new ArrayList<>();
private String target;
private String branch;
@Subscribe
public void handlePreReceiveEvent(PreReceiveRepositoryHookEvent event) {
if (target == null || branch == null) {
return;
}
preMergeDetections.add(createDto(event));
}
@Subscribe
public void handlePostReceiveEvent(PostReceiveRepositoryHookEvent event) {
if (target == null || branch == null) {
return;
}
postMergeDetections.add(createDto(event));
}
public ResultDto createDto(RepositoryHookEvent event) {
HookMergeDetectionProvider mergeDetectionProvider = event.getContext().getMergeDetectionProvider();
boolean merged = mergeDetectionProvider.branchesMerged(target, branch);
return new ResultDto(
event.getClass().getSimpleName(),
event.getRepository().getNamespace(),
event.getRepository().getName(),
merged
);
}
void initialize(String target, String branch) {
this.target = target;
this.branch = branch;
preMergeDetections.clear();
postMergeDetections.clear();
}
public List<ResultDto> getPreMergeDetections() {
return preMergeDetections;
}
public List<ResultDto> getPostMergeDetections() {
return postMergeDetections;
}
@Getter
@AllArgsConstructor
static class ResultDto extends HalRepresentation {
private String type;
private String namespace;
private String name;
private boolean merged;
}
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<!DOCTYPE plugin SYSTEM "https://download.scm-manager.org/dtd/plugin/2.0.0-01.dtd">
<plugin>
<scm-version>2</scm-version>
<information>
<displayName>Integration Test Support</displayName>
<author>Cloudogu GmbH</author>
<category>Test</category>
</information>
<conditions>
<min-version>${project.parent.version}</min-version>
</conditions>
</plugin>

View File

@@ -0,0 +1,53 @@
/*
* 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.client.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.client.spi.CheckoutCommand;
import java.io.IOException;
/**
* @since 2.4.0
*/
public final class CheckoutCommandBuilder {
private static final Logger LOG = LoggerFactory.getLogger(CheckoutCommandBuilder.class);
private final CheckoutCommand command;
public CheckoutCommandBuilder(CheckoutCommand command) {
this.command = command;
}
public CheckoutCommandBuilder checkout(String name) throws IOException {
LOG.debug("checkout {}", name);
command.checkout(name);
return this;
}
}

View File

@@ -31,5 +31,5 @@ package sonia.scm.repository.client.api;
*/
public enum ClientCommand
{
ADD, REMOVE, COMMIT, PUSH, TAG, BRANCH
ADD, REMOVE, COMMIT, PUSH, TAG, BRANCH, DELETE_REMOTE_BRANCH, CHECKOUT, MERGE
}

View File

@@ -0,0 +1,53 @@
/*
* 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.client.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.client.spi.DeleteRemoteBranchCommand;
import java.io.IOException;
/**
* @since 2.4.0
*/
public final class DeleteRemoteBranchCommandBuilder {
private static final Logger LOG = LoggerFactory.getLogger(DeleteRemoteBranchCommandBuilder.class);
private DeleteRemoteBranchCommand command;
public DeleteRemoteBranchCommandBuilder(DeleteRemoteBranchCommand command) {
this.command = command;
}
public DeleteRemoteBranchCommandBuilder delete(String name) throws IOException {
LOG.debug("delete branch {}", name);
command.delete(name);
return this;
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.client.api;
import sonia.scm.repository.client.spi.MergeCommand;
import sonia.scm.repository.client.spi.MergeRequest;
import java.io.IOException;
/**
* @since 2.4.0
*/
public final class MergeCommandBuilder {
private final MergeCommand command;
MergeCommandBuilder(MergeCommand command) {
this.command = command;
}
public void merge(String branch) throws IOException {
MergeRequest request = new MergeRequest();
request.setBranch(branch);
command.merge(request);
}
}

View File

@@ -60,6 +60,18 @@ public final class RepositoryClient implements Closeable {
return new BranchCommandBuilder(clientProvider.getBranchCommand());
}
public DeleteRemoteBranchCommandBuilder getDeleteRemoteBranchCommand() {
logger.trace("delete branch command");
return new DeleteRemoteBranchCommandBuilder(clientProvider.getDeleteRemoteBranchCommand());
}
public CheckoutCommandBuilder getCheckoutCommand() {
logger.trace("create checkout command");
return new CheckoutCommandBuilder(clientProvider.getCheckoutCommand());
}
public CommitCommandBuilder getCommitCommand() {
logger.trace("create commit command");
@@ -84,6 +96,12 @@ public final class RepositoryClient implements Closeable {
return new TagCommandBuilder(clientProvider.getTagCommand());
}
public MergeCommandBuilder getMergeCommand() {
logger.trace("create merge command");
return new MergeCommandBuilder(clientProvider.getMergeCommand());
}
public File getWorkingCopy() {
return clientProvider.getWorkingCopy();
}

View File

@@ -0,0 +1,35 @@
/*
* 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.client.spi;
import java.io.IOException;
/**
* @since 2.4.0
*/
public interface CheckoutCommand {
void checkout(String name) throws IOException;
}

View File

@@ -0,0 +1,32 @@
/*
* 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.client.spi;
import java.io.IOException;
public interface DeleteRemoteBranchCommand {
void delete(String name) throws IOException;
}

View File

@@ -0,0 +1,37 @@
/*
* 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.client.spi;
import sonia.scm.repository.Changeset;
import java.io.IOException;
/**
* @since 2.4.0
*/
public interface MergeCommand {
Changeset merge(MergeRequest request) throws IOException;
}

View File

@@ -0,0 +1,89 @@
/*
* 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.client.spi;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
/**
* @since 2.4.0
*/
public final class MergeRequest {
private String branch;
private String message;
@Override
public boolean equals(Object obj) {
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final MergeRequest other = (MergeRequest) obj;
return Objects.equal(branch, other.branch)
&& Objects.equal(message, other.message);
}
@Override
public int hashCode() {
return Objects.hashCode(branch, message);
}
public void reset() {
this.branch = null;
this.message = null;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("branch", branch)
.add("message", message)
.toString();
}
public void setBranch(String branch) {
this.branch = branch;
}
public void setMessage(String message) {
this.message = message;
}
String getBranch() {
return branch;
}
String getMessage() {
return message;
}
}

View File

@@ -87,6 +87,14 @@ public abstract class RepositoryClientProvider implements Closeable
throw new ClientCommandNotSupportedException(ClientCommand.BRANCH);
}
public DeleteRemoteBranchCommand getDeleteRemoteBranchCommand() {
throw new ClientCommandNotSupportedException(ClientCommand.DELETE_REMOTE_BRANCH);
}
public CheckoutCommand getCheckoutCommand() {
throw new ClientCommandNotSupportedException(ClientCommand.CHECKOUT);
}
/**
* Method description
*
@@ -131,6 +139,10 @@ public abstract class RepositoryClientProvider implements Closeable
throw new ClientCommandNotSupportedException(ClientCommand.TAG);
}
public MergeCommand getMergeCommand() {
throw new ClientCommandNotSupportedException(ClientCommand.MERGE);
}
/**
* Returns the working copy of the repository client.
*