diff --git a/src/main/java/org/sonar/plugins/github/GitHubPlugin.java b/src/main/java/org/sonar/plugins/github/GitHubPlugin.java index ff4d840..53d34ca 100644 --- a/src/main/java/org/sonar/plugins/github/GitHubPlugin.java +++ b/src/main/java/org/sonar/plugins/github/GitHubPlugin.java @@ -59,7 +59,13 @@ description = "Issues will not be reported as inline comments but only in the global summary comment", project = true, global = true, - type = PropertyType.BOOLEAN) + type = PropertyType.BOOLEAN), + @Property( + key = GitHubPlugin.GITHUB_PROJECT_ID, + name = "Project id", + description = "Project id used to distinguish between comments from different SonarQube projects", + project = true, + global = true) }) public class GitHubPlugin implements Plugin { @@ -68,6 +74,7 @@ public class GitHubPlugin implements Plugin { public static final String GITHUB_REPO = "sonar.github.repository"; public static final String GITHUB_PULL_REQUEST = "sonar.github.pullRequest"; public static final String GITHUB_DISABLE_INLINE_COMMENTS = "sonar.github.disableInlineComments"; + public static final String GITHUB_PROJECT_ID = "sonar.github.projectId"; @Override diff --git a/src/main/java/org/sonar/plugins/github/GitHubPluginConfiguration.java b/src/main/java/org/sonar/plugins/github/GitHubPluginConfiguration.java index b627be4..3a634b8 100644 --- a/src/main/java/org/sonar/plugins/github/GitHubPluginConfiguration.java +++ b/src/main/java/org/sonar/plugins/github/GitHubPluginConfiguration.java @@ -136,6 +136,10 @@ public boolean tryReportIssuesInline() { return !settings.getBoolean(GitHubPlugin.GITHUB_DISABLE_INLINE_COMMENTS); } + public String projectId() { + return settings.getString(GitHubPlugin.GITHUB_PROJECT_ID); + } + /** * Checks if a proxy was passed with command line parameters or configured in the system. * If only an HTTP proxy was configured then it's properties are copied to the HTTPS proxy (like SonarQube configuration) diff --git a/src/main/java/org/sonar/plugins/github/GlobalReport.java b/src/main/java/org/sonar/plugins/github/GlobalReport.java index 2b6b104..b8d9df2 100644 --- a/src/main/java/org/sonar/plugins/github/GlobalReport.java +++ b/src/main/java/org/sonar/plugins/github/GlobalReport.java @@ -21,6 +21,8 @@ import java.util.Locale; import javax.annotation.Nullable; + +import org.apache.commons.lang.StringUtils; import org.kohsuke.github.GHCommitState; import org.sonar.api.batch.postjob.issue.PostJobIssue; import org.sonar.api.batch.rule.Severity; @@ -30,15 +32,17 @@ public class GlobalReport { private int[] newIssuesBySeverity = new int[Severity.values().length]; private int extraIssueCount = 0; private int maxGlobalReportedIssues; + private String projectId; private final ReportBuilder builder; - public GlobalReport(MarkDownUtils markDownUtils, boolean tryReportIssuesInline) { - this(markDownUtils, tryReportIssuesInline, GitHubPluginConfiguration.MAX_GLOBAL_ISSUES); + public GlobalReport(MarkDownUtils markDownUtils, boolean tryReportIssuesInline, String projectId) { + this(markDownUtils, tryReportIssuesInline, GitHubPluginConfiguration.MAX_GLOBAL_ISSUES, projectId); } - public GlobalReport(MarkDownUtils markDownUtils, boolean tryReportIssuesInline, int maxGlobalReportedIssues) { + public GlobalReport(MarkDownUtils markDownUtils, boolean tryReportIssuesInline, int maxGlobalReportedIssues, String projectId) { this.tryReportIssuesInline = tryReportIssuesInline; this.maxGlobalReportedIssues = maxGlobalReportedIssues; + this.projectId = projectId; this.builder = new MarkDownReportBuilder(markDownUtils); } @@ -48,13 +52,18 @@ private void increment(Severity severity) { public String formatForMarkdown() { int newIssues = newIssues(Severity.BLOCKER) + newIssues(Severity.CRITICAL) + newIssues(Severity.MAJOR) + newIssues(Severity.MINOR) + newIssues(Severity.INFO); - if (newIssues == 0) { - return "SonarQube analysis reported no issues."; - } boolean hasInlineIssues = newIssues > extraIssueCount; boolean extraIssuesTruncated = extraIssueCount > maxGlobalReportedIssues; - builder.append("SonarQube analysis reported ").append(newIssues).append(" issue").append(newIssues > 1 ? "s" : "").append("\n"); + if (!StringUtils.isEmpty(projectId)) { + builder.appendProjectId(projectId).append(" "); + } + if (newIssues == 0) { + builder.append("SonarQube analysis reported no issues."); + return builder.toString(); + } else { + builder.append("SonarQube analysis reported ").append(newIssues).append(" issue").append(newIssues > 1 ? "s" : "").append("\n"); + } if (hasInlineIssues || extraIssuesTruncated) { appendSummaryBySeverity(builder); } diff --git a/src/main/java/org/sonar/plugins/github/MarkDownReportBuilder.java b/src/main/java/org/sonar/plugins/github/MarkDownReportBuilder.java index c288b57..f06b1ce 100644 --- a/src/main/java/org/sonar/plugins/github/MarkDownReportBuilder.java +++ b/src/main/java/org/sonar/plugins/github/MarkDownReportBuilder.java @@ -50,6 +50,12 @@ private IssueHolder(PostJobIssue issue, String gitHubUrl) { this.markDownUtils = markDownUtils; } + @Override + public ReportBuilder appendProjectId(String projectId) { + sb.append(markDownUtils.projectId(projectId)); + return this; + } + @Override public ReportBuilder append(Object o) { sb.append(o); diff --git a/src/main/java/org/sonar/plugins/github/MarkDownUtils.java b/src/main/java/org/sonar/plugins/github/MarkDownUtils.java index 7fcca91..df6ea75 100644 --- a/src/main/java/org/sonar/plugins/github/MarkDownUtils.java +++ b/src/main/java/org/sonar/plugins/github/MarkDownUtils.java @@ -47,6 +47,12 @@ public MarkDownUtils(Settings settings) { this.ruleUrlPrefix = baseUrl; } + public static String projectId(String projectId) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(projectId).append("]"); + return sb.toString(); + } + public String inlineIssue(Severity severity, String message, String ruleKey) { String ruleLink = getRuleLink(ruleKey); StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/org/sonar/plugins/github/PullRequestFacade.java b/src/main/java/org/sonar/plugins/github/PullRequestFacade.java index 5e5ae9c..3e23ad9 100644 --- a/src/main/java/org/sonar/plugins/github/PullRequestFacade.java +++ b/src/main/java/org/sonar/plugins/github/PullRequestFacade.java @@ -31,6 +31,8 @@ import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + +import org.apache.commons.lang.StringUtils; import org.kohsuke.github.GHCommitState; import org.kohsuke.github.GHCommitStatus; import org.kohsuke.github.GHIssueComment; @@ -72,6 +74,7 @@ public class PullRequestFacade { public PullRequestFacade(GitHubPluginConfiguration config) { this.config = config; + } public void init(int pullRequestNumber, File projectBaseDir) { @@ -136,6 +139,10 @@ private void loadExistingReviewComments() throws IOException { // Ignore comments from other users continue; } + if (!StringUtils.isEmpty(config.projectId()) && !comment.getBody().startsWith(MarkDownUtils.projectId(config.projectId()))) { + // Ignore comments that don't contain projectId + continue; + } if (!existingReviewCommentsByLocationByFile.containsKey(comment.getPath())) { existingReviewCommentsByLocationByFile.put(comment.getPath(), new HashMap()); } @@ -255,6 +262,10 @@ private boolean findAndDeleteOthers(@Nullable String markup) throws IOException boolean found = false; for (GHIssueComment comment : pr.listComments()) { if (myself.equals(comment.getUser().getLogin())) { + if (!StringUtils.isEmpty(config.projectId()) && !comment.getBody().startsWith(MarkDownUtils.projectId(config.projectId()))) { + // Ignore comments that don't contain projectId + continue; + } if (markup == null || found || !markup.equals(comment.getBody())) { comment.delete(); continue; diff --git a/src/main/java/org/sonar/plugins/github/PullRequestIssuePostJob.java b/src/main/java/org/sonar/plugins/github/PullRequestIssuePostJob.java index ffcc023..f0c367f 100644 --- a/src/main/java/org/sonar/plugins/github/PullRequestIssuePostJob.java +++ b/src/main/java/org/sonar/plugins/github/PullRequestIssuePostJob.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.stream.StreamSupport; import org.apache.commons.lang.StringUtils; + import org.kohsuke.github.GHCommitState; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; @@ -61,7 +62,7 @@ public void describe(PostJobDescriptor descriptor) { @Override public void execute(PostJobContext context) { - GlobalReport report = new GlobalReport(markDownUtils, gitHubPluginConfiguration.tryReportIssuesInline()); + GlobalReport report = new GlobalReport(markDownUtils, gitHubPluginConfiguration.tryReportIssuesInline(), gitHubPluginConfiguration.projectId()); try { Map> commentsToBeAddedByLine = processIssues(report, context.issues()); @@ -117,7 +118,11 @@ private boolean tryReportInline(Map> comm } Map commentsByLine = commentToBeAddedByFileAndByLine.get(inputFile); if (!commentsByLine.containsKey(line)) { - commentsByLine.put(line, new StringBuilder()); + StringBuilder stringBuilder = new StringBuilder(); + if (!StringUtils.isEmpty(gitHubPluginConfiguration.projectId())) { + stringBuilder.append(markDownUtils.projectId(gitHubPluginConfiguration.projectId())).append("\n"); + } + commentsByLine.put(line, stringBuilder); } commentsByLine.get(line).append(markDownUtils.inlineIssue(issue.severity(), message, ruleKey)).append("\n"); return true; diff --git a/src/main/java/org/sonar/plugins/github/ReportBuilder.java b/src/main/java/org/sonar/plugins/github/ReportBuilder.java index f5cdc15..d2d32fc 100644 --- a/src/main/java/org/sonar/plugins/github/ReportBuilder.java +++ b/src/main/java/org/sonar/plugins/github/ReportBuilder.java @@ -24,6 +24,14 @@ import org.sonar.api.batch.rule.Severity; public interface ReportBuilder { + /** + * Append project id to the report. + * + * @param projectId Project id to append + * @return a reference to this object + */ + ReportBuilder appendProjectId(String projectId); + /** * Append an object to the report, using its toString() method. * diff --git a/src/test/java/org/sonar/plugins/github/GlobalReportTest.java b/src/test/java/org/sonar/plugins/github/GlobalReportTest.java index 007d3cf..b8f20ce 100644 --- a/src/test/java/org/sonar/plugins/github/GlobalReportTest.java +++ b/src/test/java/org/sonar/plugins/github/GlobalReportTest.java @@ -71,7 +71,7 @@ private PostJobIssue newMockedIssue(String componentKey, @CheckForNull DefaultIn @Test public void noIssues() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); String desiredMarkdown = "SonarQube analysis reported no issues."; @@ -80,9 +80,20 @@ public void noIssues() { assertThat(formattedGlobalReport).isEqualTo(desiredMarkdown); } + @Test + public void noIssuesWithProjectId() { + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, "project-id"); + + String desiredMarkdown = "[project-id] SonarQube analysis reported no issues."; + + String formattedGlobalReport = globalReport.formatForMarkdown(); + + assertThat(formattedGlobalReport).isEqualTo(desiredMarkdown); + } + @Test public void oneIssue() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue", "rule"), GITHUB_URL, true); String desiredMarkdown = "SonarQube analysis reported 1 issue\n" + @@ -95,9 +106,24 @@ public void oneIssue() { assertThat(formattedGlobalReport).isEqualTo(desiredMarkdown); } + @Test + public void oneIssueWithProjectId() { + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, "project-id"); + globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue", "rule"), GITHUB_URL, true); + + String desiredMarkdown = "[project-id] SonarQube analysis reported 1 issue\n" + + "* ![INFO][INFO] 1 info\n" + + "\nWatch the comments in this conversation to review them.\n" + + "\n[INFO]: https://sonarsource.github.io/sonar-github/severity-info.png 'Severity: INFO'"; + + String formattedGlobalReport = globalReport.formatForMarkdown(); + + assertThat(formattedGlobalReport).isEqualTo(desiredMarkdown); + } + @Test public void oneIssueOnDir() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); globalReport.process(newMockedIssue("component0", null, null, Severity.INFO, true, "Issue0", "rule0"), null, false); String desiredMarkdown = "SonarQube analysis reported 1 issue\n\n" + @@ -113,7 +139,7 @@ public void oneIssueOnDir() { @Test public void shouldFormatIssuesForMarkdownNoInline() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue", "rule"), GITHUB_URL, true); globalReport.process(newMockedIssue("component", null, null, Severity.MINOR, true, "Issue", "rule"), GITHUB_URL, true); globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue", "rule"), GITHUB_URL, true); @@ -141,7 +167,7 @@ public void shouldFormatIssuesForMarkdownNoInline() { @Test public void shouldFormatIssuesForMarkdownMixInlineGlobal() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue 0", "rule0"), GITHUB_URL, true); globalReport.process(newMockedIssue("component", null, null, Severity.MINOR, true, "Issue 1", "rule1"), GITHUB_URL, false); globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue 2", "rule2"), GITHUB_URL, true); @@ -175,7 +201,7 @@ public void shouldFormatIssuesForMarkdownMixInlineGlobal() { @Test public void shouldFormatIssuesForMarkdownWhenInlineCommentsDisabled() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false, ""); globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue 0", "rule0"), GITHUB_URL, false); globalReport.process(newMockedIssue("component", null, null, Severity.MINOR, true, "Issue 1", "rule1"), GITHUB_URL, false); globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue 2", "rule2"), GITHUB_URL, false); @@ -206,7 +232,7 @@ public void shouldFormatIssuesForMarkdownWhenInlineCommentsDisabled() { @Test public void shouldFormatIssuesForMarkdownWhenInlineCommentsDisabledAndLimitReached() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false, 4); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false, 4, ""); globalReport.process(newMockedIssue("component", null, null, Severity.INFO, true, "Issue 0", "rule0"), GITHUB_URL, false); globalReport.process(newMockedIssue("component", null, null, Severity.MINOR, true, "Issue 1", "rule1"), GITHUB_URL, false); globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue 2", "rule2"), GITHUB_URL, false); @@ -241,7 +267,7 @@ public void shouldFormatIssuesForMarkdownWhenInlineCommentsDisabledAndLimitReach @Test public void shouldLimitGlobalIssues() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), true, ""); for (int i = 0; i < 17; i++) { globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue number:" + i, "rule" + i), GITHUB_URL + "/File.java#L" + i, false); } @@ -280,7 +306,7 @@ public void shouldLimitGlobalIssues() { @Test public void shouldLimitGlobalIssuesWhenInlineCommentsDisabled() { - GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false); + GlobalReport globalReport = new GlobalReport(new MarkDownUtils(settings), false, ""); for (int i = 0; i < 17; i++) { globalReport.process(newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue number:" + i, "rule" + i), GITHUB_URL + "/File.java#L" + i, false); } diff --git a/src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java b/src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java index c9278f8..38931fa 100644 --- a/src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java +++ b/src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java @@ -56,17 +56,25 @@ public class PullRequestIssuePostJobTest { @Before public void prepare() throws Exception { pullRequestFacade = mock(PullRequestFacade.class); - Settings settings = new Settings(new PropertyDefinitions(PropertyDefinition.builder(CoreProperties.SERVER_BASE_URL) - .name("Server base URL") - .description("HTTP URL of this SonarQube server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails.") - .category(CoreProperties.CATEGORY_GENERAL) - .defaultValue(CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE) - .build())); + Settings settings = new Settings(new PropertyDefinitions( + PropertyDefinition.builder(CoreProperties.SERVER_BASE_URL) + .name("Server base URL") + .description("HTTP URL of this SonarQube server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails.") + .category(CoreProperties.CATEGORY_GENERAL) + .defaultValue(CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE) + .build(), + PropertyDefinition.builder(GitHubPlugin.GITHUB_PROJECT_ID) + .name("Project Id") + .description("Project Id") + .category(CoreProperties.CATEGORY_GENERAL) + .defaultValue("") + .build())); GitHubPluginConfiguration config = new GitHubPluginConfiguration(settings, new System2()); context = mock(PostJobContext.class); settings.setProperty("sonar.host.url", "http://192.168.0.1"); settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver"); + settings.setProperty(GitHubPlugin.GITHUB_PROJECT_ID, "project-id"); pullRequestIssuePostJob = new PullRequestIssuePostJob(config, pullRequestFacade, new MarkDownUtils(settings)); }