Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions nebula-archrules-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ description = "Plugins for authoring and running Nebula ArchRules"
repositories {
mavenCentral()
}
dependencies {
testImplementation("net.javacrumbs.json-unit:json-unit-assertj:5.0.0")
testImplementation("org.json:json:20250517")
}
gradlePlugin {
plugins {
create("library") {
Expand Down
15 changes: 13 additions & 2 deletions nebula-archrules-gradle-plugin/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
cglib:cglib-nodep:3.2.2=integTestRuntimeClasspath,testRuntimeClasspath
com.jayway.jsonpath:json-path:2.9.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.netflix.nebula:nebula-test:11.7.1=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.15.11=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.javacrumbs.json-unit:json-unit-assertj:5.0.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.javacrumbs.json-unit:json-unit-core:5.0.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.javacrumbs.json-unit:json-unit-json-path:5.0.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.minidev:accessors-smart:2.5.0=integTestRuntimeClasspath,testRuntimeClasspath
net.minidev:json-smart:2.5.0=integTestRuntimeClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.assertj:assertj-core:3.27.3=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.assertj:assertj-core:3.27.6=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest-core:3.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest:3.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:2.2.0=kotlinCompilerPluginClasspathIntegTest,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-build-tools-api:2.2.0=kotlinBuildToolsApiClasspath
org.jetbrains.kotlin:kotlin-build-tools-impl:2.2.0=kotlinBuildToolsApiClasspath
Expand All @@ -24,6 +32,7 @@ org.jetbrains.kotlin:kotlin-scripting-jvm:2.2.0=kotlinBuildToolsApiClasspath,kot
org.jetbrains.kotlin:kotlin-stdlib:2.2.0=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathIntegTest,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains:annotations:13.0=compileClasspath,integTestCompileClasspath,integTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathIntegTest,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,testCompileClasspath,testRuntimeClasspath
org.json:json:20250517=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.12.2=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.12.2=integTestRuntimeClasspath,testRuntimeClasspath
Expand All @@ -34,4 +43,6 @@ org.junit.platform:junit-platform-engine:1.14.0=integTestCompileClasspath,integT
org.junit.platform:junit-platform-launcher:1.14.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.objenesis:objenesis:2.4=integTestRuntimeClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=integTestCompileClasspath,integTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm:9.3=integTestRuntimeClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.11=integTestRuntimeClasspath,testRuntimeClasspath
empty=annotationProcessor,integTestAnnotationProcessor,integTestKotlinScriptDefExtensions,kotlinScriptDefExtensions,runtimeClasspath,testAnnotationProcessor,testKotlinScriptDefExtensions
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.netflix.nebula.archrules.gradle

import nebula.test.dsl.*
import nebula.test.dsl.TestKitAssertions.assertThat
import net.javacrumbs.jsonunit.assertj.assertThatJson
import org.gradle.testfixtures.ProjectBuilder
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File

class ArchrulesLibraryPluginTest {
@TempDir
lateinit var projectDir: File

@Test
fun `plugin registers library dependency`() {
Expand All @@ -18,4 +25,71 @@ class ArchrulesLibraryPluginTest {
assertThat(coreLibrary).isNotNull
assertThat(coreLibrary!!.version).isEqualTo("latest.release")
}

@Test
fun `plugin produces maven publication`() {
val runner = testProject(projectDir) {
settings {
name("library-with-rules")
}
rootProject {
group("com.example")
// a library that contains production code and rules to go along with it
plugins {
id("java-library")
id("com.netflix.nebula.archrules.library")
id("maven-publish")
}
repositories {
maven("https://netflixoss.jfrog.io/artifactory/gradle-plugins")
mavenCentral()
}
declareMavenPublication()
src {
main {
exampleLibraryClass()
}
sourceSet("archRules") {
exampleDeprecatedArchRule()
}
}
}
}

val result = runner.run(
"build",
"archRulesJar",
"generateMetadataFileForMavenPublication", // to test publication metadata without actually publishing,
"-Pversion=0.0.1"
)

assertThat(result.task(":compileArchRulesJava"))
.`as`("compile task runs for the archRules source set")
.hasOutcome(TaskOutcome.SUCCESS)
assertThat(result.task(":archRulesJar"))
.hasOutcome(TaskOutcome.SUCCESS)
assertThat(result)
.hasNoMutableStateWarnings()
.hasNoDeprecationWarnings()

assertThat(projectDir.resolve("build/libs/library-with-rules-0.0.1.jar"))
.`as`("Library Jar is created")
.exists()
assertThat(projectDir.resolve("build/libs/library-with-rules-0.0.1-archrules.jar"))
.`as`("ArchRules Jar is created")
.exists()

val moduleMetadata = projectDir.resolve("build/publications/maven/module.json")
assertThat(moduleMetadata)
.`as`("Gradle Module Metadata is created")
.exists()
val moduleMetadataJson = moduleMetadata.readText()
println(moduleMetadataJson)
assertThatJson(moduleMetadataJson)
.inPath("$.variants[?(@.name=='runtimeElements')].files[0]")
.isArray
.first().isObject
.containsEntry("name", "library-with-rules-0.0.1.jar")

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,51 +28,10 @@ internal class IntegrationTest {
}
src {
main {
java(
"com/example/library/LibraryClass.java",
//language=java
"""
package com.example.library;

public class LibraryClass {

}
"""
)
exampleLibraryClass()
}
sourceSet("archRules") {
java(
"com/example/library/LibraryArchRules.java",
//language=java
"""
package com.example.library;

import com.netflix.nebula.archrules.core.ArchRulesService;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.Priority;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
import java.util.Map;
import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.target;
import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.targetOwner;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;

public class LibraryArchRules implements ArchRulesService {
private final ArchRule noDeprecated = ArchRuleDefinition.priority(Priority.LOW)
.noClasses()
.should().accessTargetWhere(targetOwner(annotatedWith(Deprecated.class)))
.orShould().accessTargetWhere(target(annotatedWith(Deprecated.class)))
.orShould().dependOnClassesThat().areAnnotatedWith(Deprecated.class)
.allowEmptyShould(true)
.as("No code should reference deprecated APIs")
.because("usage of deprecated APIs introduces risk that future upgrades and migrations will be blocked");

@Override
public Map<String, ArchRule> getRules() {
return Map.of("deprecated", noDeprecated);
}
}
"""
)
exampleDeprecatedArchRule()
}
}
}
Expand All @@ -88,28 +47,16 @@ public class LibraryArchRules implements ArchRulesService {
}
}

val result = runner.run("check", "archRulesJar") {
val result = runner.run("check") {
withGradleVersion(gradleVersion.version)
}

assertThat(result.task(":library-with-rules:compileArchRulesJava"))
.`as`("compile task runs for the archRules source set")
.hasOutcome(TaskOutcome.SUCCESS)
assertThat(result.task(":library-with-rules:archRulesJar"))
.hasOutcome(TaskOutcome.SUCCESS)
assertThat(result.task(":library-with-rules:check"))
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE)
assertThat(result.task(":code-to-check:check"))
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE)
assertThat(result)
.hasNoMutableStateWarnings()
.hasNoDeprecationWarnings()

assertThat(projectDir.resolve("library-with-rules/build/libs/library-with-rules.jar"))
.`as`("Library Jar is created")
.exists()
assertThat(projectDir.resolve("library-with-rules/build/libs/library-with-rules-archrules.jar"))
.`as`("ArchRules Jar is created")
.exists()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.netflix.nebula.archrules.gradle

import nebula.test.dsl.ProjectBuilder
import nebula.test.dsl.SourceSetBuilder

fun ProjectBuilder.declareMavenPublication() {
//language=kotlin
rawBuildScript(
"""
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
"""
)
}

fun SourceSetBuilder.exampleLibraryClass() {
java(
"com/example/library/LibraryClass.java",
//language=java
"""
package com.example.library;

public class LibraryClass {

}
"""
)
}

fun SourceSetBuilder.exampleDeprecatedArchRule() {
java(
"com/example/library/LibraryArchRules.java",
//language=java
"""
package com.example.library;

import com.netflix.nebula.archrules.core.ArchRulesService;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.Priority;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
import java.util.Map;
import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.target;
import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.targetOwner;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;

public class LibraryArchRules implements ArchRulesService {
private final ArchRule noDeprecated = ArchRuleDefinition.priority(Priority.LOW)
.noClasses()
.should().accessTargetWhere(targetOwner(annotatedWith(Deprecated.class)))
.orShould().accessTargetWhere(target(annotatedWith(Deprecated.class)))
.orShould().dependOnClassesThat().areAnnotatedWith(Deprecated.class)
.allowEmptyShould(true)
.as("No code should reference deprecated APIs")
.because("usage of deprecated APIs introduces risk that future upgrades and migrations will be blocked");

@Override
public Map<String, ArchRule> getRules() {
return Map.of("deprecated", noDeprecated);
}
}
"""
)
}