diff --git a/CHANGELOG.md b/CHANGELOG.md index f73a07d9eb..af08fdd72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The chip input api now provides an external add button - OmniSearchbar now makes use of the Combobox +## [2.46.3] - 2024-05-29 +### Fixed +- Exception in SVN repositories due to incorrect git initialization (Backport from 2.47) +- Default branch evaluation on git config initialization + ## [2.46.2] - 2024-03-04 ### Fixed - Rendering PDF files in source view @@ -1513,6 +1518,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [2.46.0]: https://scm-manager.org/download/2.46.0 [2.46.1]: https://scm-manager.org/download/2.46.1 [2.46.2]: https://scm-manager.org/download/2.46.2 +[2.46.3]: https://scm-manager.org/download/2.46.3 [2.47.0]: https://scm-manager.org/download/2.47.0 [2.48.0]: https://scm-manager.org/download/2.48.0 [2.48.1]: https://scm-manager.org/download/2.48.1 diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfigInitializer.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfigInitializer.java index 7d43d1f044..0155ba209d 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfigInitializer.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfigInitializer.java @@ -30,10 +30,14 @@ import jakarta.inject.Inject; import sonia.scm.EagerSingleton; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.plugin.Extension; +import sonia.scm.repository.api.RepositoryService; +import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.store.ConfigurationStore; +import java.io.IOException; import java.util.Comparator; import java.util.List; +import java.util.Optional; @Extension @EagerSingleton @@ -41,22 +45,31 @@ public class GitRepositoryConfigInitializer { private final GitRepositoryHandler repoHandler; private final GitRepositoryConfigStoreProvider storeProvider; + private final RepositoryServiceFactory serviceFactory; @Inject - public GitRepositoryConfigInitializer(GitRepositoryHandler repoHandler, GitRepositoryConfigStoreProvider storeProvider) { + public GitRepositoryConfigInitializer(GitRepositoryHandler repoHandler, GitRepositoryConfigStoreProvider storeProvider, RepositoryServiceFactory serviceFactory) { this.repoHandler = repoHandler; this.storeProvider = storeProvider; + this.serviceFactory = serviceFactory; } @Subscribe - public void initConfig(PostReceiveRepositoryHookEvent event) { + public void initConfig(PostReceiveRepositoryHookEvent event) throws IOException { if (GitRepositoryHandler.TYPE_NAME.equals(event.getRepository().getType())) { ConfigurationStore store = storeProvider.get(event.getRepository()); GitRepositoryConfig repositoryConfig = store.get(); if (repositoryConfig == null || Strings.isNullOrEmpty(repositoryConfig.getDefaultBranch())) { - List defaultBranchCandidates = event.getContext().getBranchProvider().getCreatedOrModified(); - - String defaultBranch = determineDefaultBranch(defaultBranchCandidates); + String defaultBranch; + try (RepositoryService service = serviceFactory.create(event.getRepository())) { + List branches = service.getBranchesCommand().getBranches().getBranches(); + Optional repoDefaultBranch = branches.stream().filter(Branch::isDefaultBranch).findFirst(); + if (repoDefaultBranch.isPresent()) { + defaultBranch = repoDefaultBranch.get().getName(); + } else { + defaultBranch = determineDefaultBranchFromPush(event); + } + } GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig(defaultBranch); store.set(gitRepositoryConfig); @@ -64,7 +77,8 @@ public class GitRepositoryConfigInitializer { } } - private String determineDefaultBranch(List defaultBranchCandidates) { + private String determineDefaultBranchFromPush(PostReceiveRepositoryHookEvent event) { + List defaultBranchCandidates = event.getContext().getBranchProvider().getCreatedOrModified(); String globalConfigDefaultBranch = repoHandler.getConfig().getDefaultBranch(); if (defaultBranchCandidates.contains(globalConfigDefaultBranch)) { return globalConfigDefaultBranch; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryConfigInitializerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryConfigInitializerTest.java index 2278fc00fe..cbf39c1086 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryConfigInitializerTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryConfigInitializerTest.java @@ -28,18 +28,24 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; +import sonia.scm.repository.api.BranchesCommandBuilder; import sonia.scm.repository.api.HookBranchProvider; import sonia.scm.repository.api.HookContext; +import sonia.scm.repository.api.RepositoryService; +import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.store.ConfigurationStore; +import java.io.IOException; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -62,11 +68,20 @@ class GitRepositoryConfigInitializerTest { @Mock private PostReceiveRepositoryHookEvent event; + @Mock + private RepositoryServiceFactory serviceFactory; + @Mock + private RepositoryService service; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private BranchesCommandBuilder branchesCommand; + @Mock + private Branches branches; + @InjectMocks private GitRepositoryConfigInitializer initializer; @Test - void shouldSkipNonGitRepositories() { + void shouldSkipNonGitRepositories() throws IOException { REPOSITORY.setType("svn"); initializer.initConfig(event); @@ -83,13 +98,16 @@ class GitRepositoryConfigInitializerTest { class ForGitRepositories { @BeforeEach - void initMocks() { + void initMocks() throws IOException { when(storeProvider.get(REPOSITORY)).thenReturn(configStore); REPOSITORY.setType("git"); + lenient().when(serviceFactory.create(event.getRepository())).thenReturn(service); + lenient().when(service.getBranchesCommand()).thenReturn(branchesCommand); + lenient().when(branchesCommand.getBranches()).thenReturn(branches); } @Test - void shouldDoNothingIfDefaultBranchAlreadySet() { + void shouldDoNothingIfDefaultBranchAlreadySet() throws IOException { when(configStore.get()).thenReturn(new GitRepositoryConfig("any")); initializer.initConfig(event); @@ -104,11 +122,11 @@ class GitRepositoryConfigInitializerTest { void initRepoHandler() { GitConfig gitConfig = new GitConfig(); gitConfig.setDefaultBranch("global_default"); - when(repoHandler.getConfig()).thenReturn(gitConfig); + lenient().when(repoHandler.getConfig()).thenReturn(gitConfig); } @Test - void shouldSetDefaultBranchIfNoGitConfigYet() { + void shouldSetDefaultBranchFromPushIfNoGitConfigYet() throws IOException { when(configStore.get()).thenReturn(null); initEvent(List.of("main")); @@ -118,7 +136,7 @@ class GitRepositoryConfigInitializerTest { } @Test - void shouldDetermineAndApplyDefaultBranch_GlobalDefault() { + void shouldDetermineAndApplyDefaultBranch_GlobalDefault() throws IOException { initEvent(List.of("global_default", "main", "master")); when(configStore.get()).thenReturn(new GitRepositoryConfig(null)); @@ -131,7 +149,7 @@ class GitRepositoryConfigInitializerTest { } @Test - void shouldDetermineAndApplyDefaultBranch_Main() { + void shouldDetermineAndApplyDefaultBranch_Main() throws IOException { initEvent(List.of("master", "main")); when(configStore.get()).thenReturn(new GitRepositoryConfig(null)); @@ -144,7 +162,7 @@ class GitRepositoryConfigInitializerTest { } @Test - void shouldDetermineAndApplyDefaultBranch_Master() { + void shouldDetermineAndApplyDefaultBranch_Master() throws IOException { initEvent(List.of("develop", "master")); when(configStore.get()).thenReturn(new GitRepositoryConfig(null)); @@ -157,7 +175,21 @@ class GitRepositoryConfigInitializerTest { } @Test - void shouldDetermineAndApplyDefaultBranch_BestMatch() { + void shouldDetermineAndApplyDefaultBranchFromRepository_Staging() throws IOException { + when(configStore.get()).thenReturn(null); + initEvent(List.of("master", "main")); + when(branches.getBranches()).thenReturn(List.of(new Branch("staging", "abc", true))); + + initializer.initConfig(event); + + verify(configStore).set(argThat(arg -> { + assertThat(arg.getDefaultBranch()).isEqualTo("staging"); + return true; + })); + } + + @Test + void shouldDetermineAndApplyDefaultBranch_BestMatch() throws IOException { initEvent(List.of("test/123", "trillian", "dent")); when(configStore.get()).thenReturn(new GitRepositoryConfig(null)); @@ -170,7 +202,7 @@ class GitRepositoryConfigInitializerTest { } @Test - void shouldDetermineAndApplyDefaultBranch_AnyFallback() { + void shouldDetermineAndApplyDefaultBranch_AnyFallback() throws IOException { initEvent(List.of("test/123", "x/y/z")); when(configStore.get()).thenReturn(new GitRepositoryConfig(null)); @@ -185,10 +217,10 @@ class GitRepositoryConfigInitializerTest { private void initEvent(List branches) { HookContext hookContext = mock(HookContext.class); - when(event.getContext()).thenReturn(hookContext); + lenient().when(event.getContext()).thenReturn(hookContext); HookBranchProvider branchProvider = mock(HookBranchProvider.class); - when(hookContext.getBranchProvider()).thenReturn(branchProvider); - when(branchProvider.getCreatedOrModified()).thenReturn(branches); + lenient().when(hookContext.getBranchProvider()).thenReturn(branchProvider); + lenient().when(branchProvider.getCreatedOrModified()).thenReturn(branches); } } }