diff --git a/README.md b/README.md index 822e75f..fce99f2 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ It is possible to filter issues by: 3. Changed lines only - when only several lines are changed in a commit user may not want other lines to be commented by Gerrit. With "Add comments to changed lines only" unchanged in the commit lines will not be commented in Gerrit. 4. Included paths' glob pattern - The glob pattern that file paths should match to be included by the filter. Leaving a blank value or setting value `**/*` means any file path will match. Tested file paths always start with `/`. +5. Excluded paths' glob pattern - The glob pattern that file paths should match to be excluded by the filter. Leaving a blank value means no file path will match. Tested file paths always start with `/`. ![Filter settings](doc/filter-settings.png) @@ -328,6 +329,7 @@ node { newIssuesOnly : false, changedLinesOnly: false, includedPathsGlobPattern: null, + excludedPathsGlobPattern: null, ], noIssuesTitleTemplate : 'SonarQube violations have not been found.', someIssuesTitleTemplate: ' SonarQube violations have been found.', @@ -339,6 +341,7 @@ node { newIssuesOnly : false, changedLinesOnly: false, includedPathsGlobPattern: null, + excludedPathsGlobPattern: null, ], category : 'Code-Review', noIssuesScore : 0, @@ -407,6 +410,7 @@ node { newIssuesOnly : false, changedLinesOnly: false, includedPathsGlobPattern: null, + excludedPathsGlobPattern: null, ], noIssuesTitleTemplate : 'SonarQube violations have not been found.', someIssuesTitleTemplate: ' SonarQube violations have been found.', @@ -418,6 +422,7 @@ node { newIssuesOnly : false, changedLinesOnly: false, includedPathsGlobPattern: null, + excludedPathsGlobPattern: null, ], category : 'Code-Review', noIssuesScore : 0, diff --git a/doc/filter-settings.png b/doc/filter-settings.png index 8092c91..d8340ff 100644 Binary files a/doc/filter-settings.png and b/doc/filter-settings.png differ diff --git a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/ByGlobPatternPredicate.java b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/ByGlobPatternPredicate.java index b8d616c..babbb26 100644 --- a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/ByGlobPatternPredicate.java +++ b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/ByGlobPatternPredicate.java @@ -10,14 +10,33 @@ public class ByGlobPatternPredicate implements Predicate { private final PathMatcher pathMatcher; + private final boolean negated; public ByGlobPatternPredicate(String pattern) { - this.pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern); + this(pattern, false); + } + + public ByGlobPatternPredicate(String pattern, boolean negated) { + this(FileSystems.getDefault().getPathMatcher("glob:" + pattern), negated); + } + + public ByGlobPatternPredicate(PathMatcher pathMatcher, boolean negated) { + this.pathMatcher = pathMatcher; + this.negated = negated; + } + + public ByGlobPatternPredicate negate() { + return new ByGlobPatternPredicate(pathMatcher, !negated); } @Override public boolean apply(Issue input) { String filepath = StringUtils.prependIfMissing(input.getFilepath(), "/"); - return pathMatcher.matches(Paths.get(filepath)); + boolean matched = pathMatcher.matches(Paths.get(filepath)); + if (negated) { + return !matched; + } else { + return matched; + } } } diff --git a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilter.java b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilter.java index aabbe3a..194d1c0 100644 --- a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilter.java +++ b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilter.java @@ -51,6 +51,11 @@ public Iterable filter() { toBeApplied.add(new ByGlobPatternPredicate(includedPathsGlobPattern)); } + String excludedPathsGlobPattern = filterConfig.getExcludedPathsGlobPattern(); + if (excludedPathsGlobPattern != null) { + toBeApplied.add(new ByGlobPatternPredicate(excludedPathsGlobPattern).negate()); + } + return Iterables.filter(issues, Predicates.and(toBeApplied)); } } diff --git a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig.java b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig.java index 7a2502e..f42d1ed 100644 --- a/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig.java +++ b/src/main/java/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig.java @@ -20,6 +20,7 @@ public class IssueFilterConfig extends AbstractDescribableImpl + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig/config.properties b/src/main/resources/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig/config.properties index 7dc6838..e2a5d15 100644 --- a/src/main/resources/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig/config.properties +++ b/src/main/resources/org/jenkinsci/plugins/sonargerrit/sonar/IssueFilterConfig/config.properties @@ -5,6 +5,8 @@ jenkins.plugin.settings.gerrit.filter.lines.changed=Affect changed lines only jenkins.plugin.settings.gerrit.filter.lines.changed.description=Reports issues for changed as well as not changed lines in files affected by patchset when unchecked jenkins.plugin.settings.gerrit.filter.included-paths-glob-pattern=Included paths'' glob pattern jenkins.plugin.settings.gerrit.filter.included-paths-glob-pattern.description=The glob pattern that file paths should match to be included by the filter. Leaving a blank value or setting value ''**/*'' means any file path will match. Tested file paths always start with ''/''. +jenkins.plugin.settings.gerrit.filter.excluded-paths-glob-pattern=Excluded paths'' glob pattern +jenkins.plugin.settings.gerrit.filter.excluded-paths-glob-pattern.description=The glob pattern that file paths should match to be excluded by the filter. Leaving a blank value means no file path will match. Tested file paths always start with ''/''. INFO=Info MINOR=Minor diff --git a/src/test/java/org/jenkinsci/plugins/sonargerrit/ConfigRoundTripTest.java b/src/test/java/org/jenkinsci/plugins/sonargerrit/ConfigRoundTripTest.java index 38e64ab..9909211 100644 --- a/src/test/java/org/jenkinsci/plugins/sonargerrit/ConfigRoundTripTest.java +++ b/src/test/java/org/jenkinsci/plugins/sonargerrit/ConfigRoundTripTest.java @@ -319,9 +319,13 @@ void test5(JenkinsRule jenkinsRule) throws Exception { "reviewConfig.issueFilterConfig.changedLinesOnly", "reviewConfig.issueFilterConfig.newIssuesOnly", "reviewConfig.issueFilterConfig.severity", + "reviewConfig.issueFilterConfig.includedPathsGlobPattern", + "reviewConfig.issueFilterConfig.excludedPathsGlobPattern", "scoreConfig.issueFilterConfig.changedLinesOnly", "scoreConfig.issueFilterConfig.newIssuesOnly", "scoreConfig.issueFilterConfig.severity", + "scoreConfig.issueFilterConfig.includedPathsGlobPattern", + "scoreConfig.issueFilterConfig.excludedPathsGlobPattern", "scoreConfig.noIssuesScore", "scoreConfig.issuesScore", "scoreConfig.category", diff --git a/src/test/java/org/jenkinsci/plugins/sonargerrit/gerrit/ReviewTest.java b/src/test/java/org/jenkinsci/plugins/sonargerrit/gerrit/ReviewTest.java index 858bd49..8bc1e8b 100644 --- a/src/test/java/org/jenkinsci/plugins/sonargerrit/gerrit/ReviewTest.java +++ b/src/test/java/org/jenkinsci/plugins/sonargerrit/gerrit/ReviewTest.java @@ -41,7 +41,7 @@ class ReviewTest { + "-Dsonar.pullrequest.base=${env.GERRIT_BRANCH} " + "-Dsonar.pullrequest.branch=${env.GERRIT_REFSPEC}"; private static final String FILEPATH = - "src/main/java/org/example/UselessConstructorDeclaration.java"; + "child1/src/main/java/org/example/UselessConstructorDeclaration.java"; private static Cluster cluster; private static GerritGit git; @@ -54,15 +54,35 @@ static void beforeAll(Cluster cluster, @TempDir Path workTree) throws Exception git = GerritGit.createAndCloneRepository(cluster.gerrit(), workTree); git.addAndCommitFile( - "pom.xml", - "\n" - + "\n" - + " 4.0.0\n" - + "\n" - + " org.example\n" - + " example\n" - + " 1.0-SNAPSHOT\n" - + ""); + "pom.xml", + "\n" + + "\n" + + " 4.0.0\n" + + "\n" + + " org.example\n" + + " example\n" + + " 1.0-SNAPSHOT\n" + + " pom" + + "\n" + + "\n" + + "child1" + + "" + + "") + .addAndCommitFile( + "child1/pom.xml", + "\n" + + "\n" + + " 4.0.0\n" + + "\n" + + "\n" + + " org.example\n" + + " example\n" + + " 1.0-SNAPSHOT\n" + + "\n" + + "\n" + + " child1\n" + + "\n" + + ""); git.push(); @@ -90,7 +110,7 @@ void beforeEach() throws GitAPIException { @DisplayName("STANDARD comment type") void test1() throws Exception { GerritChange change = createChangeViolatingS1186(); - triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.STANDARD, null)); + triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.STANDARD, null, null)); ChangeInfo changeDetail = change.getDetail(); assertThat(changeDetail.labels.get(GerritServer.CODE_QUALITY_LABEL).all) @@ -107,7 +127,7 @@ void test1() throws Exception { @DisplayName("ROBOT comment type") void test2() throws Exception { GerritChange change = createChangeViolatingS1186(); - triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, null)); + triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, null, null)); ChangeInfo changeDetail = change.getDetail(); assertThat(changeDetail.labels.get(GerritServer.CODE_QUALITY_LABEL).all) @@ -130,7 +150,7 @@ void test2() throws Exception { @DisplayName("Review tag is autogenerated:sonar") void test3() throws Exception { GerritChange change = createChangeViolatingS1186(); - triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.STANDARD, null)); + triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.STANDARD, null, null)); ChangeInfo changeDetail = change.getDetail(); assertThat(changeDetail.labels.get(GerritServer.CODE_QUALITY_LABEL).all) @@ -143,7 +163,7 @@ void test3() throws Exception { @DisplayName("Issue ignored because of path glob pattern") void test4() throws Exception { GerritChange change = createChangeViolatingS1186(); - triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, "/[!src]/**")); + triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, "/child2/**", null)); ChangeInfo changeDetail = change.getDetail(); assertThat(changeDetail.labels.get(GerritServer.CODE_QUALITY_LABEL).all) @@ -158,7 +178,7 @@ void test4() throws Exception { @DisplayName("Issue considered despite of path glob pattern") void test5() throws Exception { GerritChange change = createChangeViolatingS1186(); - triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, "/src/**")); + triggerAndAssertSuccess(createPipelineJob(change, ReviewCommentType.ROBOT, null, "/child2/**")); ChangeInfo changeDetail = change.getDetail(); assertThat(changeDetail.labels.get(GerritServer.CODE_QUALITY_LABEL).all) @@ -184,7 +204,10 @@ private GerritChange createChangeViolatingS1186() @SuppressWarnings("rawtypes") private Job createPipelineJob( - GerritChange change, ReviewCommentType commentType, String includedPathsGlobPattern) + GerritChange change, + ReviewCommentType commentType, + String includedPathsGlobPattern, + String excludedPathsGlobPattern) throws IOException { WorkflowJob job = cluster.jenkinsRule().createProject(WorkflowJob.class); int patchSetNumber = 1; @@ -192,6 +215,11 @@ private Job createPipelineJob( Optional.ofNullable(includedPathsGlobPattern) .map(s -> StringUtils.wrap(s, "'")) .orElse(null); + + String quotedExcludedPathsGlobPattern = + Optional.ofNullable(excludedPathsGlobPattern) + .map(s -> StringUtils.wrap(s, "'")) + .orElse(null); String script = "node {\n" + "stage('Build') {\n" @@ -227,7 +255,8 @@ private Job createPipelineJob( + "severity: 'MINOR',\n" + "newIssuesOnly: false,\n" + "changedLinesOnly: true,\n" - + String.format("includedPathsGlobPattern: %s\n", quotedIncludedPathsGlobPattern) + + String.format("includedPathsGlobPattern: %s, \n", quotedIncludedPathsGlobPattern) + + String.format("excludedPathsGlobPattern: %s\n", quotedExcludedPathsGlobPattern) + "]\n" // issueFilterConfig + "],\n" // reviewConfig + "scoreConfig: [\n" @@ -235,7 +264,8 @@ private Job createPipelineJob( + "severity: 'MINOR',\n" + "newIssuesOnly: false,\n" + "changedLinesOnly: true,\n" - + String.format("includedPathsGlobPattern: %s\n", quotedIncludedPathsGlobPattern) + + String.format("includedPathsGlobPattern: %s,\n", quotedIncludedPathsGlobPattern) + + String.format("excludedPathsGlobPattern: %s\n", quotedExcludedPathsGlobPattern) + "],\n" // issueFilterConfig + String.format("category: '%s',\n", GerritServer.CODE_QUALITY_LABEL) + "noIssuesScore: 1,\n"