Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Mercurial subrepositories #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added its/scm-repo/dummy-hg-with-subrepo.zip
Binary file not shown.
21 changes: 21 additions & 0 deletions its/src/test/java/com/sonarsource/it/scm/MercurialTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@ public void testBlame() throws Exception {
MapEntry.entry(3, new LineData("f553ba9f524c", "2012-07-18T18:26:11+0200", "david@gageot.net")));
}

@Test
public void testBlameSubrepo() throws Exception {
File projectDir = temp.newFolder();
ZipUtils.unzip(new File("scm-repo/dummy-hg-with-subrepo.zip"), projectDir);
File fileInRepo = new File(projectDir, "dummy-hg-with-subrepo/fileinrepo.txt");
File fileInSubRepo = new File(projectDir, "dummy-hg-with-subrepo/subrepo/fileinsubrepo.txt");
MavenBuild sonar = MavenBuild.create(fileInRepo)
.setGoals("verify sonar:sonar")
.setProperty("sonar.scm.disabled", "false");
orchestrator.executeBuilds(sonar);
assertThat(getScmData("dummy-hg-with-subrepo:dummy:fileinrepo.txt"))
.contains(
MapEntry.entry(0, new LineData("6db77db9dbc4", "2021-10-02 13:45:42+0200", "koenpoppe")),
MapEntry.entry(2, new LineData("1c43854ced78", "2021-10-02 13:46:33+0200", "koenpoppe")));
assertThat(getScmData("dummy-hg-with-subrepo:dummy:subrepo/fileinsubrepo.txt"))
.contains(
MapEntry.entry(0, new LineData("ba3299cdfa0e", "2021-10-02 13:40:29+0200", "koenpoppe")),
MapEntry.entry(1, new LineData("73172c209d54", "2021-10-02 13:40:42+0200", "koenpoppe")),
MapEntry.entry(2, new LineData("f5825590563b", "2021-10-02 13:41:07+0200", "koenpoppe")));
}

private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

private class LineData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.sonar.plugins.scm.mercurial;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -112,8 +113,21 @@ private int execute(Command cl, StreamConsumer consumer, StreamConsumer stderr)
}

private Command createCommandLine(File workingDirectory, String filename) {
// Determine root repo, i.e., where the .hg directory is
Path workingPath = workingDirectory.toPath();
Path filePath = new File(workingDirectory, filename).toPath();
Path repoRootPath = filePath.getParent();
while (!repoRootPath.equals(workingPath)) {
File hgDir = new File(repoRootPath.toFile(), ".hg");
if (hgDir.exists()) {
break;
}
repoRootPath = repoRootPath.getParent();
}
String filePathInRepo = repoRootPath.relativize(filePath).toString();

Command cl = Command.create("hg");
cl.setDirectory(workingDirectory);
cl.setDirectory(repoRootPath.toFile());
cl.addArgument("blame");
// Hack to support Mercurial prior to 2.1
if (!settings.getBoolean("sonar.mercurial.considerWhitespaces")) {
Expand All @@ -131,7 +145,7 @@ private Command createCommandLine(File workingDirectory, String filename) {
// Make filename safe for usage with the "hg" command
// See Guideline 10 at https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
cl.addArgument("--");
cl.addArgument(filename);
cl.addArgument(filePathInRepo);
return cl;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ public void shouldProcessMaliciousFileBlockingAttack() throws IOException {
new MercurialBlameCommand(commandExecutor, new MapSettings())
.blame(input, result);

assertThat(commandCaptor.getValue().getDirectory()).isEqualTo(baseDir);
assertThat(commandCaptor.getValue().getArguments())
.containsExactly("blame", "-w", "-v", "--user", "--date", "--changeset", "--", MALICIOUS_FILENAME);

Expand All @@ -199,4 +200,80 @@ public void shouldProcessMaliciousFileBlockingAttack() throws IOException {
.author("john.something@sonarsource.com")));
}

@Test
public void takeHgDirFromSubrepository() throws IOException {
// In main repository
{
File mainrepoSource = new File(baseDir, "src/mainrepoSource.xoo");
FileUtils.write(mainrepoSource, "sample content");
InputFile inputFile = new TestInputFileBuilder("mainrepoSource", "src/mainrepoSource.xoo")
.setModuleBaseDir(baseDir.toPath())
.build();
fs.add(inputFile);

BlameOutput result = mock(BlameOutput.class);
CommandExecutor commandExecutor = mock(CommandExecutor.class);

ArgumentCaptor<Command> commandCaptor = ArgumentCaptor.forClass(Command.class);
when(commandExecutor.execute(commandCaptor.capture(), any(), any(), anyLong())).thenAnswer((Answer<Integer>) invocation -> {
StreamConsumer outConsumer = (StreamConsumer) invocation.getArguments()[1];
outConsumer.consumeLine("John Something <john.something@sonarsource.com> 447af27e2bc1 Tue Nov 04 11:01:10 2020 +0100: foo");
return 0;
});

when(input.filesToBlame()).thenReturn(singletonList(inputFile));
new MercurialBlameCommand(commandExecutor, new MapSettings())
.blame(input, result);

assertThat(commandCaptor.getValue().getDirectory()).isEqualTo(baseDir);
assertThat(commandCaptor.getValue().getArguments())
.containsExactly("blame", "-w", "-v", "--user", "--date", "--changeset", "--", "src/mainrepoSource.xoo");

verify(result).blameResult(inputFile,
singletonList(new BlameLine()
.date(DateUtils.parseDateTime("2020-11-04T11:01:10+0100"))
.revision("447af27e2bc1")
.author("john.something@sonarsource.com")));
}

// In sub repository
{
File subrepoDir = new File(baseDir, "subrepo");
assertThat(subrepoDir.mkdir()).isTrue();
File subrepoHgDir = new File(subrepoDir, ".hg");
assertThat(subrepoHgDir.mkdir()).isTrue();
File subrepoSource = new File(subrepoDir, "src/subrepoSource.xoo");
FileUtils.write(subrepoSource, "sample content");
InputFile inputFile = new TestInputFileBuilder("bar", "subrepo/src/subrepoSource.xoo")
.setModuleBaseDir(baseDir.toPath())
.build();
fs.add(inputFile);

BlameOutput result = mock(BlameOutput.class);
CommandExecutor commandExecutor = mock(CommandExecutor.class);

ArgumentCaptor<Command> commandCaptor = ArgumentCaptor.forClass(Command.class);
when(commandExecutor.execute(commandCaptor.capture(), any(), any(), anyLong())).thenAnswer((Answer<Integer>) invocation -> {
StreamConsumer outConsumer = (StreamConsumer) invocation.getArguments()[1];
outConsumer.consumeLine("John Something <john.something@sonarsource.com> 447af27e2bc1 Tue Nov 04 11:01:10 2020 +0100: foo");
return 0;
});

when(input.filesToBlame()).thenReturn(singletonList(inputFile));

new MercurialBlameCommand(commandExecutor, new MapSettings())
.blame(input, result);

assertThat(commandCaptor.getValue().getDirectory()).isEqualTo(subrepoDir);
assertThat(commandCaptor.getValue().getArguments())
.containsExactly("blame", "-w", "-v", "--user", "--date", "--changeset", "--", "src/subrepoSource.xoo");

verify(result).blameResult(inputFile,
singletonList(new BlameLine()
.date(DateUtils.parseDateTime("2020-11-04T11:01:10+0100"))
.revision("447af27e2bc1")
.author("john.something@sonarsource.com")));
}
}

}