]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14461 main branch detection
authorlukasz-jarocki-sonarsource <77498856+lukasz-jarocki-sonarsource@users.noreply.github.com>
Wed, 17 Feb 2021 15:22:59 +0000 (16:22 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 19 Feb 2021 20:07:20 +0000 (20:07 +0000)
sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java

index 6dfbe7960736c07da0e19256876bc20658798f1f..b208a2359d43a34854e67e63fd26ec2f09608438 100644 (file)
@@ -67,6 +67,17 @@ public abstract class ScmProvider {
     return null;
   }
 
+  /**
+   * Return the main branch name.
+   *
+   * @return null if the SCM provider was not able to find the main branch.
+   * @since 8.8
+   */
+  @CheckForNull
+  public String getMainBranch(Path rootBaseDir) {
+    return null;
+  }
+
   /**
    * Return a map between paths given as argument and the corresponding line numbers which are new compared to the provided target branch.
    * If nothing is returned for a file, the scanner will consider that the provider was unable to determine changes for that file and it will
index 84a0ee85431a3e210b7a2d5c076955fbb97a421c..adaa253f690c3e5309517f8c64fdf907cb948504 100644 (file)
@@ -61,6 +61,9 @@ public class MetadataPublisher implements ReportPublisherStep {
   private final InputComponentStore componentStore;
   private final ScmConfiguration scmConfiguration;
 
+  private ScmProvider scmProvider;
+  private Path projectBasedir;
+
   public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, QualityProfiles qProfiles,
     CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration,
     ScmRevision scmRevision, ForkDateSupplier forkDateSupplier, InputComponentStore componentStore, ScmConfiguration scmConfiguration) {
@@ -95,7 +98,6 @@ public class MetadataPublisher implements ReportPublisherStep {
     addScmInformation(builder);
     addForkPoint(builder);
     addNotAnalyzedFileCountsByLanguage(builder);
-    addMainBranch(builder);
 
     for (QProfile qp : qProfiles.findAll()) {
       builder.putQprofilesPerLanguage(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder()
@@ -110,6 +112,9 @@ public class MetadataPublisher implements ReportPublisherStep {
         .setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build());
     }
 
+    scmProvider = scmConfiguration.provider();
+    projectBasedir = moduleHierarchy.root().getBaseDir();
+
     addModulesRelativePaths(builder);
     addMainBranch(builder);
 
@@ -117,7 +122,16 @@ public class MetadataPublisher implements ReportPublisherStep {
   }
 
   private void addMainBranch(ScannerReport.Metadata.Builder builder) {
-    builder.setGitDefaultMainBranch("");
+    if (scmProvider == null) {
+      return;
+    }
+    String mainBranch = scmProvider.getMainBranch(projectBasedir);
+    if (mainBranch != null && !mainBranch.isEmpty()) {
+      LOG.debug("The main branch for '{}' is '{}'", projectBasedir.toString(), mainBranch);
+      builder.setGitDefaultMainBranch(mainBranch);
+    } else {
+      LOG.debug("The main branch for '{}' has not been found", projectBasedir.toString());
+    }
   }
 
   private void addForkPoint(ScannerReport.Metadata.Builder builder) {
@@ -140,12 +154,10 @@ public class MetadataPublisher implements ReportPublisherStep {
       }
     }
 
-    ScmProvider scmProvider = scmConfiguration.provider();
     if (scmProvider == null) {
       return;
     }
 
-    Path projectBasedir = moduleHierarchy.root().getBaseDir();
     try {
       builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir)));
     } catch (UnsupportedOperationException e) {
index c7654504d0bc7e9187c912871e16f890f9c4f320..4303fa222ee44586ad66e1bfd264358ab8aa0bc0 100644 (file)
@@ -97,6 +97,21 @@ public class GitScmProvider extends ScmProvider {
     return this.jgitBlameCommand;
   }
 
+  @CheckForNull
+  @Override
+  public String getMainBranch(Path rootBaseDir) {
+    try (Repository repo = buildRepo(rootBaseDir)) {
+      Set<String> branches = repo.getConfig().getSubsections("branch");
+      if(!branches.isEmpty()) {
+        // .git/config file will have the default branch at the time of cloning as its first branch
+        return branches.iterator().next();
+      }
+    } catch (IOException e) {
+      LOG.debug("Couldn't build a repo in order to retrieve the default branch name", e);
+    }
+    return null;
+  }
+
   @CheckForNull
   @Override
   public Set<Path> branchChangedFiles(String targetBranchName, Path rootBaseDir) {
index a7bda1c999253524117550c621feb561b4e90ee6..d38a149a668ce6c238653bfcd4e08c075ecb0b0e 100644 (file)
@@ -286,4 +286,49 @@ public class MetadataPublisherTest {
     ScannerReport.Metadata metadata = reader.readMetadata();
     assertThat(metadata.getRelativePathFromScmRoot()).isEmpty();
   }
+
+  @Test
+  public void addMainBranch_givenDefaultMainBranchSet_writeItToMetadata() throws IOException {
+    ScmProvider scmProvider = mock(ScmProvider.class);
+    when(scmProvider.getMainBranch(any())).thenReturn("Main");
+    when(scmProvider.relativePathFromScmRoot(any())).thenReturn(mock(Path.class));
+    when(scmConfiguration.provider()).thenReturn(scmProvider);
+
+    File outputDir = temp.newFolder();
+    underTest.publish(new ScannerReportWriter(outputDir));
+
+    ScannerReportReader reader = new ScannerReportReader(outputDir);
+    ScannerReport.Metadata metadata = reader.readMetadata();
+    assertThat(metadata.getGitDefaultMainBranch()).isEqualTo("Main");
+  }
+
+  @Test
+  public void addMainBranch_givenEmptyDefaultMainBranchSet_emptyDefaultMainBranchInMetadata() throws IOException {
+    ScmProvider scmProvider = mock(ScmProvider.class);
+    when(scmProvider.getMainBranch(any())).thenReturn("");
+    when(scmProvider.relativePathFromScmRoot(any())).thenReturn(mock(Path.class));
+    when(scmConfiguration.provider()).thenReturn(scmProvider);
+
+    File outputDir = temp.newFolder();
+    underTest.publish(new ScannerReportWriter(outputDir));
+
+    ScannerReportReader reader = new ScannerReportReader(outputDir);
+    ScannerReport.Metadata metadata = reader.readMetadata();
+    assertThat(metadata.getGitDefaultMainBranch()).isEmpty();
+  }
+
+  @Test
+  public void addMainBranch_givenNullMainBranchSet_emptyDefaultMainBranchInMetadata() throws IOException {
+    ScmProvider scmProvider = mock(ScmProvider.class);
+    when(scmProvider.getMainBranch(any())).thenReturn(null);
+    when(scmProvider.relativePathFromScmRoot(any())).thenReturn(mock(Path.class));
+    when(scmConfiguration.provider()).thenReturn(scmProvider);
+
+    File outputDir = temp.newFolder();
+    underTest.publish(new ScannerReportWriter(outputDir));
+
+    ScannerReportReader reader = new ScannerReportReader(outputDir);
+    ScannerReport.Metadata metadata = reader.readMetadata();
+    assertThat(metadata.getGitDefaultMainBranch()).isEmpty();
+  }
 }
index 4efb0ddcb3335392719c218105969d11d6fd559e..4a9a6121aa978e0abda09e777c36e5087d18ca46 100644 (file)
@@ -39,15 +39,19 @@ import java.util.Random;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.api.CreateBranchCommand;
 import org.eclipse.jgit.api.DiffCommand;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.junit.Before;
 import org.junit.Rule;
@@ -732,6 +736,132 @@ public class GitScmProviderTest {
     assertThat(provider.revisionId(projectDir)).isNull();
   }
 
+  @Test
+  public void getMainBranch_givenRepoWithOneBranchCalledMain_returnMainBranchCalledMain() throws Exception {
+    //given
+    worktree = temp.newFolder().toPath();
+    Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile());
+    repo.create();
+    git = new Git(repo);
+
+    addBranchInConfig("Main");
+
+    Path projectDir = worktree.resolve("project");
+    Files.createDirectory(projectDir);
+
+    GitScmProvider provider = newGitScmProvider();
+
+    //when
+    String mainBranch = provider.getMainBranch(projectDir);
+
+    //then
+    assertThat(mainBranch).isEqualTo("Main");
+  }
+
+  @Test
+  public void getMainBranch_givenRepoWithTwoBranches_returnFirstBranch() throws Exception {
+    //given
+    worktree = temp.newFolder().toPath();
+    Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile());
+    repo.create();
+    git = new Git(repo);
+
+    addBranchInConfig("First");
+    addBranchInConfig("Second");
+
+    Path projectDir = worktree.resolve("project");
+    Files.createDirectory(projectDir);
+
+    GitScmProvider provider = newGitScmProvider();
+
+    //when
+    String mainBranch = provider.getMainBranch(projectDir);
+
+    //then
+    assertThat(mainBranch).isEqualTo("First");
+  }
+
+  @Test
+  public void getMainBranch_givenNoBranches_dontThrowException() throws Exception {
+    //given
+    worktree = temp.newFolder().toPath();
+    Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile());
+    repo.create();
+    git = new Git(repo);
+
+    Path projectDir = worktree.resolve("project");
+    Files.createDirectory(projectDir);
+
+    GitScmProvider provider = newGitScmProvider();
+
+    //when
+    String mainBranch = provider.getMainBranch(projectDir);
+
+    //then no exception
+    assertThat(mainBranch).isNullOrEmpty();
+  }
+
+  @Test
+  public void getMainBranch_givenRepositoryNotFoundExceptionWhenBuildingRepo_returnNull() throws Exception {
+    //given
+
+    worktree = temp.newFolder().toPath();
+    Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile());
+    repo.create();
+    git = new Git(repo);
+    repo.getObjectDatabase().close(); //This is here to force RepositoryBuilder to throw subclass of IOException
+
+    Path projectDir = worktree.resolve("project");
+    Files.createDirectory(projectDir);
+
+    GitScmProvider provider = newGitScmProvider();
+
+    //when
+    String mainBranch = provider.getMainBranch(projectDir);
+
+    //then no exception
+    assertThat(mainBranch).isNullOrEmpty();
+  }
+
+  @Test
+  public void getMainBranch_givenIOExceptionWhenBuildingRepo_returnNull() throws Exception {
+    //given
+
+    worktree = temp.newFolder().toPath();
+    Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile());
+    repo.create();
+    git = new Git(repo);
+
+    Path projectDir = worktree.resolve("project");
+    Files.createDirectory(projectDir);
+
+    GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) {
+      @Override
+      Repository buildRepo(Path basedir) throws IOException {
+        throw new IOException();
+      }
+    };
+
+    //when
+    String mainBranch = provider.getMainBranch(projectDir);
+
+    //then no exception
+    assertThat(mainBranch).isNullOrEmpty();
+  }
+
+  /**
+   * Normally after cloning the repository we would have at least one
+   * branch it git config. This method adds these branches without
+   * cloning any repository (because unit tests ought to be fast)
+   */
+  private void addBranchInConfig(String ... branches) throws IOException {
+    for(String branch : branches) {
+      git.getRepository().getConfig().setStringList("branch", branch, "remote", Arrays.asList("origin"));
+      git.getRepository().getConfig().setStringList("branch", branch, "merge", Arrays.asList("refs/head/" + branch));
+    }
+    git.getRepository().getConfig().save();
+  }
+
   private String randomizedContent(String prefix, int numLines) {
     StringBuilder sb = new StringBuilder();
     for (int line = 0; line < numLines; line++) {