diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/BaseMigrationStrategy.java b/scm-webapp/src/main/java/sonia/scm/repository/update/BaseMigrationStrategy.java new file mode 100644 index 0000000000..3ac0a6fd68 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/BaseMigrationStrategy.java @@ -0,0 +1,60 @@ +package sonia.scm.repository.update; + +import sonia.scm.SCMContextProvider; +import sonia.scm.migration.UpdateException; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.stream.Stream; + +abstract class BaseMigrationStrategy implements MigrationStrategy.Instance { + + private final SCMContextProvider contextProvider; + + BaseMigrationStrategy(SCMContextProvider contextProvider) { + this.contextProvider = contextProvider; + } + + Path getSourceDataPath(String name, String type) { + return Arrays.stream(name.split("/")) + .reduce(getTypeDependentPath(type), (path, namePart) -> path.resolve(namePart), (p1, p2) -> p1); + } + + Path getTypeDependentPath(String type) { + return contextProvider.getBaseDirectory().toPath().resolve("repositories").resolve(type); + } + + Stream listSourceDirectory(Path sourceDirectory) { + try { + return Files.list(sourceDirectory); + } catch (IOException e) { + throw new UpdateException("could not read original directory", e); + } + } + + void createDataDirectory(Path target) { + try { + Files.createDirectories(target); + } catch (IOException e) { + throw new UpdateException("could not create data directory " + target, e); + } + } + + void moveFile(Path sourceFile, Path targetFile) { + try { + Files.move(sourceFile, targetFile); + } catch (IOException e) { + throw new UpdateException("could not move data file from " + sourceFile + " to " + targetFile, e); + } + } + + 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); + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/CopyMigrationStrategy.java b/scm-webapp/src/main/java/sonia/scm/repository/update/CopyMigrationStrategy.java index 857c3c3428..84835543e0 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/update/CopyMigrationStrategy.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/CopyMigrationStrategy.java @@ -1,25 +1,20 @@ package sonia.scm.repository.update; import sonia.scm.SCMContextProvider; -import sonia.scm.migration.UpdateException; import sonia.scm.repository.AbstractSimpleRepositoryHandler; import sonia.scm.repository.RepositoryLocationResolver; import javax.inject.Inject; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.stream.Stream; -class CopyMigrationStrategy implements MigrationStrategy.Instance { +class CopyMigrationStrategy extends BaseMigrationStrategy { - private final SCMContextProvider contextProvider; private final RepositoryLocationResolver locationResolver; @Inject public CopyMigrationStrategy(SCMContextProvider contextProvider, RepositoryLocationResolver locationResolver) { - this.contextProvider = contextProvider; + super(contextProvider); this.locationResolver = locationResolver; } @@ -33,19 +28,9 @@ class CopyMigrationStrategy implements MigrationStrategy.Instance { 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 list = listSourceDirectory(sourceDirectory); - list.forEach( + listSourceDirectory(sourceDirectory).forEach( sourceFile -> { Path targetFile = targetDirectory.resolve(sourceFile.getFileName()); if (Files.isDirectory(sourceFile)) { @@ -56,28 +41,4 @@ class CopyMigrationStrategy implements MigrationStrategy.Instance { } ); } - - private Stream 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); - } - } } diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/InlineMigrationStrategy.java b/scm-webapp/src/main/java/sonia/scm/repository/update/InlineMigrationStrategy.java index 1374461bea..dc4fdb067d 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/update/InlineMigrationStrategy.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/InlineMigrationStrategy.java @@ -1,21 +1,42 @@ package sonia.scm.repository.update; import sonia.scm.SCMContextProvider; +import sonia.scm.repository.AbstractSimpleRepositoryHandler; import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; -class InlineMigrationStrategy implements MigrationStrategy.Instance { - - private final SCMContextProvider contextProvider; +class InlineMigrationStrategy extends BaseMigrationStrategy { @Inject public InlineMigrationStrategy(SCMContextProvider contextProvider) { - this.contextProvider = contextProvider; + super(contextProvider); } @Override public Path migrate(String id, String name, String type) { - return null; + Path repositoryBasePath = getSourceDataPath(name, type); + Path targetDataPath = repositoryBasePath + .resolve(AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY); + moveData(repositoryBasePath, targetDataPath); + return repositoryBasePath; + } + + private void moveData(Path sourceDirectory, Path targetDirectory) { + createDataDirectory(targetDirectory); + listSourceDirectory(sourceDirectory) + .filter(sourceFile -> !targetDirectory.equals(sourceFile)) + .forEach( + sourceFile -> { + Path targetFile = targetDirectory.resolve(sourceFile.getFileName()); + if (Files.isDirectory(sourceFile)) { + moveData(sourceFile, targetFile); + } else { + moveFile(sourceFile, targetFile); + } + } + ); } } diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/MoveMigrationStrategy.java b/scm-webapp/src/main/java/sonia/scm/repository/update/MoveMigrationStrategy.java index 8a8a3b3823..e0abc7ad9b 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/update/MoveMigrationStrategy.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/MoveMigrationStrategy.java @@ -3,7 +3,6 @@ package sonia.scm.repository.update; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContextProvider; -import sonia.scm.migration.UpdateException; import sonia.scm.repository.AbstractSimpleRepositoryHandler; import sonia.scm.repository.RepositoryLocationResolver; @@ -11,22 +10,19 @@ import javax.inject.Inject; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; -import java.util.stream.Stream; import static java.util.Arrays.asList; -class MoveMigrationStrategy implements MigrationStrategy.Instance { +class MoveMigrationStrategy extends BaseMigrationStrategy { private static final Logger LOG = LoggerFactory.getLogger(MoveMigrationStrategy.class); - private final SCMContextProvider contextProvider; private final RepositoryLocationResolver locationResolver; @Inject public MoveMigrationStrategy(SCMContextProvider contextProvider, RepositoryLocationResolver locationResolver) { - this.contextProvider = contextProvider; + super(contextProvider); this.locationResolver = locationResolver; } @@ -58,19 +54,9 @@ class MoveMigrationStrategy implements MigrationStrategy.Instance { } } - 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 moveData(Path sourceDirectory, Path targetDirectory) { createDataDirectory(targetDirectory); - Stream list = listSourceDirectory(sourceDirectory); - list.forEach( + listSourceDirectory(sourceDirectory).forEach( sourceFile -> { Path targetFile = targetDirectory.resolve(sourceFile.getFileName()); if (Files.isDirectory(sourceFile)) { @@ -86,28 +72,4 @@ class MoveMigrationStrategy implements MigrationStrategy.Instance { LOG.warn("could not delete source repository directory {}", sourceDirectory); } } - - private Stream listSourceDirectory(Path sourceDirectory) { - try { - return Files.list(sourceDirectory); - } catch (IOException e) { - throw new UpdateException("could not read original directory", e); - } - } - - private void moveFile(Path sourceFile, Path targetFile) { - try { - Files.move(sourceFile, targetFile); - } catch (IOException e) { - throw new UpdateException("could not move data 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); - } - } } diff --git a/scm-webapp/src/test/java/sonia/scm/repository/update/InlineMigrationStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/repository/update/InlineMigrationStrategyTest.java new file mode 100644 index 0000000000..ed0d7eeb40 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/repository/update/InlineMigrationStrategyTest.java @@ -0,0 +1,49 @@ +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 java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(TempDirectory.class) +@ExtendWith(MockitoExtension.class) +class InlineMigrationStrategyTest { + + @Mock + SCMContextProvider contextProvider; + + @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); + } + + @Test + void shouldUseExistingDirectory(@TempDirectory.TempDir Path tempDir) { + Path target = new InlineMigrationStrategy(contextProvider).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git"); + assertThat(target).isEqualTo(resolveOldDirectory(tempDir)); + } + + @Test + void shouldMoveDataDirectory(@TempDirectory.TempDir Path tempDir) { + new InlineMigrationStrategy(contextProvider).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git"); + assertThat(resolveOldDirectory(tempDir).resolve("data")).exists(); + } + + private Path resolveOldDirectory(Path tempDir) { + return tempDir.resolve("repositories").resolve("git").resolve("some").resolve("more").resolve("directories").resolve("than").resolve("one"); + } +}