build: add ArchUnit, JaCoCo, Spotless, SpotBugs, Error Prone/NullAway, forbidden-apis, CPD, dependency-analysis

This commit is contained in:
loki5512344 2026-10-06 13:50:25 +02:00
parent 7e227e39a4
commit b771cc1c98
Signed by: boba
GPG key ID: 253067914055423B
10 changed files with 275 additions and 2 deletions

View file

@ -1,3 +1,4 @@
import net.ltgt.gradle.errorprone.errorprone
import java.io.File
import java.net.URI
import java.util.zip.ZipFile
@ -15,11 +16,18 @@ buildscript {
}
plugins {
id("java")
id("java-library")
id("com.gradleup.shadow") version("9.6.1")
id("xyz.jpenilla.run-paper") version("3.0.2")
checkstyle
pmd
jacoco
id("net.ltgt.errorprone") version("4.3.0")
id("com.diffplug.spotless") version("7.0.4")
id("com.github.spotbugs") version("6.1.13")
id("de.thetaphi.forbiddenapis") version("3.9")
id("com.autonomousapps.dependency-analysis") version("2.19.0")
id("org.owasp.dependencycheck") version("12.1.3")
}
group = "dev.loki"
@ -144,6 +152,8 @@ val remapNms by tasks.registering {
}
tasks.withType<JavaCompile>().configureEach { dependsOn(remapNms) }
// Analysis tasks (dependency-analysis, spotbugs, ...) read the remapped NMS jars too.
tasks.matching { it.name != "remapNms" && it.name != "clean" }.configureEach { mustRunAfter(remapNms) }
// Resolve the org.bukkit:bukkit capability conflict that newer paper-api artifacts
// introduce (they both bundle the bukkit classes and declare the capability).
@ -201,11 +211,16 @@ dependencies {
compileOnly("me.filoghost.holographicdisplays:holographicdisplays-api:3.0.0-SNAPSHOT")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
// Error Prone + NullAway (compile-time bug/null-safety checks)
errorprone("com.google.errorprone:error_prone_core:2.41.0")
errorprone("com.uber.nullaway:nullaway:0.12.10")
// Annotations
compileOnly("org.jetbrains:annotations:24.1.0")
// Testing
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0-M1")
testImplementation("com.tngtech.archunit:archunit-junit5:1.4.1")
testImplementation("org.mockito:mockito-core:5.10.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.10.0")
testImplementation("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT")
@ -246,6 +261,17 @@ tasks.withType<Test> {
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release = javaRelease
options.errorprone {
disableWarningsInGeneratedCode = true
// NullAway: only code annotated @NullMarked-style packages is checked once adopted;
// start as warnings so the baseline is visible but does not block.
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.WARN)
option("NullAway:AnnotatedPackages", "dev.loki.loparkour")
option("NullAway:ExcludedFieldAnnotations", "org.jetbrains.annotations.Nullable")
}
if (name == "compileTestJava") {
options.errorprone.isEnabled.set(false)
}
}
tasks.withType<Javadoc> {
@ -299,5 +325,94 @@ tasks.withType<Pmd>().configureEach {
exclude("**/build/generated/**")
}
// ---------------------------------------------------------------------------
// Extra quality gates (see TODO.md, task 2). Existing debt is recorded, new
// violations fail the build; tighten thresholds as the refactor progresses.
// ---------------------------------------------------------------------------
jacoco {
toolVersion = "0.8.13"
}
tasks.test {
finalizedBy(tasks.jacocoTestReport)
}
tasks.jacocoTestReport {
dependsOn(tasks.test)
reports {
xml.required.set(true)
html.required.set(true)
}
}
tasks.jacocoTestCoverageVerification {
violationRules {
rule {
limit {
counter = "LINE"
minimum = "0.05".toBigDecimal() // baseline; raise toward 0.5 (core 0.7)
}
}
}
}
tasks.check {
dependsOn(tasks.jacocoTestCoverageVerification, "cpdCheck")
}
spotless {
java {
target("src/**/*.java")
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
}
spotbugs {
toolVersion = "4.9.3"
effort = com.github.spotbugs.snom.Effort.MAX
reportLevel = com.github.spotbugs.snom.Confidence.MEDIUM
ignoreFailures = true // report-only until the baseline is triaged
}
tasks.withType<com.github.spotbugs.snom.SpotBugsTask>().configureEach {
reports.create("html") { required = true }
}
forbiddenApis {
bundledSignatures = setOf("jdk-unsafe-21", "jdk-deprecated-21", "jdk-system-out", "jdk-non-portable")
ignoreFailures = true // report-only until the baseline is triaged
}
// Copy/paste detector (DRY). The de.aaschmid.cpd plugin does not support PMD 7, so run PMD's CPD directly.
val cpdTool: Configuration by configurations.creating
dependencies {
cpdTool("net.sourceforge.pmd:pmd-cli:7.0.0")
cpdTool("net.sourceforge.pmd:pmd-java:7.0.0")
}
val cpdCheck by tasks.registering(JavaExec::class) {
group = "verification"
description = "Finds duplicated code (min 100 tokens). Report-only until duplicates are triaged."
classpath = cpdTool
mainClass.set("net.sourceforge.pmd.cli.PmdCli")
args("cpd", "--minimum-tokens", "100", "--language", "java", "--dir", "src/main/java",
"--format", "text")
isIgnoreExitValue = true // exit code 4 = duplicates found
doFirst {
val dir = layout.buildDirectory.dir("reports/cpd").get().asFile
dir.mkdirs()
standardOutput = File(dir, "cpd.txt").outputStream()
}
}
dependencyCheck {
// Run explicitly: ./gradlew dependencyCheckAnalyze (set NVD_API_KEY for speed).
failBuildOnCVSS = 7.0f
nvd.apiKey = System.getenv("NVD_API_KEY")
}
// Default task
defaultTasks("clean", "build")