Skip to content

Commit 69fac8b

Browse files
committed
add json report task which will run by default
1 parent 94ac687 commit 69fac8b

File tree

9 files changed

+141
-6
lines changed

9 files changed

+141
-6
lines changed

nebula-archrules-gradle-plugin/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ repositories {
88
}
99
dependencies {
1010
implementation(project(":nebula-archrules-core"))
11+
implementation("tools.jackson.core:jackson-databind:3.0.2")
1112
testImplementation("net.javacrumbs.json-unit:json-unit-assertj:5.0.0")
1213
testImplementation("org.json:json:20250517")
1314
}

nebula-archrules-gradle-plugin/gradle.lockfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Manual edits can break the build and are not advised.
33
# This file is expected to be part of source control.
44
cglib:cglib-nodep:3.2.2=integTestRuntimeClasspath,testRuntimeClasspath
5+
com.fasterxml.jackson.core:jackson-annotations:2.20=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
56
com.jayway.jsonpath:json-path:2.9.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
67
com.netflix.nebula:nebula-test:11.7.1=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
78
com.tngtech.archunit:archunit:1.4.1=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -46,4 +47,6 @@ org.objenesis:objenesis:2.4=integTestRuntimeClasspath,testRuntimeClasspath
4647
org.opentest4j:opentest4j:1.3.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
4748
org.ow2.asm:asm:9.3=integTestRuntimeClasspath,testRuntimeClasspath
4849
org.slf4j:slf4j-api:2.0.17=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
50+
tools.jackson.core:jackson-core:3.0.2=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
51+
tools.jackson.core:jackson-databind:3.0.2=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
4952
empty=annotationProcessor,integTestAnnotationProcessor,integTestKotlinScriptDefExtensions,kotlinScriptDefExtensions,testAnnotationProcessor,testKotlinScriptDefExtensions

nebula-archrules-gradle-plugin/src/main/java/com/netflix/nebula/archrules/gradle/ArchRuleAttribute.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@
44
import org.gradle.api.attributes.Attribute;
55
import org.jspecify.annotations.NonNull;
66

7+
/**
8+
* Attribute used to denote an archrules library, and to resolve archrules variants
9+
*/
710
public interface ArchRuleAttribute extends Named {
11+
12+
/**
13+
* Attribute used to denote an archrules library, and to resolve archrules variants
14+
*/
815
Attribute<@NonNull ArchRuleAttribute> ARCH_RULES_ATTRIBUTE =
916
Attribute.of("com.netflix.nebula.archrules", ArchRuleAttribute.class);
17+
18+
/**
19+
* The only recognized value of this attribute
20+
*/
1021
String ARCH_RULES = "arch-rules";
1122
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.netflix.nebula.archrules.gradle;
2+
3+
import org.gradle.api.DefaultTask;
4+
import org.gradle.api.provider.ListProperty;
5+
import org.gradle.api.provider.Property;
6+
import org.gradle.api.tasks.*;
7+
import org.jspecify.annotations.NonNull;
8+
import tools.jackson.databind.json.JsonMapper;
9+
10+
import java.io.File;
11+
import java.util.List;
12+
13+
/**
14+
* Produces a JSON report of all ArchRules failures
15+
*/
16+
@CacheableTask
17+
abstract public class PrintJsonReportTask extends DefaultTask {
18+
19+
/**
20+
* The data files to read in. These files should container binary data representing {@link RuleResult}s
21+
* @return all data files to process
22+
*/
23+
@InputFiles
24+
@PathSensitive(PathSensitivity.RELATIVE)
25+
abstract public ListProperty<@NonNull File> getDataFiles();
26+
27+
/**
28+
* File to output JSON to
29+
* @return file for output
30+
*/
31+
@OutputFile
32+
abstract public Property<@NonNull File> getJsonReportFile();
33+
34+
/**
35+
* The action for this task
36+
*/
37+
@TaskAction
38+
public void printReport() {
39+
List<RuleResult> list = getDataFiles().get().stream()
40+
.flatMap(it -> ViolationsUtil.readDetails(it).stream())
41+
.toList();
42+
43+
final var report = new JsonReportRoot(list);
44+
new JsonMapper().writeValue(getJsonReportFile().get(), report);
45+
}
46+
47+
record JsonReportRoot(List<RuleResult> violations) {
48+
}
49+
}

nebula-archrules-gradle-plugin/src/main/kotlin/com/netflix/nebula/archrules/gradle/ArchrulesRunnerPlugin.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,20 @@ class ArchrulesRunnerPlugin : Plugin<Project> {
6969
}
7070
checkTask
7171
}
72+
73+
val dataFiles = checkTasks.stream()
74+
.map { it.get().dataFile.get() }
75+
.toList()
76+
77+
val jsonReportTask = project.tasks.register<PrintJsonReportTask>("archRulesJsonReport") {
78+
getDataFiles().set(dataFiles)
79+
getJsonReportFile().set(archRulesReportDir.map { it.file("report.json").asFile })
80+
dependsOn(checkTasks)
81+
}
82+
7283
project.tasks.named("check") {
7384
dependsOn(checkTasks)
85+
finalizedBy(jsonReportTask)
7486
}
7587
}
7688
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.netflix.nebula.archrules.gradle
2+
3+
import java.io.File
4+
import java.io.FileInputStream
5+
import java.io.IOException
6+
import java.io.ObjectInputStream
7+
8+
class ViolationsUtil {
9+
companion object {
10+
@JvmStatic
11+
fun readDetails(dataFile: File): List<RuleResult> {
12+
val list: MutableList<RuleResult> = mutableListOf()
13+
try {
14+
ObjectInputStream(FileInputStream(dataFile)).use { objectInputStream ->
15+
val numObjects = objectInputStream.readInt()
16+
repeat(numObjects) {
17+
list.add(objectInputStream.readObject() as RuleResult)
18+
}
19+
}
20+
} catch (e: IOException) {
21+
throw RuntimeException(e)
22+
} catch (e: ClassNotFoundException) {
23+
throw RuntimeException(e)
24+
}
25+
return list
26+
}
27+
}
28+
}

nebula-archrules-gradle-plugin/src/test/kotlin/com/netflix/nebula/archrules/gradle/ArchrulesLibraryPluginTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class ArchrulesLibraryPluginTest {
2929
@Test
3030
fun `plugin produces maven publication`() {
3131
val runner = testProject(projectDir) {
32+
properties {
33+
gradleCache(true)
34+
}
3235
settings {
3336
name("library-with-rules")
3437
}
@@ -65,7 +68,7 @@ class ArchrulesLibraryPluginTest {
6568

6669
assertThat(result.task(":compileArchRulesJava"))
6770
.`as`("compile task runs for the archRules source set")
68-
.hasOutcome(TaskOutcome.SUCCESS)
71+
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.FROM_CACHE)
6972
assertThat(result.task(":archRulesJar"))
7073
.hasOutcome(TaskOutcome.SUCCESS)
7174
assertThat(result)
@@ -102,6 +105,9 @@ println(moduleMetadataJson)
102105
@Test
103106
fun `plugin sets up tests for rules`() {
104107
val runner = testProject(projectDir) {
108+
properties {
109+
gradleCache(true)
110+
}
105111
settings {
106112
name("library-with-rules")
107113
}

nebula-archrules-gradle-plugin/src/test/kotlin/com/netflix/nebula/archrules/gradle/ArchrulesRunnerPluginTest.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package com.netflix.nebula.archrules.gradle
33
import nebula.test.dsl.TestKitAssertions.assertThat
44
import nebula.test.dsl.main
55
import nebula.test.dsl.plugins
6+
import nebula.test.dsl.properties
67
import nebula.test.dsl.repositories
78
import nebula.test.dsl.rootProject
9+
import nebula.test.dsl.run
810
import nebula.test.dsl.settings
911
import nebula.test.dsl.sourceSet
1012
import nebula.test.dsl.src
@@ -16,6 +18,8 @@ import org.gradle.testkit.runner.TaskOutcome
1618
import org.junit.jupiter.api.Disabled
1719
import org.junit.jupiter.api.Test
1820
import org.junit.jupiter.api.io.TempDir
21+
import org.junit.jupiter.params.ParameterizedTest
22+
import org.junit.jupiter.params.provider.EnumSource
1923
import java.io.File
2024
import java.io.FileInputStream
2125
import java.io.IOException
@@ -34,9 +38,13 @@ class ArchrulesRunnerPluginTest {
3438
assertThat(configuration).isNotNull
3539
}
3640

37-
@Test
38-
fun `plugin checks each sourceset`() {
41+
@ParameterizedTest
42+
@EnumSource(SupportedGradleVersion::class)
43+
fun `plugin checks each sourceset`(gradleVersion: SupportedGradleVersion) {
3944
val runner = testProject(projectDir) {
45+
properties {
46+
gradleCache(true)
47+
}
4048
settings {
4149
name("consumer")
4250
}
@@ -63,7 +71,10 @@ class ArchrulesRunnerPluginTest {
6371
}
6472
}
6573

66-
val result = runner.run("check", "--stacktrace", "-x", "test")
74+
val result = runner.run("check", "--stacktrace", "-x", "test"){
75+
withGradleVersion(gradleVersion.version)
76+
forwardOutput()
77+
}
6778

6879
assertThat(result.task(":checkArchRulesMain"))
6980
.`as`("archRules run for main source set")
@@ -73,6 +84,10 @@ class ArchrulesRunnerPluginTest {
7384
.`as`("archRules run for test source set")
7485
.hasOutcome(TaskOutcome.SUCCESS)
7586

87+
assertThat(result.task(":archRulesJsonReport"))
88+
.`as`("archRules json report runs by default")
89+
.hasOutcome(TaskOutcome.SUCCESS)
90+
7691
assertThat(result)
7792
.hasNoMutableStateWarnings()
7893
.hasNoDeprecationWarnings()
@@ -90,11 +105,19 @@ class ArchrulesRunnerPluginTest {
90105
.exists()
91106
val testErrors = readDetails(testReport)
92107
assertThat(testErrors).hasSize(1)
108+
109+
val jsonReport = projectDir.resolve("build/reports/archrules/report.json")
110+
assertThat(jsonReport)
111+
.`as`("json report created")
112+
.exists()
93113
}
94114

95115
@Test
96116
fun `plugin checks each sourceset from its runtime`() {
97117
val runner = testProject(projectDir) {
118+
properties {
119+
gradleCache(true)
120+
}
98121
settings {
99122
name("consumer")
100123
}
@@ -166,5 +189,4 @@ class ArchrulesRunnerPluginTest {
166189
}
167190
return list
168191
}
169-
170-
}
192+
}

nebula-archrules-gradle-plugin/src/test/kotlin/com/netflix/nebula/archrules/gradle/IntegrationTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ internal class IntegrationTest {
1616
@EnumSource(SupportedGradleVersion::class)
1717
fun test(gradleVersion: SupportedGradleVersion) {
1818
val runner = testProject(projectDir) {
19+
properties {
20+
gradleCache(true)
21+
}
1922
subProject("library-with-rules") {
2023
// a library that contains production code and rules to go along with it
2124
plugins {

0 commit comments

Comments
 (0)