-
-
Notifications
You must be signed in to change notification settings - Fork 146
Expand file tree
/
Copy pathbuild.gradle.kts
More file actions
507 lines (441 loc) · 21.8 KB
/
build.gradle.kts
File metadata and controls
507 lines (441 loc) · 21.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
/**
* BentoBox Gradle Build Configuration
*
* This build script configures the compilation, testing, packaging, and publishing
* of the BentoBox Minecraft plugin. It handles:
* - Java 21 compilation with proper module access
* - Multi-repository dependency resolution
* - JAR shading and minimization
* - Test execution with JUnit 5
* - Code coverage reporting with JaCoCo
* - Maven publication to the BentoBox repository
*/
// ============================================================================
// PLUGINS: Core build functionality
// ============================================================================
// Apply necessary plugins for Java development, publishing, testing, and shading
plugins {
// Standard Java development plugin - provides compile, test, jar tasks
java
// Maven Publishing - allows publishing artifacts to Maven repositories
`maven-publish`
// JaCoCo (Java Code Coverage) - generates code coverage reports for CI/CD
id("jacoco")
// Shadow Plugin - shades (embeds) dependencies into the final JAR and minimizes unused code
id("com.gradleup.shadow") version "9.3.0"
// Paperweight UserDev - simplifies development against PaperMC with proper mappings and reobfuscation
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
// Sonarcube
id("org.sonarqube") version "7.2.1.6560"
}
// Add paperweight reobf configuration so the userdev plugin reobfuscates artifacts
// using the Mojang production mappings as required by paperweight-userdev.
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
// ============================================================================
// PROJECT COORDINATES & VERSIONING
// ============================================================================
// These properties define the artifact's identity in the Maven repository
group = "world.bentobox" // From <groupId>
// Base properties from <properties>
val buildVersion = "3.15.0"
val buildNumberDefault = "-LOCAL" // Local build identifier
val snapshotSuffix = "-SNAPSHOT" // Indicates development/snapshot version
// CI/CD Logic (Translates Maven <profiles>)
// Default version format: 3.10.2-LOCAL-SNAPSHOT
var finalBuildNumber = buildNumberDefault
var finalRevision = "$buildVersion$snapshotSuffix$finalBuildNumber"
// 'ci' profile logic: Activated by env.BUILD_NUMBER from CI/CD pipeline
// Overrides build number with actual CI build number
val envBuildNumber = System.getenv("BUILD_NUMBER")
if (!envBuildNumber.isNullOrBlank()) {
finalBuildNumber = "-b$envBuildNumber"
finalRevision = "$buildVersion$snapshotSuffix"
}
// 'master' profile logic: Activated when building from origin/master branch
// Removes -LOCAL and -SNAPSHOT suffixes for release builds
val envGitBranch = System.getenv("GIT_BRANCH")
if (envGitBranch == "origin/master") {
finalBuildNumber = "" // No build number for releases
finalRevision = buildVersion // Clean version number
}
version = finalRevision
// ============================================================================
// DEPENDENCY VERSIONS
// ============================================================================
// Centralized version management for all external dependencies
val javaVersion = "21"
val junitVersion = "5.10.2"
val mockitoVersion = "5.11.0"
val mockBukkitVersion = "v1.21-SNAPSHOT"
val mongodbVersion = "3.12.12"
val mariadbVersion = "3.0.5"
val mysqlVersion = "8.0.27"
val postgresqlVersion = "42.2.18"
val hikaricpVersion = "5.0.1"
val paperVersion = "1.21.11-R0.1-SNAPSHOT"
val bstatsVersion = "3.0.0"
val vaultVersion = "1.7.1"
val levelVersion = "2.21.3"
val placeholderapiVersion = "2.11.7"
val myworldsVersion = "1.19.3-v1"
val awaitilityVersion = "4.2.2"
val mythicDistVersion = "5.9.5"
val multiverseCore5Version = "5.0.0-SNAPSHOT"
val multiverseCore4Version = "4.3.16"
val langUtilsVersion = "3.2.2"
val slimefun4Version = "RC-37"
val itemsAdderVersion = "4.0.2-beta-release-11"
val fancyNpcsVersion = "2.4.4"
val znpcsplusApiVersion = "2.0.0-SNAPSHOT"
val fancyHologramsVersion = "2.4.1"
val commonsLangVersion = "2.6"
val jaxbApiVersion = "2.3.0"
val gsonRecordTypeAdapterFactoryVersion = "0.3.0"
val jdtAnnotationVersion = "2.2.600"
val multilibVersion = "1.1.13"
val oraxenVersion = "1.193.1"
val blueMapApiVersion = "v2.6.2"
val dynmapApiVersion = "3.4"
// Store versions in extra properties for resource filtering (used in plugin.yml, config.yml)
extra["java.version"] = javaVersion
extra["junit.version"] = junitVersion
extra["mockito.version"] = mockitoVersion
extra["mock-bukkit.version"] = mockBukkitVersion
extra["mongodb.version"] = mongodbVersion
extra["mariadb.version"] = mariadbVersion
extra["mysql.version"] = mysqlVersion
extra["postgresql.version"] = postgresqlVersion
extra["hikaricp.version"] = hikaricpVersion
extra["paper.version"] = paperVersion
extra["bstats.version"] = bstatsVersion
extra["vault.version"] = vaultVersion
extra["level.version"] = levelVersion
extra["placeholderapi.version"] = placeholderapiVersion
extra["myworlds.version"] = myworldsVersion
extra["build.version"] = buildVersion
extra["build.number"] = finalBuildNumber
extra["revision"] = finalRevision
// ============================================================================
// JAVA CONFIGURATION
// ============================================================================
// Configures Java compiler and toolchain settings
java {
// Use Java 21 toolchain for compilation (enforced regardless of JVM running Gradle)
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
tasks.withType<JavaCompile> {
// Ensure UTF-8 encoding for all source files
options.encoding = "UTF-8"
// Suppress all deprecation and removal warnings during compilation
options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal"))
// Set explicit Java release version for Eclipse compatibility
options.release.set(21)
}
tasks.compileTestJava {
// Ensure UTF-8 encoding for all source files
options.encoding = "UTF-8"
// Suppress all deprecation and removal warnings during compilation
options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal"))
// Set explicit Java release version for Eclipse compatibility
options.release.set(21)
}
// ============================================================================
// REPOSITORIES
// ============================================================================
// Defines where dependencies are downloaded from (in order of precedence)
repositories {
// Gradle Plugin Portal - for resolving Gradle plugins
gradlePluginPortal()
// PaperMC Maven Repository - for Paper API and related libraries
maven("https://repo.papermc.io/repository/maven-public/") { name = "PaperMC" } // Paper API
// Standard Maven Central Repository - most common Java libraries
mavenCentral()
// Custom repositories for Minecraft and plugin-specific libraries
maven("https://jitpack.io") { name = "JitPack" } // GitHub repository packages
maven("https://repo.codemc.org/repository/maven-public") { name = "CodeMC-Public" }
maven("https://libraries.minecraft.net/") { name = "MinecraftLibs" } // Official Minecraft libraries
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { name = "Spigot-Snapshots" }
maven("https://repo.codemc.io/repository/nms/") { name = "NMS-Repo" } // NMS (internal Minecraft code)
maven("https://ci.mg-dev.eu/plugin/repository/everything") { name = "MG-Dev-CI" }
maven("https://repo.onarandombox.com/multiverse-releases") { name = "Multiverse-Releases" }
maven("https://repo.onarandombox.com/multiverse-snapshots") { name = "Multiverse-Snapshots" }
maven("https://mvn.lumine.io/repository/maven-public/") { name = "Lumine-Releases" } // Mythic mobs
maven("https://repo.clojars.org/") { name = "Clojars" }
maven("https://repo.fancyplugins.de/releases") { name = "FancyPlugins-Releases" }
maven("https://repo.pyr.lol/snapshots") { name = "Pyr-Snapshots" }
maven("https://maven.devs.beer/") { name = "MatteoDev" }
maven("https://repo.mikeprimm.com/") { name = "Dynmap" }
maven("https://repo.oraxen.com/releases") { name = "Oraxen" } // Custom items plugin
maven("https://repo.codemc.org/repository/bentoboxworld/") { name = "BentoBoxWorld-Repo" }
maven("https://repo.extendedclip.com/releases/") { name = "Placeholder-API-Releases" }
}
// ============================================================================
// DEPENDENCIES
// ============================================================================
// Defines all external libraries needed for compilation and testing
dependencies {
// --- Test Dependencies: Only used during testing, not in production ---
testImplementation(platform("org.junit:junit-bom:$junitVersion"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$junitVersion")
testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion")
testImplementation("org.mockito:mockito-core:$mockitoVersion")
testImplementation("com.github.MockBukkit:MockBukkit:$mockBukkitVersion")
testImplementation("org.awaitility:awaitility:$awaitilityVersion")
testImplementation("io.papermc.paper:paper-api:$paperVersion")
testImplementation("com.github.MilkBowl:VaultAPI:$vaultVersion")
testImplementation("me.clip:placeholderapi:$placeholderapiVersion")
testImplementation("commons-lang:commons-lang:$commonsLangVersion")
// --- Compile Only Dependencies: Provided by the server at runtime ---
compileOnly("org.mongodb:mongodb-driver:$mongodbVersion")
compileOnly("com.zaxxer:HikariCP:$hikaricpVersion")
testImplementation("com.zaxxer:HikariCP:$hikaricpVersion")
compileOnly("com.github.MilkBowl:VaultAPI:$vaultVersion")
compileOnly("me.clip:placeholderapi:$placeholderapiVersion")
compileOnly("com.bergerkiller.bukkit:MyWorlds:$myworldsVersion") {
exclude(group = "org.spigotmc", module = "spigot-api")
}
compileOnly("io.lumine:Mythic-Dist:$mythicDistVersion")
compileOnly("org.mvplugins.multiverse.core:multiverse-core:$multiverseCore5Version")
compileOnly("com.onarandombox.multiversecore:multiverse-core:$multiverseCore4Version") {
exclude(group = "org.spigotmc", module = "spigot-api")
}
compileOnly("com.github.apachezy:LangUtils:$langUtilsVersion")
compileOnly("com.github.Slimefun:Slimefun4:$slimefun4Version")
compileOnly("dev.lone:api-itemsadder:$itemsAdderVersion")
compileOnly("de.oliver:FancyNpcs:$fancyNpcsVersion")
compileOnly("lol.pyr:znpcsplus-api:$znpcsplusApiVersion")
compileOnly("de.oliver:FancyHolograms:$fancyHologramsVersion")
compileOnly("world.bentobox:level:$levelVersion-SNAPSHOT")
compileOnly("commons-lang:commons-lang:$commonsLangVersion")
compileOnly("com.github.BlueMap-Minecraft:BlueMapAPI:$blueMapApiVersion")
testImplementation("com.github.BlueMap-Minecraft:BlueMapAPI:$blueMapApiVersion")
compileOnly("us.dynmap:DynmapCoreAPI:$dynmapApiVersion")
compileOnly("us.dynmap:dynmap-api:$dynmapApiVersion") {
exclude(group = "org.bukkit", module = "bukkit")
}
testImplementation("us.dynmap:DynmapCoreAPI:$dynmapApiVersion")
testImplementation("us.dynmap:dynmap-api:$dynmapApiVersion") {
exclude(group = "org.bukkit", module = "bukkit")
}
compileOnly("io.th0rgal:oraxen:$oraxenVersion") {
exclude(group = "me.gabytm.util", module = "actions-spigot")
exclude(group = "org.jetbrains", module = "annotations")
exclude(group = "com.ticxo", module = "PlayerAnimator")
exclude(group = "com.github.stefvanschie.inventoryframework", module = "IF")
exclude(group = "io.th0rgal", module = "protectionlib")
exclude(group = "dev.triumphteam", module = "triumph-gui")
exclude(group = "org.bstats", module = "bstats-bukkit")
exclude(group = "com.jeff-media", module = "custom-block-data")
exclude(group = "com.jeff-media", module = "persistent-data-serializer")
exclude(group = "com.jeff-media", module = "MorePersistentDataTypes")
exclude(group = "gs.mclo", module = "java")
}
// --- Implementation Dependencies: Shaded into final JAR ---
implementation("org.bstats:bstats-bukkit:$bstatsVersion")
implementation("javax.xml.bind:jaxb-api:$jaxbApiVersion")
implementation("com.github.Marcono1234:gson-record-type-adapter-factory:$gsonRecordTypeAdapterFactoryVersion")
implementation("org.eclipse.jdt:org.eclipse.jdt.annotation:$jdtAnnotationVersion")
implementation("com.github.puregero:multilib:$multilibVersion")
// --- Paperweight Development Bundle (Provided by plugin development tools) ---
paperweight.paperDevBundle(paperVersion)
}
paperweight {
addServerDependencyTo = configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME).map { setOf(it) }
javaLauncher = javaToolchains.launcherFor {
// Use the project's configured Java version for paperweight tools (needs Java 21+)
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
sonar {
properties {
property("sonar.projectKey", "BentoBoxWorld_BentoBox")
property("sonar.organization", "bentobox-world")
}
}
// ============================================================================
// RESOURCE PROCESSING
// ============================================================================
// Filters and copies resources (plugin.yml, config files, locales) to build output
tasks.processResources {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(sourceSets.main.get().resources.srcDirs)
// Replace variables in plugin.yml and config.yml with actual version strings
// This allows version info to be read at runtime by the plugin
filesMatching(listOf("plugin.yml", "config.yml")) {
filter { line ->
line.replace($$"${mysql.version}", mysqlVersion)
.replace($$"${mariadb.version}", mariadbVersion)
.replace($$"${postgresql.version}", postgresqlVersion)
.replace($$"${mongodb.version}", mongodbVersion)
.replace($$"${hikaricp.version}", hikaricpVersion)
.replace($$"${build.number}", finalBuildNumber)
.replace($$"${project.version}", project.version.toString())
.replace($$"${project.description}", project.description ?: "")
.replace($$"${revision}", project.version.toString())
}
}
finalizedBy("copyLocales")
}
// Copy locale files without filtering (prevents corruption of translation files)
tasks.register<Copy>("copyLocales") {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from("src/main/resources/locales")
into("${tasks.processResources.get().destinationDir}/locales")
}
// Ensure test compilation waits for locale files to be copied
tasks.compileTestJava {
dependsOn("copyLocales")
}
// Set the final JAR filename to match project name and version
tasks.jar {
archiveFileName.set("${project.name}-${project.version}.jar")
dependsOn("copyLocales") // Explicit dependency for Gradle 9.0+ strict validation
}
// ============================================================================
// JAR SHADING & MINIMIZATION
// ============================================================================
// Shadow Plugin: Embeds dependencies into JAR and removes unused code
// This creates a "fat JAR" with all required dependencies
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
// Enable minimization: removes unused classes/methods from shaded dependencies
// Reduces JAR size significantly
minimize()
// Exclude these artifacts from being shaded (already provided by server or incompatible)
exclude(
"org.apache.maven:*:*", // Maven tools not needed at runtime
"com.google.code.gson:*:*", // Often provided by server
"org.mongodb:*:*", // Optional dependency
"org.eclipse.jdt:*:*" // Optional dependency
)
// Relocate (rename) packages to avoid conflicts with other plugins
// This prevents "duplicate class" errors when multiple plugins have same dependency
relocate("org.bstats", "world.bentobox.bentobox.util.metrics")
relocate("io.papermc.lib", "world.bentobox.bentobox.paperlib")
relocate("com.github.puregero.multilib", "world.bentobox.bentobox.multilib")
// Remove the "-all" suffix from the shaded JAR filename
archiveClassifier.set("")
}
// Make the shaded JAR the primary artifact for the 'build' task
tasks.build {
dependsOn(tasks.shadowJar)
}
// ============================================================================
// TEST EXECUTION
// ============================================================================
// Configures JUnit 5 testing with special Java module access for Java 21
tasks.test {
// Use JUnit Platform (required for JUnit 5)
useJUnitPlatform()
// Enable Java 21 preview features and dynamic agent loading
jvmArgs("--enable-preview", "-XX:+EnableDynamicAgentLoading")
// Add --add-opens: Required for Java 21+ to allow reflection access to restricted modules
// Necessary for mocking frameworks and other testing utilities
val openModules = listOf(
"java.base/java.lang", "java.base/java.math", "java.base/java.io", "java.base/java.util",
"java.base/java.util.stream", "java.base/java.text", "java.base/java.util.regex",
"java.base/java.nio.channels.spi", "java.base/sun.nio.ch", "java.base/java.net",
"java.base/java.util.concurrent", "java.base/sun.nio.fs", "java.base/sun.nio.cs",
"java.base/java.nio.file", "java.base/java.nio.charset", "java.base/java.lang.reflect",
"java.logging/java.util.logging", "java.base/java.lang.ref", "java.base/java.util.jar",
"java.base/java.util.zip", "java.base/java.security", "java.base/jdk.internal.misc"
)
for (module in openModules) {
jvmArgs("--add-opens", "$module=ALL-UNNAMED")
}
}
// ============================================================================
// CODE COVERAGE (JACOCO)
// ============================================================================
// Generates code coverage reports to measure test coverage
tasks.jacocoTestReport {
reports {
xml.required.set(true) // XML format for CI/CD tools like SonarCloud
html.required.set(true) // HTML format for human viewing
}
// Exclude certain classes from coverage analysis
classDirectories.setFrom(
sourceSets.main.get().output.asFileTree.matching {
exclude("**/*Names*", "org/bukkit/Material*") // Generated/external classes
}
)
}
// ============================================================================
// JAVADOC & SOURCE ARTIFACTS
// ============================================================================
// Creates additional JARs for publication: sources and javadoc
tasks.javadoc {
source = sourceSets.main.get().allJava
options {
(this as StandardJavadocDocletOptions).apply {
// Suppress warnings and keep output quiet
addStringOption("Xdoclint:none", "-quiet")
source = javaVersion
}
}
dependsOn("copyLocales") // Ensure locales are available before generating javadoc (Gradle 9 validation)
// Some external mapped server sources contain type-use annotations that the Javadoc tool
// treats as errors. Allow Javadoc to complete without failing the build.
isFailOnError = false
}
// Creates BentoBox-<version>-sources.jar containing all source code
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
// Creates BentoBox-<version>-javadoc.jar containing generated documentation
tasks.register<Jar>("javadocJar") {
archiveClassifier.set("javadoc")
from(tasks.javadoc)
}
// ============================================================================
// PUBLICATION TO MAVEN REPOSITORY
// ============================================================================
// Publishes build artifacts to the BentoBox Maven repository
publishing {
publications {
create<MavenPublication>("mavenJava") {
// Use the shaded (shadow) JAR as the main artifact, not the plain JAR
artifact(tasks.shadowJar.get()) {
builtBy(tasks.shadowJar)
}
// Also attach source code and javadoc for developers
artifact(tasks.getByName("sourcesJar"))
artifact(tasks.getByName("javadocJar"))
// Set Maven coordinates
groupId = project.group as String
artifactId = rootProject.name
version = project.version as String
}
}
// Configure publication target repository (CodeMC Nexus)
repositories {
val isSnapshot = version.toString().endsWith("SNAPSHOT")
val repoUrl = if (isSnapshot) {
"https://repo.codemc.io/repository/bentoboxworld/"
} else {
"https://repo.codemc.io/repository/bentoboxworld/"
}
maven(repoUrl) {
val mavenUsername: String? by project
val mavenPassword: String? by project
if (mavenUsername != null && mavenPassword != null) {
credentials {
username = mavenUsername
password = mavenPassword
}
}
}
}
}
// ============================================================================
// ARCHIVE NAMING
// ============================================================================
// Sets the base name for all generated artifacts
base {
archivesName.set("BentoBox") // Final JARs will be: BentoBox-<version>.jar, etc.
}