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 80c8c196dc..05de362b90 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 @@ -30,15 +30,16 @@ 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.ConcurrentModificationException; import sonia.scm.ContextEntry; import sonia.scm.NoChangesMadeException; +import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; +import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.GitWorkingCopyFactory; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; +import sonia.scm.store.ConfigurationStore; import sonia.scm.web.lfs.LfsBlobStoreFactory; import javax.inject.Inject; @@ -50,21 +51,22 @@ import java.util.concurrent.locks.Lock; 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 GitWorkingCopyFactory workingCopyFactory; private final LfsBlobStoreFactory lfsBlobStoreFactory; + private final GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider; @Inject - GitModifyCommand(GitContext context, GitRepositoryHandler repositoryHandler, LfsBlobStoreFactory lfsBlobStoreFactory) { - this(context, repositoryHandler.getWorkingCopyFactory(), lfsBlobStoreFactory); + GitModifyCommand(GitContext context, GitRepositoryHandler repositoryHandler, LfsBlobStoreFactory lfsBlobStoreFactory, GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) { + this(context, repositoryHandler.getWorkingCopyFactory(), lfsBlobStoreFactory, gitRepositoryConfigStoreProvider); } - GitModifyCommand(GitContext context, GitWorkingCopyFactory workingCopyFactory, LfsBlobStoreFactory lfsBlobStoreFactory) { + GitModifyCommand(GitContext context, GitWorkingCopyFactory workingCopyFactory, LfsBlobStoreFactory lfsBlobStoreFactory, GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) { super(context); this.workingCopyFactory = workingCopyFactory; this.lfsBlobStoreFactory = lfsBlobStoreFactory; + this.gitRepositoryConfigStoreProvider = gitRepositoryConfigStoreProvider; } @Override @@ -112,9 +114,15 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman if (StringUtils.isNotBlank(branch)) { try { getClone().checkout().setName(branch).setCreateBranch(true).call(); + ConfigurationStore store = gitRepositoryConfigStoreProvider + .get(repository); + GitRepositoryConfig gitRepositoryConfig = store + .getOptional() + .orElse(new GitRepositoryConfig()); + gitRepositoryConfig.setDefaultBranch(branch); + store.set(gitRepositoryConfig); } catch (GitAPIException e) { throw new InternalRepositoryException(repository, "could not create default branch for initial commit", e); - } } } 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 473356d8bf..97abda18a8 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 @@ -27,23 +27,17 @@ 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.CanceledException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.GpgSignature; import org.eclipse.jgit.lib.GpgSigner; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.junit.jupiter.api.BeforeEach; import org.junit.rules.TemporaryFolder; import sonia.scm.AlreadyExistsException; import sonia.scm.BadRequestException; @@ -53,7 +47,6 @@ import sonia.scm.repository.GitTestHelper; import sonia.scm.repository.Person; import sonia.scm.repository.work.NoneCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; -import sonia.scm.security.PublicKey; import sonia.scm.web.lfs.LfsBlobStoreFactory; import java.io.File; @@ -62,6 +55,7 @@ import java.nio.file.Files; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static sonia.scm.repository.spi.GitRepositoryConfigStoreProviderTestUtil.createGitRepositoryConfigStoreProvider; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") public class GitModifyCommandTest extends AbstractGitCommandTestBase { @@ -381,7 +375,11 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase { } private GitModifyCommand createCommand() { - return new GitModifyCommand(createContext(), new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), lfsBlobStoreFactory); + return new GitModifyCommand( + createContext(), + new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), + lfsBlobStoreFactory, + createGitRepositoryConfigStoreProvider()); } @FunctionalInterface diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java deleted file mode 100644 index f9b1a61a8c..0000000000 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_InitialCommitTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository.spi; - -import com.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.lib.GpgSigner; -import org.eclipse.jgit.lib.Ref; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import sonia.scm.repository.GitTestHelper; -import sonia.scm.repository.Person; -import sonia.scm.repository.work.NoneCachingWorkingCopyPool; -import sonia.scm.repository.work.WorkdirProvider; -import sonia.scm.web.lfs.LfsBlobStoreFactory; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; - -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 GitModifyCommand_InitialCommitTest extends AbstractGitCommandTestBase { - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Rule - public ShiroRule shiro = new ShiroRule(); - @Rule - public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule(); - - @BeforeClass - public static void setSigner() { - GpgSigner.setDefault(new GitTestHelper.SimpleGpgSigner()); - } - - @Test - public void shouldCreateCommitOnMasterByDefault() throws IOException, GitAPIException { - createContext().getGlobalConfig().setDefaultBranch(""); - - executeModifyCommand(); - - try (Git git = new Git(createContext().open())) { - List branches = git.branchList().call(); - assertThat(branches).extracting("name").containsExactly("refs/heads/master"); - } - } - - @Test - public void shouldCreateCommitWithConfiguredDefaultBranch() throws IOException, GitAPIException { - createContext().getGlobalConfig().setDefaultBranch("main"); - - executeModifyCommand(); - - try (Git git = new Git(createContext().open())) { - List branches = git.branchList().call(); - assertThat(branches).extracting("name").containsExactly("refs/heads/main"); - } - } - - @Test - public void shouldCreateCommitWithBranchFromRequestIfPresent() throws IOException, GitAPIException { - createContext().getGlobalConfig().setDefaultBranch("main"); - - ModifyCommandRequest request = createRequest(); - request.setBranch("different"); - createCommand().execute(request); - - try (Git git = new Git(createContext().open())) { - List branches = git.branchList().call(); - assertThat(branches).extracting("name").containsExactly("refs/heads/different"); - } - } - - private void executeModifyCommand() throws IOException { - createCommand().execute(createRequest()); - } - - private ModifyCommandRequest createRequest() throws IOException { - File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); - - ModifyCommandRequest request = new ModifyCommandRequest(); - request.setCommitMessage("initial commit"); - request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_file", newFile, false)); - request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); - return request; - } - - private GitModifyCommand createCommand() { - return new GitModifyCommand(createContext(), new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), mock(LfsBlobStoreFactory.class)); - } - - @Override - protected String getZippedRepositoryResource() { - return "sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip"; - } -} 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 6709505c90..ccb0e200c0 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 @@ -50,6 +50,7 @@ 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; +import static sonia.scm.repository.spi.GitRepositoryConfigStoreProviderTestUtil.createGitRepositoryConfigStoreProvider; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { @@ -131,7 +132,11 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase { } private GitModifyCommand createCommand() { - return new GitModifyCommand(createContext(), new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), lfsBlobStoreFactory); + return new GitModifyCommand( + createContext(), + new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), + lfsBlobStoreFactory, + createGitRepositoryConfigStoreProvider()); } @Override diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java index 5898845a74..c9307b9578 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommand_withEmptyRepositoryTest.java @@ -29,14 +29,18 @@ import com.github.sdorra.shiro.SubjectAware; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.GpgSigner; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import sonia.scm.repository.GitTestHelper; import sonia.scm.repository.Person; import sonia.scm.repository.work.NoneCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; @@ -45,9 +49,11 @@ import sonia.scm.web.lfs.LfsBlobStoreFactory; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static sonia.scm.repository.spi.GitRepositoryConfigStoreProviderTestUtil.createGitRepositoryConfigStoreProvider; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret") public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommandTestBase { @@ -61,6 +67,11 @@ public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommand private final LfsBlobStoreFactory lfsBlobStoreFactory = mock(LfsBlobStoreFactory.class); + @BeforeClass + public static void setSigner() { + GpgSigner.setDefault(new GitTestHelper.SimpleGpgSigner()); + } + @Test public void shouldCreateNewFileInEmptyRepository() throws IOException, GitAPIException { File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); @@ -79,6 +90,44 @@ public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommand assertInTree(assertions); } + @Test + public void shouldCreateCommitOnMasterByDefault() throws IOException, GitAPIException { + createContext().getGlobalConfig().setDefaultBranch(""); + + executeModifyCommand(); + + try (Git git = new Git(createContext().open())) { + List branches = git.branchList().call(); + assertThat(branches).extracting("name").containsExactly("refs/heads/master"); + } + } + + @Test + public void shouldCreateCommitWithConfiguredDefaultBranch() throws IOException, GitAPIException { + createContext().getGlobalConfig().setDefaultBranch("main"); + + executeModifyCommand(); + + try (Git git = new Git(createContext().open())) { + List branches = git.branchList().call(); + assertThat(branches).extracting("name").containsExactly("refs/heads/main"); + } + } + + @Test + public void shouldCreateCommitWithBranchFromRequestIfPresent() throws IOException, GitAPIException { + createContext().getGlobalConfig().setDefaultBranch("main"); + + ModifyCommandRequest request = createRequest(); + request.setBranch("different"); + createCommand().execute(request); + + try (Git git = new Git(createContext().open())) { + List branches = git.branchList().call(); + assertThat(branches).extracting("name").containsExactly("refs/heads/different"); + } + } + @Override protected String getZippedRepositoryResource() { return "sonia/scm/repository/spi/scm-git-empty-repo.zip"; @@ -97,12 +146,30 @@ public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommand } } - private RevCommit getLastCommit(Git git) throws GitAPIException { - return git.log().setMaxCount(1).call().iterator().next(); + private RevCommit getLastCommit(Git git) throws GitAPIException, IOException { + return git.log().setMaxCount(1).all().call().iterator().next(); + } + + private void executeModifyCommand() throws IOException { + createCommand().execute(createRequest()); + } + + private ModifyCommandRequest createRequest() throws IOException { + File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("initial commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("new_file", newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + return request; } private GitModifyCommand createCommand() { - return new GitModifyCommand(createContext(), new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), lfsBlobStoreFactory); + return new GitModifyCommand( + createContext(), + new SimpleGitWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())), + lfsBlobStoreFactory, + createGitRepositoryConfigStoreProvider()); } @FunctionalInterface diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitRepositoryConfigStoreProviderTestUtil.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitRepositoryConfigStoreProviderTestUtil.java new file mode 100644 index 0000000000..1229c5f3cc --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitRepositoryConfigStoreProviderTestUtil.java @@ -0,0 +1,47 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.spi; + +import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; +import sonia.scm.repository.GitRepositoryConfig; +import sonia.scm.repository.Repository; +import sonia.scm.store.ConfigurationStore; +import sonia.scm.store.InMemoryConfigurationStore; + +import java.util.HashMap; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GitRepositoryConfigStoreProviderTestUtil { + + static GitRepositoryConfigStoreProvider createGitRepositoryConfigStoreProvider() { + GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider = mock(GitRepositoryConfigStoreProvider.class); + HashMap> storeMap = new HashMap<>(); + when(gitRepositoryConfigStoreProvider.get(any())).thenAnswer(invocation -> storeMap.computeIfAbsent(invocation.getArgument(0, Repository.class).getId(), id -> new InMemoryConfigurationStore<>())); + return gitRepositoryConfigStoreProvider; + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip deleted file mode 100644 index 80f2dbd18b..0000000000 Binary files a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-empty-repo-test.zip and /dev/null differ