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
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
# Nebula ArchRules

This repository contains several libraries of ArchRules which can be used in projects by using the [ArchRules Runner](https://github.com/nebula-plugins/nebula-archrules-plugin?tab=readme-ov-file#running-rules) plugin.
## ArchRule Building Blocks
This repository contains the `archrules-common` library which contains ArchUnit primitives (DescribedPredicates, ChainableFunctions, and ArchConditions) which assist in writing rules. These extend what comes out-of-the box in ArchUnit to cover more cases.

[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-common?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-common/)

## Deprecation Rules
## ArchRule Libraries
This repository also contains several libraries of ArchRules which can be used in projects by using the [ArchRules Runner](https://github.com/nebula-plugins/nebula-archrules-plugin?tab=readme-ov-file#running-rules) plugin.


### Deprecation Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-deprecation?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-deprecation/)

## Gradle Plugin Development
### Gradle Plugin Development
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-gradle-plugin-development?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-gradle-plugin-development/)

These rules enforce best practices when developing Gradle plugins.

## Guava Rules
### Guava Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-guava?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-guava/)

These rules detect the usage of certain APIs from Guava which have standard library replacements.

## Javax Rules
### Javax Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-joda?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-javax/)

These rules enforce the usage of `jakarta` over `javax`.

## Joda Rules
### Joda Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-joda?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-joda/)

These rules enforce the usage of `java.time` over Joda Time.

## Nullability Rules
### Nullability Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-nullability?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-nullability/)

These rules enforce JSpecify nullability annotations on public code. Kotlin classes are exempt from the rule, as Kotlin has nullability built into its type system, which is compatible with JSpecify.

## Security Rules
### Security Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-security?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-archrules-security/)

These rules ensure calls are not made to known insecure OSS Java APIs.

## Testing Frameworks Rules
### Testing Frameworks Rules
[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-testing-frameworks?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-testing-frameworks/)

These rules enforce upgrading to JUnit Jupiter.
Expand Down
34 changes: 34 additions & 0 deletions archrules-common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
plugins {
id("com.netflix.nebula.library")
}
description = "Common Predicates and Chanable Functions for building rules"

dependencies {
implementation(libs.jspecify)
api("com.tngtech.archunit:archunit:1.+")

testImplementation(libs.assertj)
testImplementation(libs.logback)
testImplementation("org.jetbrains.kotlin:kotlin-stdlib:2.2.0")
testImplementation("com.netflix.nebula:nebula-archrules-core:0.+")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
}
tasks.named<JavaCompile>("compileTestJava") {
javaCompiler.set(javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(11))
})
}
dependencyLocking {
lockAllConfigurations()
}
testing {
suites {
named<JvmTestSuite>("test") {
useJUnitJupiter()
}
}
}
30 changes: 30 additions & 0 deletions archrules-common/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
ch.qos.logback:logback-core:1.5.20=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
com.netflix.nebula:archrules-deprecation:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime
com.netflix.nebula:archrules-joda:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime
com.netflix.nebula:archrules-nullability:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime
com.netflix.nebula:archrules-security:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime
com.netflix.nebula:archrules-testing-frameworks:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime
com.netflix.nebula:nebula-archrules-core:0.5.5=archRules,mainArchRulesRuntime
com.netflix.nebula:nebula-archrules-core:0.7.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
com.tngtech.archunit:archunit:1.4.1=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.assertj:assertj-core:3.27.6=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.2.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.jetbrains:annotations:13.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.12.2=testArchRulesRuntime,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.12.2=testArchRulesRuntime,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.12.2=testArchRulesRuntime,testRuntimeClasspath
org.junit:junit-bom:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import org.jspecify.annotations.NullMarked;

import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;

@NullMarked
public class CanBeAnnotated {
public static class Predicates {

/**
* Annotated with Java or Kotlin deprecation annotations
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.properties.CanBeAnnotated> deprecated() {
return annotatedWith(Deprecated.class)
.or(annotatedWith("kotlin.Deprecated"))
.or(annotatedWith("kotlin.DeprecatedSinceKotlin"))
.as("deprecated");
}

/**
* Annotated with Java deprecation annotation with the forRemoval property set to true
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.properties.CanBeAnnotated> deprecatedForRemoval() {
return annotatedWith(JavaAnnotation.Predicates.deprecatedForRemoval())
.as("deprecated for removal");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class Dependency {
public static class Predicates {

/**
* tests that a class and a dependency are in the same package
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.Dependency> resideInSamePackage() {
return new DescribedPredicate<com.tngtech.archunit.core.domain.Dependency>("reside in same package") {
@Override
public boolean test(com.tngtech.archunit.core.domain.Dependency dependency) {
return dependency.getOriginClass().getPackageName()
.equals(dependency.getTargetClass().getPackageName());
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class JavaAccess {
public static class Predicates {

/**
* tests that an access's origin and target are in the same package
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.JavaAccess<?>> targetHasOwnerInSamePackage() {
return new DescribedPredicate<com.tngtech.archunit.core.domain.JavaAccess<?>>(
"in the same package") {
@Override
public boolean test(com.tngtech.archunit.core.domain.JavaAccess javaAccess) {
return javaAccess.getOriginOwner().getPackage()
.equals(javaAccess.getTargetOwner().getPackage());
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class JavaAnnotation {
public static class Predicates {

/**
* Java deprecation annotation with the forRemoval property set to true
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.JavaAnnotation<?>> deprecatedForRemoval() {
return new DescribedPredicate<com.tngtech.archunit.core.domain.JavaAnnotation<?>>("@Deprecated(forRemoval=true)") {
@Override
public boolean test(com.tngtech.archunit.core.domain.JavaAnnotation<?> annotation) {
if (!annotation.getRawType().isAssignableTo(Deprecated.class)) {
return false;
}
return annotation.get("forRemoval")
.filter(it -> it instanceof Boolean)
.map(value -> (Boolean) value)
.orElse(false);
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.JavaModifier;
import org.jspecify.annotations.NullMarked;

/**
* Matches getter methods (getX(), isX()) that follow JavaBeans naming conventions.
*/
@NullMarked
class JavaBeanGetterPredicate extends DescribedPredicate<JavaMethod> {

JavaBeanGetterPredicate() {
super("getter");
}

@Override
public boolean test(JavaMethod input) {
if (input.getModifiers().contains(JavaModifier.STATIC)) {
return false;
}
if (!input.getParameters().isEmpty()) {
return false;
}
if (input.getName().startsWith("get")) {
return input.getName().length() >= 4 && !Character.isLowerCase(input.getName().charAt(3));
}
if (input.getName().startsWith("is")) {
if (input.getRawReturnType().isAssignableTo(Boolean.class)) {
return false;
}
return !Character.isLowerCase(input.getName().charAt(2));
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.Dependency;
import com.tngtech.archunit.core.domain.JavaPackage;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.conditions.NebulaAnyDependencyCondition;
import org.jspecify.annotations.NullMarked;

import java.lang.annotation.Annotation;

import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_FROM_SELF;
import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_PACKAGE;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.is;

@NullMarked
public class JavaClass {
public static class Predicates {
/**
* evaluates the predicate on the class's package
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.JavaClass> resideInAPackageThat(
DescribedPredicate<? super JavaPackage> condition) {
return condition.onResultOf(GET_PACKAGE)
.as("residing in package that %s", condition.getDescription());
}

/**
* checks if the class's package is annotated with a specific annotation
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.JavaClass> resideInPackageAnnotatedWith(
Class<? extends Annotation> annotationClass
) {
return resideInAPackageThat(is(annotatedWith(annotationClass)));
}
}

public static class Conditions {
/**
* Can be removed once <a href="https://github.com/TNG/ArchUnit/pull/1580">haveAnyDependenciesThat</a> is merged.
*/
@Deprecated
public static ArchCondition<com.tngtech.archunit.core.domain.JavaClass> haveAnyDependenciesThat(
DescribedPredicate<? super Dependency> predicate) {
return new NebulaAnyDependencyCondition(
"have any dependencies that " + predicate.getDescription(),
predicate,
GET_DIRECT_DEPENDENCIES_FROM_SELF);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.netflix.nebula.archrules.common;

import com.tngtech.archunit.base.DescribedPredicate;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class JavaMethod {
public static class Predicates {

/**
* checks if a method is a getter according to JavaBean conventions
*/
public static DescribedPredicate<com.tngtech.archunit.core.domain.JavaMethod> aGetter() {
return new JavaBeanGetterPredicate();
}
}
}
Loading