Skip to content

Commit 9a94a3f

Browse files
committed
Support transitive archrules
fixes #24
1 parent c405eec commit 9a94a3f

File tree

7 files changed

+573
-74
lines changed

7 files changed

+573
-74
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ testing {
3939
suites{
4040
named<JvmTestSuite>("test"){
4141
useJUnitJupiter()
42+
targets.all {
43+
testTask.configure {
44+
maxParallelForks = 2
45+
}
46+
}
4247
}
4348
}
4449
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class ArchrulesLibraryPlugin : Plugin<Project> {
2828
project.pluginManager.withPlugin("java") {
2929
val javaExt = project.extensions.getByType<JavaPluginExtension>()
3030
val archRulesSourceSet = javaExt.sourceSets.create("archRules")
31+
project.configurations.named(archRulesSourceSet.implementationConfigurationName).configure {
32+
extendsFrom(project.configurations.getByName(javaExt.sourceSets.getByName("main").implementationConfigurationName))
33+
}
3134
project.dependencies.add(
3235
archRulesSourceSet.implementationConfigurationName,
3336
"com.netflix.nebula:nebula-archrules-core:$version"
@@ -50,7 +53,7 @@ class ArchrulesLibraryPlugin : Plugin<Project> {
5053
val jarTask = project.tasks.register<Jar>("archRulesJar") {
5154
description = "Assembles a jar archive containing the classes of the arch rules."
5255
group = "build"
53-
from(archRulesSourceSet.output)
56+
from(archRulesSourceSet.output, javaExt.sourceSets.getByName("main").output)
5457
archiveClassifier.set("arch-rules")
5558
dependsOn(generateServicesTask)
5659
}
@@ -74,6 +77,17 @@ class ArchrulesLibraryPlugin : Plugin<Project> {
7477
dependsOn(generateServicesTask)
7578
}
7679
}
80+
project.configurations.named(compileClasspathConfigurationName){
81+
extendsFrom(project.configurations.getByName(javaExt.sourceSets.getByName("main").compileClasspathConfigurationName))
82+
attributes {
83+
attribute(ArchRuleAttribute.ARCH_RULES_ATTRIBUTE, project.objects.named(ARCH_RULES))
84+
}
85+
}
86+
project.configurations.named(runtimeClasspathConfigurationName){
87+
attributes {
88+
attribute(ArchRuleAttribute.ARCH_RULES_ATTRIBUTE, project.objects.named(ARCH_RULES))
89+
}
90+
}
7791
}
7892
}
7993
}

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

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,19 @@ class ArchrulesRunnerPlugin : Plugin<Project> {
6060

6161
fun Project.configureCheckTaskForSourceSet(sourceSet: SourceSet) {
6262
val archRulesReportDir = project.layout.buildDirectory.dir("reports/archrules")
63+
val sourceSetArchRulesRuntime = configurations.resolvable(sourceSet.name+"ArchRulesRuntime"){
64+
extendsFrom(configurations.getByName(sourceSet.runtimeClasspathConfigurationName))
65+
attributes {
66+
attribute(
67+
ArchRuleAttribute.ARCH_RULES_ATTRIBUTE,
68+
project.objects.named<ArchRuleAttribute>(ARCH_RULES)
69+
)
70+
}
71+
}
6372
tasks.register<CheckRulesTask>("checkArchRules" + sourceSet.name.capitalized()) {
6473
description = "Checks ArchRules on ${sourceSet.name}"
65-
val artifactView =
66-
project.configurations.getByName(sourceSet.runtimeClasspathConfigurationName)
67-
.incoming
68-
.artifactView {
69-
withVariantReselection()
70-
attributes {
71-
attribute(
72-
ArchRuleAttribute.ARCH_RULES_ATTRIBUTE,
73-
project.objects.named<ArchRuleAttribute>(ARCH_RULES)
74-
)
75-
attribute(
76-
Usage.USAGE_ATTRIBUTE,
77-
project.objects.named<Usage>(Usage.JAVA_RUNTIME)
78-
)
79-
attribute(
80-
Category.CATEGORY_ATTRIBUTE,
81-
project.objects.named<Category>(Category.LIBRARY)
82-
)
83-
attribute(
84-
Bundling.BUNDLING_ATTRIBUTE,
85-
project.objects.named(Bundling.EXTERNAL)
86-
)
87-
}
88-
lenient(false)
89-
}
9074
rulesClasspath.setFrom(
91-
artifactView.files,
75+
sourceSetArchRulesRuntime,
9276
project.configurations.getByName("archRules")
9377
)
9478
dataFile.set(archRulesReportDir.map {

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

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.netflix.nebula.archrules.gradle
22

33
import nebula.test.dsl.*
44
import nebula.test.dsl.TestKitAssertions.assertThat
5+
import net.javacrumbs.jsonunit.assertj.JsonAssertions.json
56
import net.javacrumbs.jsonunit.assertj.assertThatJson
67
import org.gradle.testfixtures.ProjectBuilder
78
import org.gradle.testkit.runner.TaskOutcome
@@ -77,7 +78,8 @@ class ArchrulesLibraryPluginTest {
7778
.hasNoMutableStateWarnings()
7879
.hasNoDeprecationWarnings()
7980

80-
val serviceFile = projectDir.resolve("build/resources/archRules/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
81+
val serviceFile =
82+
projectDir.resolve("build/resources/archRules/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
8183
assertThat(serviceFile)
8284
.`as`("service file is created")
8385
.exists()
@@ -117,7 +119,7 @@ class ArchrulesLibraryPluginTest {
117119
}
118120

119121
@Test
120-
fun `plugin produces proper outgoingVariants`() {
122+
fun `main dependencies are included in archRules`() {
121123
val runner = testProject(projectDir) {
122124
properties {
123125
gradleCache(true)
@@ -138,6 +140,7 @@ class ArchrulesLibraryPluginTest {
138140
mavenCentral()
139141
}
140142
declareMavenPublication()
143+
dependencies("""implementation("com.google.guava:guava:33.5.0-jre")""")
141144
src {
142145
main {
143146
exampleLibraryClass()
@@ -149,15 +152,45 @@ class ArchrulesLibraryPluginTest {
149152
}
150153
}
151154

152-
val result = runner.run("outgoingVariants", "-Pversion=0.0.1")
153-
assertThat(result.output)
154-
.contains("Variant archRulesRuntimeElements")
155-
.contains("Variant testResultsElementsForArchRulesTest")
156-
.doesNotContain("Variant archRulesApiElements")
155+
val result = runner.run(
156+
"build",
157+
"archRulesJar",
158+
"generateMetadataFileForMavenPublication", // to test publication metadata without actually publishing,
159+
"-Pversion=0.0.1"
160+
)
161+
162+
assertThat(result)
163+
.hasNoMutableStateWarnings()
164+
.hasNoDeprecationWarnings()
165+
166+
val moduleMetadata = projectDir.resolve("build/publications/maven/module.json")
167+
assertThat(moduleMetadata)
168+
.`as`("Gradle Module Metadata is created")
169+
.exists()
170+
171+
val moduleMetadataJson = moduleMetadata.readText()
172+
173+
assertThatJson(moduleMetadataJson)
174+
.inPath("$.variants[?(@.name=='archRulesRuntimeElements')].dependencies[1]")
175+
.isArray
176+
.contains(
177+
json(
178+
//language=json
179+
"""
180+
{
181+
"group": "com.google.guava",
182+
"module": "guava",
183+
"version": {
184+
"requires": "33.5.0-jre"
185+
}
186+
}
187+
"""
188+
)
189+
)
157190
}
158191

159192
@Test
160-
fun `plugin sets up tests for rules`() {
193+
fun `plugin produces proper outgoingVariants`() {
161194
val runner = testProject(projectDir) {
162195
properties {
163196
gradleCache(true)
@@ -167,48 +200,45 @@ class ArchrulesLibraryPluginTest {
167200
}
168201
rootProject {
169202
group("com.example")
203+
// a library that contains production code and rules to go along with it
170204
plugins {
171205
id("java-library")
172206
id("com.netflix.nebula.archrules.library")
207+
id("maven-publish")
173208
}
174209
repositories {
175210
maven("https://netflixoss.jfrog.io/artifactory/gradle-plugins")
176211
mavenCentral()
177212
}
213+
declareMavenPublication()
178214
src {
179215
main {
180216
exampleLibraryClass()
181217
}
182218
sourceSet("archRules") {
183219
exampleDeprecatedArchRule()
184220
}
185-
sourceSet("archRulesTest") {
186-
exampleTestForArchRule()
187-
}
188221
}
189222
}
190223
}
191224

192-
val result = runner.run("check")
193-
194-
assertThat(result.task(":archRulesTest"))
195-
.`as`("archRules test task runs")
196-
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.FROM_CACHE)
197-
assertThat(result)
198-
.hasNoMutableStateWarnings()
199-
.hasNoDeprecationWarnings()
225+
val result = runner.run("outgoingVariants", "-Pversion=0.0.1")
226+
assertThat(result.output)
227+
.contains("Variant archRulesRuntimeElements")
228+
.contains("Variant testResultsElementsForArchRulesTest")
229+
.doesNotContain("Variant archRulesApiElements")
200230
}
201231

202232
@Test
203-
fun `plugin sets up tests for rules with dependencies`() {
233+
fun `plugin sets up tests for rules`() {
204234
val runner = testProject(projectDir) {
205235
properties {
206236
gradleCache(true)
207237
}
208238
settings {
209239
name("library-with-rules")
210240
}
211-
subProject("rules") {
241+
rootProject {
212242
group("com.example")
213243
plugins {
214244
id("java-library")
@@ -218,43 +248,23 @@ class ArchrulesLibraryPluginTest {
218248
maven("https://netflixoss.jfrog.io/artifactory/gradle-plugins")
219249
mavenCentral()
220250
}
221-
dependencies(
222-
"""archRulesImplementation(project(":helper"))""",
223-
"""archRulesTestImplementation("org.jspecify:jspecify:1.0.0")"""
224-
)
225251
src {
226252
main {
227253
exampleLibraryClass()
228254
}
229255
sourceSet("archRules") {
230-
exampleNullabilityArchRule() // rules that uses a helper from a dependency
256+
exampleDeprecatedArchRule()
231257
}
232258
sourceSet("archRulesTest") {
233-
exampleTestForNullabilityArchRule()
234-
}
235-
}
236-
}
237-
subProject("helper") {
238-
group("com.example")
239-
plugins {
240-
id("java-library")
241-
}
242-
repositories {
243-
maven("https://netflixoss.jfrog.io/artifactory/gradle-plugins")
244-
mavenCentral()
245-
}
246-
dependencies("""implementation("com.tngtech.archunit:archunit:1.4.1")""")
247-
src {
248-
main {
249-
exampleHelperClass()
259+
exampleTestForArchRule()
250260
}
251261
}
252262
}
253263
}
254264

255-
val result = runner.run("check", "--stacktrace")
265+
val result = runner.run("check")
256266

257-
assertThat(result.task(":rules:archRulesTest"))
267+
assertThat(result.task(":archRulesTest"))
258268
.`as`("archRules test task runs")
259269
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.FROM_CACHE)
260270
assertThat(result)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ internal class IntegrationTest {
4444
id("java")
4545
id("com.netflix.nebula.archrules.runner")
4646
}
47+
repositories {
48+
maven("https://netflixoss.jfrog.io/artifactory/gradle-plugins")
49+
mavenCentral()
50+
}
4751
dependencies(
4852
"""implementation(project(":library-with-rules"))"""
4953
)

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,16 @@ import org.junit.jupiter.api.Test;
167167
import org.junit.jupiter.api.Assertions;
168168
169169
public class LibraryArchRulesTest {
170+
@Deprecated
171+
static void deprecatedMethod(){
172+
}
170173
static class PassingCode {
171174
public void aMethod() {
172-
LibraryClass.newApi();
173175
}
174176
}
175177
static class FailingCode {
176178
public void aMethod() {
177-
LibraryClass.deprecatedApi();
179+
deprecatedMethod();
178180
}
179181
}
180182

0 commit comments

Comments
 (0)