diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-09-29 18:04:53 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-10-02 17:52:23 +0200 |
commit | 327799ec3efa6e9cb6180cd40a7d41cee0c1ce43 (patch) | |
tree | 461c6b5ef75775907a985cb0634499a71fc09653 /plugins | |
parent | 6122d2708aa9b2e43e6f7be50b54d6d8f7319e9b (diff) | |
download | sonarqube-327799ec3efa6e9cb6180cd40a7d41cee0c1ce43.tar.gz sonarqube-327799ec3efa6e9cb6180cd40a7d41cee0c1ce43.zip |
SONAR-5677 Provide JGit implementation
Diffstat (limited to 'plugins')
8 files changed, 244 insertions, 10 deletions
diff --git a/plugins/sonar-git-plugin/pom.xml b/plugins/sonar-git-plugin/pom.xml index ef707db2795..98cac3424e4 100644 --- a/plugins/sonar-git-plugin/pom.xml +++ b/plugins/sonar-git-plugin/pom.xml @@ -24,6 +24,11 @@ <artifactId>sonar-plugin-api</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>3.5.0.201409260305-r</version> + </dependency> <!-- unit tests --> <dependency> diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java index e5c12c42900..958f838a9b0 100644 --- a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitPlugin.java @@ -20,16 +20,31 @@ package org.sonar.plugins.scm.git; import com.google.common.collect.ImmutableList; +import org.sonar.api.PropertyType; import org.sonar.api.SonarPlugin; +import org.sonar.api.config.PropertyDefinition; import java.util.List; public final class GitPlugin extends SonarPlugin { + static final String GIT_IMPLEMENTATION_PROP_KEY = "sonar.git.implementation"; + static final String JGIT = "jgit"; + static final String EXE = "exe"; + public List getExtensions() { return ImmutableList.of( GitScmProvider.class, - GitBlameCommand.class); + GitBlameCommand.class, + JGitBlameCommand.class, + + PropertyDefinition.builder(GIT_IMPLEMENTATION_PROP_KEY) + .name("Git implementation") + .description("Use pure Java implementation by default but you can use command line git executable in case of issue.") + .defaultValue(JGIT) + .type(PropertyType.SINGLE_SELECT_LIST) + .options(EXE, JGIT) + .build()); } } diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java index a0dfeb92601..677bb6aa063 100644 --- a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitScmProvider.java @@ -21,15 +21,20 @@ package org.sonar.plugins.scm.git; import org.sonar.api.batch.scm.BlameCommand; import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.api.config.Settings; import java.io.File; public class GitScmProvider extends ScmProvider { - private GitBlameCommand blameCommand; + private final GitBlameCommand blameCommand; + private final JGitBlameCommand jgitBlameCommand; + private final Settings settings; - public GitScmProvider(GitBlameCommand blameCommand) { + public GitScmProvider(Settings settings, GitBlameCommand blameCommand, JGitBlameCommand jgitBlameCommand) { + this.settings = settings; this.blameCommand = blameCommand; + this.jgitBlameCommand = jgitBlameCommand; } @Override @@ -44,7 +49,13 @@ public class GitScmProvider extends ScmProvider { @Override public BlameCommand blameCommand() { - return this.blameCommand; + String implem = settings.getString(GitPlugin.GIT_IMPLEMENTATION_PROP_KEY); + if (GitPlugin.EXE.equals(implem)) { + return this.blameCommand; + } else if (GitPlugin.JGIT.equals(implem)) { + return this.jgitBlameCommand; + } else { + throw new IllegalArgumentException("Illegal value for " + GitPlugin.GIT_IMPLEMENTATION_PROP_KEY + ": " + implem); + } } - } diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java new file mode 100644 index 00000000000..4f0e99af560 --- /dev/null +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.plugins.scm.git; + +import org.eclipse.jgit.api.Git; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.scm.BlameCommand; +import org.sonar.api.batch.scm.BlameLine; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +public class JGitBlameCommand implements BlameCommand, BatchComponent { + + @Override + public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { + Git git = null; + File basedir = fs.baseDir(); + try { + git = Git.open(basedir); + for (InputFile inputFile : files) { + String filename = inputFile.relativePath(); + org.eclipse.jgit.blame.BlameResult blameResult = git.blame().setFilePath(filename).call(); + List<BlameLine> lines = new ArrayList<BlameLine>(); + for (int i = 0; i < blameResult.getResultContents().size(); i++) { + lines.add(new org.sonar.api.batch.scm.BlameLine(blameResult.getSourceAuthor(i).getWhen(), + blameResult.getSourceCommit(i).getName(), + blameResult.getSourceAuthor(i).getEmailAddress(), + blameResult.getSourceCommitter(i).getEmailAddress())); + } + result.add(inputFile, lines); + } + } catch (Exception e) { + throw new IllegalStateException("JGit blame failure!", e); + } finally { + if (git != null && git.getRepository() != null) { + git.getRepository().close(); + } + } + } + +} diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java index d175cdcbe91..d77cf7f242d 100644 --- a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java +++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitPluginTest.java @@ -27,6 +27,6 @@ public class GitPluginTest { @Test public void getExtensions() { - assertThat(new GitPlugin().getExtensions()).hasSize(2); + assertThat(new GitPlugin().getExtensions()).hasSize(4); } } diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitScmProviderTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitScmProviderTest.java index 9bc5801d055..97450a991fa 100644 --- a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitScmProviderTest.java +++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitScmProviderTest.java @@ -21,7 +21,10 @@ package org.sonar.plugins.scm.git; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; import java.io.File; import java.io.IOException; @@ -32,22 +35,43 @@ public class GitScmProviderTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public ExpectedException thrown = ExpectedException.none(); @Test public void sanityCheck() { - assertThat(new GitScmProvider(null).key()).isEqualTo("git"); + assertThat(new GitScmProvider(null, null, null).key()).isEqualTo("git"); + } + + @Test + public void selectImplem() { GitBlameCommand blameCommand = new GitBlameCommand(); - assertThat(new GitScmProvider(blameCommand).blameCommand()).isEqualTo(blameCommand); + JGitBlameCommand jblameCommand = new JGitBlameCommand(); + Settings settings = new Settings(new PropertyDefinitions(new GitPlugin().getExtensions())); + GitScmProvider gitScmProvider = new GitScmProvider(settings, blameCommand, jblameCommand); + + assertThat(gitScmProvider.blameCommand()).isEqualTo(jblameCommand); + + settings.setProperty(GitPlugin.GIT_IMPLEMENTATION_PROP_KEY, GitPlugin.EXE); + assertThat(gitScmProvider.blameCommand()).isEqualTo(blameCommand); + + settings.setProperty(GitPlugin.GIT_IMPLEMENTATION_PROP_KEY, GitPlugin.JGIT); + assertThat(gitScmProvider.blameCommand()).isEqualTo(jblameCommand); + + settings.setProperty(GitPlugin.GIT_IMPLEMENTATION_PROP_KEY, "foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Illegal value for " + GitPlugin.GIT_IMPLEMENTATION_PROP_KEY + ": foo"); + gitScmProvider.blameCommand(); } @Test public void testAutodetection() throws IOException { File baseDirEmpty = temp.newFolder(); - assertThat(new GitScmProvider(null).supports(baseDirEmpty)).isFalse(); + assertThat(new GitScmProvider(null, null, null).supports(baseDirEmpty)).isFalse(); File gitBaseDir = temp.newFolder(); new File(gitBaseDir, ".git").mkdir(); - assertThat(new GitScmProvider(null).supports(gitBaseDir)).isTrue(); + assertThat(new GitScmProvider(null, null, null).supports(gitBaseDir)).isTrue(); } } diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java new file mode 100644 index 00000000000..58aad97d844 --- /dev/null +++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java @@ -0,0 +1,115 @@ +package org.sonar.plugins.scm.git; + +import com.google.common.io.Closeables; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameLine; +import org.sonar.api.utils.DateUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class JGitBlameCommandTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void testBlame() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); + + JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(); + + DefaultFileSystem fs = new DefaultFileSystem(); + fs.setBaseDir(new File(projectDir, "dummy-git")); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/org/dummy/Dummy.java"); + fs.add(inputFile); + + BlameResult blameResult = mock(BlameResult.class); + jGitBlameCommand.blame(fs, Arrays.<InputFile>asList(inputFile), blameResult); + + Date revisionDate = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); + String revision = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; + String author = "david@gageot.net"; + verify(blameResult).add(inputFile, + Arrays.asList( + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author), + new BlameLine(revisionDate, revision, author))); + + } + + private static void javaUnzip(File zip, File toDir) { + try { + ZipFile zipFile = new ZipFile(zip); + try { + Enumeration<? extends ZipEntry> entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + File to = new File(toDir, entry.getName()); + if (entry.isDirectory()) { + FileUtils.forceMkdir(to); + } else { + File parent = to.getParentFile(); + if (parent != null) { + FileUtils.forceMkdir(parent); + } + + OutputStream fos = new FileOutputStream(to); + try { + IOUtils.copy(zipFile.getInputStream(entry), fos); + } finally { + Closeables.closeQuietly(fos); + } + } + } + } finally { + zipFile.close(); + } + } catch (Exception e) { + throw new IllegalStateException("Fail to unzip " + zip + " to " + toDir, e); + } + } + +} diff --git a/plugins/sonar-git-plugin/test-repos/dummy-git.zip b/plugins/sonar-git-plugin/test-repos/dummy-git.zip Binary files differnew file mode 100644 index 00000000000..e019a80dee2 --- /dev/null +++ b/plugins/sonar-git-plugin/test-repos/dummy-git.zip |