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

Gradle plugin: use PatternSet instead of FileCollection for inputs #1118

Merged
merged 11 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
9 changes: 8 additions & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ jobs:
path: diktat-ruleset/target/diktat-*.jar
# no need to store artifact longer, it is used only by dependant jobs
retention-days: 1
- name: Upload gradle reports
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: gradle-test-report-${{ runner.os }}
path: 'diktat-gradle-plugin/build/reports/'
retention-days: 1

run_diktat_from_CLI:
name: Run diktat via CLI
Expand Down Expand Up @@ -110,7 +117,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a duplicated execution on ubuntu: main job (build and test and upload codecov) is basically the same, but also with codecov. So there is no need for a separate build_and_test on ubuntu

os: [ windows-latest, macos-latest ]
steps:
- uses: actions/[email protected]
- name: Set up JDK 11
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
![deteKT static analysis](https://github.com/cqfn/diKTat/workflows/Run%20deteKT/badge.svg)
![diKTat code style](https://github.com/cqfn/diKTat/workflows/Run%20diKTat%20from%20release%20version/badge.svg?branch=master)
[![License](https://img.shields.io/github/license/cqfn/diKtat)](https://github.com/cqfn/diKTat/blob/master/LICENSE)
[![codecov](https://codecov.io/gh/cqfn/diKTat/branch/master/graph/badge.svg)](https://codecov.io/gh/cqfn/diKTat)
[![codecov](https://codecov.io/gh/diktat-static-analysis/diKTat/branch/master/graph/badge.svg)](https://codecov.io/gh/diktat-static-analysis/diKTat)

[![Releases](https://img.shields.io/github/v/release/cqfn/diKTat)](https://github.com/cqfn/diKTat/releases)
[![Maven Central](https://img.shields.io/maven-central/v/org.cqfn.diktat/diktat-rules)](https://mvnrepository.com/artifact/org.cqfn.diktat)
Expand Down Expand Up @@ -133,9 +133,11 @@ apply(plugin = "org.cqfn.diktat.diktat-gradle-plugin")
You can then configure diktat using `diktat` extension:
```kotlin
diktat {
inputs = files("src/**/*.kt") // file collection that will be checked by diktat
inputs {
include("src/**/*.kt") // path matching this pattern (per PatternFilterable) that will be checked by diktat
exclude("src/test/kotlin/excluded/**") // path matching this pattern will not be checked by diktat
}
debug = true // turn on debug logging
excludes = files("src/test/kotlin/excluded") // these files will not be checked by diktat
}
```

Expand Down
21 changes: 9 additions & 12 deletions diktat-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ repositories {
// default value is needed for correct gradle loading in IDEA; actual value from maven is used during build
val ktlintVersion = project.properties.getOrDefault("ktlintVersion", "0.43.0") as String
val diktatVersion = project.version.takeIf { it.toString() != Project.DEFAULT_VERSION } ?: "0.5.2"
val junitVersion = project.properties.getOrDefault("junitVersion", "5.7.0") as String
val junitVersion = project.properties.getOrDefault("junitVersion", "5.8.1") as String
val jacocoVersion = project.properties.getOrDefault("jacocoVersion", "0.8.7") as String
dependencies {
implementation(kotlin("gradle-plugin-api"))
Expand All @@ -40,6 +40,7 @@ dependencies {
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7")
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
exclude("org.jetbrains.kotlin", "kotlin-stdlib-common")
exclude("org.slf4j", "slf4j-log4j12")
}

testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
Expand All @@ -49,6 +50,8 @@ dependencies {
val generateVersionsFile by tasks.registering {
val versionsFile = File("$buildDir/generated/src/generated/Versions.kt")

inputs.property("diktat version", diktatVersion)
inputs.property("ktlint version", ktlintVersion)
outputs.file(versionsFile)

doFirst {
Expand All @@ -72,7 +75,6 @@ tasks.withType<KotlinCompile> {
languageVersion = "1.3"
apiVersion = "1.3"
jvmTarget = "1.8"
useIR = false // for compatibility with older gradle
}

dependsOn.add(generateVersionsFile)
Expand All @@ -93,7 +95,6 @@ java {

// === testing & code coverage, jacoco is run independent from maven
val functionalTestTask by tasks.register<Test>("functionalTest")
val jacocoMergeTask by tasks.register<JacocoMerge>("jacocoMerge")
tasks.withType<Test> {
useJUnitPlatform()
}
Expand All @@ -102,7 +103,7 @@ jacoco.toolVersion = jacocoVersion
// === integration testing
// fixme: should probably use KotlinSourceSet instead
val functionalTest = sourceSets.create("functionalTest") {
compileClasspath += sourceSets.main.get().output + configurations.testRuntimeClasspath
compileClasspath += sourceSets.main.get().output + configurations.testRuntimeClasspath.get()
runtimeClasspath += output + compileClasspath
}
tasks.getByName<Test>("functionalTest") {
Expand All @@ -123,25 +124,21 @@ tasks.getByName<Test>("functionalTest") {
Thread.sleep(5_000)
}
}
finalizedBy(jacocoMergeTask)
finalizedBy(tasks.jacocoTestReport)
}
tasks.check { dependsOn(tasks.jacocoTestReport) }
jacocoTestKit {
applyTo("functionalTestRuntimeOnly", tasks.named("functionalTest"))
}
tasks.getByName("jacocoMerge", JacocoMerge::class) {
dependsOn(functionalTestTask)
tasks.jacocoTestReport {
dependsOn(tasks.withType<Test>())
executionData(
fileTree("$buildDir/jacoco").apply {
include("*.exec")
}
)
}
tasks.jacocoTestReport {
dependsOn(jacocoMergeTask)
executionData("$buildDir/jacoco/jacocoMerge.exec")
reports {
// xml report is used by codecov
xml.isEnabled = true
xml.required.set(true)
}
}
2 changes: 1 addition & 1 deletion diktat-gradle-plugin/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ pluginManagement {
mavenCentral()
gradlePluginPortal()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS
import java.io.File

class DiktatGradlePluginFunctionalTest {
Expand Down Expand Up @@ -46,7 +44,7 @@ class DiktatGradlePluginFunctionalTest {
buildFile.appendText(
"""${System.lineSeparator()}
diktat {
inputs = files("src/**/*.kt")
inputs { include("src/**/*.kt") }
reporterType = "json"
output = "test.txt"
}
Expand All @@ -69,7 +67,7 @@ class DiktatGradlePluginFunctionalTest {
buildFile.appendText(
"""${System.lineSeparator()}
diktat {
inputs = files("src/**/*.kt")
inputs { include("src/**/*.kt") }
}
""".trimIndent()
)
Expand All @@ -88,61 +86,37 @@ class DiktatGradlePluginFunctionalTest {
buildFile.appendText(
"""${System.lineSeparator()}
diktat {
inputs = files("src/**/*.kt")
excludes = files("src/**/Test.kt")
inputs {
include("src/**/*.kt")
exclude("src/**/Test.kt")
}
}
""".trimIndent()
)
val result = runDiktat(testProjectDir)

val diktatCheckBuildResult = result.task(":$DIKTAT_CHECK_TASK")
requireNotNull(diktatCheckBuildResult)
Assertions.assertEquals(TaskOutcome.SUCCESS, diktatCheckBuildResult.outcome)
Assertions.assertEquals(TaskOutcome.NO_SOURCE, diktatCheckBuildResult.outcome)
}

@Test
fun `should not run diktat with ktlint's default includes when no files match include patterns`() {
buildFile.appendText(
"""${System.lineSeparator()}
diktat {
inputs = files("nonexistent-directory/src/**/*.kt")
inputs { include ("nonexistent-directory/src/**/*.kt") }
}
""".trimIndent()
)
val result = runDiktat(testProjectDir, arguments = listOf("--info"))

// if patterns in gradle are not checked for matching, they are passed to ktlint, which does nothing
val diktatCheckBuildResult = result.task(":$DIKTAT_CHECK_TASK")
requireNotNull(diktatCheckBuildResult)
Assertions.assertEquals(TaskOutcome.SUCCESS, diktatCheckBuildResult.outcome)
Assertions.assertEquals(TaskOutcome.NO_SOURCE, diktatCheckBuildResult.outcome)
Assertions.assertFalse(
result.output.contains("Skipping diktat execution")
)
Assertions.assertFalse(
result.output.contains("Inputs for $DIKTAT_CHECK_TASK do not exist, will not run diktat")
)
}

@Test
fun `should not run diktat with ktlint's default includes when no files match include patterns - 2`() {
buildFile.appendText(
"""${System.lineSeparator()}
diktat {
inputs = fileTree("nonexistent-directory/src").apply { include("**/*.kt") }
}
""".trimIndent()
)
val result = runDiktat(testProjectDir, arguments = listOf("--info"))

val diktatCheckBuildResult = result.task(":$DIKTAT_CHECK_TASK")
requireNotNull(diktatCheckBuildResult)
Assertions.assertEquals(TaskOutcome.SUCCESS, diktatCheckBuildResult.outcome)
Assertions.assertTrue(
result.output.contains("Skipping diktat execution")
)
Assertions.assertTrue(
result.output.contains("Inputs for $DIKTAT_CHECK_TASK do not exist, will not run diktat")
)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class DiktatGradlePluginMultiprojectFunctionalTest {

val diktatCheckRootResult = result.task(":$DIKTAT_CHECK_TASK")
requireNotNull(diktatCheckRootResult)
Assertions.assertEquals(TaskOutcome.SUCCESS, diktatCheckRootResult.outcome) {
Assertions.assertEquals(TaskOutcome.NO_SOURCE, diktatCheckRootResult.outcome) {
"Task for root project with empty sources should succeed"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package org.cqfn.diktat.plugin.gradle

import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.util.PatternFilterable
import org.gradle.api.tasks.util.PatternSet
import java.io.File

/**
* An extension to configure diktat in build.gradle(.kts) file
*/
open class DiktatExtension {
open class DiktatExtension(
private val patternSet: PatternSet
) {
/**
* Boolean flag to support `ignoreFailures` property of [VerificationTask].
*/
Expand All @@ -29,18 +36,31 @@ open class DiktatExtension {
var output: String = ""

/**
* Path to diktat yml config file. Can be either absolute or relative to project's root directory.
* Default value: `diktat-analysis.yml` in rootDir.
* Paths that will be excluded from diktat run
*/
lateinit var diktatConfigFile: File
@Deprecated("Configuration via inputs/excludes is unsupported, use inputs(Action)")
var excludes: FileCollection? = null

/**
* Paths that will be excluded from diktat run
* Paths that will be scanned for .kt(s) files
*/
lateinit var excludes: FileCollection
@Deprecated("Configuration via inputs/excludes is unsupported, use inputs(Action)")
var inputs: FileCollection? = null

/**
* Paths that will be scanned for .kt(s) files
* Path to diktat yml config file. Can be either absolute or relative to project's root directory.
* Default value: `diktat-analysis.yml` in rootDir.
*/
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
lateinit var diktatConfigFile: File

/**
* Configure input files for diktat task
*
* @param action configuration lambda for `PatternFilterable`
*/
lateinit var inputs: FileCollection
fun inputs(action: PatternFilterable.() -> Unit) {
action.invoke(patternSet)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.attributes.Bundling
import org.gradle.api.tasks.util.PatternSet

/**
* Plugin that configures diktat and registers tasks to run diktat
Expand All @@ -16,12 +17,13 @@ class DiktatGradlePlugin : Plugin<Project> {
* @param project a gradle [Project] that the plugin is applied to
*/
override fun apply(project: Project) {
val diktatExtension = project.extensions.create(DIKTAT_EXTENSION, DiktatExtension::class.java).apply {
inputs = project.fileTree("src").apply {
include("**/*.kt")
}
val patternSet = PatternSet()
val diktatExtension = project.extensions.create(
DIKTAT_EXTENSION,
DiktatExtension::class.java,
patternSet
).apply {
diktatConfigFile = project.rootProject.file("diktat-analysis.yml")
excludes = project.files()
}

// only gradle 7+ (or maybe 6.8) will embed kotlin 1.4+, kx.serialization is incompatible with kotlin 1.3, so until then we have to use JavaExec wrapper
Expand All @@ -43,8 +45,8 @@ class DiktatGradlePlugin : Plugin<Project> {
configuration.dependencies.add(project.dependencies.create("org.cqfn.diktat:diktat-rules:$DIKTAT_VERSION"))
}

project.registerDiktatCheckTask(diktatExtension, diktatConfiguration)
project.registerDiktatFixTask(diktatExtension, diktatConfiguration)
project.registerDiktatCheckTask(diktatExtension, diktatConfiguration, patternSet)
project.registerDiktatFixTask(diktatExtension, diktatConfiguration, patternSet)
}

companion object {
Expand Down
Loading