From 37d3a81acb578772fce2a64a69a015ea5bfadde5 Mon Sep 17 00:00:00 2001 From: Roberto Perez Alcolea Date: Wed, 10 Dec 2025 15:31:00 -0800 Subject: [PATCH 1/4] archrules-gradle-plugin-development: Add GradleTaskActionRule to detect configuration cache breaking patterns in @TaskAction methods --- .../build.gradle.kts | 24 +++ .../gradle.lockfile | 27 ++++ .../gradleplugins/GradleTaskActionRule.java | 127 ++++++++++++++++ .../GradleTaskActionRuleTest.java | 142 ++++++++++++++++++ settings.gradle.kts | 3 +- 5 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 archrules-gradle-plugin-development/build.gradle.kts create mode 100644 archrules-gradle-plugin-development/gradle.lockfile create mode 100644 archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java create mode 100644 archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRuleTest.java diff --git a/archrules-gradle-plugin-development/build.gradle.kts b/archrules-gradle-plugin-development/build.gradle.kts new file mode 100644 index 0000000..77d570c --- /dev/null +++ b/archrules-gradle-plugin-development/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + id("com.netflix.nebula.library") + id("com.netflix.nebula.archrules.library") +} +description = "Arch Rules for detecting bad practices when developing Gradle plugins" +repositories { + mavenCentral() +} +dependencies { + archRulesImplementation(libs.jspecify) + + archRulesTestImplementation(libs.assertj) + archRulesTestImplementation(libs.logback) + archRulesTestImplementation(gradleApi()) +} +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + +dependencyLocking { + lockAllConfigurations() +} diff --git a/archrules-gradle-plugin-development/gradle.lockfile b/archrules-gradle-plugin-development/gradle.lockfile new file mode 100644 index 0000000..53f13ad --- /dev/null +++ b/archrules-gradle-plugin-development/gradle.lockfile @@ -0,0 +1,27 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +ch.qos.logback:logback-classic:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +ch.qos.logback:logback-core:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +com.netflix.nebula:archrules-deprecation:0.4.0=archRules +com.netflix.nebula:archrules-joda:0.4.0=archRules +com.netflix.nebula:archrules-nullability:0.4.0=archRules +com.netflix.nebula:archrules-testing-frameworks:0.4.0=archRules +com.netflix.nebula:nebula-archrules-core:0.3.0=archRules +com.netflix.nebula:nebula-archrules-core:0.5.2=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +com.tngtech.archunit:archunit:1.4.1=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +net.bytebuddy:byte-buddy:1.17.7=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=archRulesTestCompileClasspath +org.assertj:assertj-core:3.27.6=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.jspecify:jspecify:1.0.0=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.12.2=archRulesTestArchRulesRuntime,archRulesTestRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.junit.platform:junit-platform-commons:1.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.junit.platform:junit-platform-engine:1.12.2=archRulesTestArchRulesRuntime,archRulesTestRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.12.2=archRulesTestArchRulesRuntime,archRulesTestRuntimeClasspath +org.junit:junit-bom:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +org.slf4j:slf4j-api:2.0.17=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +empty=annotationProcessor,archRulesAnnotationProcessor,archRulesTestAnnotationProcessor,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testAnnotationProcessor,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java new file mode 100644 index 0000000..4483e4b --- /dev/null +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java @@ -0,0 +1,127 @@ +package com.netflix.nebula.archrules.gradleplugins; + +import com.netflix.nebula.archrules.core.ArchRulesService; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaAccess; +import com.tngtech.archunit.core.domain.JavaFieldAccess; +import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.Priority; +import com.tngtech.archunit.lang.SimpleConditionEvent; +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; +import org.jspecify.annotations.NullMarked; + +import java.util.HashMap; +import java.util.Map; + +/** + * Rules for Gradle task action methods to ensure Gradle 10 compatibility. + */ +@NullMarked +public class GradleTaskActionRule implements ArchRulesService { + + /** + * Prevents {@code @TaskAction} methods from accessing {@code Project}. + *

+ * Accessing {@code Project} in task actions breaks configuration cache and will + * cause runtime errors in Gradle 10+. Move Project access to configuration time + * (constructor/initializer) and use task properties instead. + */ + public static ArchRule taskActionShouldNotAccessProject = createTaskActionRule( + notAccessProject(), + "access the Project object", + "Accessing Project in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + + "Move Project access to task configuration time and use task inputs/properties instead.", + "task_project" + ); + + /** + * Prevents {@code @TaskAction} methods from calling {@code getTaskDependencies()}. + *

+ * Calling {@code getTaskDependencies()} in task actions breaks configuration cache and will + * cause runtime errors in Gradle 10+. Task dependencies should be declared at configuration time. + */ + public static ArchRule taskActionShouldNotCallGetTaskDependencies = createTaskActionRule( + notCallGetTaskDependencies(), + "call getTaskDependencies()", + "Calling getTaskDependencies() in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + + "Declare task dependencies at configuration time instead.", + "task_dependencies" + ); + + private static ArchRule createTaskActionRule(ArchCondition condition, String actionDescription, String reason, String docAnchor) { + return ArchRuleDefinition.priority(Priority.MEDIUM) + .methods() + .that(areAnnotatedWithTaskAction()) + .should(condition) + .allowEmptyShould(true) + .as("Methods annotated with @TaskAction should not " + actionDescription) + .because(reason + " See https://docs.gradle.org/9.2.0/userguide/upgrading_version_7.html#" + docAnchor); + } + + private static DescribedPredicate areAnnotatedWithTaskAction() { + return new DescribedPredicate("are annotated with @TaskAction") { + @Override + public boolean test(JavaMethod method) { + return method.isAnnotatedWith("org.gradle.api.tasks.TaskAction"); + } + }; + } + + private static ArchCondition notAccessProject() { + return notAccessType("Project", "org.gradle.api.Project", "getProject"); + } + + private static ArchCondition notCallGetTaskDependencies() { + return notAccessType("TaskDependency", "org.gradle.api.tasks.TaskDependency", "getTaskDependencies"); + } + + private static ArchCondition notAccessType(String displayName, String fullyQualifiedClassName, String getterMethodName) { + return new ArchCondition("not access " + displayName) { + @Override + public void check(JavaMethod method, ConditionEvents events) { + for (JavaAccess access : method.getAccessesFromSelf()) { + if (isTargetTypeAccess(access) || isGetterCall(access) || isTargetTypeFieldAccess(access)) { + String message = String.format( + "Method %s.%s() accesses %s at %s", + method.getOwner().getSimpleName(), + method.getName(), + displayName, + access.getDescription() + ); + events.add(SimpleConditionEvent.violated(access, message)); + } + } + } + + private boolean isTargetTypeAccess(JavaAccess access) { + return fullyQualifiedClassName.equals(access.getTargetOwner().getFullName()); + } + + private boolean isGetterCall(JavaAccess access) { + if (!getterMethodName.equals(access.getName())) { + return false; + } + return access.getTargetOwner().isAssignableTo("org.gradle.api.Task"); + } + + private boolean isTargetTypeFieldAccess(JavaAccess access) { + if (access instanceof JavaFieldAccess) { + JavaFieldAccess fieldAccess = (JavaFieldAccess) access; + return fieldAccess.getTarget().getRawType().isAssignableTo(fullyQualifiedClassName); + } + return false; + } + }; + } + + @Override + public Map getRules() { + Map rules = new HashMap<>(); + rules.put("gradle-task-action-project-access", taskActionShouldNotAccessProject); + rules.put("gradle-task-action-task-dependencies", taskActionShouldNotCallGetTaskDependencies); + return rules; + } +} diff --git a/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRuleTest.java b/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRuleTest.java new file mode 100644 index 0000000..19a6be3 --- /dev/null +++ b/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRuleTest.java @@ -0,0 +1,142 @@ +package com.netflix.nebula.archrules.gradleplugins; + +import com.netflix.nebula.archrules.core.Runner; +import com.tngtech.archunit.lang.EvaluationResult; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GradleTaskActionRuleTest { + private static final Logger LOG = LoggerFactory.getLogger(GradleTaskActionRuleTest.class); + + @Test + public void taskActionAccessingProject_should_fail() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotAccessProject, + TaskAccessingProject.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isTrue(); + } + + @Test + public void taskActionCallingGetProject_should_fail() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotAccessProject, + TaskCallingGetProject.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isTrue(); + } + + @Test + public void taskActionNotAccessingProject_should_pass() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotAccessProject, + TaskNotAccessingProject.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isFalse(); + } + + @Test + public void taskActionAccessingProjectInConstructor_should_pass() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotAccessProject, + TaskAccessingProjectInConstructor.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isFalse(); + } + + @Test + public void taskActionCallingGetTaskDependencies_should_fail() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotCallGetTaskDependencies, + TaskCallingGetTaskDependencies.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isTrue(); + } + + @Test + public void taskActionNotCallingGetTaskDependencies_should_pass() { + final EvaluationResult result = Runner.check( + GradleTaskActionRule.taskActionShouldNotCallGetTaskDependencies, + TaskNotCallingGetTaskDependencies.class + ); + LOG.info(result.getFailureReport().toString()); + assertThat(result.hasViolation()).isFalse(); + } + + @SuppressWarnings("unused") + public static abstract class TaskAccessingProject extends DefaultTask { + @TaskAction + public void execute() { + Project project = getProject(); + String version = project.getVersion().toString(); + System.out.println("Version: " + version); + } + } + + @SuppressWarnings("unused") + public static abstract class TaskCallingGetProject extends DefaultTask { + @TaskAction + public void execute() { + getProject(); + System.out.println("Task executed"); + } + } + + @SuppressWarnings("unused") + public static abstract class TaskNotAccessingProject extends DefaultTask { + @Input + public abstract Property getVersion(); + + @TaskAction + public void execute() { + String version = getVersion().get(); + System.out.println("Version: " + version); + } + } + + @SuppressWarnings("unused") + public static abstract class TaskAccessingProjectInConstructor extends DefaultTask { + @Input + public abstract Property getVersion(); + + public TaskAccessingProjectInConstructor() { + getVersion().set(getProject().getVersion().toString()); + } + + @TaskAction + public void execute() { + String version = getVersion().get(); + System.out.println("Version: " + version); + } + } + + @SuppressWarnings("unused") + public static abstract class TaskCallingGetTaskDependencies extends DefaultTask { + @TaskAction + public void execute() { + getTaskDependencies(); + System.out.println("Task executed"); + } + } + + @SuppressWarnings("unused") + public static abstract class TaskNotCallingGetTaskDependencies extends DefaultTask { + @TaskAction + public void execute() { + System.out.println("Task executed"); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index c803853..85c4d90 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,4 +20,5 @@ rootProject.name = "nebula-archrules" include(":archrules-deprecation") include(":archrules-testing-frameworks") include(":archrules-joda") -include(":archrules-nullability") \ No newline at end of file +include(":archrules-nullability") +include(":archrules-gradle-plugin-development") \ No newline at end of file From 26ac0e6ee2caa1313cbe1e27f126a89cb2224f91 Mon Sep 17 00:00:00 2001 From: Roberto Perez Alcolea Date: Thu, 11 Dec 2025 07:52:53 -0800 Subject: [PATCH 2/4] Remove redundant repositories blocks --- archrules-deprecation/build.gradle.kts | 4 +--- archrules-gradle-plugin-development/build.gradle.kts | 5 ++--- archrules-joda/build.gradle.kts | 5 ++--- archrules-testing-frameworks/build.gradle.kts | 4 +--- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/archrules-deprecation/build.gradle.kts b/archrules-deprecation/build.gradle.kts index de14a6d..372da28 100644 --- a/archrules-deprecation/build.gradle.kts +++ b/archrules-deprecation/build.gradle.kts @@ -3,9 +3,7 @@ plugins { id("com.netflix.nebula.archrules.library") } description = "Arch Rules for detecting usage of deprecated code" -repositories { - mavenCentral() -} + dependencies { archRulesImplementation(libs.jspecify) diff --git a/archrules-gradle-plugin-development/build.gradle.kts b/archrules-gradle-plugin-development/build.gradle.kts index 77d570c..e844f58 100644 --- a/archrules-gradle-plugin-development/build.gradle.kts +++ b/archrules-gradle-plugin-development/build.gradle.kts @@ -2,10 +2,9 @@ plugins { id("com.netflix.nebula.library") id("com.netflix.nebula.archrules.library") } + description = "Arch Rules for detecting bad practices when developing Gradle plugins" -repositories { - mavenCentral() -} + dependencies { archRulesImplementation(libs.jspecify) diff --git a/archrules-joda/build.gradle.kts b/archrules-joda/build.gradle.kts index 8059abd..5e4ed92 100644 --- a/archrules-joda/build.gradle.kts +++ b/archrules-joda/build.gradle.kts @@ -2,10 +2,9 @@ plugins { id("com.netflix.nebula.library") id("com.netflix.nebula.archrules.library") } + description = "Arch Rules for detecting usage of Joda" -repositories { - mavenCentral() -} + dependencies { archRulesImplementation(libs.jspecify) diff --git a/archrules-testing-frameworks/build.gradle.kts b/archrules-testing-frameworks/build.gradle.kts index 2a377c1..1729470 100644 --- a/archrules-testing-frameworks/build.gradle.kts +++ b/archrules-testing-frameworks/build.gradle.kts @@ -6,9 +6,7 @@ plugins { id("com.netflix.nebula.archrules.library") } description = "Arch Rules for detecting usage of Junit4 and Testcontainers 1.x" -repositories { - mavenCentral() -} + dependencies { archRulesImplementation(libs.jspecify) From 919fc34717fc67cd864401a631838da41add4b73 Mon Sep 17 00:00:00 2001 From: Roberto Perez Alcolea Date: Thu, 11 Dec 2025 07:55:37 -0800 Subject: [PATCH 3/4] mark rules as final --- .../nebula/archrules/deprecation/DeprecationRule.java | 4 ++-- .../archrules/gradleplugins/GradleTaskActionRule.java | 4 ++-- .../java/com/netflix/nebula/archrules/joda/JodaRule.java | 2 +- .../nebula/archrules/testingframeworks/JUnit4Rule.java | 2 +- .../archrules/testingframeworks/Testcontainers1xRule.java | 6 +++--- .../archrules/testingframeworks/Testcontainers2xRule.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java b/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java index 546a9d2..7307714 100644 --- a/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java +++ b/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java @@ -26,7 +26,7 @@ public class DeprecationRule implements ArchRulesService { * - Kotlin @Deprecated annotations * - Kotlin @DeprecatedSinceKotlin annotations */ - public static ArchRule deprecationRule = ArchRuleDefinition.priority(Priority.LOW) + public static final ArchRule deprecationRule = ArchRuleDefinition.priority(Priority.LOW) .noClasses() // Java deprecated .should().dependOnClassesThat().areAnnotatedWith(Deprecated.class) @@ -58,7 +58,7 @@ public boolean test(JavaAnnotation annotation) { } }; - public static ArchRule deprecationForRemovalRule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule deprecationForRemovalRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should().dependOnClassesThat().areAnnotatedWith(deprecatedForRemoval) .orShould().accessTargetWhere(targetOwner(annotatedWith(deprecatedForRemoval))) diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java index 4483e4b..e841371 100644 --- a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java @@ -29,7 +29,7 @@ public class GradleTaskActionRule implements ArchRulesService { * cause runtime errors in Gradle 10+. Move Project access to configuration time * (constructor/initializer) and use task properties instead. */ - public static ArchRule taskActionShouldNotAccessProject = createTaskActionRule( + public static final ArchRule taskActionShouldNotAccessProject = createTaskActionRule( notAccessProject(), "access the Project object", "Accessing Project in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + @@ -43,7 +43,7 @@ public class GradleTaskActionRule implements ArchRulesService { * Calling {@code getTaskDependencies()} in task actions breaks configuration cache and will * cause runtime errors in Gradle 10+. Task dependencies should be declared at configuration time. */ - public static ArchRule taskActionShouldNotCallGetTaskDependencies = createTaskActionRule( + public static final ArchRule taskActionShouldNotCallGetTaskDependencies = createTaskActionRule( notCallGetTaskDependencies(), "call getTaskDependencies()", "Calling getTaskDependencies() in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + diff --git a/archrules-joda/src/archRules/java/com/netflix/nebula/archrules/joda/JodaRule.java b/archrules-joda/src/archRules/java/com/netflix/nebula/archrules/joda/JodaRule.java index 1e025c5..0af8c46 100644 --- a/archrules-joda/src/archRules/java/com/netflix/nebula/archrules/joda/JodaRule.java +++ b/archrules-joda/src/archRules/java/com/netflix/nebula/archrules/joda/JodaRule.java @@ -15,7 +15,7 @@ public class JodaRule implements ArchRulesService { /** * This rule is a stop-gap to find all usages of Joda. */ - public static ArchRule jodaRule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule jodaRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should(GeneralCodingRules.USE_JODATIME) .allowEmptyShould(true) diff --git a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/JUnit4Rule.java b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/JUnit4Rule.java index 363ef0a..c2e27ae 100644 --- a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/JUnit4Rule.java +++ b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/JUnit4Rule.java @@ -17,7 +17,7 @@ public class JUnit4Rule implements ArchRulesService { /** * This rule is a stop-gap to find all usages of JUnit4. */ - public static ArchRule junit4Rule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule junit4Rule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should().dependOnClassesThat(resideInAPackage("org.junit..") .and(resideOutsideOfPackage("org.junit.jupiter.."))) diff --git a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers1xRule.java b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers1xRule.java index 2043c13..32b4e14 100644 --- a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers1xRule.java +++ b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers1xRule.java @@ -44,7 +44,7 @@ public class Testcontainers1xRule implements ArchRulesService { *

* The fix: Replace {@code DockerComposeContainer} with {@code ComposeContainer} everywhere. */ - public static ArchRule dockerComposeContainerRule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule dockerComposeContainerRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should().dependOnClassesThat().haveSimpleName("DockerComposeContainer") .allowEmptyShould(true) @@ -60,7 +60,7 @@ public class Testcontainers1xRule implements ArchRulesService { *

* The fix: Change {@code container.getContainerIpAddress()} to {@code container.getHost()}. */ - public static ArchRule containerIpAddressMethodRule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule containerIpAddressMethodRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should().callMethod("org.testcontainers.containers.ContainerState", "getContainerIpAddress") .allowEmptyShould(true) @@ -85,7 +85,7 @@ public class Testcontainers1xRule implements ArchRulesService { * *

*/ - public static ArchRule noArgConstructorRule = ArchRuleDefinition.priority(Priority.MEDIUM) + public static final ArchRule noArgConstructorRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() .should().callConstructorWhere(target(new DescribedPredicate("no-arg Testcontainers container constructor") { @Override diff --git a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers2xRule.java b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers2xRule.java index aef6983..4a0fa8e 100644 --- a/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers2xRule.java +++ b/archrules-testing-frameworks/src/archRules/java/com/netflix/nebula/archrules/testingframeworks/Testcontainers2xRule.java @@ -45,7 +45,7 @@ public class Testcontainers2xRule implements ArchRulesService { * * @see TestcontainersContainerModuleMapper for the full container-to-module mapping */ - public static ArchRule legacyContainerPackageRule = ArchRuleDefinition.priority(Priority.LOW) + public static final ArchRule legacyContainerPackageRule = ArchRuleDefinition.priority(Priority.LOW) .classes() .should(notDependOnDeprecatedContainerPackages()) .allowEmptyShould(true) From 0d9f5b270b10fae168679761f20fa05d58f61057 Mon Sep 17 00:00:00 2001 From: John Burns Date: Thu, 11 Dec 2025 11:07:27 -0600 Subject: [PATCH 4/4] make rules a little more ideomatic and reduce redundant message strings by leveraging automatic message generation (#23) --- .editorconfig | 23 ++++++++++ .../gradleplugins/GradleTaskActionRule.java | 44 +++++++++---------- 2 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..07b230b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +ij_continuation_indent_size = 8 +tab_width = 4 +trim_trailing_whitespace = true + +[{*.kt,*.kts}] +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_name_count_to_use_star_import_for_members = 20 +ij_kotlin_name_count_to_use_star_import = 20 +ij_kotlin_continuation_indent_size = 4 +ktlint_code_style=intellij_idea + +[*.java] +ij_java_names_count_to_use_import_on_demand = 20 +ij_java_class_count_to_use_import_on_demand = 20 \ No newline at end of file diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java index e841371..a13f079 100644 --- a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskActionRule.java @@ -29,13 +29,16 @@ public class GradleTaskActionRule implements ArchRulesService { * cause runtime errors in Gradle 10+. Move Project access to configuration time * (constructor/initializer) and use task properties instead. */ - public static final ArchRule taskActionShouldNotAccessProject = createTaskActionRule( - notAccessProject(), - "access the Project object", - "Accessing Project in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + - "Move Project access to task configuration time and use task inputs/properties instead.", - "task_project" - ); + public static final ArchRule taskActionShouldNotAccessProject = ArchRuleDefinition.priority(Priority.MEDIUM) + .methods() + .that(areAnnotatedWithTaskAction()) + .should(notAccessProject()) + .allowEmptyShould(true) + .because( + "Accessing Project in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + + "Move Project access to task configuration time and use task inputs/properties instead. " + + "See https://docs.gradle.org/9.2.0/userguide/upgrading_version_7.html#task_project" + ); /** * Prevents {@code @TaskAction} methods from calling {@code getTaskDependencies()}. @@ -43,23 +46,16 @@ public class GradleTaskActionRule implements ArchRulesService { * Calling {@code getTaskDependencies()} in task actions breaks configuration cache and will * cause runtime errors in Gradle 10+. Task dependencies should be declared at configuration time. */ - public static final ArchRule taskActionShouldNotCallGetTaskDependencies = createTaskActionRule( - notCallGetTaskDependencies(), - "call getTaskDependencies()", - "Calling getTaskDependencies() in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + - "Declare task dependencies at configuration time instead.", - "task_dependencies" - ); - - private static ArchRule createTaskActionRule(ArchCondition condition, String actionDescription, String reason, String docAnchor) { - return ArchRuleDefinition.priority(Priority.MEDIUM) - .methods() - .that(areAnnotatedWithTaskAction()) - .should(condition) - .allowEmptyShould(true) - .as("Methods annotated with @TaskAction should not " + actionDescription) - .because(reason + " See https://docs.gradle.org/9.2.0/userguide/upgrading_version_7.html#" + docAnchor); - } + public static final ArchRule taskActionShouldNotCallGetTaskDependencies = ArchRuleDefinition.priority(Priority.MEDIUM) + .methods() + .that(areAnnotatedWithTaskAction()) + .should(notCallGetTaskDependencies()) + .allowEmptyShould(true) + .because( + "Calling getTaskDependencies() in @TaskAction methods breaks configuration cache and will be removed in Gradle 10. " + + "Declare task dependencies at configuration time instead. " + + "See https://docs.gradle.org/9.2.0/userguide/upgrading_version_7.html#task_dependencies" + ); private static DescribedPredicate areAnnotatedWithTaskAction() { return new DescribedPredicate("are annotated with @TaskAction") {