diff --git a/build.gradle.kts b/build.gradle.kts index cd79b01c47..e0f7318ee2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,28 +17,28 @@ //to build everything: "gradlew build" //to build and upload everything: "gradlew release" -import Build_gradle.Pom import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import de.marcphilipp.gradle.nexus.InitializeNexusStagingRepository -import de.marcphilipp.gradle.nexus.NexusPublishExtension -import io.codearte.gradle.nexus.BaseStagingTask -import io.codearte.gradle.nexus.NexusStagingExtension +import io.github.gradlenexus.publishplugin.AbstractNexusStagingRepositoryTask import org.apache.tools.ant.filters.ReplaceTokens import java.time.Duration -// Don't remove this, its needed for reasons.... -typealias Pom = org.gradle.api.publish.maven.MavenPom - plugins { signing `java-library` `maven-publish` - id("io.codearte.nexus-staging") version "0.30.0" - id("de.marcphilipp.nexus-publish") version "0.4.0" + id("io.github.gradle-nexus.publish-plugin") version "1.1.0" id("com.github.johnrengelman.shadow") version "8.1.1" } + +//////////////////////////////////// +// // +// Project Configuration // +// // +//////////////////////////////////// + + val javaVersion = JavaVersion.current() val versionObj = Version(major = "5", minor = "0", revision = "0", classifier = "beta.22") val isCI = System.getProperty("BUILD_NUMBER") != null // jenkins @@ -53,13 +53,13 @@ val commitHash: String by lazy { val commit = System.getenv("GIT_COMMIT") ?: System.getProperty("GIT_COMMIT") ?: System.getenv("GITHUB_SHA") // We only set the commit hash on CI builds since we don't want dirty local repos to set a wrong commit if (isCI && commit != null) - commit.substring(0, 7) + commit.take(7) else "DEV" } val previousVersion: Version by lazy { - val file = File(".version") + val file = layout.projectDirectory.file(".version").asFile if (file.canRead()) Version.parse(file.readText().trim()) else @@ -67,12 +67,15 @@ val previousVersion: Version by lazy { } val isNewVersion = previousVersion != versionObj + // Use normal version string for new releases and commitHash for other builds project.version = "$versionObj" + if (isNewVersion) "" else "_$commitHash" - project.group = "net.dv8tion" -val archivesBaseName = "JDA" + +base { + archivesName.set("JDA") +} java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -88,6 +91,13 @@ configure { } +//////////////////////////////////// +// // +// Dependency Configuration // +// // +//////////////////////////////////// + + repositories { mavenLocal() mavenCentral() @@ -138,44 +148,48 @@ dependencies { testImplementation(libs.commons.lang3) } -val compileJava: JavaCompile by tasks -val shadowJar: ShadowJar by tasks -val javadoc: Javadoc by tasks -val jar: Jar by tasks -val build: Task by tasks -val clean: Task by tasks -val test: Test by tasks -val check: Task by tasks -shadowJar.archiveClassifier.set("withDependencies") +//////////////////////////////////// +// // +// Build Task Configuration // +// // +//////////////////////////////////// -fun nullable(string: String?): String { - return if (string == null) "null" - else "\"$string\"" + +val jar by tasks.getting(Jar::class) { + archiveBaseName.set(project.name) + manifest.attributes( + "Implementation-Version" to project.version, + "Automatic-Module-Name" to "net.dv8tion.jda") +} + +val shadowJar by tasks.getting(ShadowJar::class) { + archiveClassifier.set("withDependencies") + exclude("*.pom") } -val sourcesForRelease = task("sourcesForRelease") { +val sourcesForRelease by tasks.creating(Copy::class) { from("src/main/java") { include("**/JDAInfo.java") val tokens = mapOf( - "versionMajor" to versionObj.major, - "versionMinor" to versionObj.minor, - "versionRevision" to versionObj.revision, - "versionClassifier" to nullable(versionObj.classifier), - "commitHash" to commitHash + "versionMajor" to versionObj.major, + "versionMinor" to versionObj.minor, + "versionRevision" to versionObj.revision, + "versionClassifier" to nullableReplacement(versionObj.classifier), + "commitHash" to commitHash ) // Allow for setting null on some strings without breaking the source // for this, we have special tokens marked with "!@...@!" which are replaced to @...@ filter { it.replace(Regex("\"!@|@!\""), "@") } // Then we can replace the @...@ with the respective values here - filter(mapOf("tokens" to tokens)) + filter("tokens" to tokens) } into("build/filteredSrc") includeEmptyDirs = false } -val generateJavaSources = task("generateJavaSources") { +val generateJavaSources by tasks.creating(SourceTask::class) { val javaSources = sourceSets["main"].allJava.filter { it.name != "JDAInfo.java" }.asFileTree @@ -184,7 +198,7 @@ val generateJavaSources = task("generateJavaSources") { dependsOn(sourcesForRelease) } -val noOpusJar = task("noOpusJar") { +val noOpusJar by tasks.creating(ShadowJar::class) { dependsOn(shadowJar) archiveClassifier.set(shadowJar.archiveClassifier.get() + "-no-opus") @@ -198,7 +212,7 @@ val noOpusJar = task("noOpusJar") { manifest.inheritFrom(jar.manifest) } -val minimalJar = task("minimalJar") { +val minimalJar by tasks.creating(ShadowJar::class) { dependsOn(shadowJar) minimize() archiveClassifier.set(shadowJar.archiveClassifier.get() + "-min") @@ -212,7 +226,7 @@ val minimalJar = task("minimalJar") { manifest.inheritFrom(jar.manifest) } -val sourcesJar = task("sourcesJar") { +val sourcesJar by tasks.creating(Jar::class) { archiveClassifier.set("sources") from("src/main/java") { exclude("**/JDAInfo.java") @@ -222,42 +236,7 @@ val sourcesJar = task("sourcesJar") { dependsOn(sourcesForRelease) } -val javadocJar = task("javadocJar") { - dependsOn(javadoc) - archiveClassifier.set("javadoc") - from(javadoc.destinationDir) -} - -tasks.withType { - exclude("*.pom") -} - -tasks.withType { - val arguments = mutableListOf("-Xlint:deprecation", "-Xlint:unchecked") - options.encoding = "UTF-8" - options.isIncremental = true - if (javaVersion.isJava9Compatible) doFirst { - arguments += "--release" - arguments += "8" - } - doFirst { - options.compilerArgs = arguments - } -} - -compileJava.apply { - source = generateJavaSources.source - dependsOn(generateJavaSources) -} - -jar.apply { - archiveBaseName.set(project.name) - manifest.attributes(mapOf( - "Implementation-Version" to project.version, - "Automatic-Module-Name" to "net.dv8tion.jda")) -} - -javadoc.apply { +val javadoc by tasks.getting(Javadoc::class) { isFailOnError = isCI options.memberLevel = JavadocMemberLevel.PUBLIC options.encoding = "UTF-8" @@ -301,7 +280,34 @@ javadoc.apply { exclude("com/iwebpp/crypto") } -build.apply { +val javadocJar by tasks.creating(Jar::class) { + dependsOn(javadoc) + archiveClassifier.set("javadoc") + from(javadoc.destinationDir) +} + +tasks.withType { + options.encoding = "UTF-8" + options.isIncremental = true + + val args = mutableListOf("-Xlint:deprecation", "-Xlint:unchecked") + + if (javaVersion.isJava9Compatible) { + args.add("--release") + args.add("8") + } + + doFirst { + options.compilerArgs = args + } +} + +val compileJava by tasks.getting(JavaCompile::class) { + dependsOn(generateJavaSources) + source = generateJavaSources.source +} + +val build by tasks.getting(Task::class) { dependsOn(jar) dependsOn(javadocJar) dependsOn(sourcesJar) @@ -309,75 +315,43 @@ build.apply { dependsOn(noOpusJar) dependsOn(minimalJar) - jar.mustRunAfter(clean) + jar.mustRunAfter(tasks.clean) shadowJar.mustRunAfter(sourcesJar) } -test.apply { +val test by tasks.getting(Test::class) { useJUnitPlatform() failFast = true } -fun getProjectProperty(name: String) = project.properties[name] as? String - -class Version( - val major: String, - val minor: String, - val revision: String, - val classifier: String? = null -) { - companion object { - fun parse(string: String): Version { - val (major, minor, revision) = string.substringBefore("-").split(".") - val classifier = if ("-" in string) string.substringAfter("-") else null - return Version(major, minor, revision, classifier) - } - } - - override fun equals(other: Any?): Boolean { - if (other === this) return true - if (other !is Version) return false - return major == other.major - && minor == other.minor - && revision == other.revision - && classifier == other.classifier - } - - override fun toString(): String { - return "$major.$minor.$revision" + if (classifier != null) "-$classifier" else "" - } -} - +//////////////////////////////////// +// // +// Publishing And Signing // +// // +//////////////////////////////////// -//////////////////////////////////////// -//////////////////////////////////////// -//// //// -//// Publishing And Signing //// -//// //// -//////////////////////////////////////// -//////////////////////////////////////// // Generate pom file for maven central -fun generatePom(pom: Pom) { - pom.packaging = "jar" - pom.name.set(project.name) - pom.description.set("Java wrapper for the popular chat & VOIP service: Discord https://discord.com") - pom.url.set("https://github.com/discord-jda/JDA") - pom.scm { +fun MavenPom.populate() { + packaging = "jar" + name.set(project.name) + description.set("Java wrapper for the popular chat & VOIP service: Discord https://discord.com") + url.set("https://github.com/discord-jda/JDA") + scm { url.set("https://github.com/discord-jda/JDA") connection.set("scm:git:git://github.com/discord-jda/JDA") developerConnection.set("scm:git:ssh:git@github.com:discord-jda/JDA") } - pom.licenses { + licenses { license { name.set("The Apache Software License, Version 2.0") url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") distribution.set("repo") } } - pom.developers { + developers { developer { id.set("Minn") name.set("Florian Spieß") @@ -391,17 +365,14 @@ fun generatePom(pom: Pom) { } } - -// Publish - // Skip fat jar publication (See https://github.com/johnrengelman/shadow/issues/586) components.java.withVariantsFromConfiguration(configurations.shadowRuntimeElements.get()) { skip() } val SoftwareComponentContainer.java - get() = components.getByName("java") as AdhocComponentWithVariants + get() = components.getByName("java") publishing { publications { - register("Release", MavenPublication::class) { + register("Release") { from(components["java"]) artifactId = project.name @@ -411,88 +382,115 @@ publishing { artifact(sourcesJar) artifact(javadocJar) - generatePom(pom) + pom.populate() } } } - -// Turn off sign tasks if we don't have a key +val ossrhConfigured = getProjectProperty("ossrhUser") != null val canSign = getProjectProperty("signing.keyId") != null +val shouldPublish = isNewVersion && canSign && ossrhConfigured + if (canSign) { signing { sign(publishing.publications.getByName("Release")) } } -// Staging and Promotion +nexusPublishing { + repositories.sonatype { + username.set(getProjectProperty("ossrhUser")) + password.set(getProjectProperty("ossrhPassword")) + stagingProfileId.set(getProjectProperty("stagingProfileId")) + } -configure { - username = getProjectProperty("ossrhUser") ?: "" - password = getProjectProperty("ossrhPassword") ?: "" - stagingProfileId = getProjectProperty("stagingProfileId") ?: "" -} + connectTimeout.set(Duration.ofMinutes(1)) + clientTimeout.set(Duration.ofMinutes(10)) -configure { - nexusPublishing { - repositories.sonatype { - username.set(getProjectProperty("ossrhUser") ?: "") - password.set(getProjectProperty("ossrhPassword") ?: "") - stagingProfileId.set(getProjectProperty("stagingProfileId") ?: "") - } - // Sonatype is very slow :) - connectTimeout.set(Duration.ofMinutes(1)) - clientTimeout.set(Duration.ofMinutes(10)) + transitionCheckOptions { + maxRetries.set(100) + delayBetween.set(Duration.ofSeconds(5)) } } -// This links the close/release tasks to the right repository (from the publication above) -val ossrhConfigured = getProjectProperty("ossrhUser") != null -val shouldPublish = isNewVersion && canSign && ossrhConfigured +//////////////////////////////////// +// // +// Release Task Configuration // +// // +//////////////////////////////////// -// Turn off the staging tasks if we don't want to publish -tasks.withType { - enabled = shouldPublish + +val rebuild by tasks.creating(Task::class) { + group = "build" + + dependsOn(build) + dependsOn(tasks.clean) + build.mustRunAfter(tasks.clean) } -tasks.withType { +val publishingTasks = tasks.withType { enabled = shouldPublish - // We give each step an hour because it takes very long sometimes ... - numberOfRetries = 30 // 30 tries - delayBetweenRetriesInMillis = 2 * 60 * 1000 // 2 minutes + mustRunAfter(rebuild) + dependsOn(rebuild) } -// Getting staging profile is fine though -tasks.getByName("getStagingProfile").enabled = ossrhConfigured - -tasks.create("release") { - // Only close repository after release is published - val closeRepository by tasks - closeRepository.mustRunAfter(tasks.withType()) - dependsOn(tasks.withType()) - - // Closes the sonatype repository and publishes to maven central - val closeAndReleaseRepository: Task by tasks - dependsOn(closeAndReleaseRepository) +tasks.withType { + enabled = shouldPublish +} - // Builds all jars for publications - dependsOn(build) +val release by tasks.creating(Task::class) { + group = "publishing" enabled = shouldPublish + dependsOn(publishingTasks) + doLast { // Only runs when shouldPublish = true - println("Saving version $versionObj to .version") - val file = File(".version") - file.createNewFile() - file.writeText(versionObj.toString()) + logger.lifecycle("Saving version $versionObj to .version") + val file = layout.projectDirectory.file(".version") + file.asFile.createNewFile() + file.asFile.writeText(versionObj.toString()) } } -tasks.withType { - enabled = shouldPublish +afterEvaluate { + val closeAndReleaseSonatypeStagingRepository by tasks.getting + closeAndReleaseSonatypeStagingRepository.apply { + release.dependsOn(this) + mustRunAfter(publishingTasks) + } +} + + +//////////////////////////////////// +// // +// Helpers // +// // +//////////////////////////////////// + + +fun getProjectProperty(name: String) = project.properties[name] as? String + +fun nullableReplacement(string: String?): String { + return if (string == null) "null" + else "\"$string\"" } -// Gradle stop complaining please -tasks.withType { - duplicatesStrategy = DuplicatesStrategy.INCLUDE +data class Version( + val major: String, + val minor: String, + val revision: String, + val classifier: String? = null +) { + companion object { + fun parse(string: String): Version { + val (major, minor, revision) = string.substringBefore("-").split(".") + val classifier = string.substringAfter("-").takeIf { "-" in string } + return Version(major, minor, revision, classifier) + } + } + + override fun toString(): String { + return "$major.$minor.$revision" + if (classifier != null) "-$classifier" else "" + } }