From 0a5575c2fd5e26eb7cf9e9f27990ed1bb3abd81f Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 30 Sep 2019 15:58:33 +0200 Subject: [PATCH 01/22] add HgModifyCommand / implement delete method --- .../scm/repository/spi/HgModifyCommand.java | 72 +++++++++++++++++++ .../spi/HgRepositoryServiceProvider.java | 13 +++- .../spi/HgRepositoryServiceResolver.java | 10 +-- .../repository/spi/HgModifyCommandTest.java | 37 ++++++++++ 4 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java new file mode 100644 index 0000000000..df4c7f3420 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -0,0 +1,72 @@ +package sonia.scm.repository.spi; + +import com.aragost.javahg.Changeset; +import com.aragost.javahg.Repository; +import com.aragost.javahg.commands.CommitCommand; +import com.aragost.javahg.commands.PushCommand; +import com.aragost.javahg.commands.RemoveCommand; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.util.WorkingCopy; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class HgModifyCommand implements ModifyCommand { + + private final HgRepositoryHandler handler; + private HgCommandContext context; + private final HgWorkdirFactory workdirFactory; + + public HgModifyCommand(HgRepositoryHandler handler, HgCommandContext context, HgWorkdirFactory workdirFactory) { + this.handler = handler; + this.context = context; + this.workdirFactory = workdirFactory; + } + + @Override + public String execute(ModifyCommandRequest request) { + + try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { + Repository workingRepository = workingCopy.getWorkingRepository(); + request.getRequests().forEach( + partialRequest -> { + try { + partialRequest.execute(new Worker() { + @Override + public void delete(String toBeDeleted) { + RemoveCommand.on(workingRepository).execute(toBeDeleted); + } + + @Override + public void create(String toBeCreated, File file, boolean overwrite) { + + } + + @Override + public void modify(String path, File file) { + + } + + @Override + public void move(String sourcePath, String targetPath) { + + } + }); + } catch (IOException e) { + e.printStackTrace(); // TODO + } + } + ); + + CommitCommand.on(workingRepository).user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())).message(request.getCommitMessage()).execute(); + List execute = PushCommand.on(workingRepository).execute(); + System.out.println(execute); + return execute.get(0).getNode(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index d60e888cac..79812a7f68 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -66,7 +66,8 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider Command.INCOMING, Command.OUTGOING, Command.PUSH, - Command.PULL + Command.PULL, + Command.MODIFY ); //J+ @@ -77,10 +78,11 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider //~--- constructors --------------------------------------------------------- HgRepositoryServiceProvider(HgRepositoryHandler handler, - HgHookManager hookManager, Repository repository) + HgHookManager hookManager, Repository repository, HgWorkdirFactory workdirFactory) { this.repository = repository; this.handler = handler; + this.workdirFactory = workdirFactory; this.repositoryDirectory = handler.getDirectory(repository.getId()); this.context = new HgCommandContext(hookManager, handler, repository, repositoryDirectory); @@ -238,6 +240,11 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider return new HgPushCommand(handler, context, repository); } + @Override + public ModifyCommand getModifyCommand() { + return new HgModifyCommand(handler, context, workdirFactory); + } + /** * Method description * @@ -287,4 +294,6 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider /** Field description */ private File repositoryDirectory; + + private final HgWorkdirFactory workdirFactory; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java index d6d04ee017..7b793e1b57 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java @@ -47,15 +47,17 @@ import sonia.scm.repository.Repository; public class HgRepositoryServiceResolver implements RepositoryServiceResolver { - private HgRepositoryHandler handler; - private HgHookManager hookManager; + private final HgRepositoryHandler handler; + private final HgHookManager hookManager; + private final HgWorkdirFactory workdirFactory; @Inject public HgRepositoryServiceResolver(HgRepositoryHandler handler, - HgHookManager hookManager) + HgHookManager hookManager, HgWorkdirFactory workdirFactory) { this.handler = handler; this.hookManager = hookManager; + this.workdirFactory = workdirFactory; } @Override @@ -63,7 +65,7 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver HgRepositoryServiceProvider provider = null; if (HgRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { - provider = new HgRepositoryServiceProvider(handler, hookManager, repository); + provider = new HgRepositoryServiceProvider(handler, hookManager, repository, workdirFactory); } return provider; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java new file mode 100644 index 0000000000..e612590ce2 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -0,0 +1,37 @@ +package sonia.scm.repository.spi; + +import com.google.inject.util.Providers; +import org.junit.Test; +import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.Person; +import sonia.scm.repository.util.WorkdirProvider; +import sonia.scm.web.HgRepositoryEnvironmentBuilder; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HgModifyCommandTest extends AbstractHgCommandTestBase { + + @Test + public void shouldRemoveFiles() throws IOException { + HgHookManager hookManager = mock(HgHookManager.class); + when(hookManager.getChallenge()).thenReturn("CHALLENGE"); + when(hookManager.getCredentials()).thenReturn("SECRET:SECRET"); + when(hookManager.createUrl()).thenReturn("http://localhost"); + HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); + + HgModifyCommand hgModifyCommand = new HgModifyCommand(handler, cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.DeleteFileRequest("a.txt")); + request.setCommitMessage("this is great"); + request.setAuthor(new Person("Arthur Dent", "dent@hitchhiker.com")); + + String result = hgModifyCommand.execute(request); + + assertThat(cmdContext.open().tip().getNode()).isEqualTo(result); + cmdContext.close(); + } +} From fb5617d94048ce26439972507c4e03fd8c680801 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 1 Oct 2019 14:41:56 +0200 Subject: [PATCH 02/22] implement create method for HgModifyCommand --- .../scm/repository/spi/HgModifyCommand.java | 53 ++++++++++- .../repository/spi/HgModifyCommandTest.java | 91 ++++++++++++++++++- 2 files changed, 138 insertions(+), 6 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index df4c7f3420..de39c233b4 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -5,13 +5,23 @@ import com.aragost.javahg.Repository; import com.aragost.javahg.commands.CommitCommand; import com.aragost.javahg.commands.PushCommand; import com.aragost.javahg.commands.RemoveCommand; +import org.apache.commons.lang.StringUtils; +import sonia.scm.ContextEntry; import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.util.WorkingCopy; import java.io.File; import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static sonia.scm.AlreadyExistsException.alreadyExists; +import static sonia.scm.ContextEntry.ContextBuilder.entity; + public class HgModifyCommand implements ModifyCommand { private final HgRepositoryHandler handler; @@ -39,8 +49,22 @@ public class HgModifyCommand implements ModifyCommand { } @Override - public void create(String toBeCreated, File file, boolean overwrite) { - + public void create(String toBeCreated, File file, boolean overwrite) throws IOException { + Path targetFile = new File(workingRepository.getDirectory(), toBeCreated).toPath(); + if (overwrite) { + Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + } else { + try { + Files.move(file.toPath(), targetFile); + } catch (FileAlreadyExistsException e) { + throw alreadyExists(createFileContext(toBeCreated)); + } + } + try { + addFileToHg(targetFile.toFile()); + } catch (Exception e) { + throwInternalRepositoryException("could not add new file to index", e); + } } @Override @@ -52,6 +76,28 @@ public class HgModifyCommand implements ModifyCommand { public void move(String sourcePath, String targetPath) { } + + private void createDirectories(Path targetFile) throws IOException { + try { + Files.createDirectories(targetFile.getParent()); + } catch (FileAlreadyExistsException e) { + throw alreadyExists(createFileContext(targetFile.toString())); + } + } + + private ContextEntry.ContextBuilder createFileContext(String path) { + ContextEntry.ContextBuilder contextBuilder = entity("file", path); + if (!StringUtils.isEmpty(request.getBranch())) { + contextBuilder.in("branch", request.getBranch()); + } + contextBuilder.in(context.getScmRepository()); + return contextBuilder; + } + + private void addFileToHg(File file) { + workingRepository.workingCopy().add(file.getAbsolutePath()); + } + }); } catch (IOException e) { e.printStackTrace(); // TODO @@ -67,6 +113,9 @@ public class HgModifyCommand implements ModifyCommand { e.printStackTrace(); return null; } + } + private String throwInternalRepositoryException(String message, Exception e) { + throw new InternalRepositoryException(context.getScmRepository(), message, e); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index e612590ce2..d808b9e454 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -1,12 +1,19 @@ package sonia.scm.repository.spi; import com.google.inject.util.Providers; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import sonia.scm.AlreadyExistsException; import sonia.scm.repository.HgHookManager; import sonia.scm.repository.Person; import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.web.HgRepositoryEnvironmentBuilder; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; @@ -15,15 +22,28 @@ import static org.mockito.Mockito.when; public class HgModifyCommandTest extends AbstractHgCommandTestBase { - @Test - public void shouldRemoveFiles() throws IOException { + private HgModifyCommand hgModifyCommand; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void initHgModifyCommand() { HgHookManager hookManager = mock(HgHookManager.class); when(hookManager.getChallenge()).thenReturn("CHALLENGE"); when(hookManager.getCredentials()).thenReturn("SECRET:SECRET"); when(hookManager.createUrl()).thenReturn("http://localhost"); HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); + hgModifyCommand = new HgModifyCommand(handler, cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); + } - HgModifyCommand hgModifyCommand = new HgModifyCommand(handler, cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); + @After + public void closeRepository() throws IOException { + cmdContext.close(); + } + + @Test + public void shouldRemoveFiles() { ModifyCommandRequest request = new ModifyCommandRequest(); request.addRequest(new ModifyCommandRequest.DeleteFileRequest("a.txt")); request.setCommitMessage("this is great"); @@ -32,6 +52,69 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { String result = hgModifyCommand.execute(request); assertThat(cmdContext.open().tip().getNode()).isEqualTo(result); - cmdContext.close(); + } + + @Test + public void shouldCreateFilesWithoutOverwrite() throws IOException { + + File testFile = temporaryFolder.newFile("Answer.txt"); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request.setCommitMessage("I found the answer"); + request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + String changeSet = hgModifyCommand.execute(request); + + assertThat(cmdContext.open().tip().getNode()).isEqualTo(changeSet); + assertThat(cmdContext.open().tip().getAddedFiles().size()).isEqualTo(1); + } + + @Test + public void shouldOverwriteExistingFiles() throws IOException { + + File testFile = temporaryFolder.newFile("Answer.txt"); + new FileOutputStream(testFile).write(21); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request.setCommitMessage("I found the answer"); + request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + hgModifyCommand.execute(request); + + new FileOutputStream(testFile).write(42); + ModifyCommandRequest request2 = new ModifyCommandRequest(); + request2.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, true)); + request2.setCommitMessage(" Now i really found the answer"); + request2.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + String changeSet2 = hgModifyCommand.execute(request2); + + assertThat(cmdContext.open().tip().getNode()).isEqualTo(changeSet2); + assertThat(cmdContext.open().tip().getModifiedFiles().size()).isEqualTo(1); + assertThat(cmdContext.open().tip().getModifiedFiles().get(0)).isEqualTo(testFile.getName()); + } + + @Test(expected = AlreadyExistsException.class) + public void shouldThrowFileAlreadyExistsException() throws IOException { + + File testFile = temporaryFolder.newFile("Answer.txt"); + new FileOutputStream(testFile).write(21); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request.setCommitMessage("I found the answer"); + request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + hgModifyCommand.execute(request); + + new FileOutputStream(testFile).write(42); + ModifyCommandRequest request2 = new ModifyCommandRequest(); + request2.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request2.setCommitMessage(" Now i really found the answer"); + request2.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + hgModifyCommand.execute(request2); } } From 75346c925e20bd34ea63fb769c08cd945d94d62f Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 2 Oct 2019 10:00:57 +0200 Subject: [PATCH 03/22] implement Modify Command --- .../scm/repository/spi/HgModifyCommand.java | 28 ++++--- .../repository/spi/HgModifyCommandTest.java | 79 +++++++++++++++---- 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index de39c233b4..a0795b337d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -3,6 +3,7 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Changeset; import com.aragost.javahg.Repository; import com.aragost.javahg.commands.CommitCommand; +import com.aragost.javahg.commands.ExecutionException; import com.aragost.javahg.commands.PushCommand; import com.aragost.javahg.commands.RemoveCommand; import org.apache.commons.lang.StringUtils; @@ -21,6 +22,7 @@ import java.util.List; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static sonia.scm.AlreadyExistsException.alreadyExists; import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; public class HgModifyCommand implements ModifyCommand { @@ -51,6 +53,7 @@ public class HgModifyCommand implements ModifyCommand { @Override public void create(String toBeCreated, File file, boolean overwrite) throws IOException { Path targetFile = new File(workingRepository.getDirectory(), toBeCreated).toPath(); + createDirectories(targetFile); if (overwrite) { Files.move(file.toPath(), targetFile, REPLACE_EXISTING); } else { @@ -62,19 +65,28 @@ public class HgModifyCommand implements ModifyCommand { } try { addFileToHg(targetFile.toFile()); - } catch (Exception e) { + } catch (ExecutionException e) { throwInternalRepositoryException("could not add new file to index", e); } } @Override - public void modify(String path, File file) { - + public void modify(String path, File file) throws IOException { + Path targetFile = new File(workingRepository.getDirectory(), path).toPath(); + createDirectories(targetFile); + if (!targetFile.toFile().exists()) { + throw notFound(createFileContext(path)); + } + Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + try { + addFileToHg(targetFile.toFile()); + } catch (ExecutionException e) { + throwInternalRepositoryException("could not modify existing file", e); + } } @Override public void move(String sourcePath, String targetPath) { - } private void createDirectories(Path targetFile) throws IOException { @@ -97,20 +109,18 @@ public class HgModifyCommand implements ModifyCommand { private void addFileToHg(File file) { workingRepository.workingCopy().add(file.getAbsolutePath()); } - }); } catch (IOException e) { - e.printStackTrace(); // TODO + throwInternalRepositoryException("could not execute command on repository", e); } } ); CommitCommand.on(workingRepository).user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())).message(request.getCommitMessage()).execute(); List execute = PushCommand.on(workingRepository).execute(); - System.out.println(execute); return execute.get(0).getNode(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException | ExecutionException e) { + throwInternalRepositoryException("could not execute command on repository", e); return null; } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index d808b9e454..b8ce787937 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import sonia.scm.AlreadyExistsException; +import sonia.scm.NotFoundException; import sonia.scm.repository.HgHookManager; import sonia.scm.repository.Person; import sonia.scm.repository.util.WorkdirProvider; @@ -56,8 +57,7 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { @Test public void shouldCreateFilesWithoutOverwrite() throws IOException { - - File testFile = temporaryFolder.newFile("Answer.txt"); + File testFile = temporaryFolder.newFile(); ModifyCommandRequest request = new ModifyCommandRequest(); request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); @@ -72,20 +72,11 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { @Test public void shouldOverwriteExistingFiles() throws IOException { - - File testFile = temporaryFolder.newFile("Answer.txt"); - new FileOutputStream(testFile).write(21); - - ModifyCommandRequest request = new ModifyCommandRequest(); - request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); - request.setCommitMessage("I found the answer"); - request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); - - hgModifyCommand.execute(request); + File testFile = temporaryFolder.newFile(); new FileOutputStream(testFile).write(42); ModifyCommandRequest request2 = new ModifyCommandRequest(); - request2.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, true)); + request2.addRequest(new ModifyCommandRequest.CreateFileRequest("a.txt", testFile, true)); request2.setCommitMessage(" Now i really found the answer"); request2.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); @@ -93,13 +84,13 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { assertThat(cmdContext.open().tip().getNode()).isEqualTo(changeSet2); assertThat(cmdContext.open().tip().getModifiedFiles().size()).isEqualTo(1); - assertThat(cmdContext.open().tip().getModifiedFiles().get(0)).isEqualTo(testFile.getName()); + assertThat(cmdContext.open().tip().getModifiedFiles().get(0)).isEqualTo("a.txt"); } @Test(expected = AlreadyExistsException.class) public void shouldThrowFileAlreadyExistsException() throws IOException { - File testFile = temporaryFolder.newFile("Answer.txt"); + File testFile = temporaryFolder.newFile(); new FileOutputStream(testFile).write(21); ModifyCommandRequest request = new ModifyCommandRequest(); @@ -117,4 +108,62 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { hgModifyCommand.execute(request2); } + + @Test + public void shouldModifyExistingFile() throws IOException { + File testFile = temporaryFolder.newFile("a.txt"); + + new FileOutputStream(testFile).write(42); + ModifyCommandRequest request2 = new ModifyCommandRequest(); + request2.addRequest(new ModifyCommandRequest.ModifyFileRequest("a.txt", testFile)); + request2.setCommitMessage(" Now i really found the answer"); + request2.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + String changeSet2 = hgModifyCommand.execute(request2); + + assertThat(cmdContext.open().tip().getNode()).isEqualTo(changeSet2); + assertThat(cmdContext.open().tip().getModifiedFiles().size()).isEqualTo(1); + assertThat(cmdContext.open().tip().getModifiedFiles().get(0)).isEqualTo(testFile.getName()); + } + + @Test(expected = NotFoundException.class) + public void shouldThrowNotFoundExceptionIfFileDoesNotExist() throws IOException { + File testFile = temporaryFolder.newFile("Answer.txt"); + + new FileOutputStream(testFile).write(42); + ModifyCommandRequest request2 = new ModifyCommandRequest(); + request2.addRequest(new ModifyCommandRequest.ModifyFileRequest("Answer.txt", testFile)); + request2.setCommitMessage(" Now i really found the answer"); + request2.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + + hgModifyCommand.execute(request2); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowNPEIfAuthorIsMissing() throws IOException { + File testFile = temporaryFolder.newFile(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request.setCommitMessage("I found the answer"); + hgModifyCommand.execute(request); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowNPEIfCommitMessageIsMissing() throws IOException { + File testFile = temporaryFolder.newFile(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("Answer.txt", testFile, false)); + request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + hgModifyCommand.execute(request); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void shouldThrowIndexOutOfBoundExceptionIfRequestIsMissing() throws IOException { + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("I found the answer"); + request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); + hgModifyCommand.execute(request); + } } From 6417ab57e8a9e59ac5db7459c61fb71b123c45bf Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 2 Oct 2019 10:43:35 +0200 Subject: [PATCH 04/22] remove unnecessary handler from HgModifyCommand / move NoChangesMadeException to scm-core --- .../java/sonia/scm/NoChangesMadeException.java | 18 ++++++++++++++++++ .../scm/repository/spi/GitModifyCommand.java | 17 +++-------------- .../scm/repository/spi/HgModifyCommand.java | 11 ++++++----- .../spi/HgRepositoryServiceProvider.java | 2 +- .../repository/spi/HgModifyCommandTest.java | 12 +++++------- 5 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/NoChangesMadeException.java diff --git a/scm-core/src/main/java/sonia/scm/NoChangesMadeException.java b/scm-core/src/main/java/sonia/scm/NoChangesMadeException.java new file mode 100644 index 0000000000..9bf0363398 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/NoChangesMadeException.java @@ -0,0 +1,18 @@ +package sonia.scm; + +import sonia.scm.repository.Repository; + +public class NoChangesMadeException extends BadRequestException { + public NoChangesMadeException(Repository repository, String branch) { + super(ContextEntry.ContextBuilder.entity(repository).build(), "no changes detected to branch " + branch); + } + + public NoChangesMadeException(Repository repository) { + super(ContextEntry.ContextBuilder.entity(repository).build(), "no changes detected"); + } + + @Override + public String getCode() { + return "40RaYIeeR1"; + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 0f7c9296a7..5f2af87d2a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -6,9 +6,9 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; -import sonia.scm.BadRequestException; import sonia.scm.ConcurrentModificationException; import sonia.scm.ContextEntry; +import sonia.scm.NoChangesMadeException; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; @@ -68,10 +68,10 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman for (ModifyCommandRequest.PartialRequest r : request.getRequests()) { r.execute(this); } - failIfNotChanged(NoChangesMadeException::new); + failIfNotChanged(() -> new NoChangesMadeException(repository, ModifyWorker.this.request.getBranch())); Optional revCommit = doCommit(request.getCommitMessage(), request.getAuthor()); push(); - return revCommit.orElseThrow(NoChangesMadeException::new).name(); + return revCommit.orElseThrow(() -> new NoChangesMadeException(repository, ModifyWorker.this.request.getBranch())).name(); } @Override @@ -156,17 +156,6 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman public void move(String sourcePath, String targetPath) { } - - private class NoChangesMadeException extends BadRequestException { - public NoChangesMadeException() { - super(ContextEntry.ContextBuilder.entity(context.getRepository()).build(), "no changes detected to branch " + ModifyWorker.this.request.getBranch()); - } - - @Override - public String getCode() { - return "40RaYIeeR1"; - } - } } private String throwInternalRepositoryException(String message, Exception e) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index a0795b337d..4d5e77681e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -6,9 +6,10 @@ import com.aragost.javahg.commands.CommitCommand; import com.aragost.javahg.commands.ExecutionException; import com.aragost.javahg.commands.PushCommand; import com.aragost.javahg.commands.RemoveCommand; +import com.aragost.javahg.commands.StatusCommand; import org.apache.commons.lang.StringUtils; import sonia.scm.ContextEntry; -import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.NoChangesMadeException; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.util.WorkingCopy; @@ -26,12 +27,10 @@ import static sonia.scm.NotFoundException.notFound; public class HgModifyCommand implements ModifyCommand { - private final HgRepositoryHandler handler; private HgCommandContext context; private final HgWorkdirFactory workdirFactory; - public HgModifyCommand(HgRepositoryHandler handler, HgCommandContext context, HgWorkdirFactory workdirFactory) { - this.handler = handler; + public HgModifyCommand(HgCommandContext context, HgWorkdirFactory workdirFactory) { this.context = context; this.workdirFactory = workdirFactory; } @@ -115,7 +114,9 @@ public class HgModifyCommand implements ModifyCommand { } } ); - + if (StatusCommand.on(workingRepository).lines().isEmpty()) { + throw new NoChangesMadeException(context.getScmRepository()); + } CommitCommand.on(workingRepository).user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())).message(request.getCommitMessage()).execute(); List execute = PushCommand.on(workingRepository).execute(); return execute.get(0).getNode(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index 79812a7f68..7cd4c3602b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -242,7 +242,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider @Override public ModifyCommand getModifyCommand() { - return new HgModifyCommand(handler, context, workdirFactory); + return new HgModifyCommand(context, workdirFactory); } /** diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index b8ce787937..124c96bd26 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import sonia.scm.AlreadyExistsException; +import sonia.scm.NoChangesMadeException; import sonia.scm.NotFoundException; import sonia.scm.repository.HgHookManager; import sonia.scm.repository.Person; @@ -35,7 +36,7 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { when(hookManager.getCredentials()).thenReturn("SECRET:SECRET"); when(hookManager.createUrl()).thenReturn("http://localhost"); HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); - hgModifyCommand = new HgModifyCommand(handler, cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); + hgModifyCommand = new HgModifyCommand(cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); } @After @@ -159,11 +160,8 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { hgModifyCommand.execute(request); } - @Test(expected = IndexOutOfBoundsException.class) - public void shouldThrowIndexOutOfBoundExceptionIfRequestIsMissing() throws IOException { - ModifyCommandRequest request = new ModifyCommandRequest(); - request.setCommitMessage("I found the answer"); - request.setAuthor(new Person("Trillian Astra", "trillian@hitchhiker.com")); - hgModifyCommand.execute(request); + @Test(expected = NoChangesMadeException.class) + public void shouldThrowNoChangesMadeExceptionIfEmptyLocalChangesetAfterRequest() { + hgModifyCommand.execute(new ModifyCommandRequest()); } } From d1f7362ab110e8d69cabf7a0567de627db0d33a3 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 2 Oct 2019 11:34:42 +0200 Subject: [PATCH 05/22] set author from subject if not available in request for MergeCommandBuilder and ModifyCommandBuilder --- .../repository/api/MergeCommandBuilder.java | 2 ++ .../repository/api/ModifyCommandBuilder.java | 2 ++ .../sonia/scm/repository/util/AuthorUtil.java | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index 8fcfc937e5..0a2267e888 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import sonia.scm.repository.Person; import sonia.scm.repository.spi.MergeCommand; import sonia.scm.repository.spi.MergeCommandRequest; +import sonia.scm.repository.util.AuthorUtil; /** * Use this {@link MergeCommandBuilder} to merge two branches of a repository ({@link #executeMerge()}) or to check if @@ -126,6 +127,7 @@ public class MergeCommandBuilder { * @return The result of the merge. */ public MergeCommandResult executeMerge() { + AuthorUtil.setAuthorIfNotAvailable(request); Preconditions.checkArgument(request.isValid(), "revision to merge and target revision is required"); return mergeCommand.merge(request); } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java index 1eed0697cb..87d868c04a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.repository.Person; import sonia.scm.repository.spi.ModifyCommand; import sonia.scm.repository.spi.ModifyCommandRequest; +import sonia.scm.repository.util.AuthorUtil; import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.util.IOUtil; @@ -110,6 +111,7 @@ public class ModifyCommandBuilder { * @return The revision of the new commit. */ public String execute() { + AuthorUtil.setAuthorIfNotAvailable(request); try { Preconditions.checkArgument(request.isValid(), "commit message, branch and at least one request are required"); return command.execute(request); diff --git a/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java new file mode 100644 index 0000000000..263bba8ddb --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java @@ -0,0 +1,31 @@ +package sonia.scm.repository.util; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import sonia.scm.repository.Person; +import sonia.scm.repository.spi.MergeCommandRequest; +import sonia.scm.repository.spi.ModifyCommandRequest; +import sonia.scm.user.User; + +public class AuthorUtil { + + public static void setAuthorIfNotAvailable(ModifyCommandRequest request) { + if (request.getAuthor() == null) { + request.setAuthor(createAuthorFromSubject()); + } + } + + public static void setAuthorIfNotAvailable(MergeCommandRequest request) { + if (request.getAuthor() == null) { + request.setAuthor(createAuthorFromSubject()); + } + } + + private static Person createAuthorFromSubject() { + Subject subject = SecurityUtils.getSubject(); + User user = subject.getPrincipals().oneByType(User.class); + String name = user.getDisplayName(); + String email = user.getMail(); + return new Person(name, email); + } +} From 9120d492ab4763cde5bd8686b5fa8376efb42348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 4 Oct 2019 19:46:16 +0200 Subject: [PATCH 06/22] LFS upload POC --- .../scm/repository/spi/GitModifyCommand.java | 13 ++- .../spi/GitRepositoryServiceProvider.java | 2 +- .../spi/LfsBlobStoreCleanFilter.java | 101 ++++++++++++++++++ .../spi/LfsBlobStoreCleanFilterFactory.java | 26 +++++ .../repository/spi/GitModifyCommandTest.java | 6 +- .../spi/GitModifyCommand_LFSTest.java | 83 ++++++++++++++ 6 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 0f7c9296a7..19f18f0625 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -3,6 +3,7 @@ package sonia.scm.repository.spi; import org.apache.commons.lang.StringUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.attributes.FilterCommandRegistry; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; @@ -12,6 +13,7 @@ import sonia.scm.ContextEntry; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; +import sonia.scm.web.lfs.LfsBlobStoreFactory; import java.io.File; import java.io.IOException; @@ -30,10 +32,12 @@ import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { private final GitWorkdirFactory workdirFactory; + private final LfsBlobStoreFactory lfsBlobStoreFactory; - GitModifyCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) { + GitModifyCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory, LfsBlobStoreFactory lfsBlobStoreFactory) { super(context, repository); this.workdirFactory = workdirFactory; + this.lfsBlobStoreFactory = lfsBlobStoreFactory; } @Override @@ -87,10 +91,17 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman throw alreadyExists(createFileContext(toBeCreated)); } } + + LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); + + String registerKey = "git-lfs clean -- '" + toBeCreated + "'"; + FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); try { addFileToGit(toBeCreated); } catch (GitAPIException e) { throwInternalRepositoryException("could not add new file to index", e); + } finally { + FilterCommandRegistry.unregister(registerKey); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java index 4c02a3a73c..dc43b8d9b7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java @@ -273,7 +273,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider @Override public ModifyCommand getModifyCommand() { - return new GitModifyCommand(context, repository, handler.getWorkdirFactory()); + return new GitModifyCommand(context, repository, handler.getWorkdirFactory(), lfsBlobStoreFactory); } @Override diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java new file mode 100644 index 0000000000..d47673c970 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java @@ -0,0 +1,101 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.attributes.FilterCommand; +import org.eclipse.jgit.lfs.Lfs; +import org.eclipse.jgit.lfs.LfsPointer; +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.LongObjectId; +import org.eclipse.jgit.lib.Repository; +import sonia.scm.store.Blob; +import sonia.scm.store.BlobStore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static org.eclipse.jgit.lfs.lib.Constants.LONG_HASH_FUNCTION; + +/** + * Adapted version of JGit's {@link org.eclipse.jgit.lfs.CleanFilter} to write the + * lfs file directly to the lfs blob store. + */ +public class LfsBlobStoreCleanFilter extends FilterCommand { + + + private Lfs lfsUtil; + private final BlobStore lfsBlobStore; + private final Path targetFile; + private final DigestOutputStream digestOutputStream; + + private long size; + + public LfsBlobStoreCleanFilter(Repository db, InputStream in, OutputStream out, BlobStore lfsBlobStore, Path targetFile) + throws IOException { + super(in, out); + lfsUtil = new Lfs(db); + this.lfsBlobStore = lfsBlobStore; + this.targetFile = targetFile; + Files.createDirectories(lfsUtil.getLfsTmpDir()); + + + MessageDigest md ; + try { + md = MessageDigest.getInstance(LONG_HASH_FUNCTION); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + digestOutputStream = new DigestOutputStream(new OutputStream() { + @Override + public void write(int b) { + } + }, md); + + } + + @Override + public int run() throws IOException { + try { + byte[] buf = new byte[8192]; + int length = in.read(buf); + if (length != -1) { + digestOutputStream.write(buf, 0, length); + size += length; + return length; + } else { + digestOutputStream.close(); + AnyLongObjectId loid = LongObjectId.fromRaw(digestOutputStream.getMessageDigest().digest()); + + Blob existingBlob = lfsBlobStore.get(loid.getName()); + if (existingBlob != null) { + long blobSize = existingBlob.getSize(); + if (blobSize != size) { + throw new RuntimeException("lfs entry already exists for loid " + loid.getName() + " but has wrong size"); + } + } else { + Blob newBlob = lfsBlobStore.create(loid.getName()); + OutputStream outputStream = newBlob.getOutputStream(); + Files.copy(targetFile, outputStream); + newBlob.commit(); + } + + LfsPointer lfsPointer = new LfsPointer(loid, size); + lfsPointer.encode(out); + in.close(); + out.close(); + return -1; + } + } catch (IOException e) { + if (digestOutputStream != null) { + digestOutputStream.close(); + } + in.close(); + out.close(); + throw e; + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java new file mode 100644 index 0000000000..6951ec1b14 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java @@ -0,0 +1,26 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.lib.Repository; +import sonia.scm.web.lfs.LfsBlobStoreFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Path; + +public class LfsBlobStoreCleanFilterFactory { + + private final LfsBlobStoreFactory blobStoreFactory; + private final sonia.scm.repository.Repository repository; + private final Path targetFile; + + public LfsBlobStoreCleanFilterFactory(LfsBlobStoreFactory blobStoreFactory, sonia.scm.repository.Repository repository, Path targetFile) { + this.blobStoreFactory = blobStoreFactory; + this.repository = repository; + this.targetFile = targetFile; + } + + LfsBlobStoreCleanFilter createFilter(Repository db, InputStream in, OutputStream out) throws IOException { + return new LfsBlobStoreCleanFilter(db, in, out, blobStoreFactory.getLfsBlobStore(repository), targetFile); + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java index 4fbf7eaf02..e26458a4ec 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java @@ -20,12 +20,14 @@ import sonia.scm.NotFoundException; import sonia.scm.ScmConstraintViolationException; import sonia.scm.repository.Person; import sonia.scm.repository.util.WorkdirProvider; +import sonia.scm.web.lfs.LfsBlobStoreFactory; import java.io.File; import java.io.IOException; import java.nio.file.Files; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") public class GitModifyCommandTest extends AbstractGitCommandTestBase { @@ -37,6 +39,8 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase { @Rule public ShiroRule shiro = new ShiroRule(); + private final LfsBlobStoreFactory lfsBlobStoreFactory = mock(LfsBlobStoreFactory.class); + @Test public void shouldCreateCommit() throws IOException, GitAPIException { File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); @@ -296,7 +300,7 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase { } private GitModifyCommand createCommand() { - return new GitModifyCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider())); + return new GitModifyCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider()), lfsBlobStoreFactory); } @FunctionalInterface diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java new file mode 100644 index 0000000000..9414497bcb --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -0,0 +1,83 @@ +package sonia.scm.repository.spi; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import sonia.scm.repository.Person; +import sonia.scm.repository.util.WorkdirProvider; +import sonia.scm.store.Blob; +import sonia.scm.store.BlobStore; +import sonia.scm.web.lfs.LfsBlobStoreFactory; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") +public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); + @Rule + public ShiroRule shiro = new ShiroRule(); + + private final LfsBlobStoreFactory lfsBlobStoreFactory = mock(LfsBlobStoreFactory.class); + + @Test + public void shouldCreateCommit() throws IOException, GitAPIException { + BlobStore blobStore = mock(BlobStore.class); + Blob blob = mock(Blob.class); + when(lfsBlobStoreFactory.getLfsBlobStore(any())).thenReturn(blobStore); + when(blobStore.create("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(blob); + when(blobStore.get("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(null, blob); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + when(blob.getOutputStream()).thenReturn(outputStream); + when(blob.getSize()).thenReturn((long) "new content".length()); + + File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("test commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_lfs.png", newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + String newRef = command.execute(request); + + try (Git git = new Git(createContext().open())) { + RevCommit lastCommit = getLastCommit(git); + assertThat(lastCommit.getFullMessage()).isEqualTo("test commit"); + assertThat(lastCommit.getAuthorIdent().getName()).isEqualTo("Dirk Gently"); + assertThat(newRef).isEqualTo(lastCommit.toObjectId().name()); + } + + assertThat(outputStream.toString()).isEqualTo("new content"); + } + + private RevCommit getLastCommit(Git git) throws GitAPIException { + return git.log().setMaxCount(1).call().iterator().next(); + } + + private GitModifyCommand createCommand() { + return new GitModifyCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider()), lfsBlobStoreFactory); + } + + @Override + protected String getZippedRepositoryResource() { + return "sonia/scm/repository/spi/scm-git-spi-lfs-test.zip"; + } +} From 100c73c33657cf809e45f10c7aa1d348719c2534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Sun, 6 Oct 2019 19:41:23 +0200 Subject: [PATCH 07/22] Add missing test repository --- .../scm/repository/spi/scm-git-spi-lfs-test.zip | Bin 0 -> 14315 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-lfs-test.zip diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-lfs-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-lfs-test.zip new file mode 100644 index 0000000000000000000000000000000000000000..b97f51968427060cc523a8b1594930e1cd487f9a GIT binary patch literal 14315 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 literal 0 HcmV?d00001 From a40410d1ae51264efab0fa27489f3867e83119a4 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 7 Oct 2019 11:10:37 +0200 Subject: [PATCH 08/22] fix hg modify command after factory changes --- .../src/main/java/sonia/scm/repository/spi/HgModifyCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index 4d5e77681e..7f9df4f60b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -38,7 +38,7 @@ public class HgModifyCommand implements ModifyCommand { @Override public String execute(ModifyCommandRequest request) { - try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { + try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context, request.getBranch())) { Repository workingRepository = workingCopy.getWorkingRepository(); request.getRequests().forEach( partialRequest -> { From 120416c4d62a67af3e989b1175f68df2f214048b Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 7 Oct 2019 13:49:43 +0200 Subject: [PATCH 09/22] pull modify changes from working copy to central repository so that mercurial hooks gets triggered --- .../scm/repository/spi/HgModifyCommand.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index 7f9df4f60b..eaec861335 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -4,7 +4,7 @@ import com.aragost.javahg.Changeset; import com.aragost.javahg.Repository; import com.aragost.javahg.commands.CommitCommand; import com.aragost.javahg.commands.ExecutionException; -import com.aragost.javahg.commands.PushCommand; +import com.aragost.javahg.commands.PullCommand; import com.aragost.javahg.commands.RemoveCommand; import com.aragost.javahg.commands.StatusCommand; import org.apache.commons.lang.StringUtils; @@ -118,14 +118,26 @@ public class HgModifyCommand implements ModifyCommand { throw new NoChangesMadeException(context.getScmRepository()); } CommitCommand.on(workingRepository).user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())).message(request.getCommitMessage()).execute(); - List execute = PushCommand.on(workingRepository).execute(); + List execute = pullModifyChangesToCentralRepository(request, workingCopy); return execute.get(0).getNode(); - } catch (IOException | ExecutionException e) { + } catch (ExecutionException e) { throwInternalRepositoryException("could not execute command on repository", e); return null; } } + private List pullModifyChangesToCentralRepository(ModifyCommandRequest request, WorkingCopy workingCopy) { + try { + com.aragost.javahg.commands.PullCommand pullCommand = PullCommand.on(workingCopy.getCentralRepository()); + workdirFactory.configure(pullCommand); + return pullCommand.execute(workingCopy.getDirectory().getAbsolutePath()); + } catch (Exception e) { + throw new IntegrateChangesFromWorkdirException(context.getScmRepository(), + String.format("Could not pull modify changes from working copy to central repository", request.getBranch()), + e); + } + } + private String throwInternalRepositoryException(String message, Exception e) { throw new InternalRepositoryException(context.getScmRepository(), message, e); } From cb6f1bfa2203e790a1ebf77633d7923350e32f42 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 7 Oct 2019 14:59:46 +0200 Subject: [PATCH 10/22] remove unused move-method from ModifyCommand / create interface for CommandRequests with Author --- .../repository/api/ModifyCommandBuilder.java | 11 ---------- .../repository/spi/MergeCommandRequest.java | 3 ++- .../scm/repository/spi/ModifyCommand.java | 2 -- .../repository/spi/ModifyCommandRequest.java | 18 ++--------------- .../sonia/scm/repository/util/AuthorUtil.java | 16 +++++++-------- .../api/ModifyCommandBuilderTest.java | 10 ---------- .../scm/repository/spi/GitModifyCommand.java | 5 ----- .../scm/repository/spi/HgModifyCommand.java | 4 ---- .../repository/spi/HgModifyCommandTest.java | 17 +++++++++------- .../api/v2/resources/MergeResourceTest.java | 20 +++++++++++++++++++ 10 files changed, 41 insertions(+), 65 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java index 87d868c04a..05c1babe03 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java @@ -95,17 +95,6 @@ public class ModifyCommandBuilder { return this; } - /** - * Move an existing file. - * @param sourcePath The path and the name of the file that should be moved. - * @param targetPath The new path and name the file should be moved to. - * @return This builder instance. - */ - public ModifyCommandBuilder moveFile(String sourcePath, String targetPath) { - request.addRequest(new ModifyCommandRequest.MoveFileRequest(sourcePath, targetPath)); - return this; - } - /** * Apply the changes and create a new commit with the given message and author. * @return The revision of the new commit. diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java index 45dfc0b2b7..223cf8c49e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -5,10 +5,11 @@ import com.google.common.base.Objects; import com.google.common.base.Strings; import sonia.scm.Validateable; import sonia.scm.repository.Person; +import sonia.scm.repository.util.AuthorUtil.CommandWithAuthor; import java.io.Serializable; -public class MergeCommandRequest implements Validateable, Resetable, Serializable, Cloneable { +public class MergeCommandRequest implements Validateable, Resetable, Serializable, Cloneable, CommandWithAuthor { private static final long serialVersionUID = -2650236557922431528L; diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommand.java index b3ba882040..1bc64e0e08 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommand.java @@ -13,7 +13,5 @@ public interface ModifyCommand { void create(String toBeCreated, File file, boolean overwrite) throws IOException; void modify(String path, File file) throws IOException; - - void move(String sourcePath, String targetPath); } } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommandRequest.java index d61e17c785..43814f8729 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyCommandRequest.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.Validateable; import sonia.scm.repository.Person; +import sonia.scm.repository.util.AuthorUtil.CommandWithAuthor; import sonia.scm.util.IOUtil; import java.io.File; @@ -13,7 +14,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class ModifyCommandRequest implements Resetable, Validateable { +public class ModifyCommandRequest implements Resetable, Validateable, CommandWithAuthor { private static final Logger LOG = LoggerFactory.getLogger(ModifyCommandRequest.class); @@ -94,21 +95,6 @@ public class ModifyCommandRequest implements Resetable, Validateable { } } - public static class MoveFileRequest implements PartialRequest { - private final String sourcePath; - private final String targetPath; - - public MoveFileRequest(String sourcePath, String targetPath) { - this.sourcePath = sourcePath; - this.targetPath = targetPath; - } - - @Override - public void execute(ModifyCommand.Worker worker) { - worker.move(sourcePath, targetPath); - } - } - private abstract static class ContentModificationRequest implements PartialRequest { private final File content; diff --git a/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java index 263bba8ddb..5735889c3c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java @@ -3,19 +3,11 @@ package sonia.scm.repository.util; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import sonia.scm.repository.Person; -import sonia.scm.repository.spi.MergeCommandRequest; -import sonia.scm.repository.spi.ModifyCommandRequest; import sonia.scm.user.User; public class AuthorUtil { - public static void setAuthorIfNotAvailable(ModifyCommandRequest request) { - if (request.getAuthor() == null) { - request.setAuthor(createAuthorFromSubject()); - } - } - - public static void setAuthorIfNotAvailable(MergeCommandRequest request) { + public static void setAuthorIfNotAvailable(CommandWithAuthor request) { if (request.getAuthor() == null) { request.setAuthor(createAuthorFromSubject()); } @@ -28,4 +20,10 @@ public class AuthorUtil { String email = user.getMail(); return new Person(name, email); } + + public interface CommandWithAuthor { + Person getAuthor(); + + void setAuthor(Person person); + } } diff --git a/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java b/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java index db9a247d65..6e46841cd2 100644 --- a/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java @@ -1,7 +1,6 @@ package sonia.scm.repository.api; import com.google.common.io.ByteSource; -import com.sun.org.apache.xpath.internal.operations.Bool; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -85,15 +84,6 @@ class ModifyCommandBuilderTest { verify(worker).delete("toBeDeleted"); } - @Test - void shouldExecuteMove() throws IOException { - initCommand() - .moveFile("source", "target") - .execute(); - - verify(worker).move("source", "target"); - } - @Test void shouldExecuteCreateWithByteSourceContent() throws IOException { ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index de70b2766c..10cc45631f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -143,11 +143,6 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman contextBuilder.in(context.getRepository()); return contextBuilder; } - - @Override - public void move(String sourcePath, String targetPath) { - - } } private String throwInternalRepositoryException(String message, Exception e) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index eaec861335..ee9fa002d4 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -84,10 +84,6 @@ public class HgModifyCommand implements ModifyCommand { } } - @Override - public void move(String sourcePath, String targetPath) { - } - private void createDirectories(Path targetFile) throws IOException { try { Files.createDirectories(targetFile.getParent()); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index 124c96bd26..f85f6a2e40 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -10,6 +10,7 @@ import sonia.scm.AlreadyExistsException; import sonia.scm.NoChangesMadeException; import sonia.scm.NotFoundException; import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.Person; import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.web.HgRepositoryEnvironmentBuilder; @@ -19,8 +20,6 @@ import java.io.FileOutputStream; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class HgModifyCommandTest extends AbstractHgCommandTestBase { @@ -31,12 +30,16 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { @Before public void initHgModifyCommand() { - HgHookManager hookManager = mock(HgHookManager.class); - when(hookManager.getChallenge()).thenReturn("CHALLENGE"); - when(hookManager.getCredentials()).thenReturn("SECRET:SECRET"); - when(hookManager.createUrl()).thenReturn("http://localhost"); + HgHookManager hookManager = HgTestUtil.createHookManager(); HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); - hgModifyCommand = new HgModifyCommand(cmdContext, new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider())); + SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider()) { + @Override + public void configure(com.aragost.javahg.commands.PullCommand pullCommand) { + // we do not want to configure http hooks in this unit test + } + }; + hgModifyCommand = new HgModifyCommand(cmdContext, workdirFactory + ); } @After diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java index b9e720fc71..7181912cfe 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java @@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.SubjectAware; import com.google.common.io.Resources; import com.google.inject.util.Providers; +import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ThreadContext; import org.jboss.resteasy.core.Dispatcher; @@ -24,6 +25,7 @@ import sonia.scm.repository.api.MergeDryRunCommandResult; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.spi.MergeCommand; +import sonia.scm.user.User; import sonia.scm.web.VndMediaType; import java.net.URL; @@ -32,6 +34,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static sonia.scm.repository.RepositoryTestData.createHeartOfGold; @@ -105,6 +108,10 @@ public class MergeResourceTest extends RepositoryTestBase { @Test void shouldHandleSuccessfulMerge() throws Exception { when(mergeCommand.merge(any())).thenReturn(MergeCommandResult.success()); + User user = createDummyUser("dummy"); + PrincipalCollection collection = mock(PrincipalCollection.class); + when(subject.getPrincipals()).thenReturn(collection); + when(collection.oneByType(User.class)).thenReturn(user); URL url = Resources.getResource("sonia/scm/api/v2/mergeCommand.json"); byte[] mergeCommandJson = Resources.toByteArray(url); @@ -122,6 +129,10 @@ public class MergeResourceTest extends RepositoryTestBase { @Test void shouldHandleFailedMerge() throws Exception { when(mergeCommand.merge(any())).thenReturn(MergeCommandResult.failure(asList("file1", "file2"))); + User user = createDummyUser("dummy"); + PrincipalCollection collection = mock(PrincipalCollection.class); + when(subject.getPrincipals()).thenReturn(collection); + when(collection.oneByType(User.class)).thenReturn(user); URL url = Resources.getResource("sonia/scm/api/v2/mergeCommand.json"); byte[] mergeCommandJson = Resources.toByteArray(url); @@ -189,5 +200,14 @@ public class MergeResourceTest extends RepositoryTestBase { assertThat(response.getStatus()).isEqualTo(204); } + + private User createDummyUser(String name) { + User user = new User(); + user.setName(name); + user.setType("xml"); + user.setPassword("secret"); + user.setCreationDate(System.currentTimeMillis()); + return user; + } } } From 285084aed1339068b25693b54c3dc15327e8f6e9 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 7 Oct 2019 16:51:46 +0200 Subject: [PATCH 11/22] remove closeRepository after unit tests since its already closed by abstractTestBase --- .../java/sonia/scm/repository/spi/HgModifyCommandTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index f85f6a2e40..862d2ab2ed 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -1,7 +1,6 @@ package sonia.scm.repository.spi; import com.google.inject.util.Providers; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -42,11 +41,6 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { ); } - @After - public void closeRepository() throws IOException { - cmdContext.close(); - } - @Test public void shouldRemoveFiles() { ModifyCommandRequest request = new ModifyCommandRequest(); From a1da5eaddbef039809d3fdd203946d25d763ba9a Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Mon, 7 Oct 2019 18:25:31 +0200 Subject: [PATCH 12/22] Simplify --- .../spi/LfsBlobStoreCleanFilter.java | 90 +++++++++---------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java index d47673c970..c320cf23d4 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java @@ -1,5 +1,6 @@ package sonia.scm.repository.spi; +import com.google.common.io.ByteStreams; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.lfs.Lfs; import org.eclipse.jgit.lfs.LfsPointer; @@ -8,6 +9,7 @@ import org.eclipse.jgit.lfs.lib.LongObjectId; import org.eclipse.jgit.lib.Repository; import sonia.scm.store.Blob; import sonia.scm.store.BlobStore; +import sonia.scm.util.IOUtil; import java.io.IOException; import java.io.InputStream; @@ -30,9 +32,6 @@ public class LfsBlobStoreCleanFilter extends FilterCommand { private Lfs lfsUtil; private final BlobStore lfsBlobStore; private final Path targetFile; - private final DigestOutputStream digestOutputStream; - - private long size; public LfsBlobStoreCleanFilter(Repository db, InputStream in, OutputStream out, BlobStore lfsBlobStore, Path targetFile) throws IOException { @@ -41,61 +40,52 @@ public class LfsBlobStoreCleanFilter extends FilterCommand { this.lfsBlobStore = lfsBlobStore; this.targetFile = targetFile; Files.createDirectories(lfsUtil.getLfsTmpDir()); - - - MessageDigest md ; - try { - md = MessageDigest.getInstance(LONG_HASH_FUNCTION); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - digestOutputStream = new DigestOutputStream(new OutputStream() { - @Override - public void write(int b) { - } - }, md); - } @Override public int run() throws IOException { + DigestOutputStream digestOutputStream = createDigestStream(); try { - byte[] buf = new byte[8192]; - int length = in.read(buf); - if (length != -1) { - digestOutputStream.write(buf, 0, length); - size += length; - return length; - } else { - digestOutputStream.close(); - AnyLongObjectId loid = LongObjectId.fromRaw(digestOutputStream.getMessageDigest().digest()); + long size = ByteStreams.copy(in, digestOutputStream); + AnyLongObjectId loid = LongObjectId.fromRaw(digestOutputStream.getMessageDigest().digest()); - Blob existingBlob = lfsBlobStore.get(loid.getName()); - if (existingBlob != null) { - long blobSize = existingBlob.getSize(); - if (blobSize != size) { - throw new RuntimeException("lfs entry already exists for loid " + loid.getName() + " but has wrong size"); - } - } else { - Blob newBlob = lfsBlobStore.create(loid.getName()); - OutputStream outputStream = newBlob.getOutputStream(); - Files.copy(targetFile, outputStream); - newBlob.commit(); + Blob existingBlob = lfsBlobStore.get(loid.getName()); + if (existingBlob != null) { + long blobSize = existingBlob.getSize(); + if (blobSize != size) { + // Mathematicians say this will never happen + throw new RuntimeException("lfs entry already exists for loid " + loid.getName() + " but has wrong size"); } + } else { + Blob newBlob = lfsBlobStore.create(loid.getName()); + OutputStream outputStream = newBlob.getOutputStream(); + Files.copy(targetFile, outputStream); + newBlob.commit(); + } - LfsPointer lfsPointer = new LfsPointer(loid, size); - lfsPointer.encode(out); - in.close(); - out.close(); - return -1; - } - } catch (IOException e) { - if (digestOutputStream != null) { - digestOutputStream.close(); - } - in.close(); - out.close(); - throw e; + LfsPointer lfsPointer = new LfsPointer(loid, size); + lfsPointer.encode(out); + return -1; + } finally { + IOUtil.close(digestOutputStream); + IOUtil.close(in); + IOUtil.close(out); } } + + private DigestOutputStream createDigestStream() { + MessageDigest md ; + try { + md = MessageDigest.getInstance(LONG_HASH_FUNCTION); + } catch (NoSuchAlgorithmException e) { + // Yes there is such a hash function (should be sha256) + throw new RuntimeException(e); + } + return new DigestOutputStream(new OutputStream() { + @Override + public void write(int b) { + // no further target here, we are just interested in the digest + } + }, md); + } } From 24e18b4414b453faccd4b6b4fa2b4455c7faf995 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Mon, 7 Oct 2019 18:45:12 +0200 Subject: [PATCH 13/22] Use no-op filter as default for lfs --- pom.xml | 2 +- .../repository/spi/GitLfsFilterModule.java | 36 ++++++++++++ .../scm/repository/spi/GitModifyCommand.java | 38 +++++++----- .../spi/GitModifyCommand_LFSTest.java | 58 +++++++++++++------ 4 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java diff --git a/pom.xml b/pom.xml index 3ab3cb82a3..c14ce8b39b 100644 --- a/pom.xml +++ b/pom.xml @@ -843,7 +843,7 @@ 1.4.0 - v5.4.0.201906121030-r-scm1 + v5.4.0.201906121030-r-scm2 1.9.0-scm3 diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java new file mode 100644 index 0000000000..2faa2f550f --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java @@ -0,0 +1,36 @@ +package sonia.scm.repository.spi; + +import com.google.common.io.ByteStreams; +import com.google.inject.Binder; +import com.google.inject.Module; +import org.eclipse.jgit.attributes.FilterCommand; +import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.lib.Repository; +import sonia.scm.plugin.Extension; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.regex.Pattern; + +@Extension +public class GitLfsFilterModule implements Module { + @Override + public void configure(Binder binder) { + FilterCommandRegistry.register(Pattern.compile("git-lfs (smudge|clean) -- .*"), NoOpFilterCommand::new); + } + + private static class NoOpFilterCommand extends FilterCommand { + NoOpFilterCommand(Repository db, InputStream in, OutputStream out) { + super(in, out); + } + + @Override + public int run() throws IOException { + ByteStreams.copy(in, out); + in.close(); + out.close(); + return -1; + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 19f18f0625..1b748a6f1e 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -1,5 +1,6 @@ package sonia.scm.repository.spi; +import com.google.common.util.concurrent.Striped; import org.apache.commons.lang.StringUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; @@ -22,6 +23,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Optional; +import java.util.concurrent.locks.Lock; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static sonia.scm.AlreadyExistsException.alreadyExists; @@ -31,6 +33,8 @@ import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { + private static final Striped REGISTER_LOCKS = Striped.lock(5); + private final GitWorkdirFactory workdirFactory; private final LfsBlobStoreFactory lfsBlobStoreFactory; @@ -92,17 +96,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } } - LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); - - String registerKey = "git-lfs clean -- '" + toBeCreated + "'"; - FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); - try { - addFileToGit(toBeCreated); - } catch (GitAPIException e) { - throwInternalRepositoryException("could not add new file to index", e); - } finally { - FilterCommandRegistry.unregister(registerKey); - } + addToGitWithLfsSupport(toBeCreated, targetFile); } @Override @@ -113,10 +107,26 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman throw notFound(createFileContext(path)); } Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + + addToGitWithLfsSupport(path, targetFile); + } + + private void addToGitWithLfsSupport(String path, Path targetFile) { + REGISTER_LOCKS.get(targetFile).lock(); try { - addFileToGit(path); - } catch (GitAPIException e) { - throwInternalRepositoryException("could not add new file to index", e); + LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); + + String registerKey = "git-lfs clean -- '" + path + "'"; + FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); + try { + addFileToGit(path); + } catch (GitAPIException e) { + throwInternalRepositoryException("could not add file to index", e); + } finally { + FilterCommandRegistry.unregister(registerKey); + } + } finally { + REGISTER_LOCKS.get(targetFile).unlock(); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java index 9414497bcb..c7bbb2a71f 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -38,25 +38,8 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { @Test public void shouldCreateCommit() throws IOException, GitAPIException { - BlobStore blobStore = mock(BlobStore.class); - Blob blob = mock(Blob.class); - when(lfsBlobStoreFactory.getLfsBlobStore(any())).thenReturn(blobStore); - when(blobStore.create("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(blob); - when(blobStore.get("fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601")).thenReturn(null, blob); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - when(blob.getOutputStream()).thenReturn(outputStream); - when(blob.getSize()).thenReturn((long) "new content".length()); - - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); - - GitModifyCommand command = createCommand(); - - ModifyCommandRequest request = new ModifyCommandRequest(); - request.setCommitMessage("test commit"); - request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_lfs.png", newFile, false)); - request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); - - String newRef = command.execute(request); + String newRef = createCommit("new_lfs.png", "new content", "fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601", outputStream); try (Git git = new Git(createContext().open())) { RevCommit lastCommit = getLastCommit(git); @@ -68,6 +51,45 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { assertThat(outputStream.toString()).isEqualTo("new content"); } + @Test + public void shouldCreateSecondCommits() throws IOException, GitAPIException { + new GitLfsFilterModule().configure(null); + createCommit("new_lfs.png", "new content", "fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601", new ByteArrayOutputStream()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + String newRef = createCommit("more_lfs.png", "more content", "2c2316737c9313956dfc0083da3a2a62ce259f66484f3e26440f0d1b02dd4128", outputStream); + + try (Git git = new Git(createContext().open())) { + RevCommit lastCommit = getLastCommit(git); + assertThat(lastCommit.getFullMessage()).isEqualTo("test commit"); + assertThat(lastCommit.getAuthorIdent().getName()).isEqualTo("Dirk Gently"); + assertThat(newRef).isEqualTo(lastCommit.toObjectId().name()); + } + + assertThat(outputStream.toString()).isEqualTo("more content"); + } + + private String createCommit(String fileName, String content, String hashOfContent, ByteArrayOutputStream outputStream) throws IOException { + BlobStore blobStore = mock(BlobStore.class); + Blob blob = mock(Blob.class); + when(lfsBlobStoreFactory.getLfsBlobStore(any())).thenReturn(blobStore); + when(blobStore.create(hashOfContent)).thenReturn(blob); + when(blobStore.get(hashOfContent)).thenReturn(null, blob); + when(blob.getOutputStream()).thenReturn(outputStream); + when(blob.getSize()).thenReturn((long) content.length()); + + File newFile = Files.write(temporaryFolder.newFile().toPath(), content.getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("test commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest(fileName, newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + return command.execute(request); + } + private RevCommit getLastCommit(Git git) throws GitAPIException { return git.log().setMaxCount(1).call().iterator().next(); } From 76c7821ed2d86ab7d0476cdef47243b9f93c449f Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Tue, 8 Oct 2019 11:58:13 +0200 Subject: [PATCH 14/22] Reduce code redundancy --- .../repository/spi/ModifyWorkerHelper.java | 88 +++++++++++++++++++ .../scm/repository/spi/GitModifyCommand.java | 81 ++++------------- .../scm/repository/spi/HgModifyCommand.java | 67 ++++---------- .../spi/HgRepositoryServiceProvider.java | 2 +- 4 files changed, 123 insertions(+), 115 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/spi/ModifyWorkerHelper.java diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ModifyWorkerHelper.java b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyWorkerHelper.java new file mode 100644 index 0000000000..a1ff6b0eb3 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/spi/ModifyWorkerHelper.java @@ -0,0 +1,88 @@ +package sonia.scm.repository.spi; + +import org.apache.commons.lang.StringUtils; +import sonia.scm.ContextEntry; +import sonia.scm.repository.Repository; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static sonia.scm.AlreadyExistsException.alreadyExists; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + +/** + * This "interface" is not really intended to be used as an interface but rather as + * a base class to reduce code redundancy in Worker instances. + */ +public interface ModifyWorkerHelper extends ModifyCommand.Worker { + + @Override + default void delete(String toBeDeleted) throws IOException { + Path fileToBeDeleted = new File(getWorkDir(), toBeDeleted).toPath(); + try { + Files.delete(fileToBeDeleted); + } catch (NoSuchFileException e) { + throw notFound(createFileContext(toBeDeleted)); + } + doScmDelete(toBeDeleted); + } + + void doScmDelete(String toBeDeleted); + + @Override + default void create(String toBeCreated, File file, boolean overwrite) throws IOException { + Path targetFile = new File(getWorkDir(), toBeCreated).toPath(); + createDirectories(targetFile); + if (overwrite) { + Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + } else { + try { + Files.move(file.toPath(), targetFile); + } catch (FileAlreadyExistsException e) { + throw alreadyExists(createFileContext(toBeCreated)); + } + } + addFileToScm(toBeCreated, targetFile); + } + + default void modify(String path, File file) throws IOException { + Path targetFile = new File(getWorkDir(), path).toPath(); + createDirectories(targetFile); + if (!targetFile.toFile().exists()) { + throw notFound(createFileContext(path)); + } + Files.move(file.toPath(), targetFile, REPLACE_EXISTING); + addFileToScm(path, targetFile); + } + + void addFileToScm(String name, Path file); + + default ContextEntry.ContextBuilder createFileContext(String path) { + ContextEntry.ContextBuilder contextBuilder = entity("file", path); + if (!StringUtils.isEmpty(getBranch())) { + contextBuilder.in("branch", getBranch()); + } + contextBuilder.in(getRepository()); + return contextBuilder; + } + + default void createDirectories(Path targetFile) throws IOException { + try { + Files.createDirectories(targetFile.getParent()); + } catch (FileAlreadyExistsException e) { + throw alreadyExists(createFileContext(targetFile.toString())); + } + } + + File getWorkDir(); + + Repository getRepository(); + + String getBranch(); +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 10cc45631f..da7f0150f5 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -5,7 +5,6 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.revwalk.RevCommit; import sonia.scm.ConcurrentModificationException; -import sonia.scm.ContextEntry; import sonia.scm.NoChangesMadeException; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.InternalRepositoryException; @@ -13,17 +12,9 @@ import sonia.scm.repository.Repository; import java.io.File; import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Optional; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static sonia.scm.AlreadyExistsException.alreadyExists; -import static sonia.scm.ContextEntry.ContextBuilder.entity; -import static sonia.scm.NotFoundException.notFound; - public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { private final GitWorkdirFactory workdirFactory; @@ -38,7 +29,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman return inClone(clone -> new ModifyWorker(clone, request), workdirFactory, request.getBranch()); } - private class ModifyWorker extends GitCloneWorker implements Worker { + private class ModifyWorker extends GitCloneWorker implements ModifyWorkerHelper { private final File workDir; private final ModifyCommandRequest request; @@ -67,35 +58,9 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } @Override - public void create(String toBeCreated, File file, boolean overwrite) throws IOException { - Path targetFile = new File(workDir, toBeCreated).toPath(); - createDirectories(targetFile); - if (overwrite) { - Files.move(file.toPath(), targetFile, REPLACE_EXISTING); - } else { - try { - Files.move(file.toPath(), targetFile); - } catch (FileAlreadyExistsException e) { - throw alreadyExists(createFileContext(toBeCreated)); - } - } + public void addFileToScm(String name, Path file) { try { - addFileToGit(toBeCreated); - } catch (GitAPIException e) { - throwInternalRepositoryException("could not add new file to index", e); - } - } - - @Override - public void modify(String path, File file) throws IOException { - Path targetFile = new File(workDir, path).toPath(); - createDirectories(targetFile); - if (!targetFile.toFile().exists()) { - throw notFound(createFileContext(path)); - } - Files.move(file.toPath(), targetFile, REPLACE_EXISTING); - try { - addFileToGit(path); + addFileToGit(name); } catch (GitAPIException e) { throwInternalRepositoryException("could not add new file to index", e); } @@ -106,13 +71,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } @Override - public void delete(String toBeDeleted) throws IOException { - Path fileToBeDeleted = new File(workDir, toBeDeleted).toPath(); - try { - Files.delete(fileToBeDeleted); - } catch (NoSuchFileException e) { - throw notFound(createFileContext(toBeDeleted)); - } + public void doScmDelete(String toBeDeleted) { try { getClone().rm().addFilepattern(removeStartingPathSeparators(toBeDeleted)).call(); } catch (GitAPIException e) { @@ -120,29 +79,27 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } } + @Override + public File getWorkDir() { + return workDir; + } + + @Override + public Repository getRepository() { + return repository; + } + + @Override + public String getBranch() { + return request.getBranch(); + } + private String removeStartingPathSeparators(String path) { while (path.startsWith(File.separator)) { path = path.substring(1); } return path; } - - private void createDirectories(Path targetFile) throws IOException { - try { - Files.createDirectories(targetFile.getParent()); - } catch (FileAlreadyExistsException e) { - throw alreadyExists(createFileContext(targetFile.toString())); - } - } - - private ContextEntry.ContextBuilder createFileContext(String path) { - ContextEntry.ContextBuilder contextBuilder = entity("file", path); - if (!StringUtils.isEmpty(request.getBranch())) { - contextBuilder.in("branch", request.getBranch()); - } - contextBuilder.in(context.getRepository()); - return contextBuilder; - } } private String throwInternalRepositoryException(String message, Exception e) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index ee9fa002d4..cd0a649b6e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -7,24 +7,15 @@ import com.aragost.javahg.commands.ExecutionException; import com.aragost.javahg.commands.PullCommand; import com.aragost.javahg.commands.RemoveCommand; import com.aragost.javahg.commands.StatusCommand; -import org.apache.commons.lang.StringUtils; -import sonia.scm.ContextEntry; import sonia.scm.NoChangesMadeException; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.util.WorkingCopy; import java.io.File; import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static sonia.scm.AlreadyExistsException.alreadyExists; -import static sonia.scm.ContextEntry.ContextBuilder.entity; -import static sonia.scm.NotFoundException.notFound; - public class HgModifyCommand implements ModifyCommand { private HgCommandContext context; @@ -43,62 +34,34 @@ public class HgModifyCommand implements ModifyCommand { request.getRequests().forEach( partialRequest -> { try { - partialRequest.execute(new Worker() { - @Override - public void delete(String toBeDeleted) { - RemoveCommand.on(workingRepository).execute(toBeDeleted); - } + partialRequest.execute(new ModifyWorkerHelper() { @Override - public void create(String toBeCreated, File file, boolean overwrite) throws IOException { - Path targetFile = new File(workingRepository.getDirectory(), toBeCreated).toPath(); - createDirectories(targetFile); - if (overwrite) { - Files.move(file.toPath(), targetFile, REPLACE_EXISTING); - } else { - try { - Files.move(file.toPath(), targetFile); - } catch (FileAlreadyExistsException e) { - throw alreadyExists(createFileContext(toBeCreated)); - } - } + public void addFileToScm(String name, Path file) { try { - addFileToHg(targetFile.toFile()); + addFileToHg(file.toFile()); } catch (ExecutionException e) { throwInternalRepositoryException("could not add new file to index", e); } } @Override - public void modify(String path, File file) throws IOException { - Path targetFile = new File(workingRepository.getDirectory(), path).toPath(); - createDirectories(targetFile); - if (!targetFile.toFile().exists()) { - throw notFound(createFileContext(path)); - } - Files.move(file.toPath(), targetFile, REPLACE_EXISTING); - try { - addFileToHg(targetFile.toFile()); - } catch (ExecutionException e) { - throwInternalRepositoryException("could not modify existing file", e); - } + public void doScmDelete(String toBeDeleted) { + RemoveCommand.on(workingRepository).execute(toBeDeleted); } - private void createDirectories(Path targetFile) throws IOException { - try { - Files.createDirectories(targetFile.getParent()); - } catch (FileAlreadyExistsException e) { - throw alreadyExists(createFileContext(targetFile.toString())); - } + @Override + public sonia.scm.repository.Repository getRepository() { + return context.getScmRepository(); } - private ContextEntry.ContextBuilder createFileContext(String path) { - ContextEntry.ContextBuilder contextBuilder = entity("file", path); - if (!StringUtils.isEmpty(request.getBranch())) { - contextBuilder.in("branch", request.getBranch()); - } - contextBuilder.in(context.getScmRepository()); - return contextBuilder; + @Override + public String getBranch() { + return request.getBranch(); + } + + public File getWorkDir() { + return workingRepository.getDirectory(); } private void addFileToHg(File file) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index 7cd4c3602b..832c9aead3 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -242,7 +242,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider @Override public ModifyCommand getModifyCommand() { - return new HgModifyCommand(context, workdirFactory); + return new HgModifyCommand(context, handler.getWorkdirFactory()); } /** From 416eccacc895c76bced8d786f7833f23b006d723 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 8 Oct 2019 15:46:56 +0200 Subject: [PATCH 15/22] small fixes --- .../scm/repository/spi/HgModifyCommand.java | 2 +- .../spi/HgRepositoryServiceProvider.java | 5 +---- .../spi/HgRepositoryServiceResolver.java | 6 ++--- .../api/v2/resources/MergeResourceTest.java | 22 ++++++------------- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index cd0a649b6e..0294b6902b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -92,7 +92,7 @@ public class HgModifyCommand implements ModifyCommand { return pullCommand.execute(workingCopy.getDirectory().getAbsolutePath()); } catch (Exception e) { throw new IntegrateChangesFromWorkdirException(context.getScmRepository(), - String.format("Could not pull modify changes from working copy to central repository", request.getBranch()), + String.format("Could not pull modify changes from working copy to central repository for branch %s", request.getBranch()), e); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index 832c9aead3..c80699add8 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -78,11 +78,10 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider //~--- constructors --------------------------------------------------------- HgRepositoryServiceProvider(HgRepositoryHandler handler, - HgHookManager hookManager, Repository repository, HgWorkdirFactory workdirFactory) + HgHookManager hookManager, Repository repository) { this.repository = repository; this.handler = handler; - this.workdirFactory = workdirFactory; this.repositoryDirectory = handler.getDirectory(repository.getId()); this.context = new HgCommandContext(hookManager, handler, repository, repositoryDirectory); @@ -294,6 +293,4 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider /** Field description */ private File repositoryDirectory; - - private final HgWorkdirFactory workdirFactory; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java index 7b793e1b57..7519cb564d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java @@ -49,15 +49,13 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver private final HgRepositoryHandler handler; private final HgHookManager hookManager; - private final HgWorkdirFactory workdirFactory; @Inject public HgRepositoryServiceResolver(HgRepositoryHandler handler, - HgHookManager hookManager, HgWorkdirFactory workdirFactory) + HgHookManager hookManager) { this.handler = handler; this.hookManager = hookManager; - this.workdirFactory = workdirFactory; } @Override @@ -65,7 +63,7 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver HgRepositoryServiceProvider provider = null; if (HgRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { - provider = new HgRepositoryServiceProvider(handler, hookManager, repository, workdirFactory); + provider = new HgRepositoryServiceProvider(handler, hookManager, repository); } return provider; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java index 7181912cfe..202b2d20f1 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MergeResourceTest.java @@ -108,10 +108,7 @@ public class MergeResourceTest extends RepositoryTestBase { @Test void shouldHandleSuccessfulMerge() throws Exception { when(mergeCommand.merge(any())).thenReturn(MergeCommandResult.success()); - User user = createDummyUser("dummy"); - PrincipalCollection collection = mock(PrincipalCollection.class); - when(subject.getPrincipals()).thenReturn(collection); - when(collection.oneByType(User.class)).thenReturn(user); + mockUser(); URL url = Resources.getResource("sonia/scm/api/v2/mergeCommand.json"); byte[] mergeCommandJson = Resources.toByteArray(url); @@ -129,10 +126,7 @@ public class MergeResourceTest extends RepositoryTestBase { @Test void shouldHandleFailedMerge() throws Exception { when(mergeCommand.merge(any())).thenReturn(MergeCommandResult.failure(asList("file1", "file2"))); - User user = createDummyUser("dummy"); - PrincipalCollection collection = mock(PrincipalCollection.class); - when(subject.getPrincipals()).thenReturn(collection); - when(collection.oneByType(User.class)).thenReturn(user); + mockUser(); URL url = Resources.getResource("sonia/scm/api/v2/mergeCommand.json"); byte[] mergeCommandJson = Resources.toByteArray(url); @@ -201,13 +195,11 @@ public class MergeResourceTest extends RepositoryTestBase { assertThat(response.getStatus()).isEqualTo(204); } - private User createDummyUser(String name) { - User user = new User(); - user.setName(name); - user.setType("xml"); - user.setPassword("secret"); - user.setCreationDate(System.currentTimeMillis()); - return user; + + private void mockUser() { + PrincipalCollection collection = mock(PrincipalCollection.class); + when(subject.getPrincipals()).thenReturn(collection); + when(collection.oneByType(User.class)).thenReturn(new User("dummy")); } } } From 7af882fe8efe154cbc2c1f1d5ac35234fcddcbab Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Tue, 8 Oct 2019 16:19:30 +0200 Subject: [PATCH 16/22] Insert some logging --- .../sonia/scm/repository/spi/GitModifyCommand.java | 5 +++++ .../scm/repository/spi/LfsBlobStoreCleanFilter.java | 13 ++++++++++--- .../repository/spi/GitModifyCommand_LFSTest.java | 1 - 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 7c7f948846..72238e117e 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -6,6 +6,8 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.attributes.FilterCommandRegistry; import org.eclipse.jgit.revwalk.RevCommit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.BadRequestException; import sonia.scm.ConcurrentModificationException; import sonia.scm.ContextEntry; @@ -30,6 +32,7 @@ import static sonia.scm.NotFoundException.notFound; public class GitModifyCommand extends AbstractGitCommand implements ModifyCommand { + private static final Logger LOG = LoggerFactory.getLogger(GitModifyCommand.class); private static final Striped REGISTER_LOCKS = Striped.lock(5); private final GitWorkdirFactory workdirFactory; @@ -109,12 +112,14 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); String registerKey = "git-lfs clean -- '" + path + "'"; + LOG.info("register lfs filter command factory for command '{}'", registerKey); FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); try { addFileToGit(path); } catch (GitAPIException e) { throwInternalRepositoryException("could not add file to index", e); } finally { + LOG.info("unregister lfs filter command factory for command \"{}\"", registerKey); FilterCommandRegistry.unregister(registerKey); } } finally { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java index c320cf23d4..3ab8a3e8cf 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java @@ -7,6 +7,8 @@ import org.eclipse.jgit.lfs.LfsPointer; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; import org.eclipse.jgit.lfs.lib.LongObjectId; import org.eclipse.jgit.lib.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.store.Blob; import sonia.scm.store.BlobStore; import sonia.scm.util.IOUtil; @@ -28,6 +30,7 @@ import static org.eclipse.jgit.lfs.lib.Constants.LONG_HASH_FUNCTION; */ public class LfsBlobStoreCleanFilter extends FilterCommand { + private static final Logger LOG = LoggerFactory.getLogger(LfsBlobStoreCleanFilter.class); private Lfs lfsUtil; private final BlobStore lfsBlobStore; @@ -44,20 +47,24 @@ public class LfsBlobStoreCleanFilter extends FilterCommand { @Override public int run() throws IOException { + LOG.info("running scm lfs filter for file {}", targetFile); DigestOutputStream digestOutputStream = createDigestStream(); try { long size = ByteStreams.copy(in, digestOutputStream); AnyLongObjectId loid = LongObjectId.fromRaw(digestOutputStream.getMessageDigest().digest()); + String hash = loid.getName(); - Blob existingBlob = lfsBlobStore.get(loid.getName()); + Blob existingBlob = lfsBlobStore.get(hash); if (existingBlob != null) { + LOG.info("found existing lfs blob for oid {}", hash); long blobSize = existingBlob.getSize(); if (blobSize != size) { // Mathematicians say this will never happen - throw new RuntimeException("lfs entry already exists for loid " + loid.getName() + " but has wrong size"); + throw new RuntimeException("lfs entry already exists for loid " + hash + " but has wrong size"); } } else { - Blob newBlob = lfsBlobStore.create(loid.getName()); + LOG.info("uploading new lfs blob for oid {}", hash); + Blob newBlob = lfsBlobStore.create(hash); OutputStream outputStream = newBlob.getOutputStream(); Files.copy(targetFile, outputStream); newBlob.commit(); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java index 8ecca84812..b0b2b675b4 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -65,7 +65,6 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { @Test public void shouldCreateSecondCommits() throws IOException, GitAPIException { - new GitLfsFilterModule().configure(null); createCommit("new_lfs.png", "new content", "fe32608c9ef5b6cf7e3f946480253ff76f24f4ec0678f3d0f07f9844cbff9601", new ByteArrayOutputStream()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); From a81049eea3706ce6ab05b227c10b3508acc77ed3 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 9 Oct 2019 10:57:58 +0200 Subject: [PATCH 17/22] Register own git config --- .../spi/GitLfsFilterContextListener.java | 77 +++++++++++++++++++ .../repository/spi/GitLfsFilterModule.java | 43 ----------- .../spi/GitModifyCommand_LFSTest.java | 4 +- 3 files changed, 79 insertions(+), 45 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterContextListener.java delete mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterContextListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterContextListener.java new file mode 100644 index 0000000000..101c242d65 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterContextListener.java @@ -0,0 +1,77 @@ +package sonia.scm.repository.spi; + +import com.google.common.io.ByteStreams; +import org.eclipse.jgit.attributes.FilterCommand; +import org.eclipse.jgit.attributes.FilterCommandRegistry; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.FS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.SCMContextProvider; +import sonia.scm.plugin.Extension; + +import javax.inject.Inject; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; + +@Extension +public class GitLfsFilterContextListener implements ServletContextListener { + + public static final String GITCONFIG = "[filter \"lfs\"]\n" + + "clean = git-lfs clean -- %f\n" + + "smudge = git-lfs smudge -- %f\n" + + "process = git-lfs filter-process\n" + + "required = true\n"; + public static final Pattern COMMAND_NAME_PATTERN = Pattern.compile("git-lfs (smudge|clean) -- .*"); + + private static final Logger LOG = LoggerFactory.getLogger(GitLfsFilterContextListener.class); + + private final SCMContextProvider contextProvider; + + @Inject + public GitLfsFilterContextListener(SCMContextProvider contextProvider) { + this.contextProvider = contextProvider; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + Path gitconfig = contextProvider.getBaseDirectory().toPath().resolve("gitconfig"); + try { + Files.write(gitconfig, GITCONFIG.getBytes(Charset.defaultCharset()), TRUNCATE_EXISTING, CREATE); + FS.DETECTED.setGitSystemConfig(gitconfig.toFile()); + LOG.info("wrote git config file: {}", gitconfig); + } catch (IOException e) { + LOG.error("could not write git config in path {}; git lfs support may not work correctly", gitconfig, e); + } + FilterCommandRegistry.register(COMMAND_NAME_PATTERN, NoOpFilterCommand::new); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + FilterCommandRegistry.unregister(COMMAND_NAME_PATTERN); + } + + private static class NoOpFilterCommand extends FilterCommand { + NoOpFilterCommand(Repository db, InputStream in, OutputStream out) { + super(in, out); + } + + @Override + public int run() throws IOException { + ByteStreams.copy(in, out); + in.close(); + out.close(); + return -1; + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java deleted file mode 100644 index 912379fd7b..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLfsFilterModule.java +++ /dev/null @@ -1,43 +0,0 @@ -package sonia.scm.repository.spi; - -import com.google.common.io.ByteStreams; -import com.google.inject.Binder; -import com.google.inject.Module; -import org.eclipse.jgit.attributes.FilterCommand; -import org.eclipse.jgit.attributes.FilterCommandRegistry; -import org.eclipse.jgit.lib.Repository; -import sonia.scm.plugin.Extension; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.regex.Pattern; - -@Extension -public class GitLfsFilterModule implements Module { - - public static final Pattern COMMAND_NAME_PATTERN = Pattern.compile("git-lfs (smudge|clean) -- .*"); - - @Override - public void configure(Binder binder) { - FilterCommandRegistry.register(COMMAND_NAME_PATTERN, NoOpFilterCommand::new); - } - - void unregister() { - FilterCommandRegistry.unregister(COMMAND_NAME_PATTERN); - } - - private static class NoOpFilterCommand extends FilterCommand { - NoOpFilterCommand(Repository db, InputStream in, OutputStream out) { - super(in, out); - } - - @Override - public int run() throws IOException { - ByteStreams.copy(in, out); - in.close(); - out.close(); - return -1; - } - } -} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java index b0b2b675b4..b1a5c7bbcc 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_LFSTest.java @@ -40,12 +40,12 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { @Before public void registerFilter() { - new GitLfsFilterModule().configure(null); + new GitLfsFilterContextListener(contextProvider).contextInitialized(null); } @After public void unregisterFilter() { - new GitLfsFilterModule().unregister(); + new GitLfsFilterContextListener(contextProvider).contextDestroyed(null); } @Test From 369ad9e788483a426b7c480fb43f674643d3c7cc Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 9 Oct 2019 11:27:54 +0200 Subject: [PATCH 18/22] Reduce log levels --- .../scm/repository/spi/GitModifyCommand.java | 4 ++-- .../spi/LfsBlobStoreCleanFilter.java | 21 ++++++++----------- .../spi/LfsBlobStoreCleanFilterFactory.java | 9 ++++---- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 72238e117e..f87dc038c0 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -112,14 +112,14 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman LfsBlobStoreCleanFilterFactory cleanFilterFactory = new LfsBlobStoreCleanFilterFactory(lfsBlobStoreFactory, repository, targetFile); String registerKey = "git-lfs clean -- '" + path + "'"; - LOG.info("register lfs filter command factory for command '{}'", registerKey); + LOG.debug("register lfs filter command factory for command '{}'", registerKey); FilterCommandRegistry.register(registerKey, cleanFilterFactory::createFilter); try { addFileToGit(path); } catch (GitAPIException e) { throwInternalRepositoryException("could not add file to index", e); } finally { - LOG.info("unregister lfs filter command factory for command \"{}\"", registerKey); + LOG.debug("unregister lfs filter command factory for command \"{}\"", registerKey); FilterCommandRegistry.unregister(registerKey); } } finally { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java index 3ab8a3e8cf..7c9c3d3225 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java @@ -2,11 +2,9 @@ package sonia.scm.repository.spi; import com.google.common.io.ByteStreams; import org.eclipse.jgit.attributes.FilterCommand; -import org.eclipse.jgit.lfs.Lfs; import org.eclipse.jgit.lfs.LfsPointer; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; import org.eclipse.jgit.lfs.lib.LongObjectId; -import org.eclipse.jgit.lib.Repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.store.Blob; @@ -28,26 +26,24 @@ import static org.eclipse.jgit.lfs.lib.Constants.LONG_HASH_FUNCTION; * Adapted version of JGit's {@link org.eclipse.jgit.lfs.CleanFilter} to write the * lfs file directly to the lfs blob store. */ -public class LfsBlobStoreCleanFilter extends FilterCommand { +class LfsBlobStoreCleanFilter extends FilterCommand { private static final Logger LOG = LoggerFactory.getLogger(LfsBlobStoreCleanFilter.class); - private Lfs lfsUtil; private final BlobStore lfsBlobStore; private final Path targetFile; - public LfsBlobStoreCleanFilter(Repository db, InputStream in, OutputStream out, BlobStore lfsBlobStore, Path targetFile) - throws IOException { + LfsBlobStoreCleanFilter(InputStream in, OutputStream out, BlobStore lfsBlobStore, Path targetFile) { super(in, out); - lfsUtil = new Lfs(db); this.lfsBlobStore = lfsBlobStore; this.targetFile = targetFile; - Files.createDirectories(lfsUtil.getLfsTmpDir()); } @Override + // Suppress warning for RuntimeException after check for wrong size, because mathematicians say this will never happen + @SuppressWarnings("squid:S00112") public int run() throws IOException { - LOG.info("running scm lfs filter for file {}", targetFile); + LOG.debug("running scm lfs filter for file {}", targetFile); DigestOutputStream digestOutputStream = createDigestStream(); try { long size = ByteStreams.copy(in, digestOutputStream); @@ -56,14 +52,13 @@ public class LfsBlobStoreCleanFilter extends FilterCommand { Blob existingBlob = lfsBlobStore.get(hash); if (existingBlob != null) { - LOG.info("found existing lfs blob for oid {}", hash); + LOG.debug("found existing lfs blob for oid {}", hash); long blobSize = existingBlob.getSize(); if (blobSize != size) { - // Mathematicians say this will never happen throw new RuntimeException("lfs entry already exists for loid " + hash + " but has wrong size"); } } else { - LOG.info("uploading new lfs blob for oid {}", hash); + LOG.debug("uploading new lfs blob for oid {}", hash); Blob newBlob = lfsBlobStore.create(hash); OutputStream outputStream = newBlob.getOutputStream(); Files.copy(targetFile, outputStream); @@ -80,6 +75,8 @@ public class LfsBlobStoreCleanFilter extends FilterCommand { } } + // Suppress warning for RuntimeException after check for wrong size, because hash alg for sha256 is built in + @SuppressWarnings("squid:S00112") private DigestOutputStream createDigestStream() { MessageDigest md ; try { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java index 6951ec1b14..d6de8e83df 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilterFactory.java @@ -8,19 +8,20 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Path; -public class LfsBlobStoreCleanFilterFactory { +class LfsBlobStoreCleanFilterFactory { private final LfsBlobStoreFactory blobStoreFactory; private final sonia.scm.repository.Repository repository; private final Path targetFile; - public LfsBlobStoreCleanFilterFactory(LfsBlobStoreFactory blobStoreFactory, sonia.scm.repository.Repository repository, Path targetFile) { + LfsBlobStoreCleanFilterFactory(LfsBlobStoreFactory blobStoreFactory, sonia.scm.repository.Repository repository, Path targetFile) { this.blobStoreFactory = blobStoreFactory; this.repository = repository; this.targetFile = targetFile; } - LfsBlobStoreCleanFilter createFilter(Repository db, InputStream in, OutputStream out) throws IOException { - return new LfsBlobStoreCleanFilter(db, in, out, blobStoreFactory.getLfsBlobStore(repository), targetFile); + @SuppressWarnings("squid:S1172") // suppress unused parameter to keep the api compatible to jgit's FilterCommandFactory + LfsBlobStoreCleanFilter createFilter(Repository db, InputStream in, OutputStream out) { + return new LfsBlobStoreCleanFilter(in, out, blobStoreFactory.getLfsBlobStore(repository), targetFile); } } From 01cb18ada84803e5aa6ec6bcd8bc455bdd7a37fd Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 9 Oct 2019 09:39:10 +0000 Subject: [PATCH 19/22] Close branch feature/editor_plugin_hg From 5eab6f5af4ba81c5d7f3afdbcdd3dc3f512fae07 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 9 Oct 2019 11:47:56 +0200 Subject: [PATCH 20/22] Fix sonar issues --- .../sonia/scm/repository/spi/GitModifyCommand.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index 83ca05fb03..a68be0a4da 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -54,10 +54,9 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman @Override String run() throws IOException { getClone().getRepository().getFullBranch(); - if (!StringUtils.isEmpty(request.getExpectedRevision())) { - if (!request.getExpectedRevision().equals(getCurrentRevision().getName())) { - throw new ConcurrentModificationException("branch", request.getBranch() == null? "default": request.getBranch()); - } + if (!StringUtils.isEmpty(request.getExpectedRevision()) + && !request.getExpectedRevision().equals(getCurrentRevision().getName())) { + throw new ConcurrentModificationException("branch", request.getBranch() == null ? "default" : request.getBranch()); } for (ModifyCommandRequest.PartialRequest r : request.getRequests()) { r.execute(this); @@ -128,9 +127,9 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } return path; } - } - private String throwInternalRepositoryException(String message, Exception e) { - throw new InternalRepositoryException(context.getRepository(), message, e); + private String throwInternalRepositoryException(String message, Exception e) { + throw new InternalRepositoryException(context.getRepository(), message, e); + } } } From 38f0ed3bbda1dba597b3f25caa10fee9c6155384 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 9 Oct 2019 13:56:44 +0200 Subject: [PATCH 21/22] Use constant from jgit --- .../repository/spi/LfsBlobStoreCleanFilter.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java index 7c9c3d3225..bb54ea162a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/LfsBlobStoreCleanFilter.java @@ -4,6 +4,7 @@ import com.google.common.io.ByteStreams; import org.eclipse.jgit.attributes.FilterCommand; import org.eclipse.jgit.lfs.LfsPointer; import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.Constants; import org.eclipse.jgit.lfs.lib.LongObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,10 +18,6 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import static org.eclipse.jgit.lfs.lib.Constants.LONG_HASH_FUNCTION; /** * Adapted version of JGit's {@link org.eclipse.jgit.lfs.CleanFilter} to write the @@ -75,21 +72,12 @@ class LfsBlobStoreCleanFilter extends FilterCommand { } } - // Suppress warning for RuntimeException after check for wrong size, because hash alg for sha256 is built in - @SuppressWarnings("squid:S00112") private DigestOutputStream createDigestStream() { - MessageDigest md ; - try { - md = MessageDigest.getInstance(LONG_HASH_FUNCTION); - } catch (NoSuchAlgorithmException e) { - // Yes there is such a hash function (should be sha256) - throw new RuntimeException(e); - } return new DigestOutputStream(new OutputStream() { @Override public void write(int b) { // no further target here, we are just interested in the digest } - }, md); + }, Constants.newMessageDigest()); } } From b999062535d991599a61a17d87be3da9a4b3dfe1 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 9 Oct 2019 12:06:07 +0000 Subject: [PATCH 22/22] Close branch feature/lfs_upload