diff --git a/CHANGELOG.md b/CHANGELOG.md
index 187ef39..8c01ea1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,26 @@
Change Log
==========
+[Version 0.6](https://github.com/novoda/gradle-static-analysis-plugin/releases/tag/v0.6)
+--------------------------
+
+- Fix release to plugin portal ([PR#82](https://github.com/novoda/gradle-static-analysis-plugin/pull/82),[PR#83](https://github.com/novoda/gradle-static-analysis-plugin/pull/83))
+- Improve plugin documentation and samples ([PR#85](https://github.com/novoda/gradle-static-analysis-plugin/pull/85),
+[PR#97](https://github.com/novoda/gradle-static-analysis-plugin/pull/97),
+[PR#99](https://github.com/novoda/gradle-static-analysis-plugin/pull/99),
+[PR#100](https://github.com/novoda/gradle-static-analysis-plugin/pull/100),
+[PR#101](https://github.com/novoda/gradle-static-analysis-plugin/pull/101),
+[PR#113](https://github.com/novoda/gradle-static-analysis-plugin/pull/113),
+[PR#123](https://github.com/novoda/gradle-static-analysis-plugin/pull/123),
+[PR#124](https://github.com/novoda/gradle-static-analysis-plugin/pull/124))
+- Improve support for Android Lint ([PR#105](https://github.com/novoda/gradle-static-analysis-plugin/pull/105))
+- Improve support for Detekt ([PR#90](https://github.com/novoda/gradle-static-analysis-plugin/pull/90), [PR#121](https://github.com/novoda/gradle-static-analysis-plugin/pull/121))
+- Rename built-in `failOnWarnings` penalty to `failFast` ([PR#92](https://github.com/novoda/gradle-static-analysis-plugin/pull/92))
+- Support multiple configurations for `Pmd`, `Findbugs`, `Checkstyle` ([PR#93](https://github.com/novoda/gradle-static-analysis-plugin/pull/93))
+- Support automatic snapshot builds from `develop` ([PR#106](https://github.com/novoda/gradle-static-analysis-plugin/pull/106),[PR#107](https://github.com/novoda/gradle-static-analysis-plugin/pull/107))
+- Automatically exclude Kotlin files from Java code quality tools ([PR#109](https://github.com/novoda/gradle-static-analysis-plugin/pull/109))
+- Integrate [KtLint](https://github.com/shyiko/ktlint) ([PR#110](https://github.com/novoda/gradle-static-analysis-plugin/pull/110))
+
[Version 0.5.2](https://github.com/novoda/gradle-static-analysis-plugin/releases/tag/v0.5.2)
--------------------------
diff --git a/README.md b/README.md
index f63c2fe..5c29744 100644
--- a/README.md
+++ b/README.md
@@ -22,9 +22,6 @@ The plugin supports various static analysis tools for Java, Kotlin and Android p
* [`FindBugs`](http://findbugs.sourceforge.net/)
* [`Detekt`](https://github.com/arturbosch/detekt)
* [`Android Lint`](https://developer.android.com/studio/write/lint.html)
-
-Support for additional tools is planned but not available yet:
-
* [`KtLint`](https://github.com/shyiko/ktlint)
Please note that the tools availability depends on the project the plugin is applied to. For more details please refer to the
@@ -44,7 +41,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.novoda:gradle-static-analysis-plugin:0.5.2'
+ classpath 'com.novoda:gradle-static-analysis-plugin:0.6'
}
}
@@ -55,7 +52,7 @@ or from the [Gradle Plugins Repository](https://plugins.gradle.org/):
```gradle
plugins {
- id 'com.novoda.static-analysis' version '0.5.2'
+ id 'com.novoda.static-analysis' version '0.6'
}
```
@@ -83,6 +80,21 @@ This will enable all the tools with their default settings. For more advanced co
## Sample app
There are two sample Android projects available, one consisting of a regular app - available [here](https://github.com/novoda/gradle-static-analysis-plugin/tree/master/sample) - and the other comprising a multi-module setup available [here](https://github.com/novoda/gradle-static-analysis-plugin/tree/master/sample-multi-module). Both sample projects showcase a setup featuring Checkstyle, FindBugs, PMD, Lint and Detekt.
+## Snapshots
+[![CI status](https://ci.novoda.com/buildStatus/icon?job=gradle-static-analysis-plugin-snapshot)](https://ci.novoda.com/job/gradle-static-analysis-plugin-snapshot/lastBuild/console) [![Download from Bintray](https://api.bintray.com/packages/novoda/snapshots/gradle-static-analysis-plugin/images/download.svg)](https://bintray.com/novoda/snapshots/gradle-static-analysis-plugin/_latestVersion)
+
+Snapshot builds from [`develop`](https://github.com/novoda/gradle-static-analysis-plugin/compare/master...develop) are automatically deployed to a [repository](https://bintray.com/novoda/snapshots/gradle-static-analysis-plugin/_latestVersion) that is not synced with JCenter.
+To consume a snapshot build add an additional maven repo as follows:
+```
+repositories {
+ maven {
+ url 'https://novoda.bintray.com/snapshots'
+ }
+}
+```
+
+You can find the latest snapshot version following this [link](https://bintray.com/novoda/snapshots/gradle-static-analysis-plugin/_latestVersion).
+
## Roadmap
The plugin is under active development and to be considered in **beta stage**. It is routinely used by many Novoda projects and
by other external projects with no known critical issues. The API is supposed to be relatively stable, but there still may be
diff --git a/buildSrc/src/main/groovy/GradlePlugins.groovy b/buildSrc/src/main/groovy/GradlePlugins.groovy
index d28ee27..1849230 100644
--- a/buildSrc/src/main/groovy/GradlePlugins.groovy
+++ b/buildSrc/src/main/groovy/GradlePlugins.groovy
@@ -1,6 +1,6 @@
class GradlePlugins {
final bintrayRelease = 'com.novoda:bintray-release:0.4.0'
- final buildProperties = 'com.novoda:gradle-build-properties-plugin:0.2'
+ final buildProperties = 'com.novoda:gradle-build-properties-plugin:0.4.1'
final gradleGit = 'org.ajoberstar:gradle-git:1.6.0'
final gradlePublish = 'com.gradle.publish:plugin-publish-plugin:0.9.9'
}
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index 59edbae..c17687b 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -8,6 +8,7 @@ the build will not fail, which is very useful for legacy projects.
* [Improve the report with a base URL](#improve-the-report-with-a-base-URL)
* [Add exclusions with `exclude` filters](#add-exclusions-with-exclude-filters)
* [Add exclusions with Android build variants](#add-exclusions-with-android-build-variants)
+ * [Consume rules from an artifact](#consume-rules-from-an-artifact)
* [Custom violations evaluator (**incubating**)](incubating/custom-evaluator.md#custom-violations-evaluator-incubating)
---
@@ -50,10 +51,10 @@ Besides manually specifying thresholds, the plugin includes a few built-in `pena
```
This will break the build if any error is found. Warnings instead are only logged and will not break the build.
-* `failOnWarnings`
+* `failFast`
```gradle
staticAnalysis {
- penalty failOnWarnings
+ penalty failFast
}
```
This policy will fail the build if any warning or error is found. It is a zero-tolerance policy, useful to keep
@@ -131,3 +132,70 @@ staticAnalysis {
Please note that this is not yet supported for Detekt.
[penaltyextensioncode]: https://github.com/novoda/gradle-static-analysis-plugin/blob/master/plugin/src/main/groovy/com/novoda/staticanalysis/PenaltyExtension.groovy
+
+
+## Consume rules from an artifact
+In order to reuse your rules among multiple projects or to easily use an open source rule set, we added support for consuming the
+rules for all supported tools from a Maven artifact.
+
+### Rules artifact
+A rule artifact is just a bundle of files that is published on a Maven repository as artifact. An example of how to do that can be be found [here](https://github.com/novoda/novoda/blob/master/scaffolding/build.gradle).
+In this case we bundle the files as jar using the using the [java plugin](https://docs.gradle.org/current/userguide/java_plugin.html) and publish it using our [bintray-release plugin](https://github.com/novoda/bintray-release).
+
+### How to define a dependency from a rules artifact
+In order to access the files inside a rule artifact you have to first define an entry in the `rules {}` extension of the plugin. An entry is defined by a name and a Maven coordinate, as follows:
+```
+staticAnalysis {
+ rules {
+ novoda {
+ maven 'com.novoda:static-analysis-rules:0.2'
+ }
+ }
+}
+```
+
+### Access rules from artifact
+Once you have defined a rule artifact you can access the files inside it specifying the path of the file inside the bundle, e.g.:
+```
+def modules = rules.novoda['checkstyle-modules.xml']
+```
+Note that `modules` is defined as [`TextResource`](https://docs.gradle.org/current/dsl/org.gradle.api.resources.TextResource.html), that is a read-only body of text backed by a string, file, archive entry, or other source. Some of the tools already accept `TextResource` as value for some of their configuration, while in other cases you have to transform a `TextResource` into a `File` or a path. Some examples of using a rule artifact in the supported tools is provided below:
+
+#### Checkstyle
+```gradle
+checkstyle {
+ toolVersion '8.8'
+ config rules.novoda['checkstyle-modules.xml']
+}
+```
+
+#### PMD
+```gradle
+pmd {
+ toolVersion '6.0.1'
+ ruleSetFiles = project.files(rules.novoda['pmd-rules.xml'].asFile().path)
+}
+```
+
+#### FindBugs
+```gradle
+findbugs {
+ excludeFilter rules.novoda['findbugs-excludes.xml'].asFile()
+}
+```
+
+#### Detekt
+```gradle
+detekt {
+ profile('main') {
+ config = rules.novoda['detekt.yml'].asFile().path
+ }
+}
+```
+
+#### Android Lint
+```gradle
+lintOptions {
+ lintConfig = rules.novoda['lint-config.xml'].asFile()
+}
+```
diff --git a/docs/supported-tools.md b/docs/supported-tools.md
index ae74f43..cba163b 100644
--- a/docs/supported-tools.md
+++ b/docs/supported-tools.md
@@ -10,9 +10,7 @@ Tool | Java | Android
(Java) | Kotlin | Android
(Kotlin)
[`FindBugs`](http://findbugs.sourceforge.net/) | :white_check_mark: | :white_check_mark: | — | —
[`Detekt`](https://github.com/arturbosch/detekt) | — | — | :white_check_mark: | :white_check_mark:
[`Android Lint`](https://developer.android.com/studio/write/lint.html) | — | :white_check_mark:️ | — | :white_check_mark:️
-[`KtLint`\*](https://github.com/shyiko/ktlint) | — | — | ✖️ | ✖️
-
-_\* Not supported [yet](https://github.com/novoda/gradle-static-analysis-plugin/issues?q=is%3Aopen+is%3Aissue+label%3A%22new+tool%22)_
+[`KtLint`](https://github.com/shyiko/ktlint) | — | — | :white_check_mark:️ | :white_check_mark:️
For additional informations and tips on how to obtain advanced behaviours with the plugin and its tools, please refer to the
[advanced usage](advanced-usage.md) page.
@@ -25,7 +23,7 @@ For additional informations and tips on how to obtain advanced behaviours with t
* [PMD](tools/pmd.md)
* [Findbugs](tools/findbugs.md)
* [Android Lint](tools/android_lint.md)
- * KtLint — _COMING SOON_
+ * [KtLint](tools/ktlint.md)
* [Example configurations](#example-configurations)
---
@@ -42,7 +40,9 @@ staticAnalysis {
checkstyle {}
pmd {}
findbugs {}
+ lintOptions {}
detekt {}
+ ktlint {}
}
```
diff --git a/docs/tools/findbugs.md b/docs/tools/findbugs.md
index 930043f..2dedd64 100644
--- a/docs/tools/findbugs.md
+++ b/docs/tools/findbugs.md
@@ -28,22 +28,4 @@ You can have multiple `exclude` statements.
For more informations about Findbugs rules, refer to the [official website](http://findbugs.sourceforge.net/bugDescriptions.html).
## Findbugs in mixed-language projects
-If your project mixes Java and Kotlin code, you most likely want to have an exclusion in place for all `*.kt` files. You can use the `exclude`
-in the configuration closure, or you can do so by adding a suppression to the filter file:
-
-`findbugs-excludes.xml`
-```xml
-
-
- ...
-
-
-
-
-
- ...
-
-
-```
-
-
+If your project mixes Java and Kotlin code, your Kotlin source files will automatically be ignored.
diff --git a/docs/tools/ktlint.md b/docs/tools/ktlint.md
new file mode 100644
index 0000000..f804859
--- /dev/null
+++ b/docs/tools/ktlint.md
@@ -0,0 +1,58 @@
+# ktlint
+[Ktlint](https://github.com/shyiko/ktlint) is a linter for Kotlin with a built-in formatter. It does not support Java. Adding
+this tool only makes sense when you have Kotlin sources in your project. In fact, it will fail to run if you have 0 Kotlin
+files.
+
+## Table of contents
+ * [IMPORTANT: setup Ktlint](#important-setup-ktlint)
+ * [Configure Ktlint](#configure-ktlint)
+---
+
+## IMPORTANT: setup Ktlint
+
+Unlike the other tools, the plugin **won't automatically add Ktlint** to your project. If you forget to do it, the plugin will
+fail the build with an error.
+
+In order to integrate Ktlint easily we choose to use the [Ktlint Gradle plugin](https://github.com/JLLeitschuh/ktlint-gradle/).
+This plugin has a very good understanding of Android source sets and build flavors. You can refer to the
+[official documentation](https://github.com/JLLeitschuh/ktlint-gradle/#how-to-use) for further details.
+
+Note that you should _not_ add the `ktlint` closure to your `build.gradle`s, unlike what the official documentation says. The
+`ktlint` closure in the `staticAnalysis` configuration gets applied to all Kotlin modules automatically.
+
+In most common cases, adding Ktlint to a project boils down to these simple steps:
+
+ 1. Add this statement to your root `build.gradle` project (change the version according to your needs):
+ ```gradle
+ plugins {
+ id 'org.jlleitschuh.gradle.ktlint' version '5.0.0'
+ // ...
+ }
+ ```
+ 2. Add this statement to each Kotlin project's `build.gradle`s:
+ ```gradle
+ plugins {
+ id 'org.jlleitschuh.gradle.ktlint'
+ // ...
+ }
+ ```
+
+## Configure Ktlint
+
+Unlike other tools, Ktlint does not offer much configuration. By default, it applies
+[Kotlin style guide](https://kotlinlang.org/docs/reference/coding-conventions.html) or
+[Android Kotlin style guide](https://android.github.io/kotlin-guides/style.html).
+
+To use Android style guide:
+
+```gradle
+ktlint {
+ android true
+}
+```
+
+For other configuration options and adding custom rules, refer to the
+[official guide](https://github.com/JLLeitschuh/ktlint-gradle/#configuration).
+
+**Note:** Failures and threshold detection is handled by Static Analysis plugin. That is why `ignoreFailures = true` is set by
+the plugin. Please do not manually override `ignoreFailures` property.
diff --git a/gradle/publish.gradle b/gradle/publish.gradle
index 79e6abf..edec54e 100644
--- a/gradle/publish.gradle
+++ b/gradle/publish.gradle
@@ -2,31 +2,65 @@ ext {
websiteUrl = 'https://github.com/novoda/gradle-static-analysis-plugin'
}
-version = '0.5.2'
+version = '0.6'
String tag = "v$project.version"
groovydoc.docTitle = 'Static Analysis Plugin'
-apply plugin: 'com.gradle.plugin-publish'
-apply plugin: 'com.novoda.bintray-release'
-publish {
- userOrg = 'novoda'
- groupId = 'com.novoda'
- artifactId = 'gradle-static-analysis-plugin'
- publishVersion = project.version
- website = websiteUrl
-}
-
apply plugin: 'com.novoda.build-properties'
buildProperties {
+
secrets {
- file(rootProject.file('secrets.properties'), '''
-This file should contain:
+ using rootProject.file('secrets.properties')
+ description = '''
+ This file should contain:
- git.username: the username used to push to the repo
- git.password: the password used to push to the repo
- gradle.publish.key: the key to publish the plugin to the Gradle Plugins Repository
- gradle.publish.secret: the secret to publish the plugin to the Gradle Plugins Repository
- ''')
+ '''
}
+
+ cli {
+ using(project)
+ }
+
+ bintray {
+ def bintrayCredentials = {
+ return isDryRun() ?
+ ['bintrayRepo': 'n/a', 'bintrayUser': 'n/a', 'bintrayKey': 'n/a'] :
+ new File("${System.getenv('BINTRAY_PROPERTIES')}")
+ }
+ using(bintrayCredentials()).or(cli)
+ description = '''This should contain the following properties:
+ | - bintrayRepo: name of the repo of the organisation to deploy the artifacts to
+ | - bintrayUser: name of the account used to deploy the artifacts
+ | - bintrayKey: API key of the account used to deploy the artifacts
+ '''.stripMargin()
+ }
+
+ publish {
+ def generateVersion = {
+ return isSnapshot() ? "SNAPSHOT-${System.getenv('BUILD_NUMBER') ?: 'LOCAL'}" : project.version
+ }
+ using(['version': "${generateVersion()}"]).or(buildProperties.bintray)
+ }
+
+}
+
+apply plugin: 'com.gradle.plugin-publish'
+apply plugin: 'com.novoda.bintray-release'
+
+publish {
+ userOrg = 'novoda'
+ repoName = buildProperties.publish['bintrayRepo'].string
+ groupId = 'com.novoda'
+ artifactId = 'gradle-static-analysis-plugin'
+
+ version = project.buildProperties.publish['version'].string
+ publishVersion = project.buildProperties.publish['version'].string
+ bintrayUser = project.buildProperties.publish['bintrayUser'].string
+ bintrayKey = project.buildProperties.publish['bintrayKey'].string
+ website = websiteUrl
}
apply plugin: 'org.ajoberstar.grgit'
@@ -129,14 +163,25 @@ task publishGroovydoc {
task publishRelease {
description = "Publish release for plugin version: $tag"
group = 'release'
- if (project.hasProperty('dryRun') && project['dryRun'] == 'false') {
- dependsOn prepareRelease, publishArtifact, publishGroovydoc, publishPlugins
- doLast {
- grgit.push {
- tags = true
- }
- }
+ if (isDryRun()) {
+ dependsOn publishArtifact
} else {
dependsOn publishArtifact
+ if (!isSnapshot()) {
+ dependsOn prepareRelease, publishGroovydoc, publishPlugins
+ doLast {
+ grgit.push {
+ tags = true
+ }
+ }
+ }
}
}
+
+boolean isDryRun() {
+ buildProperties.cli['dryRun'].or(true).boolean
+}
+
+boolean isSnapshot() {
+ buildProperties.cli['bintraySnapshot'].or(false).boolean
+}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisExtension.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisExtension.groovy
index 8dc233a..e012af8 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisExtension.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisExtension.groovy
@@ -18,11 +18,17 @@ class StaticAnalysisExtension {
it.maxErrors = 0
}
- final Action super PenaltyExtension> failOnWarnings = {
+ final Action super PenaltyExtension> failFast = {
it.maxWarnings = 0
it.maxErrors = 0
}
+ @Deprecated
+ def getFailOnWarnings() {
+ logger.warn 'com.novoda.static-analysis: failOnWarnings is deprecated. Please use `penalty failFast` for clarity.'
+ failFast
+ }
+
private final Project project
private final LogsExtension logs
private final NamedDomainObjectContainer allViolations
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisPlugin.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisPlugin.groovy
index 35d41eb..da9042f 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisPlugin.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/StaticAnalysisPlugin.groovy
@@ -4,6 +4,7 @@ import com.novoda.staticanalysis.internal.CodeQualityConfigurator
import com.novoda.staticanalysis.internal.checkstyle.CheckstyleConfigurator
import com.novoda.staticanalysis.internal.detekt.DetektConfigurator
import com.novoda.staticanalysis.internal.findbugs.FindbugsConfigurator
+import com.novoda.staticanalysis.internal.ktlint.KtlintConfigurator
import com.novoda.staticanalysis.internal.lint.LintConfigurator
import com.novoda.staticanalysis.internal.pmd.PmdConfigurator
import org.gradle.api.NamedDomainObjectContainer
@@ -40,6 +41,7 @@ class StaticAnalysisPlugin implements Plugin {
PmdConfigurator.create(project, violationsContainer, evaluateViolations),
FindbugsConfigurator.create(project, violationsContainer, evaluateViolations),
DetektConfigurator.create(project, violationsContainer, evaluateViolations),
+ KtlintConfigurator.create(project, violationsContainer, evaluateViolations),
LintConfigurator.create(project, violationsContainer, evaluateViolations)
]
}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy
index 1c19473..94b7d86 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy
@@ -83,7 +83,10 @@ abstract class CodeQualityConfigurator sourceFilter.applyTo(task) }
+ project.tasks.withType(taskClass) { task ->
+ sourceFilter.applyTo(task)
+ task.exclude '**/*.kt'
+ }
}
protected abstract Class getTaskClass()
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CollectViolationsTask.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CollectViolationsTask.groovy
index 6d1cd91..776bc0c 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CollectViolationsTask.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CollectViolationsTask.groovy
@@ -2,10 +2,12 @@ package com.novoda.staticanalysis.internal
import com.novoda.staticanalysis.Violations
import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
abstract class CollectViolationsTask extends DefaultTask {
+ @InputFile
private File xmlReportFile
private File htmlReportFile
private Violations violations
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy
index 9348104..534e0ba 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy
@@ -59,6 +59,7 @@ class CheckstyleConfigurator extends CodeQualityConfigurator
- collectViolations.xmlReportFile = checkstyle.reports.xml.destination
- collectViolations.violations = violations
- }
+ def task = project.tasks.maybeCreate("collect${checkstyle.name.capitalize()}Violations", CollectCheckstyleViolationsTask)
+ task.xmlReportFile = checkstyle.reports.xml.destination
+ task.violations = violations
+ task
}
}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy
index 1ff7116..7e239c8 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy
@@ -11,7 +11,10 @@ import org.gradle.api.Task
class DetektConfigurator implements Configurator {
private static final String DETEKT_PLUGIN = 'io.gitlab.arturbosch.detekt'
+ private static final String LAST_COMPATIBLE_DETEKT_VERSION = '1.0.0.RC8'
private static final String DETEKT_NOT_APPLIED = 'The Detekt plugin is configured but not applied. Please apply the plugin in your build script.\nFor more information see https://github.com/arturbosch/detekt.'
+ private static final String OUTPUT_NOT_DEFINED = 'Output not defined! To analyze the results, `output` needs to be defined in Detekt profile.'
+ private static final String DETEKT_CONFIGURATION_ERROR = "A problem occurred while configuring Detekt. Please make sure to use a compatible version (All versions up to $LAST_COMPATIBLE_DETEKT_VERSION)"
private final Project project
private final Violations violations
@@ -32,7 +35,7 @@ class DetektConfigurator implements Configurator {
@Override
void execute() {
- project.extensions.findByType(StaticAnalysisExtension).ext.'detekt' = { Closure config ->
+ project.extensions.findByType(StaticAnalysisExtension).ext.detekt = { Closure config ->
if (!isKotlinProject(project)) {
return
}
@@ -41,34 +44,42 @@ class DetektConfigurator implements Configurator {
throw new GradleException(DETEKT_NOT_APPLIED)
}
- project.extensions.findByName('detekt').with {
- // apply configuration closure to detekt extension
- config.delegate = it
- config()
- }
-
- configureToolTask()
+ def detekt = project.extensions.findByName('detekt')
+ config.delegate = detekt
+ config()
+ configureToolTask(detekt)
}
}
- private void configureToolTask() {
+ private void configureToolTask(detekt) {
def detektTask = project.tasks['detektCheck']
- // run detekt as part of check
- project.tasks['check'].dependsOn(detektTask)
+ detektTask.group = 'verification'
// evaluate violations after detekt
- detektTask.group = 'verification'
- def collectViolations = createCollectViolationsTask(violations)
+ def output = resolveOutput(detekt)
+ if (!output) {
+ throw new IllegalArgumentException(OUTPUT_NOT_DEFINED)
+ }
+ def collectViolations = createCollectViolationsTask(violations, project.file(output))
evaluateViolations.dependsOn collectViolations
collectViolations.dependsOn detektTask
}
- private CollectDetektViolationsTask createCollectViolationsTask(Violations violations) {
- def outputFolder = project.file(project.extensions.findByName('detekt').systemOrDefaultProfile().output)
- project.tasks.create('collectDetektViolations', CollectDetektViolationsTask) { collectViolations ->
- collectViolations.xmlReportFile = new File(outputFolder, 'detekt-checkstyle.xml')
- collectViolations.htmlReportFile = new File(outputFolder, 'detekt-report.html')
- collectViolations.violations = violations
+ private static resolveOutput(detekt) {
+ if (detekt.hasProperty('profileStorage')) {
+ detekt.profileStorage.systemOrDefault.output
+ } else if (detekt.respondsTo('systemOrDefaultProfile')) {
+ detekt.systemOrDefaultProfile().output
+ } else {
+ throw new IllegalStateException(DETEKT_CONFIGURATION_ERROR)
+ }
+ }
+
+ private CollectDetektViolationsTask createCollectViolationsTask(Violations violations, File outputFolder) {
+ project.tasks.create('collectDetektViolations', CollectDetektViolationsTask) { task ->
+ task.xmlReportFile = new File(outputFolder, 'detekt-checkstyle.xml')
+ task.htmlReportFile = new File(outputFolder, 'detekt-report.html')
+ task.violations = violations
}
}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy
index 9aed9d9..38c51c8 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy
@@ -58,18 +58,19 @@ class FindbugsConfigurator extends CodeQualityConfigurator androidSourceDirs = variant.sourceSets.collect { it.javaDirectories }.flatten()
task.with {
description = "Run FindBugs analysis for ${variant.name} classes"
source = androidSourceDirs
classpath = variant.javaCompile.classpath
+ exclude '**/*.kt'
}
sourceFilter.applyTo(task)
- task.conventionMapping.map("classes", {
+ task.conventionMapping.map("classes") {
List includes = createIncludePatterns(task.source, androidSourceDirs)
getAndroidClasses(variant, includes)
- })
+ }
task.dependsOn variant.javaCompile
}
@@ -90,6 +91,7 @@ class FindbugsConfigurator extends CodeQualityConfigurator includes = createIncludePatterns(task.source, sourceDirs)
getJavaClasses(sourceSet, includes)
})
+ task.exclude '**/*.kt'
}
}
}
@@ -140,19 +142,19 @@ class FindbugsConfigurator extends CodeQualityConfigurator
- collectViolations.xmlReportFile = findBugs.reports.xml.destination
- collectViolations.violations = violations
- }
+ def task = project.tasks.maybeCreate("collect${findBugs.name.capitalize()}Violations", CollectFindbugsViolationsTask)
+ task.xmlReportFile = findBugs.reports.xml.destination
+ task.violations = violations
+ task
}
private GenerateFindBugsHtmlReport createHtmlReportTask(FindBugs findBugs, File xmlReportFile, File htmlReportFile) {
- project.tasks.create("generate${findBugs.name.capitalize()}HtmlReport", GenerateFindBugsHtmlReport) { generateHtmlReport ->
- generateHtmlReport.xmlReportFile = xmlReportFile
- generateHtmlReport.htmlReportFile = htmlReportFile
- generateHtmlReport.classpath = findBugs.findbugsClasspath
- generateHtmlReport.onlyIf { xmlReportFile?.exists() }
- }
+ def task = project.tasks.maybeCreate("generate${findBugs.name.capitalize()}HtmlReport", GenerateFindBugsHtmlReport)
+ task.xmlReportFile = xmlReportFile
+ task.htmlReportFile = htmlReportFile
+ task.classpath = findBugs.findbugsClasspath
+ task.onlyIf { xmlReportFile?.exists() }
+ task
}
}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintConfigurator.groovy
new file mode 100644
index 0000000..cee82a4
--- /dev/null
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintConfigurator.groovy
@@ -0,0 +1,97 @@
+package com.novoda.staticanalysis.internal.ktlint
+
+import com.novoda.staticanalysis.StaticAnalysisExtension
+import com.novoda.staticanalysis.Violations
+import com.novoda.staticanalysis.internal.Configurator
+import com.novoda.staticanalysis.internal.VariantFilter
+import com.novoda.staticanalysis.internal.checkstyle.CollectCheckstyleViolationsTask
+import org.gradle.api.GradleException
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Project
+import org.gradle.api.Task
+
+class KtlintConfigurator implements Configurator {
+
+ private static final String KTLINT_PLUGIN = 'org.jlleitschuh.gradle.ktlint'
+ private static final String KTLINT_NOT_APPLIED = 'The Ktlint plugin is configured but not applied. Please apply the plugin in your build script.\nFor more information see https://github.com/jeremymailen/kotlinter-gradle'
+
+ private final Project project
+ private final Violations violations
+ private final Task evaluateViolations
+ private final VariantFilter variantFilter
+
+ static KtlintConfigurator create(Project project,
+ NamedDomainObjectContainer violationsContainer,
+ Task evaluateViolations) {
+ Violations violations = violationsContainer.maybeCreate('ktlint')
+ return new KtlintConfigurator(project, violations, evaluateViolations)
+ }
+
+ KtlintConfigurator(Project project, Violations violations, Task evaluateViolations) {
+ this.project = project
+ this.violations = violations
+ this.evaluateViolations = evaluateViolations
+ this.variantFilter = new VariantFilter(project)
+ }
+
+ @Override
+ void execute() {
+ project.extensions.findByType(StaticAnalysisExtension).ext.ktlint = { Closure config ->
+ if (!project.plugins.hasPlugin(KTLINT_PLUGIN)) {
+ throw new GradleException(KTLINT_NOT_APPLIED)
+ }
+
+ def ktlint = project.ktlint
+ ktlint.ignoreFailures = true
+ ktlint.reporters = ['CHECKSTYLE', 'PLAIN']
+ ktlint.ext.includeVariants = { Closure filter ->
+ variantFilter.includeVariantsFilter = filter
+ }
+ config.delegate = ktlint
+ config()
+
+ project.afterEvaluate {
+
+ project.plugins.withId("kotlin") {
+ configureKotlinProject()
+ }
+ project.plugins.withId("kotlin2js") {
+ configureKotlinProject()
+ }
+ project.plugins.withId("kotlin-platform-common") {
+ configureKotlinProject()
+ }
+ project.plugins.withId('com.android.application') {
+ configureAndroidWithVariants(variantFilter.filteredApplicationVariants)
+ }
+ project.plugins.withId('com.android.library') {
+ configureAndroidWithVariants(variantFilter.filteredLibraryVariants)
+ }
+ }
+ }
+ }
+
+ private void configureKotlinProject() {
+ project.sourceSets.each { configureKtlint(it) }
+ }
+
+ private void configureAndroidWithVariants(def mainVariants) {
+ mainVariants.all { configureKtlint(it) }
+ variantFilter.filteredTestVariants.all { configureKtlint(it) }
+ variantFilter.filteredUnitTestVariants.all { configureKtlint(it) }
+ }
+
+ private void configureKtlint(def sourceSet) {
+ def collectViolations = createCollectViolationsTask(violations, sourceSet.name)
+ evaluateViolations.dependsOn collectViolations
+ }
+
+ private def createCollectViolationsTask(Violations violations, def sourceSetName) {
+ project.tasks.create("collectKtlint${sourceSetName.capitalize()}Violations", CollectCheckstyleViolationsTask) { task ->
+ task.xmlReportFile = new File(project.buildDir, "reports/ktlint/ktlint-${sourceSetName}.xml")
+ task.htmlReportFile = new File(project.buildDir, "reports/ktlint/ktlint-${sourceSetName}.txt")
+ task.violations = violations
+ task.dependsOn project.tasks["ktlint${sourceSetName.capitalize()}Check"]
+ }
+ }
+}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTask.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTask.groovy
index ed128e9..aeb0ecb 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTask.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTask.groovy
@@ -9,7 +9,7 @@ class CollectLintViolationsTask extends CollectViolationsTask {
@Override
void collectViolations(File xmlReportFile, File htmlReportFile, Violations violations) {
GPathResult xml = new XmlSlurper().parse(xmlReportFile)
- int errors = xml.'**'.findAll { node -> node.name() == 'issue' && node.@severity == 'Error' }.size()
+ int errors = xml.'**'.findAll { node -> node.name() == 'issue' && (node.@severity == 'Error' || node.@severity == 'Fatal') }.size()
int warnings = xml.'**'.findAll { node -> node.name() == 'issue' && node.@severity == 'Warning' }.size()
violations.addViolations(errors, warnings, htmlReportFile ?: xmlReportFile)
}
diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy
index 612bd00..d47a7eb 100644
--- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy
+++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy
@@ -62,6 +62,7 @@ class PmdConfigurator extends CodeQualityConfigurator {
task.with {
description = "Run PMD analysis for ${sourceSet.name} classes"
source = sourceSet.java.srcDirs
+ exclude '**/*.kt'
}
}
sourceFilter.applyTo(task)
@@ -82,10 +83,10 @@ class PmdConfigurator extends CodeQualityConfigurator {
}
private CollectPmdViolationsTask createViolationsCollectionTask(Pmd pmd, Violations violations) {
- project.tasks.create("collect${pmd.name.capitalize()}Violations", CollectPmdViolationsTask) { collectViolations ->
- collectViolations.xmlReportFile = pmd.reports.xml.destination
- collectViolations.violations = violations
- }
+ def task = project.tasks.maybeCreate("collect${pmd.name.capitalize()}Violations", CollectPmdViolationsTask)
+ task.xmlReportFile = pmd.reports.xml.destination
+ task.violations = violations
+ task
}
}
diff --git a/plugin/src/test/fixtures/reports/lint/lint-results.xml b/plugin/src/test/fixtures/reports/lint/lint-results.xml
index f6e1641..ad46cb3 100644
--- a/plugin/src/test/fixtures/reports/lint/lint-results.xml
+++ b/plugin/src/test/fixtures/reports/lint/lint-results.xml
@@ -1,6 +1,9 @@
+
+
rules() {
- return [TestProjectRule.forKotlinProject(), TestProjectRule.forAndroidKotlinProject()]
+ private static final String DETEKT_NOT_APPLIED = 'The Detekt plugin is configured but not applied. Please apply the plugin in your build script.'
+ private static final String OUTPUT_NOT_DEFINED = 'Output not defined! To analyze the results, `output` needs to be defined in Detekt profile.'
+
+ @Parameterized.Parameters(name = "{0} with Detekt: {1}")
+ static Iterable rules() {
+ return [
+ [TestProjectRule.forKotlinProject(), "1.0.0.RC6-2"],
+ [TestProjectRule.forAndroidKotlinProject(), "1.0.0.RC6-2"],
+ [TestProjectRule.forKotlinProject(), "1.0.0.RC8"],
+ [TestProjectRule.forAndroidKotlinProject(), "1.0.0.RC8"],
+ ]*.toArray()
}
@Rule
public final TestProjectRule projectRule
+ private final String detektVersion
- DetektIntegrationTest(TestProjectRule projectRule) {
+ DetektIntegrationTest(TestProjectRule projectRule, String detektVersion) {
this.projectRule = projectRule
+ this.detektVersion = detektVersion
+ }
+
+ @Test
+ void shouldFailBuildOnConfigurationWhenNoOutputNotDefined() {
+ def emptyConfiguration = detektWith("")
+
+ def result = createProjectWithZeroThreshold(Fixtures.Detekt.SOURCES_WITH_WARNINGS)
+ .withToolsConfig(emptyConfiguration)
+ .buildAndFail('check')
+
+ assertThat(result.logs).contains(OUTPUT_NOT_DEFINED)
+ }
+
+ @Test
+ void shouldFailBuildOnConfigurationWhenDetektConfiguredButNotApplied() {
+ def result = projectRule.newProject()
+ .withToolsConfig(detektConfiguration(Fixtures.Detekt.SOURCES_WITH_ERRORS))
+ .buildAndFail('check')
+
+ assertThat(result.logs).contains(DETEKT_NOT_APPLIED)
}
@Test
@@ -74,12 +104,12 @@ class DetektIntegrationTest {
@Test
void shouldNotFailBuildWhenNoDetektWarningsOrErrorsEncounteredAndNoThresholdTrespassed() {
def testProject = projectRule.newProject()
- .withPlugin("io.gitlab.arturbosch.detekt", "1.0.0.RC6-2")
+ .withPlugin("io.gitlab.arturbosch.detekt", detektVersion)
.withPenalty('''{
maxWarnings = 0
maxErrors = 0
}''')
- testProject = testProject.withToolsConfig(detektConfigurationWithoutInput(testProject))
+ .withToolsConfig(detektConfigurationWithoutInput())
TestProject.Result result = testProject
.build('check')
@@ -88,41 +118,24 @@ class DetektIntegrationTest {
assertThat(result.logs).doesNotContainDetektViolations()
}
- @Test
- void shouldFailBuildWhenDetektConfiguredButNotApplied() {
- def testProject = projectRule.newProject()
- .withPenalty('''{
- maxWarnings = 0
- maxErrors = 0
- }''')
-
- testProject = testProject.withToolsConfig(detektConfiguration(testProject, Fixtures.Detekt.SOURCES_WITH_ERRORS))
-
- TestProject.Result result = testProject
- .buildAndFail('check')
-
- assertThat(result.logs).containsDetektNotApplied()
- }
-
private TestProject createProjectWithZeroThreshold(File sources) {
createProjectWith(sources)
}
private TestProject createProjectWith(File sources, int maxWarnings = 0, int maxErrors = 0) {
- def testProject = projectRule.newProject()
- .withPlugin("io.gitlab.arturbosch.detekt", "1.0.0.RC6-2")
+ projectRule.newProject()
+ .withPlugin("io.gitlab.arturbosch.detekt", detektVersion)
.withSourceSet('main', sources)
.withPenalty("""{
maxWarnings = ${maxWarnings}
maxErrors = ${maxErrors}
}""")
-
- testProject.withToolsConfig(detektConfiguration(testProject, sources))
+ .withToolsConfig(detektConfiguration(sources))
}
private TestProject createProjectWithoutDetekt() {
projectRule.newProject()
- .withPlugin("io.gitlab.arturbosch.detekt", "1.0.0.RC6-2")
+ .withPlugin("io.gitlab.arturbosch.detekt", detektVersion)
.withSourceSet('main', Fixtures.Detekt.SOURCES_WITH_WARNINGS)
.withPenalty('''{
maxWarnings = 0
@@ -130,27 +143,29 @@ class DetektIntegrationTest {
}''')
}
- private static GString detektConfiguration(TestProject testProject, File input) {
+ private static String detektConfiguration(File input) {
+ detektWith """
+ config = '${Fixtures.Detekt.RULES}'
+ output = "\$buildDir/reports"
+ // The input just needs to be configured for the tests.
+ // Probably detekt doesn't pick up the changed source sets.
+ // In a example project it was not needed.
+ input = "${input}"
"""
- detekt {
- profile('main') {
- config = "${Fixtures.Detekt.RULES}"
- output = "${testProject.projectDir()}/build/reports"
- // The input just needs to be configured for the tests.
- // Probably detekt doesn't pick up the changed source sets.
- // In a example project it was not needed.
- input = "${input}"
- }
- }
+ }
+
+ private static String detektConfigurationWithoutInput() {
+ detektWith """
+ config = '${Fixtures.Detekt.RULES}'
+ output = "\$buildDir/reports"
"""
}
- private static GString detektConfigurationWithoutInput(TestProject testProject) {
+ private static String detektWith(String mainProfile) {
"""
detekt {
profile('main') {
- config = "${Fixtures.Detekt.RULES}"
- output = "${testProject.projectDir()}/build/reports"
+ ${mainProfile.stripIndent()}
}
}
"""
diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy
index ae6dba1..19d96a1 100644
--- a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy
+++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy
@@ -345,6 +345,20 @@ class FindbugsIntegrationTest {
Truth.assertThat(result.outcome(':checkFindbugsClasses')).isEqualTo(TaskOutcome.SUCCESS)
}
+ @Test
+ void shouldNotFailBuildWhenFindbugsIsConfiguredMultipleTimes() {
+ projectRule.newProject()
+ .withSourceSet('main', SOURCES_WITH_LOW_VIOLATION)
+ .withPenalty('none')
+ .withToolsConfig("""
+ findbugs { }
+ findbugs {
+ ignoreFailures = false
+ }
+ """)
+ .build('check')
+ }
+
/**
* The custom task created in the snippet below will check whether {@code Findbugs} tasks with
* empty {@code source} will have empty {@code classes} too.
diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintIntegrationTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintIntegrationTest.groovy
new file mode 100644
index 0000000..e259bfe
--- /dev/null
+++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/ktlint/KtlintIntegrationTest.groovy
@@ -0,0 +1,98 @@
+package com.novoda.staticanalysis.internal.ktlint
+
+import com.novoda.test.Fixtures
+import com.novoda.test.TestProject
+import com.novoda.test.TestProjectRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+import static com.novoda.test.LogsSubject.assertThat
+
+@RunWith(Parameterized.class)
+class KtlintIntegrationTest {
+
+ private static final String KTLINT_NOT_APPLIED = 'The Ktlint plugin is configured but not applied. Please apply the plugin in your build script.'
+ public static final String DEFAULT_CONFIG = '''
+ ktlint {
+ includeVariants { it.name == "debug" }
+ }
+ '''
+ public static final String EMPTY_CONFIG = 'ktlint {}'
+
+ @Parameterized.Parameters(name = '{0}')
+ static def rules() {
+ return [
+ [TestProjectRule.forKotlinProject(), 'main'].toArray(),
+ [TestProjectRule.forAndroidKotlinProject(), 'debug'].toArray()
+ ]
+ }
+
+ @Rule
+ public final TestProjectRule projectRule
+ private final String sourceSetName;
+
+ KtlintIntegrationTest(TestProjectRule projectRule, String sourceSetName) {
+ this.projectRule = projectRule
+ this.sourceSetName = sourceSetName
+ }
+
+ @Test
+ void shouldNotFailWhenKtlintIsNotConfigured() {
+ def result = createProjectWith(Fixtures.Ktlint.SOURCES_WITH_ERROR)
+ .build('evaluateViolations')
+
+ assertThat(result.logs).doesNotContainKtlintViolations()
+ }
+
+ @Test
+ void shouldFailBuildOnConfigurationWhenKtlintConfiguredButNotApplied() {
+ def result = projectRule.newProject()
+ .withToolsConfig(EMPTY_CONFIG)
+ .buildAndFail('evaluateViolations')
+
+ assertThat(result.logs).contains(KTLINT_NOT_APPLIED)
+ }
+
+ @Test
+ void shouldFailBuildWhenKtlintErrorsOverTheThreshold() {
+ def result = createProjectWith(Fixtures.Ktlint.SOURCES_WITH_ERROR)
+ .withToolsConfig(DEFAULT_CONFIG)
+ .buildAndFail('evaluateViolations')
+
+ assertThat(result.logs).containsLimitExceeded(1, 0)
+ assertThat(result.logs).containsKtlintViolations(1,
+ result.buildFileUrl("reports/ktlint/ktlint-${sourceSetName}.txt"))
+ }
+
+ @Test
+ void shouldNotFailWhenErrorsAreWithinThreshold() {
+ def result = createProjectWith(Fixtures.Ktlint.SOURCES_WITH_ERROR, 1)
+ .withToolsConfig(DEFAULT_CONFIG)
+ .build('evaluateViolations')
+
+ assertThat(result.logs).containsKtlintViolations(1,
+ result.buildFileUrl("reports/ktlint/ktlint-${sourceSetName}.txt"))
+ }
+
+ @Test
+ void shouldNotFailBuildWhenNoErrorsEncounteredAndNoThresholdTrespassed() {
+ def result = createProjectWith(Fixtures.Ktlint.SOURCES_NO_ERROR, 0)
+ .withToolsConfig(EMPTY_CONFIG)
+ .build('evaluateViolations')
+
+ assertThat(result.logs).doesNotContainLimitExceeded()
+ assertThat(result.logs).doesNotContainKtlintViolations()
+ }
+
+ private TestProject createProjectWith(File sources, int maxErrors = 0) {
+ projectRule.newProject()
+ .withPlugin('org.jlleitschuh.gradle.ktlint', '4.1.0')
+ .withSourceSet('main', sources)
+ .withPenalty("""{
+ maxWarnings = 0
+ maxErrors = ${maxErrors}
+ }""")
+ }
+}
diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTaskTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTaskTest.groovy
index 3e1f7a4..bd86437 100644
--- a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTaskTest.groovy
+++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/lint/CollectLintViolationsTaskTest.groovy
@@ -18,7 +18,7 @@ class CollectLintViolationsTaskTest {
Violations violations = new Violations('Android Lint')
task.collectViolations(Fixtures.Lint.SAMPLE_REPORT, null, violations)
- assertThat(violations.getErrors()).isEqualTo(1)
+ assertThat(violations.getErrors()).isEqualTo(2)
assertThat(violations.getWarnings()).isEqualTo(1)
}
}
diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/pmd/PmdIntegrationTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/pmd/PmdIntegrationTest.groovy
index 8ce3824..41771c5 100644
--- a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/pmd/PmdIntegrationTest.groovy
+++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/pmd/PmdIntegrationTest.groovy
@@ -213,6 +213,22 @@ public class PmdIntegrationTest {
assertThat(result.logs).doesNotContainPmdViolations()
}
+ @Test
+ void shouldNotFailBuildWhenPmdIsConfiguredMultipleTimes() {
+ projectRule.newProject()
+ .withSourceSet('main', Fixtures.Pmd.SOURCES_WITH_PRIORITY_1_VIOLATION)
+ .withPenalty('none')
+ .withToolsConfig("""
+ pmd {
+ ruleSetFiles = $DEFAULT_RULES
+ }
+ pmd {
+ ignoreFailures = false
+ }
+ """)
+ .build('check')
+ }
+
private String pmd(String rules, String... configs) {
"""pmd {
ruleSetFiles = $rules
diff --git a/plugin/src/test/groovy/com/novoda/test/Fixtures.groovy b/plugin/src/test/groovy/com/novoda/test/Fixtures.groovy
index a583ed6..9c17b14 100644
--- a/plugin/src/test/groovy/com/novoda/test/Fixtures.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/Fixtures.groovy
@@ -50,6 +50,11 @@ public final class Fixtures {
public static final File RULES = new File(RULES_DIR, 'detekt/detekt.yml')
}
+ final static class Ktlint {
+ static final File SOURCES_WITH_ERROR = new File(SOURCES_DIR, 'ktlint/with-error')
+ static final File SOURCES_NO_ERROR = new File(SOURCES_DIR, 'ktlint/no-error')
+ }
+
final static class Lint {
public static final File SOURCES_WITH_WARNINGS = new File(SOURCES_DIR, 'lint/warnings')
public static final File SOURCES_WITH_ERRORS = new File(SOURCES_DIR, 'lint/errors')
diff --git a/plugin/src/test/groovy/com/novoda/test/LogsSubject.groovy b/plugin/src/test/groovy/com/novoda/test/LogsSubject.groovy
index 1abeb75..35fb1e9 100644
--- a/plugin/src/test/groovy/com/novoda/test/LogsSubject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/LogsSubject.groovy
@@ -14,11 +14,11 @@ import static com.novoda.test.TestProject.Result.Logs;
class LogsSubject extends Subject {
private static final String VIOLATIONS_LIMIT_EXCEEDED = 'Violations limit exceeded'
- private static final String DETEKT_NOT_APPLIED = 'The Detekt plugin is configured but not applied. Please apply the plugin in your build script.'
private static final String CHECKSTYLE_VIOLATIONS_FOUND = 'Checkstyle violations found'
private static final String PMD_VIOLATIONS_FOUND = 'PMD violations found'
private static final String FINDBUGS_VIOLATIONS_FOUND = 'Findbugs violations found'
private static final String DETEKT_VIOLATIONS_FOUND = 'Detekt violations found'
+ private static final String KTLINT_VIOLATIONS_FOUND = 'ktlint violations found'
private static final String LINT_VIOLATIONS_FOUND = 'Lint violations found'
private static final SubjectFactory FACTORY = new SubjectFactory() {
@@ -44,8 +44,8 @@ class LogsSubject extends Subject {
check().that(actual().output)
}
- public void containsDetektNotApplied() {
- outputSubject.contains(DETEKT_NOT_APPLIED)
+ public void contains(log) {
+ outputSubject.contains(log)
}
public void doesNotContainLimitExceeded() {
@@ -72,6 +72,10 @@ class LogsSubject extends Subject {
outputSubject.doesNotContain(DETEKT_VIOLATIONS_FOUND)
}
+ public void doesNotContainKtlintViolations() {
+ outputSubject.doesNotContain(KTLINT_VIOLATIONS_FOUND)
+ }
+
public void doesNotContainLintViolations() {
outputSubject.doesNotContain(LINT_VIOLATIONS_FOUND)
}
@@ -92,6 +96,10 @@ class LogsSubject extends Subject {
containsToolViolations(DETEKT_VIOLATIONS_FOUND, errors, warnings, reportUrls)
}
+ public void containsKtlintViolations(int errors, String... reportUrls) {
+ containsToolViolations(KTLINT_VIOLATIONS_FOUND, errors, 0, reportUrls)
+ }
+
public void containsLintViolations(int errors, int warnings, String... reportUrls) {
containsToolViolations(LINT_VIOLATIONS_FOUND, errors, warnings, reportUrls)
}
diff --git a/plugin/src/test/groovy/com/novoda/test/TestAndroidKotlinProject.groovy b/plugin/src/test/groovy/com/novoda/test/TestAndroidKotlinProject.groovy
index eb9ffaf..3fceefc 100644
--- a/plugin/src/test/groovy/com/novoda/test/TestAndroidKotlinProject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/TestAndroidKotlinProject.groovy
@@ -14,7 +14,8 @@ buildscript {
}
}
plugins {
- ${formatPlugins(project)}
+ ${formatPlugins(project)}
+ id 'com.novoda.static-analysis'
}
repositories {
google()
@@ -58,7 +59,7 @@ ${formatExtension(project)}
.collect { Map.Entry> entry ->
"""$entry.key {
manifest.srcFile '${Fixtures.ANDROID_MANIFEST}'
- kotlin {
+ java {
${entry.value.collect { "srcDir '$it'" }.join('\n\t\t\t\t')}
}
}"""
diff --git a/plugin/src/test/groovy/com/novoda/test/TestAndroidProject.groovy b/plugin/src/test/groovy/com/novoda/test/TestAndroidProject.groovy
index 2c90a82..f8d9eda 100644
--- a/plugin/src/test/groovy/com/novoda/test/TestAndroidProject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/TestAndroidProject.groovy
@@ -14,6 +14,7 @@ buildscript {
}
plugins {
${formatPlugins(project)}
+ id 'com.novoda.static-analysis'
}
repositories {
google()
diff --git a/plugin/src/test/groovy/com/novoda/test/TestJavaProject.groovy b/plugin/src/test/groovy/com/novoda/test/TestJavaProject.groovy
index e014658..ac01e0a 100644
--- a/plugin/src/test/groovy/com/novoda/test/TestJavaProject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/TestJavaProject.groovy
@@ -5,7 +5,8 @@ final class TestJavaProject extends TestProject {
private static final Closure TEMPLATE = { TestProject project ->
"""
plugins {
- ${formatPlugins(project)}
+ ${formatPlugins(project)}
+ id 'com.novoda.static-analysis'
}
repositories {
jcenter()
diff --git a/plugin/src/test/groovy/com/novoda/test/TestKotlinProject.groovy b/plugin/src/test/groovy/com/novoda/test/TestKotlinProject.groovy
index 89f80c0..2debb58 100644
--- a/plugin/src/test/groovy/com/novoda/test/TestKotlinProject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/TestKotlinProject.groovy
@@ -15,9 +15,15 @@ buildscript {
plugins {
${formatPlugins(project)}
+ id 'com.novoda.static-analysis'
}
apply plugin: 'kotlin'
+
+repositories {
+ jcenter()
+}
+
sourceSets {
${formatSourceSets(project)}
}
diff --git a/plugin/src/test/groovy/com/novoda/test/TestProject.groovy b/plugin/src/test/groovy/com/novoda/test/TestProject.groovy
index 0b53755..c8e7efd 100644
--- a/plugin/src/test/groovy/com/novoda/test/TestProject.groovy
+++ b/plugin/src/test/groovy/com/novoda/test/TestProject.groovy
@@ -34,7 +34,6 @@ ${project.additionalConfiguration}
.withPluginClasspath()
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.out))
- withPlugin('com.novoda.static-analysis')
}
private static File createProjectDir(String path) {
@@ -126,7 +125,7 @@ ${project.additionalConfiguration}
}
protected static String formatPlugins(TestProject project) {
- "${project.plugins.join('\n')}"
+ project.plugins.join('\n')
}
public static class Result {
diff --git a/sample-multi-module/build.gradle b/sample-multi-module/build.gradle
index 5493a8a..f2a0dee 100644
--- a/sample-multi-module/build.gradle
+++ b/sample-multi-module/build.gradle
@@ -1,21 +1,22 @@
buildscript {
- ext.kotlin_version = '1.2.30'
+ ext.kotlin_version = '1.2.60'
repositories {
google()
- maven { url "https://plugins.gradle.org/m2/" }
+ gradlePluginPortal()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
- classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0.RC6-3"
+ classpath 'com.android.tools.build:gradle:3.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0.RC8'
+ classpath 'gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:5.0.0'
}
}
-apply from: "$rootDir/team-props/tasks.gradle"
+apply from: rootProject.file('team-props/tasks.gradle')
-subprojects { subproject ->
+subprojects {
buildscript {
repositories {
@@ -26,5 +27,6 @@ subprojects { subproject ->
repositories {
google()
mavenCentral()
+ jcenter()
}
}
diff --git a/sample-multi-module/gradle/wrapper/gradle-wrapper.jar b/sample-multi-module/gradle/wrapper/gradle-wrapper.jar
index 7a3265e..f6b961f 100644
Binary files a/sample-multi-module/gradle/wrapper/gradle-wrapper.jar and b/sample-multi-module/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/sample-multi-module/gradle/wrapper/gradle-wrapper.properties b/sample-multi-module/gradle/wrapper/gradle-wrapper.properties
index f5737a2..7dc503f 100644
--- a/sample-multi-module/gradle/wrapper/gradle-wrapper.properties
+++ b/sample-multi-module/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Mon Mar 12 18:00:46 GMT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/sample-multi-module/team-props/detekt-config.yml b/sample-multi-module/team-props/detekt-config.yml
index cf14ee4..9059243 100644
--- a/sample-multi-module/team-props/detekt-config.yml
+++ b/sample-multi-module/team-props/detekt-config.yml
@@ -1,5 +1,4 @@
autoCorrect: true
-failFast: false
test-pattern:
active: true
@@ -19,16 +18,6 @@ test-pattern:
- 'SpreadOperator'
- 'TooManyFunctions'
-build:
- warningThreshold: 5
- failThreshold: 500
- maxIssues: 500
- weights:
- complexity: 2
- LongParameterList: 1
- style: 1
- comments: 1
-
processors:
active: true
diff --git a/sample-multi-module/team-props/findbugs-excludes.xml b/sample-multi-module/team-props/findbugs-excludes.xml
index b0bf1a4..a04c37f 100644
--- a/sample-multi-module/team-props/findbugs-excludes.xml
+++ b/sample-multi-module/team-props/findbugs-excludes.xml
@@ -9,8 +9,4 @@
-
-
-
-
diff --git a/sample-multi-module/team-props/pmd-rules.xml b/sample-multi-module/team-props/pmd-rules.xml
index a74ad7e..0e93fbf 100644
--- a/sample-multi-module/team-props/pmd-rules.xml
+++ b/sample-multi-module/team-props/pmd-rules.xml
@@ -5,7 +5,6 @@
Mandatory PMD rules description.
- .*\.kt
.*\.R\$.*
diff --git a/sample-multi-module/team-props/static-analysis.gradle b/sample-multi-module/team-props/static-analysis.gradle
index ec8f1cd..05279c1 100644
--- a/sample-multi-module/team-props/static-analysis.gradle
+++ b/sample-multi-module/team-props/static-analysis.gradle
@@ -1,5 +1,6 @@
apply plugin: com.novoda.staticanalysis.StaticAnalysisPlugin
apply plugin: 'io.gitlab.arturbosch.detekt'
+apply plugin: "org.jlleitschuh.gradle.ktlint"
staticAnalysis {
@@ -8,7 +9,6 @@ staticAnalysis {
checkstyle {
toolVersion '8.8'
exclude project.fileTree('src/test/java')
- exclude '**/*.kt'
configFile rootProject.file('team-props/checkstyle-modules.xml')
includeVariants { variant -> variant.name.contains('debug') }
}
@@ -41,4 +41,8 @@ staticAnalysis {
output = project.file("build/reports/detekt")
}
}
+
+ ktlint {
+ includeVariants { variant -> variant.name.contains('debug') }
+ }
}
diff --git a/sample/app/build.gradle b/sample/app/build.gradle
index 48794e8..53f2243 100755
--- a/sample/app/build.gradle
+++ b/sample/app/build.gradle
@@ -1,5 +1,6 @@
plugins {
- id 'io.gitlab.arturbosch.detekt' version '1.0.0.RC6-3'
+ id 'io.gitlab.arturbosch.detekt' version '1.0.0.RC8'
+ id "org.jlleitschuh.gradle.ktlint" version "5.0.0"
}
apply plugin: 'com.android.application'
@@ -9,7 +10,6 @@ apply plugin: com.novoda.staticanalysis.StaticAnalysisPlugin
android {
compileSdkVersion 27
- buildToolsVersion '27.0.3'
defaultConfig {
applicationId 'com.novoda.staticanalysis.sample'
@@ -30,7 +30,7 @@ android {
}
dependencies {
- implementation 'com.android.support:appcompat-v7:27.1.0'
+ implementation 'com.android.support:appcompat-v7:27.1.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
}
@@ -42,7 +42,6 @@ staticAnalysis {
checkstyle {
toolVersion '8.8'
exclude project.fileTree('src/test/java')
- exclude '**/*.kt'
configFile rootProject.file('team-props/checkstyle-modules.xml')
includeVariants { variant -> variant.name.contains('debug') }
}
@@ -76,4 +75,8 @@ staticAnalysis {
}
}
+ ktlint {
+ android true
+ includeVariants { variant -> variant.name.contains('debug') }
+ }
}
diff --git a/sample/build.gradle b/sample/build.gradle
index 0500e1e..c63946c 100755
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,13 +1,13 @@
buildscript {
- ext.kotlin_version = '1.2.30'
+ ext.kotlin_version = '1.2.60'
repositories {
google()
- maven { url "https://plugins.gradle.org/m2/" }
+ gradlePluginPortal()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath 'com.android.tools.build:gradle:3.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -16,5 +16,6 @@ subprojects {
repositories {
google()
mavenCentral()
+ jcenter()
}
}
diff --git a/sample/gradle/wrapper/gradle-wrapper.jar b/sample/gradle/wrapper/gradle-wrapper.jar
index 7a3265e..a5fe1cb 100755
Binary files a/sample/gradle/wrapper/gradle-wrapper.jar and b/sample/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/sample/gradle/wrapper/gradle-wrapper.properties b/sample/gradle/wrapper/gradle-wrapper.properties
index 4bfa923..bd24854 100755
--- a/sample/gradle/wrapper/gradle-wrapper.properties
+++ b/sample/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Mon Mar 12 18:00:46 GMT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
diff --git a/sample/team-props/detekt-config.yml b/sample/team-props/detekt-config.yml
index 0f8d6ff..9059243 100755
--- a/sample/team-props/detekt-config.yml
+++ b/sample/team-props/detekt-config.yml
@@ -1,5 +1,4 @@
autoCorrect: true
-failFast: false
test-pattern:
active: true
@@ -19,16 +18,6 @@ test-pattern:
- 'SpreadOperator'
- 'TooManyFunctions'
-build:
- warningThreshold: 5
- failThreshold: 10
- maxIssues: 10
- weights:
- complexity: 2
- LongParameterList: 1
- style: 1
- comments: 1
-
processors:
active: true
diff --git a/sample/team-props/findbugs-excludes.xml b/sample/team-props/findbugs-excludes.xml
index b0bf1a4..a04c37f 100755
--- a/sample/team-props/findbugs-excludes.xml
+++ b/sample/team-props/findbugs-excludes.xml
@@ -9,8 +9,4 @@
-
-
-
-
diff --git a/sample/team-props/pmd-rules.xml b/sample/team-props/pmd-rules.xml
index a74ad7e..0e93fbf 100755
--- a/sample/team-props/pmd-rules.xml
+++ b/sample/team-props/pmd-rules.xml
@@ -5,7 +5,6 @@
Mandatory PMD rules description.
- .*\.kt
.*\.R\$.*