mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 16:35:45 +01:00
Implement copy strategy
This commit is contained in:
@@ -60,7 +60,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
public static final String DEFAULT_VERSION_INFORMATION = "unknown";
|
public static final String DEFAULT_VERSION_INFORMATION = "unknown";
|
||||||
|
|
||||||
public static final String DOT = ".";
|
public static final String DOT = ".";
|
||||||
static final String REPOSITORIES_NATIVE_DIRECTORY = "data";
|
public static final String REPOSITORIES_NATIVE_DIRECTORY = "data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the logger for AbstractSimpleRepositoryHandler
|
* the logger for AbstractSimpleRepositoryHandler
|
||||||
|
|||||||
@@ -1,21 +1,83 @@
|
|||||||
package sonia.scm.repository.update;
|
package sonia.scm.repository.update;
|
||||||
|
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.migration.UpdateException;
|
||||||
|
import sonia.scm.repository.AbstractSimpleRepositoryHandler;
|
||||||
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
class CopyMigrationStrategy implements MigrationStrategy.Instance {
|
class CopyMigrationStrategy implements MigrationStrategy.Instance {
|
||||||
|
|
||||||
private final SCMContextProvider contextProvider;
|
private final SCMContextProvider contextProvider;
|
||||||
|
private final RepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CopyMigrationStrategy(SCMContextProvider contextProvider) {
|
public CopyMigrationStrategy(SCMContextProvider contextProvider, RepositoryLocationResolver locationResolver) {
|
||||||
this.contextProvider = contextProvider;
|
this.contextProvider = contextProvider;
|
||||||
|
this.locationResolver = locationResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Path migrate(String id, String name, String type) {
|
public Path migrate(String id, String name, String type) {
|
||||||
return null;
|
Path repositoryBasePath = locationResolver.forClass(Path.class).getLocation(id);
|
||||||
|
Path targetDataPath = repositoryBasePath
|
||||||
|
.resolve(AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
||||||
|
Path sourceDataPath = getSourceDataPath(name, type);
|
||||||
|
copyData(sourceDataPath, targetDataPath);
|
||||||
|
return repositoryBasePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path getSourceDataPath(String name, String type) {
|
||||||
|
return Arrays.stream(name.split("/"))
|
||||||
|
.reduce(getTypeDependentPath(type), (path, namePart) -> path.resolve(namePart), (p1, p2) -> p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path getTypeDependentPath(String type) {
|
||||||
|
return contextProvider.getBaseDirectory().toPath().resolve("repositories").resolve(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyData(Path sourceDirectory, Path targetDirectory) {
|
||||||
|
createDataDirectory(targetDirectory);
|
||||||
|
Stream<Path> list = listSourceDirectory(sourceDirectory);
|
||||||
|
list.forEach(
|
||||||
|
sourceFile -> {
|
||||||
|
Path targetFile = targetDirectory.resolve(sourceFile.getFileName());
|
||||||
|
if (Files.isDirectory(sourceFile)) {
|
||||||
|
copyData(sourceFile, targetFile);
|
||||||
|
} else {
|
||||||
|
copyFile(sourceFile, targetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<Path> listSourceDirectory(Path sourceDirectory) {
|
||||||
|
try {
|
||||||
|
return Files.list(sourceDirectory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UpdateException("could not read original directory", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyFile(Path sourceFile, Path targetFile) {
|
||||||
|
try {
|
||||||
|
Files.copy(sourceFile, targetFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UpdateException("could not copy original file from " + sourceFile + " to " + targetFile, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDataDirectory(Path target) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(target);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UpdateException("could not create data directory " + target, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package sonia.scm.repository.update;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junitpioneer.jupiter.TempDirectory;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(TempDirectory.class)
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CopyMigrationStrategyTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
SCMContextProvider contextProvider;
|
||||||
|
@Mock
|
||||||
|
RepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void mockContextProvider(@TempDirectory.TempDir Path tempDir) {
|
||||||
|
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
|
V1RepositoryFileSystem.createV1Home(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void mockLocationResolver(@TempDirectory.TempDir Path tempDir) {
|
||||||
|
RepositoryLocationResolver.RepositoryLocationResolverInstance instanceMock = mock(RepositoryLocationResolver.RepositoryLocationResolverInstance.class);
|
||||||
|
when(locationResolver.forClass(Path.class)).thenReturn(instanceMock);
|
||||||
|
when(instanceMock.getLocation(anyString())).thenAnswer(invocation -> tempDir.resolve((String) invocation.getArgument(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseStandardDirectory(@TempDirectory.TempDir Path tempDir) {
|
||||||
|
Path target = new CopyMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
|
||||||
|
assertThat(target).isEqualTo(tempDir.resolve("b4f-a9f0-49f7-ad1f-37d3aae1c55f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCopyDataDirectory(@TempDirectory.TempDir Path tempDir) {
|
||||||
|
Path target = new CopyMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
|
||||||
|
assertThat(target.resolve("data")).exists();
|
||||||
|
Path originalDataDir = tempDir
|
||||||
|
.resolve("repositories")
|
||||||
|
.resolve("git")
|
||||||
|
.resolve("some")
|
||||||
|
.resolve("more")
|
||||||
|
.resolve("directories")
|
||||||
|
.resolve("than")
|
||||||
|
.resolve("one");
|
||||||
|
assertDirectoriesEqual(target.resolve("data"), originalDataDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDirectoriesEqual(Path targetDataDir, Path originalDataDir) {
|
||||||
|
Stream<Path> list = null;
|
||||||
|
try {
|
||||||
|
list = Files.list(originalDataDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail("could not read original directory", e);
|
||||||
|
}
|
||||||
|
list.forEach(
|
||||||
|
original -> {
|
||||||
|
Path expectedTarget = targetDataDir.resolve(original.getFileName());
|
||||||
|
assertThat(expectedTarget).exists();
|
||||||
|
if (Files.isDirectory(original)) {
|
||||||
|
assertDirectoriesEqual(expectedTarget, original);
|
||||||
|
} else {
|
||||||
|
assertThat(expectedTarget).hasSameContentAs(original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package sonia.scm.repository.update;
|
||||||
|
|
||||||
|
import sonia.scm.repository.spi.ZippedRepositoryTestBase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
class V1RepositoryFileSystem {
|
||||||
|
/**
|
||||||
|
* Creates the following v1 repositories in the temp dir:
|
||||||
|
* <pre>
|
||||||
|
* <repository>
|
||||||
|
* <properties/>
|
||||||
|
* <contact>arthur@dent.uk</contact>
|
||||||
|
* <creationDate>1558423492071</creationDate>
|
||||||
|
* <description>A repository with two folders.</description>
|
||||||
|
* <id>3b91caa5-59c3-448f-920b-769aaa56b761</id>
|
||||||
|
* <name>one/directory</name>
|
||||||
|
* <public>false</public>
|
||||||
|
* <archived>false</archived>
|
||||||
|
* <type>git</type>
|
||||||
|
* </repository>
|
||||||
|
* <repository>
|
||||||
|
* <properties/>
|
||||||
|
* <contact>arthur@dent.uk</contact>
|
||||||
|
* <creationDate>1558423543716</creationDate>
|
||||||
|
* <description>A repository in deeply nested folders.</description>
|
||||||
|
* <id>c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f</id>
|
||||||
|
* <name>some/more/directories/than/one</name>
|
||||||
|
* <public>false</public>
|
||||||
|
* <archived>false</archived>
|
||||||
|
* <type>git</type>
|
||||||
|
* </repository>
|
||||||
|
* <repository>
|
||||||
|
* <properties/>
|
||||||
|
* <contact>arthur@dent.uk</contact>
|
||||||
|
* <creationDate>1558423440258</creationDate>
|
||||||
|
* <description>A simple repository without directories.</description>
|
||||||
|
* <id>454972da-faf9-4437-b682-dc4a4e0aa8eb</id>
|
||||||
|
* <lastModified>1558425918578</lastModified>
|
||||||
|
* <name>simple</name>
|
||||||
|
* <permissions>
|
||||||
|
* <groupPermission>true</groupPermission>
|
||||||
|
* <name>mice</name>
|
||||||
|
* <type>WRITE</type>
|
||||||
|
* </permissions>
|
||||||
|
* <permissions>
|
||||||
|
* <groupPermission>false</groupPermission>
|
||||||
|
* <name>dent</name>
|
||||||
|
* <type>OWNER</type>
|
||||||
|
* </permissions>
|
||||||
|
* <permissions>
|
||||||
|
* <groupPermission>false</groupPermission>
|
||||||
|
* <name>trillian</name>
|
||||||
|
* <type>READ</type>
|
||||||
|
* </permissions>
|
||||||
|
* <public>false</public>
|
||||||
|
* <archived>false</archived>
|
||||||
|
* <type>git</type>
|
||||||
|
* <url>http://localhost:8081/scm/git/simple</url>
|
||||||
|
* </repository>
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
static void createV1Home(Path tempDir) throws IOException {
|
||||||
|
ZippedRepositoryTestBase.extract(tempDir.toFile(), "sonia/scm/repository/update/scm-home.v1.zip");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,64 +63,9 @@ class XmlRepositoryV1UpdateStepTest {
|
|||||||
@Nested
|
@Nested
|
||||||
class WithExistingDatabase {
|
class WithExistingDatabase {
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the following v1 repositories in the temp dir:
|
|
||||||
* <pre>
|
|
||||||
* <repository>
|
|
||||||
* <properties/>
|
|
||||||
* <contact>arthur@dent.uk</contact>
|
|
||||||
* <creationDate>1558423492071</creationDate>
|
|
||||||
* <description>A repository with two folders.</description>
|
|
||||||
* <id>3b91caa5-59c3-448f-920b-769aaa56b761</id>
|
|
||||||
* <name>one/directory</name>
|
|
||||||
* <public>false</public>
|
|
||||||
* <archived>false</archived>
|
|
||||||
* <type>git</type>
|
|
||||||
* </repository>
|
|
||||||
* <repository>
|
|
||||||
* <properties/>
|
|
||||||
* <contact>arthur@dent.uk</contact>
|
|
||||||
* <creationDate>1558423543716</creationDate>
|
|
||||||
* <description>A repository in deeply nested folders.</description>
|
|
||||||
* <id>c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f</id>
|
|
||||||
* <name>some/more/directories/than/one</name>
|
|
||||||
* <public>false</public>
|
|
||||||
* <archived>false</archived>
|
|
||||||
* <type>git</type>
|
|
||||||
* </repository>
|
|
||||||
* <repository>
|
|
||||||
* <properties/>
|
|
||||||
* <contact>arthur@dent.uk</contact>
|
|
||||||
* <creationDate>1558423440258</creationDate>
|
|
||||||
* <description>A simple repository without directories.</description>
|
|
||||||
* <id>454972da-faf9-4437-b682-dc4a4e0aa8eb</id>
|
|
||||||
* <lastModified>1558425918578</lastModified>
|
|
||||||
* <name>simple</name>
|
|
||||||
* <permissions>
|
|
||||||
* <groupPermission>true</groupPermission>
|
|
||||||
* <name>mice</name>
|
|
||||||
* <type>WRITE</type>
|
|
||||||
* </permissions>
|
|
||||||
* <permissions>
|
|
||||||
* <groupPermission>false</groupPermission>
|
|
||||||
* <name>dent</name>
|
|
||||||
* <type>OWNER</type>
|
|
||||||
* </permissions>
|
|
||||||
* <permissions>
|
|
||||||
* <groupPermission>false</groupPermission>
|
|
||||||
* <name>trillian</name>
|
|
||||||
* <type>READ</type>
|
|
||||||
* </permissions>
|
|
||||||
* <public>false</public>
|
|
||||||
* <archived>false</archived>
|
|
||||||
* <type>git</type>
|
|
||||||
* <url>http://localhost:8081/scm/git/simple</url>
|
|
||||||
* </repository>
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
|
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
ZippedRepositoryTestBase.extract(tempDir.toFile(), "sonia/scm/repository/update/scm-home.v1.zip");
|
V1RepositoryFileSystem.createV1Home(tempDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
|||||||
Reference in New Issue
Block a user