diff --git a/.gitignore b/.gitignore index 8f0bcd583..36866d907 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,11 @@ # Build files .gradle/ -jars/ +/jars/ out/ build/ target/ *.class + +# Run directory +/run/ diff --git a/Essentials/build.gradle b/Essentials/build.gradle index 559b8fbe3..b73674c63 100644 --- a/Essentials/build.gradle +++ b/Essentials/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.shadow-module") +} + dependencies { compileOnly('com.github.milkbowl:VaultAPI:1.7') { exclude group: "org.bukkit", module: "bukkit" diff --git a/EssentialsAntiBuild/build.gradle b/EssentialsAntiBuild/build.gradle index 9bfe75531..e0b9454d2 100644 --- a/EssentialsAntiBuild/build.gradle +++ b/EssentialsAntiBuild/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.module-conventions") +} + dependencies { compileOnly project(':EssentialsX') } diff --git a/EssentialsChat/build.gradle b/EssentialsChat/build.gradle index 9bfe75531..e0b9454d2 100644 --- a/EssentialsChat/build.gradle +++ b/EssentialsChat/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.module-conventions") +} + dependencies { compileOnly project(':EssentialsX') } diff --git a/EssentialsGeoIP/build.gradle b/EssentialsGeoIP/build.gradle index bba353b28..164a37c81 100644 --- a/EssentialsGeoIP/build.gradle +++ b/EssentialsGeoIP/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.shadow-module") +} + dependencies { compileOnly project(':EssentialsX') implementation 'com.maxmind.geoip2:geoip2:2.12.0' diff --git a/EssentialsProtect/build.gradle b/EssentialsProtect/build.gradle index 9bfe75531..e0b9454d2 100644 --- a/EssentialsProtect/build.gradle +++ b/EssentialsProtect/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.module-conventions") +} + dependencies { compileOnly project(':EssentialsX') } diff --git a/EssentialsSpawn/build.gradle b/EssentialsSpawn/build.gradle index 223f36637..c472c95d5 100644 --- a/EssentialsSpawn/build.gradle +++ b/EssentialsSpawn/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.module-conventions") +} + dependencies { compileOnly project(':EssentialsX') } \ No newline at end of file diff --git a/EssentialsXMPP/build.gradle b/EssentialsXMPP/build.gradle index e6b77baff..048ae44e2 100644 --- a/EssentialsXMPP/build.gradle +++ b/EssentialsXMPP/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.shadow-module") +} + dependencies { compileOnly project(':EssentialsX') implementation 'org.igniterealtime.smack:smack:3.2.1' diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 000000000..f41c45e3d --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + `kotlin-dsl` + `groovy-gradle-plugin` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("net.kyori", "indra-common", "2.0.5") + implementation("gradle.plugin.com.github.jengelman.gradle.plugins", "shadow", "7.0.0") + implementation("xyz.jpenilla", "run-paper", "1.0.2") +} diff --git a/build-logic/src/main/groovy/GitUtil.groovy b/build-logic/src/main/groovy/GitUtil.groovy new file mode 100644 index 000000000..faf193c07 --- /dev/null +++ b/build-logic/src/main/groovy/GitUtil.groovy @@ -0,0 +1,51 @@ +import net.kyori.indra.git.IndraGitExtension +import org.eclipse.jgit.lib.Ref +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.revwalk.RevWalk +import org.gradle.api.Project + +final class GitUtil { + private GitUtil() { + } + + static int commitsSinceLastTag(Project project) { + def indraGit = project.extensions.findByType(IndraGitExtension.class) + if (indraGit == null || !indraGit.isPresent() || indraGit.tags().isEmpty()) { + return -1 + } + def tags = indraGit.tags() + def depth = 0 + def walk = new RevWalk(indraGit.git().getRepository()) + def commit = walk.parseCommit(indraGit.commit()) + while (true) { + for (tag in tags) { + if (walk.parseCommit(tag.getLeaf().getObjectId()) == commit) { + walk.dispose() + return depth + } + } + depth++ + commit = walk.parseCommit(commit.getParents()[0]) + } + } + + static String headBranchName(Project project) { + if (System.getenv("GITHUB_HEAD_REF") != null && !System.getenv("GITHUB_HEAD_REF").isEmpty()) { + return System.getenv("GITHUB_HEAD_REF") + } else if (System.getenv("GITHUB_REF") != null && !System.getenv("GITHUB_REF").isEmpty()) { + return System.getenv("GITHUB_REF").replaceFirst("refs/heads/", "") + } + + def indraGit = project.extensions.findByType(IndraGitExtension.class) + if (!indraGit.isPresent()) { + return "detached-head" + } + + Ref ref = indraGit.git().getRepository().exactRef('HEAD')?.target + if (ref == null) { + return "detached-head" + } + + return Repository.shortenRefName(ref.name) + } +} diff --git a/build-logic/src/main/kotlin/EssentialsModuleExtension.kt b/build-logic/src/main/kotlin/EssentialsModuleExtension.kt new file mode 100644 index 000000000..0dcea3164 --- /dev/null +++ b/build-logic/src/main/kotlin/EssentialsModuleExtension.kt @@ -0,0 +1,5 @@ +import org.gradle.api.Project + +abstract class EssentialsModuleExtension(private val project: Project) { + val archiveFile = project.objects.fileProperty() +} diff --git a/build-logic/src/main/kotlin/FileCopyTask.kt b/build-logic/src/main/kotlin/FileCopyTask.kt new file mode 100644 index 000000000..a27090105 --- /dev/null +++ b/build-logic/src/main/kotlin/FileCopyTask.kt @@ -0,0 +1,18 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +abstract class FileCopyTask : DefaultTask() { + @InputFile + val fileToCopy = project.objects.fileProperty() + + @OutputFile + val destination = project.objects.fileProperty() + + @TaskAction + private fun copyFile() { + destination.get().asFile.parentFile.mkdirs() + fileToCopy.get().asFile.copyTo(destination.get().asFile, overwrite = true) + } +} diff --git a/build-logic/src/main/kotlin/constants.kt b/build-logic/src/main/kotlin/constants.kt new file mode 100644 index 000000000..026fe0f0e --- /dev/null +++ b/build-logic/src/main/kotlin/constants.kt @@ -0,0 +1 @@ +const val RUN_PAPER_MINECRAFT_VERSION = "1.16.5" diff --git a/build-logic/src/main/kotlin/essentials.base-conventions.gradle.kts b/build-logic/src/main/kotlin/essentials.base-conventions.gradle.kts new file mode 100644 index 000000000..39bbb8b7b --- /dev/null +++ b/build-logic/src/main/kotlin/essentials.base-conventions.gradle.kts @@ -0,0 +1,104 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + id("java") + id("net.kyori.indra") + id("net.kyori.indra.checkstyle") + id("net.kyori.indra.publishing") +} + +val checkstyleVersion = "8.36.2" +val spigotVersion = "1.16.5-R0.1-SNAPSHOT" +val junit5Version = "5.7.0" +val mockitoVersion = "3.2.0" + +dependencies { + testImplementation("org.junit.jupiter", "junit-jupiter", junit5Version) + testImplementation("org.junit.vintage", "junit-vintage-engine", junit5Version) + testImplementation("org.mockito", "mockito-core", mockitoVersion) + + if (project.name != "1_8Provider" && project.name != "PaperProvider" && project.name != "NMSReflectionProvider") { // These providers use their own bukkit versions + api("org.spigotmc", "spigot-api", spigotVersion) + } +} + +tasks { + // Version Injection + processResources { + // Always process resources if version string or git branch changes + val fullVersion = rootProject.ext["FULL_VERSION"] as String + val gitBranch = rootProject.ext["GIT_BRANCH"] as String + inputs.property("fullVersion", fullVersion) + inputs.property("gitBranch", gitBranch) + filter( + "beginToken" to "\${", + "endToken" to "}", + "tokens" to mapOf( + "full.version" to fullVersion, + "git.branch" to gitBranch + ) + ) + } + compileJava { + options.compilerArgs.add("-Xlint:-deprecation") + } + javadoc { + title = "${project.name} API (v${rootProject.ext["FULL_VERSION"]})" + val options = options as? StandardJavadocDocletOptions ?: return@javadoc + options.links( + "https://hub.spigotmc.org/javadocs/spigot/" + ) + options.addBooleanOption("Xdoclint:none", true) + } + withType { + archiveVersion.set(rootProject.ext["FULL_VERSION"] as String) + } +} + +// Dependency caching +configurations.all { + resolutionStrategy.cacheChangingModulesFor(5, "minutes") +} + +indra { + checkstyle(checkstyleVersion) + + github("EssentialsX", "Essentials") + gpl3OnlyLicense() + + publishReleasesTo("essx", "https://repo.essentialsx.net/releases/") + publishSnapshotsTo("essx", "https://repo.essentialsx.net/snapshots/") + + configurePublications { + pom { + description.set("The essential plugin suite for Minecraft servers.") + url.set("https://essentialsx.net") + developers { + developer { + id.set("mdcfe") + name.set("MD") + email.set("md@n3fs.co.uk") + } + developer { + id.set("pop4959") + } + developer { + id.set("JRoy") + name.set("Josh Roy") + } + } + ciManagement { + system.set("Jenkins") + url.set("https://ci.ender.zone/job/EssentialsX") + } + } + } + + javaVersions { + target(8) + minimumToolchain(16) + } +} + +// undo https://github.com/KyoriPowered/indra/blob/master/indra-common/src/main/kotlin/net/kyori/indra/IndraPlugin.kt#L57 +convention.getPlugin().archivesBaseName = project.name diff --git a/build-logic/src/main/kotlin/essentials.module-conventions.gradle.kts b/build-logic/src/main/kotlin/essentials.module-conventions.gradle.kts new file mode 100644 index 000000000..2e2a2f9b9 --- /dev/null +++ b/build-logic/src/main/kotlin/essentials.module-conventions.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("essentials.base-conventions") + id("xyz.jpenilla.run-paper") +} + +val moduleExtension = extensions.create("essentialsModule", project) + +tasks { + runServer { + minecraftVersion(RUN_PAPER_MINECRAFT_VERSION) + runDirectory(rootProject.file("run")) + if (project.name != "EssentialsX") { + pluginJars.from(rootProject.project(":EssentialsX").the().archiveFile) + } + } + jar { + moduleExtension.archiveFile.set(archiveFile) + } + val copyJar = register("copyJar") { + fileToCopy.set(moduleExtension.archiveFile) + destination.set(rootProject.layout.projectDirectory.dir(provider { "jars" }).flatMap { + it.file(fileToCopy.map { file -> file.asFile.name }) + }) + } + build { + dependsOn(copyJar) + } +} diff --git a/build-logic/src/main/kotlin/essentials.parent-build-logic.gradle.kts b/build-logic/src/main/kotlin/essentials.parent-build-logic.gradle.kts new file mode 100644 index 000000000..1b4345027 --- /dev/null +++ b/build-logic/src/main/kotlin/essentials.parent-build-logic.gradle.kts @@ -0,0 +1,52 @@ +import xyz.jpenilla.runpaper.task.RunServerTask + +plugins { + id("base") + id("net.kyori.indra.git") + id("xyz.jpenilla.run-paper") +} + +runPaper { + disablePluginJarDetection() +} + +val runModules = (findProperty("runModules") as String?) + ?.trim()?.split(",") ?: emptySet() + +tasks { + runServer { + // Enable modules for this task using the runModules property. + // Ex: './gradlew :runServer -PrunModules=chat,spawn' + minecraftVersion(RUN_PAPER_MINECRAFT_VERSION) + } + register("runAll") { + group = "essentials" + description = "Run a test server with all EssentialsX modules." + minecraftVersion(RUN_PAPER_MINECRAFT_VERSION) + } + named("clean") { + delete(file("jars")) + } +} + +subprojects { + afterEvaluate { + val moduleExt = extensions.findByType() ?: return@afterEvaluate + rootProject.tasks.named("runAll").configure { + pluginJars.from(moduleExt.archiveFile) + } + if (name == "EssentialsX") { + rootProject.tasks.runServer.configure { + pluginJars.from(moduleExt.archiveFile) + } + return@afterEvaluate + } + for (module in runModules) { + if (name.contains(module, ignoreCase = true)) { + rootProject.tasks.runServer.configure { + pluginJars.from(moduleExt.archiveFile) + } + } + } + } +} diff --git a/build-logic/src/main/kotlin/essentials.shadow-module.gradle.kts b/build-logic/src/main/kotlin/essentials.shadow-module.gradle.kts new file mode 100644 index 000000000..9ebe51ea4 --- /dev/null +++ b/build-logic/src/main/kotlin/essentials.shadow-module.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("essentials.module-conventions") + id("com.github.johnrengelman.shadow") +} + +tasks { + jar { + archiveClassifier.set("unshaded") + } + shadowJar { + archiveClassifier.set(null) + } +} + +extensions.configure { + archiveFile.set(tasks.shadowJar.flatMap { it.archiveFile }) +} diff --git a/build.gradle b/build.gradle index a072a054b..986d9b91f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,222 +1,14 @@ -buildscript { - ext { - indraVersion = '2.0.4' - } -} - plugins { - id 'net.kyori.indra' version "$indraVersion" apply false - id 'net.kyori.indra.git' version "$indraVersion" - id 'net.kyori.indra.checkstyle' version "$indraVersion" apply false - id 'net.kyori.indra.publishing' version "$indraVersion" apply false - id 'com.github.johnrengelman.shadow' version '7.0.0' apply false + id("essentials.parent-build-logic") } -import org.apache.tools.ant.filters.ReplaceTokens -import org.eclipse.jgit.lib.Ref -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.revwalk.RevWalk - -allprojects { - group = 'net.essentialsx' - version = '2.19.0-SNAPSHOT' -} - -@SuppressWarnings('GrMethodMayBeStatic') -def commitsSinceLastTag() { - if (indraGit == null || !indraGit.isPresent() || indraGit.tags().isEmpty()) { - return -1 - } - def tags = indraGit.tags() - def depth = 0 - def walk = new RevWalk(indraGit.git().getRepository()) - def commit = walk.parseCommit(indraGit.commit()) - while (true) { - for (tag in tags) { - if (walk.parseCommit(tag.getLeaf().getObjectId()) == commit) { - walk.dispose() - indraGit.git().close() - return depth - } - } - depth++ - commit = walk.parseCommit(commit.getParents()[0]) - } -} - -@SuppressWarnings('GrMethodMayBeStatic') -def headBranchName() { - if (System.getenv("GITHUB_HEAD_REF") != null && !System.getenv("GITHUB_HEAD_REF").isEmpty()) { - return System.getenv("GITHUB_HEAD_REF") - } else if (System.getenv("GITHUB_REF") != null && !System.getenv("GITHUB_REF").isEmpty()) { - return System.getenv("GITHUB_REF").replaceFirst("refs/heads/", "") - } - - if (!indraGit.isPresent()) { - return "detached-head" - } - - Ref ref = indraGit.git().getRepository().exactRef('HEAD')?.target - if (ref == null) { - return "detached-head" - } - - return Repository.shortenRefName(ref.name) -} +group = "net.essentialsx" +version = "2.19.0-SNAPSHOT" project.ext { GIT_COMMIT = !indraGit.isPresent() ? "unknown" : indraGit.commit().abbreviate(7).name() - GIT_DEPTH = commitsSinceLastTag() - GIT_BRANCH = headBranchName() + GIT_DEPTH = GitUtil.commitsSinceLastTag(project) + GIT_BRANCH = GitUtil.headBranchName(project) - fullVersion = "${version}".replace("-SNAPSHOT", "-dev+${GIT_DEPTH}-${GIT_COMMIT}") - - checkstyleVersion = '8.36.2' - spigotVersion = '1.16.5-R0.1-SNAPSHOT' - junit5Version = '5.7.0' - mockitoVersion = '3.2.0' -} - -subprojects { - apply plugin: 'java' - apply plugin: 'net.kyori.indra' - apply plugin: 'net.kyori.indra.checkstyle' - apply plugin: 'net.kyori.indra.publishing' - apply plugin: 'com.github.johnrengelman.shadow' - - repositories { - maven { url = 'https://hub.spigotmc.org/nexus/content/groups/public/' } - maven { url = 'https://papermc.io/repo/repository/maven-public/' } - maven { - url = 'https://jitpack.io' - content { - includeGroup "com.github.milkbowl" - } - } - maven { - url = 'https://repo.codemc.org/repository/maven-public' - content { - includeGroup "org.bstats" - } - } - mavenCentral() { - content { - includeGroup "net.kyori" - } - } - } - - dependencies { - testImplementation "org.junit.jupiter:junit-jupiter:${junit5Version}" - testImplementation "org.junit.vintage:junit-vintage-engine:${junit5Version}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" - - if (project.name != "1_8Provider" && project.name != "PaperProvider" && project.name != "NMSReflectionProvider") { // These providers use their own bukkit versions - api "org.spigotmc:spigot-api:${spigotVersion}" - } - } - - // Dependency caching - configurations.all { - resolutionStrategy.cacheChangingModulesFor 5, 'minutes' - } - - // Version Injection - processResources { - // Always process resources if version string or git branch changes - inputs.property('fullVersion', fullVersion) - inputs.property('gitBranch', GIT_BRANCH) - filter(ReplaceTokens, beginToken: '${', - endToken: '}', tokens: ["full.version": fullVersion, "git.branch": GIT_BRANCH]) - - } - - indra { - checkstyle "$checkstyleVersion" - - github('EssentialsX', 'Essentials') - gpl3OnlyLicense() - - publishReleasesTo('essx', 'https://repo.essentialsx.net/releases/') - publishSnapshotsTo('essx', 'https://repo.essentialsx.net/snapshots/') - - configurePublications { - pom { - description = 'The essential plugin suite for Minecraft servers.' - url = 'https://essentialsx.net' - developers { - developer { - id = 'mdcfe' - name = 'MD' - email = 'md@n3fs.co.uk' - } - developer { - id = 'pop4959' - } - developer { - id = 'JRoy' - name = 'Josh Roy' - } - } - ciManagement { - system = 'Jenkins' - url = 'https://ci.ender.zone/job/EssentialsX' - } - } - } - - javaVersions { - target 8 - minimumToolchain 16 - } - } - - compileJava { - options.compilerArgs.add('-Xlint:-deprecation') - } - - javadoc { - title = "${project.name} API (v${rootProject.ext.fullVersion})" - options.links( - 'https://hub.spigotmc.org/javadocs/spigot/' - ) - options.addBooleanOption('Xdoclint:none', true) - } - - // undo https://github.com/KyoriPowered/indra/blob/master/indra-common/src/main/kotlin/net/kyori/indra/IndraPlugin.kt#L57 - archivesBaseName = project.name - - tasks.withType(Jar) { - archiveVersion.set(fullVersion) - } -} - -def outputTasks() { - [":EssentialsX:shadowJar", ":EssentialsXAntiBuild:jar", ":EssentialsXChat:jar", - ":EssentialsXGeoIP:shadowJar", ":EssentialsXProtect:jar", ":EssentialsXSpawn:jar", - ":EssentialsXXMPP:shadowJar"].stream().map({ tasks.findByPath(it) }) -} - -task copyToJars(type: Copy) { - dependsOn tasks.findByPath(":EssentialsX:processResources") - - outputTasks().forEach { - from(it) - } - - rename '(.*)-all.jar', '$1.jar' - - into file('jars') -} - -task cleanJars() { - delete file('jars') -} - -task clean() { - dependsOn cleanJars -} - -task build() { - dependsOn copyToJars + FULL_VERSION = "${version}".replace("-SNAPSHOT", "-dev+${GIT_DEPTH}-${GIT_COMMIT}") } diff --git a/providers/1_8Provider/build.gradle b/providers/1_8Provider/build.gradle index c9278d9b1..9bcea0a99 100644 --- a/providers/1_8Provider/build.gradle +++ b/providers/1_8Provider/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.base-conventions") +} + dependencies { api project(':providers:NMSReflectionProvider') } diff --git a/providers/BaseProviders/build.gradle b/providers/BaseProviders/build.gradle index e69de29bb..bba2da74c 100644 --- a/providers/BaseProviders/build.gradle +++ b/providers/BaseProviders/build.gradle @@ -0,0 +1,3 @@ +plugins { + id("essentials.base-conventions") +} diff --git a/providers/NMSReflectionProvider/build.gradle b/providers/NMSReflectionProvider/build.gradle index 7b67a051b..62c703b01 100644 --- a/providers/NMSReflectionProvider/build.gradle +++ b/providers/NMSReflectionProvider/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.base-conventions") +} + dependencies { implementation project(':providers:BaseProviders') api 'org.bukkit:bukkit:1.12.2-R0.1-SNAPSHOT' diff --git a/providers/PaperProvider/build.gradle b/providers/PaperProvider/build.gradle index af54c7b4a..29d264896 100644 --- a/providers/PaperProvider/build.gradle +++ b/providers/PaperProvider/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("essentials.base-conventions") +} + dependencies { implementation project(':providers:BaseProviders') compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 36b6b34ad..000000000 --- a/settings.gradle +++ /dev/null @@ -1,21 +0,0 @@ -rootProject.name = 'EssentialsXParent' - -// Modules -[ - "", - "AntiBuild", - "Chat", - "GeoIP", - "Protect", - "Spawn", - "XMPP", -].each { - include(":EssentialsX$it") - project(":EssentialsX$it").projectDir = file("Essentials$it") -} - -// Providers -include(':providers:BaseProviders') -include(':providers:NMSReflectionProvider') -include(':providers:PaperProvider') -include(':providers:1_8Provider') diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..16bd70739 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,42 @@ +dependencyResolutionManagement { + repositories { + maven("https://hub.spigotmc.org/nexus/content/groups/public/") + maven("https://papermc.io/repo/repository/maven-public/") + maven("https://jitpack.io") { + content { includeGroup("com.github.milkbowl") } + } + maven("https://repo.codemc.org/repository/maven-public") { + content { includeGroup("org.bstats") } + } + mavenCentral { + content { includeGroup("net.kyori") } + } + } + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) +} + +pluginManagement { + includeBuild("build-logic") +} + +rootProject.name = "EssentialsXParent" + +// Modules +sequenceOf( + "", + "AntiBuild", + "Chat", + "GeoIP", + "Protect", + "Spawn", + "XMPP", +).forEach { + include(":EssentialsX$it") + project(":EssentialsX$it").projectDir = file("Essentials$it") +} + +// Providers +include(":providers:BaseProviders") +include(":providers:NMSReflectionProvider") +include(":providers:PaperProvider") +include(":providers:1_8Provider")