mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 00:45:44 +01:00
merge with develop
This commit is contained in:
@@ -5,9 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Introduced merge detection for receive hooks ([#1278](https://github.com/scm-manager/scm-manager/pull/1278))
|
||||
- Anonymous mode for the web ui ([#1284](https://github.com/scm-manager/scm-manager/pull/1284))
|
||||
|
||||
### Fixed
|
||||
- Repository names may not end with ".git" ([#1277](https://github.com/scm-manager/scm-manager/pull/1277))
|
||||
|
||||
## [2.3.1] - 2020-08-04
|
||||
### Added
|
||||
- New api to resolve SCM-Manager root url ([#1276](https://github.com/scm-manager/scm-manager/pull/1276))
|
||||
|
||||
@@ -25,7 +25,7 @@ The location of the file depends also on the type of installation.
|
||||
|
||||
| Type of Installation | Path |
|
||||
|----------------------|---------|
|
||||
| Docker | /opt/scm-server/conf/logging.xml |
|
||||
| Docker | /etc/scm/logging.xml |
|
||||
| RPM | /etc/scm/logging.xml |
|
||||
| DEB | /etc/scm/logging.xml |
|
||||
| Unix | $EXTRACT_PATH/scm-server/conf/logging.xml |
|
||||
|
||||
24
docs/en/development/integration-tests.md
Normal file
24
docs/en/development/integration-tests.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Integration Tests
|
||||
subtitle: How to run integration tests
|
||||
displayToc: false
|
||||
---
|
||||
|
||||
You can find the integration tests in the module **scm-it** (and a few still in **scm-webapp**). To run them,
|
||||
simply start maven with the profile `it`:
|
||||
|
||||
```
|
||||
mvn install -Pit -DskipUnitTests -pl :scm-webapp,:scm-it
|
||||
```
|
||||
|
||||
This will start a jetty server and execute all integration tests in the maven phase `integration-test` using the
|
||||
normal failsafe plugin. Integration tests are all classes ending with `ITCase` and are written using JUnit.
|
||||
|
||||
To develop integration tests, you should start a local server with the **scm-integration-test-plugin**. This plugin is
|
||||
used as a way to introspect server internals. For example you can register event listeners here and access their
|
||||
triggers with a REST endpoint. Of course, this plugin is not and should not be installed in productive systems.
|
||||
You can start the server with this plugin using the following maven call:
|
||||
|
||||
```
|
||||
mvn run -pl :scm-integration-test-plugin
|
||||
```
|
||||
@@ -31,6 +31,7 @@
|
||||
- /development/definition-of-done/
|
||||
- /development/ui-dod/
|
||||
- /development/decision-table/
|
||||
- /development/integration-tests/
|
||||
|
||||
- section: Plugin Development
|
||||
entries:
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -33,18 +33,18 @@ import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.PreProcessorUtil;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.spi.HookContextProvider;
|
||||
import sonia.scm.repository.spi.HookMergeDetectionProvider;
|
||||
|
||||
/**
|
||||
* The context for all repository hooks. With the {@link HookContext} class it
|
||||
* is able to send messages back to the client, retrieve {@link Changeset}s
|
||||
* which are added during this push/commit and gives informations about changed
|
||||
* which are added during this push/commit and gives informations about changed
|
||||
* branches and tags.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.33
|
||||
*/
|
||||
public final class HookContext
|
||||
{
|
||||
public final class HookContext {
|
||||
|
||||
/**
|
||||
* the logger for HookContext
|
||||
@@ -52,8 +52,6 @@ public final class HookContext
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HookContext.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
@@ -62,9 +60,7 @@ public final class HookContext
|
||||
* @param repository
|
||||
* @param preProcessorUtil
|
||||
*/
|
||||
HookContext(HookContextProvider provider, Repository repository,
|
||||
PreProcessorUtil preProcessorUtil)
|
||||
{
|
||||
HookContext(HookContextProvider provider, Repository repository, PreProcessorUtil preProcessorUtil) {
|
||||
this.provider = provider;
|
||||
this.repository = repository;
|
||||
this.preProcessorUtil = preProcessorUtil;
|
||||
@@ -77,41 +73,33 @@ public final class HookContext
|
||||
* about changed branches during the current hook.
|
||||
*
|
||||
* @return {@link HookBranchProvider}
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
* by the underlying provider
|
||||
*
|
||||
*
|
||||
* @since 1.45
|
||||
*/
|
||||
public HookBranchProvider getBranchProvider()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create branch provider for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
public HookBranchProvider getBranchProvider() {
|
||||
logger.debug("create branch provider for repository {}",
|
||||
repository.getName());
|
||||
|
||||
return provider.getBranchProvider();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link HookTagProvider} which is able to return informations
|
||||
* about changed tags during the current hook.
|
||||
*
|
||||
* @return {@link HookTagProvider}
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
* by the underlying provider
|
||||
*
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public HookTagProvider getTagProvider()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create tag provider for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
public HookTagProvider getTagProvider() {
|
||||
logger.debug("create tag provider for repository {}",
|
||||
repository.getName());
|
||||
|
||||
return provider.getTagProvider();
|
||||
}
|
||||
@@ -122,25 +110,19 @@ public final class HookContext
|
||||
*
|
||||
*
|
||||
* @return {@link HookChangesetBuilder}
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
* by the underlying provider
|
||||
*/
|
||||
public HookChangesetBuilder getChangesetProvider()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create changeset provider for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
public HookChangesetBuilder getChangesetProvider() {
|
||||
logger.debug("create changeset provider for repository {}",
|
||||
repository.getName());
|
||||
|
||||
//J-
|
||||
return new HookChangesetBuilder(
|
||||
repository,
|
||||
repository,
|
||||
preProcessorUtil,
|
||||
provider.getChangesetProvider()
|
||||
);
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,21 +134,33 @@ public final class HookContext
|
||||
*
|
||||
* @return {@link HookMessageProvider} which is able to send message back to
|
||||
* the scm client
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
* by the underlying provider
|
||||
*/
|
||||
public HookMessageProvider getMessageProvider()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create message provider for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
public HookMessageProvider getMessageProvider() {
|
||||
logger.debug("create message provider for repository {}",
|
||||
repository.getName());
|
||||
|
||||
return provider.getMessageProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link HookMergeDetectionProvider} which is able to check whether two
|
||||
* branches have been merged with the incoming changesets.
|
||||
*
|
||||
* @return {@link HookMergeDetectionProvider} which is able to detect merges.
|
||||
*
|
||||
* @throws HookFeatureIsNotSupportedException if the feature is not supported
|
||||
* by the underlying provider
|
||||
*/
|
||||
public HookMergeDetectionProvider getMergeDetectionProvider() {
|
||||
logger.debug("create merge detection provider for repository {}",
|
||||
repository.getName());
|
||||
|
||||
return provider.getMergeDetectionProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the underlying provider support the requested feature.
|
||||
*
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
/**
|
||||
@@ -49,11 +49,18 @@ public enum HookFeature
|
||||
* @since 1.45
|
||||
*/
|
||||
BRANCH_PROVIDER,
|
||||
|
||||
|
||||
/**
|
||||
* Hook tag provider
|
||||
*
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
TAG_PROVIDER;
|
||||
TAG_PROVIDER,
|
||||
|
||||
/**
|
||||
* Provider to detect merges
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
MERGE_DETECTION_PROVIDER
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -50,7 +50,7 @@ public abstract class HookContextProvider
|
||||
/**
|
||||
* Return the provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
* The method will throw a {@link HookException} if the client is already disconnected.
|
||||
*
|
||||
*
|
||||
* @return provider specific {@link HookMessageProvider}
|
||||
*/
|
||||
public final HookMessageProvider getMessageProvider()
|
||||
@@ -86,31 +86,31 @@ public abstract class HookContextProvider
|
||||
|
||||
/**
|
||||
* Return the provider specific {@link HookBranchProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
*
|
||||
*
|
||||
* @return provider specific {@link HookBranchProvider}
|
||||
*
|
||||
*
|
||||
* @since 1.45
|
||||
*/
|
||||
public HookBranchProvider getBranchProvider()
|
||||
{
|
||||
throw new HookFeatureIsNotSupportedException(HookFeature.BRANCH_PROVIDER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the provider specific {@link HookTagProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
*
|
||||
*
|
||||
* @return provider specific {@link HookTagProvider}
|
||||
*
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public HookTagProvider getTagProvider()
|
||||
public HookTagProvider getTagProvider()
|
||||
{
|
||||
throw new HookFeatureIsNotSupportedException(HookFeature.TAG_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the provider specific {@link HookChangesetProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
*
|
||||
*
|
||||
* @return provider specific {@link HookChangesetProvider}
|
||||
*/
|
||||
public HookChangesetProvider getChangesetProvider()
|
||||
@@ -118,11 +118,21 @@ public abstract class HookContextProvider
|
||||
throw new HookFeatureIsNotSupportedException(HookFeature.CHANGESET_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the provider specific {@link HookMergeDetectionProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
*
|
||||
* @return provider specific {@link HookMergeDetectionProvider}
|
||||
*/
|
||||
public HookMergeDetectionProvider getMergeDetectionProvider()
|
||||
{
|
||||
throw new HookFeatureIsNotSupportedException(HookFeature.MERGE_DETECTION_PROVIDER);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}.
|
||||
*
|
||||
*
|
||||
* @return provider specific {@link HookChangesetProvider}
|
||||
*/
|
||||
protected HookMessageProvider createMessageProvider()
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.spi;
|
||||
|
||||
/**
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface HookMergeDetectionProvider {
|
||||
|
||||
/**
|
||||
* Checks whether <code>branch</code> has been merged into <code>target</code>. So this will also return
|
||||
* <code>true</code>, when <code>branch</code> has been deleted with this change.
|
||||
*
|
||||
* @param target The name of the branch to check, whether the other branch has been merged into.
|
||||
* @param branch The name of the branch to check, whether it has been merged into the other branch.
|
||||
* @return <code>true</code> when <code>branch</code> has been merged into <code>target</code>, <code>false</code>
|
||||
* otherwise.
|
||||
*/
|
||||
boolean branchesMerged(String target, String branch);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -273,7 +283,7 @@
|
||||
|
||||
<properties>
|
||||
<scm.stage>DEVELOPMENT</scm.stage>
|
||||
<scm.home>target/scm-it</scm.home>
|
||||
<scm.home>${project.parent.build.directory}/scm-it</scm.home>
|
||||
<scm-it.logbackConfiguration>${project.basedir}/../scm-webapp/src/main/resources/logback.default.xml</scm-it.logbackConfiguration>
|
||||
</properties>
|
||||
|
||||
|
||||
185
scm-it/src/test/java/sonia/scm/it/MergeDetectionITCase.java
Normal file
185
scm-it/src/test/java/sonia/scm/it/MergeDetectionITCase.java
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
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;
|
||||
|
||||
class MergeDetectionITCase {
|
||||
|
||||
private static final Person ARTHUR = new Person("arthur", "arthur@hitchhiker.com");
|
||||
|
||||
RepositoryClient client;
|
||||
String masterFile;
|
||||
String developFile;
|
||||
|
||||
@BeforeEach
|
||||
void createRepository(@TempDir Path tempDir) throws IOException {
|
||||
TestData.createDefault();
|
||||
|
||||
client = RepositoryUtil.createRepositoryClient("git", tempDir.toFile());
|
||||
|
||||
masterFile = createFile(tempDir, "hg2g.md");
|
||||
developFile = createFile(tempDir, "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();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
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
|
||||
void shouldDetectSimpleMergeAsMerged() throws IOException {
|
||||
client.getCheckoutCommand().checkout("master");
|
||||
client.getMergeCommand().noFf().merge("develop");
|
||||
|
||||
initializeMergeDetection("master", "develop");
|
||||
|
||||
client.getPushCommand().push();
|
||||
|
||||
Assertions.assertThat(getMergeDetectionResult("preMergeDetection", 0)).isTrue();
|
||||
Assertions.assertThat(getMergeDetectionResult("postMergeDetection", 0)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDetectFastForwardAsMerged() 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
|
||||
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
|
||||
void shouldDetectNormalPushAsNotMerged(@TempDir Path tempDir) throws IOException {
|
||||
client.getCheckoutCommand().checkout("develop");
|
||||
writeFile(tempDir, 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(Path tempDir, String name) throws IOException {
|
||||
Files.createFile(tempDir.resolve(name));
|
||||
writeFile(tempDir, name, "Some content");
|
||||
return name;
|
||||
}
|
||||
|
||||
private void writeFile(Path tempDir, String name, String content) throws IOException {
|
||||
Path file = tempDir.resolve(name);
|
||||
Files.write(file, singletonList(content));
|
||||
}
|
||||
|
||||
private String toJson(String json) {
|
||||
return json.replaceAll("'", "\"");
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -337,6 +337,16 @@ public final class GitUtil
|
||||
return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS);
|
||||
}
|
||||
|
||||
public static Ref getBranchIdOrCurrentHead(org.eclipse.jgit.lib.Repository gitRepository, String requestedBranch) throws IOException {
|
||||
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
||||
logger.trace("no default branch configured, use repository head as default");
|
||||
Optional<Ref> repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository);
|
||||
return repositoryHeadRef.orElse(null);
|
||||
} else {
|
||||
return GitUtil.getBranchId(gitRepository, requestedBranch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.spi.GitLogComputer;
|
||||
import sonia.scm.repository.spi.HookMergeDetectionProvider;
|
||||
import sonia.scm.repository.spi.LogCommandRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GitReceiveHookMergeDetectionProvider implements HookMergeDetectionProvider {
|
||||
private final Repository repository;
|
||||
private final String repositoryId;
|
||||
private final List<ReceiveCommand> receiveCommands;
|
||||
|
||||
public GitReceiveHookMergeDetectionProvider(Repository repository, String repositoryId, List<ReceiveCommand> receiveCommands) {
|
||||
this.repository = repository;
|
||||
this.repositoryId = repositoryId;
|
||||
this.receiveCommands = receiveCommands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean branchesMerged(String target, String branch) {
|
||||
LogCommandRequest request = new LogCommandRequest();
|
||||
request.setBranch(findRelevantRevisionForBranchIfToBeUpdated(branch));
|
||||
request.setAncestorChangeset(findRelevantRevisionForBranchIfToBeUpdated(target));
|
||||
request.setPagingLimit(1);
|
||||
|
||||
return new GitLogComputer(repositoryId, repository).compute(request).getTotal() == 0;
|
||||
}
|
||||
|
||||
private String findRelevantRevisionForBranchIfToBeUpdated(String branch) {
|
||||
return receiveCommands
|
||||
.stream()
|
||||
.filter(receiveCommand -> isReceiveCommandForBranch(branch, receiveCommand))
|
||||
.map(this::getRelevantRevision)
|
||||
.map(AnyObjectId::getName)
|
||||
.findFirst()
|
||||
.orElse(branch);
|
||||
}
|
||||
|
||||
private boolean isReceiveCommandForBranch(String branch, ReceiveCommand receiveCommand) {
|
||||
return receiveCommand.getType() != ReceiveCommand.Type.CREATE
|
||||
&& GitUtil.getBranch(receiveCommand.getRef()).equals(branch);
|
||||
}
|
||||
|
||||
private ObjectId getRelevantRevision(ReceiveCommand receiveCommand) {
|
||||
if (receiveCommand.getType() == ReceiveCommand.Type.DELETE) {
|
||||
return receiveCommand.getOldId();
|
||||
} else {
|
||||
return receiveCommand.getNewId();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.GitWorkingCopyFactory;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Person;
|
||||
@@ -59,6 +58,7 @@ import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
import static sonia.scm.repository.GitUtil.getBranchIdOrCurrentHead;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
@@ -123,15 +123,9 @@ class AbstractGitCommand
|
||||
Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
|
||||
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
||||
String defaultBranchName = context.getConfig().getDefaultBranch();
|
||||
if (!Strings.isNullOrEmpty(defaultBranchName)) {
|
||||
return GitUtil.getBranchId(gitRepository, defaultBranchName);
|
||||
} else {
|
||||
logger.trace("no default branch configured, use repository head as default");
|
||||
Optional<Ref> repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository);
|
||||
return repositoryHeadRef.orElse(null);
|
||||
}
|
||||
return getBranchIdOrCurrentHead(gitRepository, defaultBranchName);
|
||||
} else {
|
||||
return GitUtil.getBranchId(gitRepository, requestedBranch);
|
||||
return getBranchIdOrCurrentHead(gitRepository, requestedBranch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,27 +21,26 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
import org.eclipse.jgit.transport.ReceivePack;
|
||||
|
||||
import sonia.scm.repository.api.GitHookBranchProvider;
|
||||
import sonia.scm.repository.api.GitHookMessageProvider;
|
||||
import sonia.scm.repository.api.GitHookTagProvider;
|
||||
import sonia.scm.repository.api.GitReceiveHookMergeDetectionProvider;
|
||||
import sonia.scm.repository.api.HookBranchProvider;
|
||||
import sonia.scm.repository.api.HookFeature;
|
||||
import sonia.scm.repository.api.HookMessageProvider;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import sonia.scm.repository.api.HookTagProvider;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import sonia.scm.repository.api.GitHookTagProvider;
|
||||
import sonia.scm.repository.api.HookTagProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -50,24 +49,34 @@ import sonia.scm.repository.api.HookTagProvider;
|
||||
public class GitHookContextProvider extends HookContextProvider
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final Set<HookFeature> SUPPORTED_FEATURES =
|
||||
EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER,
|
||||
HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER);
|
||||
/**
|
||||
* Field description
|
||||
*/
|
||||
private static final Set<HookFeature> SUPPORTED_FEATURES = EnumSet.of(
|
||||
HookFeature.MESSAGE_PROVIDER,
|
||||
HookFeature.CHANGESET_PROVIDER,
|
||||
HookFeature.BRANCH_PROVIDER,
|
||||
HookFeature.TAG_PROVIDER,
|
||||
HookFeature.MERGE_DETECTION_PROVIDER
|
||||
);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new instance
|
||||
*
|
||||
* @param receivePack git receive pack
|
||||
* @param receiveCommands received commands
|
||||
*/
|
||||
public GitHookContextProvider(ReceivePack receivePack,
|
||||
List<ReceiveCommand> receiveCommands)
|
||||
{
|
||||
public GitHookContextProvider(
|
||||
ReceivePack receivePack,
|
||||
List<ReceiveCommand> receiveCommands,
|
||||
Repository repository,
|
||||
String repositoryId
|
||||
) {
|
||||
this.receivePack = receivePack;
|
||||
this.receiveCommands = receiveCommands;
|
||||
this.repository = repository;
|
||||
this.repositoryId = repositoryId;
|
||||
this.changesetProvider = new GitHookChangesetProvider(receivePack,
|
||||
receiveCommands);
|
||||
}
|
||||
@@ -99,20 +108,20 @@ public class GitHookContextProvider extends HookContextProvider
|
||||
return changesetProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HookMergeDetectionProvider getMergeDetectionProvider() {
|
||||
return new GitReceiveHookMergeDetectionProvider(repository, repositoryId, receiveCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<HookFeature> getSupportedFeatures()
|
||||
{
|
||||
return SUPPORTED_FEATURES;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final GitHookChangesetProvider changesetProvider;
|
||||
|
||||
/** Field description */
|
||||
private final List<ReceiveCommand> receiveCommands;
|
||||
|
||||
/** Field description */
|
||||
private final ReceivePack receivePack;
|
||||
private final Repository repository;
|
||||
private final String repositoryId;
|
||||
}
|
||||
|
||||
@@ -27,19 +27,12 @@ package sonia.scm.repository.spi;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.repository.GitChangesetConverter;
|
||||
@@ -48,9 +41,6 @@ import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
@@ -183,123 +173,14 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ChangesetPagingResult getChangesets(LogCommandRequest request) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("fetch changesets for request: {}", request);
|
||||
}
|
||||
|
||||
ChangesetPagingResult changesets = null;
|
||||
GitChangesetConverter converter = null;
|
||||
RevWalk revWalk = null;
|
||||
|
||||
try (org.eclipse.jgit.lib.Repository repository = open()) {
|
||||
if (!repository.getAllRefs().isEmpty()) {
|
||||
int counter = 0;
|
||||
int start = request.getPagingStart();
|
||||
|
||||
if (start < 0) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("start parameter is negative, reset to 0");
|
||||
}
|
||||
|
||||
start = 0;
|
||||
}
|
||||
|
||||
List<Changeset> changesetList = Lists.newArrayList();
|
||||
int limit = request.getPagingLimit();
|
||||
ObjectId startId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getStartChangeset())) {
|
||||
startId = repository.resolve(request.getStartChangeset());
|
||||
}
|
||||
|
||||
ObjectId endId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getEndChangeset())) {
|
||||
endId = repository.resolve(request.getEndChangeset());
|
||||
}
|
||||
|
||||
Ref branch = getBranchOrDefault(repository,request.getBranch());
|
||||
|
||||
ObjectId ancestorId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
||||
ancestorId = repository.resolve(request.getAncestorChangeset());
|
||||
if (ancestorId == null) {
|
||||
throw notFound(entity(REVISION, request.getAncestorChangeset()).in(this.repository));
|
||||
}
|
||||
}
|
||||
|
||||
revWalk = new RevWalk(repository);
|
||||
|
||||
converter = new GitChangesetConverter(repository, revWalk);
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getPath())) {
|
||||
revWalk.setTreeFilter(
|
||||
AndTreeFilter.create(
|
||||
PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
|
||||
}
|
||||
|
||||
if (branch != null) {
|
||||
if (startId != null) {
|
||||
revWalk.markStart(revWalk.lookupCommit(startId));
|
||||
} else {
|
||||
revWalk.markStart(revWalk.lookupCommit(branch.getObjectId()));
|
||||
}
|
||||
|
||||
if (ancestorId != null) {
|
||||
revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
|
||||
}
|
||||
|
||||
Iterator<RevCommit> iterator = revWalk.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
RevCommit commit = iterator.next();
|
||||
|
||||
if ((counter >= start)
|
||||
&& ((limit < 0) || (counter < start + limit))) {
|
||||
changesetList.add(converter.createChangeset(commit));
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
if (commit.getId().equals(endId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ancestorId != null) {
|
||||
throw notFound(entity(REVISION, request.getBranch()).in(this.repository));
|
||||
}
|
||||
|
||||
if (branch != null) {
|
||||
changesets = new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName()));
|
||||
} else {
|
||||
changesets = new ChangesetPagingResult(counter, changesetList);
|
||||
}
|
||||
} else if (logger.isWarnEnabled()) {
|
||||
logger.warn("the repository {} seems to be empty",
|
||||
this.repository.getName());
|
||||
|
||||
changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST);
|
||||
try (org.eclipse.jgit.lib.Repository gitRepository = open()) {
|
||||
if (Strings.isNullOrEmpty(request.getBranch())) {
|
||||
request.setBranch(context.getConfig().getDefaultBranch());
|
||||
}
|
||||
return new GitLogComputer(this.repository.getId(), gitRepository).compute(request);
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(repository, "could not create change log", e);
|
||||
}
|
||||
catch (MissingObjectException e)
|
||||
{
|
||||
throw notFound(entity(REVISION, e.getObjectId().getName()).in(repository));
|
||||
}
|
||||
catch (NotFoundException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InternalRepositoryException(repository, "could not create change log", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(converter);
|
||||
GitUtil.release(revWalk);
|
||||
}
|
||||
|
||||
return changesets;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.spi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.eclipse.jgit.errors.InvalidObjectIdException;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.repository.GitChangesetConverter;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
|
||||
public class GitLogComputer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GitLogComputer.class);
|
||||
|
||||
private final String repositoryId;
|
||||
private final Repository gitRepository;
|
||||
|
||||
public GitLogComputer(String repositoryId, Repository repository) {
|
||||
this.repositoryId = repositoryId;
|
||||
this.gitRepository = repository;
|
||||
}
|
||||
|
||||
public ChangesetPagingResult compute(LogCommandRequest request) {
|
||||
LOG.debug("fetch changesets for request: {}", request);
|
||||
|
||||
GitChangesetConverter converter = null;
|
||||
RevWalk revWalk = null;
|
||||
|
||||
try {
|
||||
if (!gitRepository.getAllRefs().isEmpty()) {
|
||||
int counter = 0;
|
||||
int start = request.getPagingStart();
|
||||
|
||||
if (start < 0) {
|
||||
LOG.error("start parameter is negative, reset to 0");
|
||||
|
||||
start = 0;
|
||||
}
|
||||
|
||||
List<Changeset> changesetList = Lists.newArrayList();
|
||||
int limit = request.getPagingLimit();
|
||||
ObjectId startId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getStartChangeset())) {
|
||||
startId = gitRepository.resolve(request.getStartChangeset());
|
||||
}
|
||||
|
||||
ObjectId endId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getEndChangeset())) {
|
||||
endId = gitRepository.resolve(request.getEndChangeset());
|
||||
}
|
||||
|
||||
Ref branch = GitUtil.getBranchIdOrCurrentHead(gitRepository, request.getBranch());
|
||||
ObjectId branchId;
|
||||
if (branch == null) {
|
||||
if (request.getBranch() != null) {
|
||||
try {
|
||||
branchId = ObjectId.fromString(request.getBranch());
|
||||
} catch (InvalidObjectIdException e) {
|
||||
throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId));
|
||||
}
|
||||
} else {
|
||||
branchId = null;
|
||||
}
|
||||
} else {
|
||||
branchId = branch.getObjectId();
|
||||
}
|
||||
|
||||
ObjectId ancestorId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
||||
ancestorId = gitRepository.resolve(request.getAncestorChangeset());
|
||||
if (ancestorId == null) {
|
||||
throw notFound(entity(GitLogCommand.REVISION, request.getAncestorChangeset()).in(sonia.scm.repository.Repository.class, repositoryId));
|
||||
}
|
||||
}
|
||||
|
||||
revWalk = new RevWalk(gitRepository);
|
||||
|
||||
converter = new GitChangesetConverter(gitRepository, revWalk);
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getPath())) {
|
||||
revWalk.setTreeFilter(
|
||||
AndTreeFilter.create(
|
||||
PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
|
||||
}
|
||||
|
||||
if (branchId != null) {
|
||||
if (startId != null) {
|
||||
revWalk.markStart(revWalk.lookupCommit(startId));
|
||||
} else {
|
||||
revWalk.markStart(revWalk.lookupCommit(branchId));
|
||||
}
|
||||
|
||||
if (ancestorId != null) {
|
||||
revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
|
||||
}
|
||||
|
||||
Iterator<RevCommit> iterator = revWalk.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
RevCommit commit = iterator.next();
|
||||
|
||||
if ((counter >= start)
|
||||
&& ((limit < 0) || (counter < start + limit))) {
|
||||
changesetList.add(converter.createChangeset(commit));
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
if (commit.getId().equals(endId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ancestorId != null) {
|
||||
throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId));
|
||||
}
|
||||
|
||||
if (branch != null) {
|
||||
return new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName()));
|
||||
} else {
|
||||
return new ChangesetPagingResult(counter, changesetList);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("the repository with id {} seems to be empty", this.repositoryId);
|
||||
|
||||
return new ChangesetPagingResult(0, Collections.emptyList());
|
||||
}
|
||||
} catch (MissingObjectException e) {
|
||||
throw notFound(entity(GitLogCommand.REVISION, e.getObjectId().getName()).in(sonia.scm.repository.Repository.class, repositoryId));
|
||||
} catch (NotFoundException e) {
|
||||
throw e;
|
||||
} catch (Exception ex) {
|
||||
throw new InternalRepositoryException(entity(sonia.scm.repository.Repository.class, repositoryId).build(), "could not create change log", ex);
|
||||
} finally {
|
||||
IOUtil.close(converter);
|
||||
GitUtil.release(revWalk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -122,8 +122,7 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook
|
||||
|
||||
logger.trace("resolved repository to {}", repositoryId);
|
||||
|
||||
GitHookContextProvider context = new GitHookContextProvider(rpack,
|
||||
receiveCommands);
|
||||
GitHookContextProvider context = new GitHookContextProvider(rpack, receiveCommands, repository, repositoryId);
|
||||
|
||||
hookEventFacade.handle(repositoryId).fireHookEvent(type, context);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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();
|
||||
} catch (GitAPIException ex) {
|
||||
throw new RepositoryClientException("could not delete branch", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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());
|
||||
org.eclipse.jgit.api.MergeCommand mergeCommand = git.merge()
|
||||
.include(request.getBranch(), resolved)
|
||||
.setMessage(request.getMessage());
|
||||
|
||||
switch (request.getFfMode()) {
|
||||
case FF:
|
||||
mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF);
|
||||
break;
|
||||
case NO_FF:
|
||||
mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.NO_FF);
|
||||
break;
|
||||
case FF_ONLY:
|
||||
mergeCommand.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF_ONLY);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown FF mode: " + request.getFfMode());
|
||||
}
|
||||
|
||||
MergeResult mergeResult = mergeCommand
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.client.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -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();
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
@@ -48,7 +48,7 @@ public class GitDiffCommand_DequoteOutputStreamTest {
|
||||
stream.write(bytes, 0, bytes.length);
|
||||
stream.flush();
|
||||
|
||||
Assertions.assertThat(buffer.toString()).isEqualTo("diff --git a/file úüþëéåëåé a b/file úüþëéåëåé b\n" +
|
||||
Assertions.assertThat(buffer.toString("UTF-8")).isEqualTo("diff --git a/file úüþëéåëåé a b/file úüþëéåëåé b\n" +
|
||||
"new file mode 100644\n" +
|
||||
"index 0000000..8cb0607\n" +
|
||||
"--- /dev/null\n" +
|
||||
|
||||
50
scm-plugins/scm-integration-test-plugin/pom.xml
Normal file
50
scm-plugins/scm-integration-test-plugin/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import sonia.scm.api.v2.resources.LinkBuilder;
|
||||
import sonia.scm.api.v2.resources.ScmPathInfoStore;
|
||||
|
||||
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.
|
||||
*/
|
||||
@Path(IntegrationTestResource.INTEGRATION_TEST_PATH_V2)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.client.api;
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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;
|
||||
private final MergeRequest request = new MergeRequest();
|
||||
|
||||
MergeCommandBuilder(MergeCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public MergeCommandBuilder ffOnly() {
|
||||
request.setFfMode(MergeRequest.FastForwardMode.FF_ONLY);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MergeCommandBuilder noFf() {
|
||||
request.setFfMode(MergeRequest.FastForwardMode.NO_FF);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MergeCommandBuilder ffIfPossible() {
|
||||
request.setFfMode(MergeRequest.FastForwardMode.FF);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void merge(String branch) throws IOException {
|
||||
request.setBranch(branch);
|
||||
command.merge(request);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* 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;
|
||||
@@ -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,10 +96,16 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
public boolean isCommandSupported(ClientCommand command) {
|
||||
return clientProvider.getSupportedClientCommands().contains(command);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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;
|
||||
private FastForwardMode ffMode = FastForwardMode.FF;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
public void setFfMode(FastForwardMode ffMode) {
|
||||
this.ffMode = ffMode;
|
||||
}
|
||||
|
||||
String getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public FastForwardMode getFfMode() {
|
||||
return ffMode;
|
||||
}
|
||||
|
||||
public enum FastForwardMode {
|
||||
FF_ONLY, FF, NO_FF
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.client.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("repository name validation", () => {
|
||||
});
|
||||
|
||||
it("should allow same names as the backend", () => {
|
||||
const validPaths = ["scm", "s", "sc", ".hiddenrepo", "b.", "...", "..c", "d..", "a..c"];
|
||||
const validPaths = ["scm", "scm.gitz", "s", "sc", ".hiddenrepo", "b.", "...", "..c", "d..", "a..c"];
|
||||
|
||||
validPaths.forEach(path => expect(validator.isNameValid(path)).toBe(true));
|
||||
});
|
||||
@@ -91,7 +91,8 @@ describe("repository name validation", () => {
|
||||
"a/..b",
|
||||
"scm/main",
|
||||
"scm/plugins/git-plugin",
|
||||
"scm/plugins/git-plugin"
|
||||
"scm/plugins/git-plugin",
|
||||
"scm.git"
|
||||
];
|
||||
|
||||
invalidPaths.forEach(path => expect(validator.isNameValid(path)).toBe(false));
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
import { validation } from "@scm-manager/ui-components";
|
||||
|
||||
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/;
|
||||
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[.]git$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/;
|
||||
|
||||
export const isNameValid = (name: string) => {
|
||||
return nameRegex.test(name);
|
||||
|
||||
@@ -324,7 +324,7 @@
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.5.0</version>
|
||||
<version>5.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test scope -->
|
||||
@@ -675,7 +675,7 @@
|
||||
|
||||
<properties>
|
||||
<scm.stage>DEVELOPMENT</scm.stage>
|
||||
<scm.home>target/scm-it</scm.home>
|
||||
<scm.home>${project.parent.build.directory}/scm-it</scm.home>
|
||||
<environment.profile>default</environment.profile>
|
||||
<jjwt.version>0.11.2</jjwt.version>
|
||||
<selenium.version>2.53.1</selenium.version>
|
||||
@@ -814,7 +814,7 @@
|
||||
<systemProperties>
|
||||
<systemProperty>
|
||||
<name>scm.home</name>
|
||||
<value>target/scm-it</value>
|
||||
<value>${scm.home}</value>
|
||||
</systemProperty>
|
||||
<systemProperty>
|
||||
<name>scm.stage</name>
|
||||
@@ -903,7 +903,7 @@
|
||||
<systemProperties>
|
||||
<systemProperty>
|
||||
<name>scm.home</name>
|
||||
<value>target/scm-it</value>
|
||||
<value>${scm.home}</value>
|
||||
</systemProperty>
|
||||
</systemProperties>
|
||||
<jettyXml>${project.basedir}/src/main/conf/jetty.xml</jettyXml>
|
||||
|
||||
Reference in New Issue
Block a user