Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gradle 6.2 as minimum #7

Closed
wants to merge 9 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/java-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Reduce number of Gradle regressions builds
run: |
# Keep only the last Gradle version per Stutter matrix; for each line this transformation is done:
# java11=6.0.1,6.9.4,7.0.2,7.6.1,8.0.2,8.1.1,8.2-rc-1
# java11=6.2.2,6.9.4,7.0.2,7.6.1,8.0.2,8.1.1,8.2-rc-1
# ->
# java11=8.2-rc-1
# The trick is that \2 will greedily eat everything before the last comma.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

This Gradle plugin is a turn-key solution for publishing to Nexus. You can use it to publish your artifacts to any Nexus instance (internal or public). It is great for publishing your open source to Sonatype, and then to Maven Central, in a fully automated fashion.

Vanilla Gradle is great but it cannot fully automate publications to Nexus. This plugin enables isolation of staging repositories so that you can reliably publish from CI, and each publication uses a brand new, explicitly created staging repo ([more](https://github.com/gradle-nexus/publish-plugin/issues/63)). Moreover, the plugin provides tasks to close and release staging repositories, covering the whole releasing process to Maven Central.
Vanilla Gradle is great, but it cannot fully automate publications to Nexus. This plugin enables isolation of staging repositories so that you can reliably publish from CI, and each publication uses a brand new, explicitly created staging repo ([more](https://github.com/gradle-nexus/publish-plugin/issues/63)). Moreover, the plugin provides tasks to close and release staging repositories, covering the whole releasing process to Maven Central.

This plugin is intended as a replacement of the [Gradle Nexus Staging Plugin](https://github.com/Codearte/gradle-nexus-staging-plugin/) and [Nexus Publish Plugin](https://github.com/marcphilipp/nexus-publish-plugin) duo. See a dedicated [migration guide](https://github.com/gradle-nexus/publish-plugin/wiki/Migration-from-gradle_nexus_staging-plugin---nexus_publish-plugin-duo).

## Usage

### Applying the plugin

The plugin must be applied to the root project and requires Gradle 6.0 or later. It is important to
The plugin must be applied to the root project and requires Gradle 6.2 or later. It is important to
set the group and the version to the root project, so the plugin can detect if it is a snapshot
version or not in order to select the correct repository where artifacts will be published.

Expand Down Expand Up @@ -202,7 +202,7 @@ By default, `initialize${repository.name.capitalize()}StagingRepository` task ad
The description can be customized via:
* `io.github.gradlenexus.publishplugin.NexusPublishExtension.getRepositoryDescription` property (default: `$group:$module:$version` of the root project)
* `io.github.gradlenexus.publishplugin.InitializeNexusStagingRepository.repositoryDescription` property
* `io.github.gradlenexus.publishplugin.FindStagingRepository.getDescriptionRegex` property (regex, default: `"\\b" + Regex.escape(repositoryDescription) + "(\\s|$)"`)
* `io.github.gradlenexus.publishplugin.FindStagingRepository.descriptionRegex` property (regex, default: `"\\b" + Regex.escape(repositoryDescription) + "(\\s|$)"`)

So the steps to publish and release in different Gradle invocations are:
1. Publish the artifacts to the staging repository: `./gradlew publishToSonatype`
Expand Down
95 changes: 61 additions & 34 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
@file:Suppress("UnstableApiUsage") // Property assignment is incubating in Gradle 8.1/8.2.

import org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
import org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

plugins {
`kotlin-dsl`
id("com.gradle.plugin-publish") version "1.2.0"
// From 6.14.0 onwards Spotless requires Gradle to be on Java 11,
// but we still use Java 8 in .github/workflows/java-versions.yml.
id("com.diffplug.spotless") version "6.19.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("org.jetbrains.gradle.plugin.idea-ext")
Expand Down Expand Up @@ -108,15 +109,15 @@ stutter {
languageVersion = JavaLanguageVersion.of(8)
}
gradleVersions {
compatibleRange("6.0")
compatibleRange("6.2")
}
}
register("java11") {
javaToolchain {
languageVersion = JavaLanguageVersion.of(11)
}
gradleVersions {
compatibleRange("6.0")
compatibleRange("6.2")
}
}
register("java17") {
Expand All @@ -130,7 +131,8 @@ stutter {
}
}

val e2eTest by sourceSets.creating { //separate infrastructure as compatTest is called multiple times with different Java versions
// Separate infrastructure as compatTest is called multiple times with different Java versions.
val e2eTest: SourceSet by sourceSets.creating {
compileClasspath += sourceSets["compatTest"].output
compileClasspath += sourceSets["main"].output
runtimeClasspath += sourceSets["compatTest"].output
Expand Down Expand Up @@ -161,28 +163,44 @@ sourceSets {
}
}

tasks {
afterEvaluate {
// This needs to be in an afterEvaluate block,
// because otherwise KotlinDslCompilerPlugins would win, and override what we've set to Kotlin 1.8.
withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
// Supporting Gradle 6.0+ needs to use Kotlin 1.3.
// See https://docs.gradle.org/current/userguide/compatibility.html
kotlinOptions.apiVersion = "1.3"
// Theoretically we could use newer language version here,
// but sadly the @kotlin.Metadata created on the classes would be incompatible with Kotlin 1.3 consumers.
kotlinOptions.languageVersion = "1.3"
doFirst {
if (kotlinOptions.apiVersion == "1.3") {
// Suppress "Language version 1.3 is deprecated and its support will be removed in a future version of Kotlin".
kotlinOptions.freeCompilerArgs += "-Xsuppress-version-warnings"
} else {
TODO("Remove -Xsuppress-version-warnings suppression, or change the condition to ${kotlinOptions.languageVersion}")
}
kotlin.target.compilations.configureEach {
// Supporting Gradle 6.2+ needs to use Kotlin 1.3.
// See https://docs.gradle.org/current/userguide/compatibility.html
// For future maintainer: Kotlin 1.9.0 dropped support for Kotlin 1.3, it'll only support 1.4+.
// This means Gradle 7.0 will be the lowest supportable version for plugins.
val usedKotlinVersion = @Suppress("DEPRECATION") KotlinVersion.KOTLIN_1_3

compilerOptions.configure {
// Gradle fully supports running on Java 8: https://docs.gradle.org/current/userguide/compatibility.html,
// so we should allow users to do that too.
jvmTarget = JvmTarget.fromTarget(JavaVersion.VERSION_1_8.toString())

// Suppress "Language version 1.3 is deprecated and its support will be removed in a future version of Kotlin".
freeCompilerArgs.add("-Xsuppress-version-warnings")
}
compileTaskProvider.configure {
// These two (api & lang) needs to be here instead of in compilations.compilerOptions.configure { },
// to prevent KotlinDslCompilerPlugins overriding to Kotlin 1.8.
compilerOptions.apiVersion = usedKotlinVersion
// Theoretically we could use newer language version here,
// but sadly the @kotlin.Metadata created on the classes would be incompatible with older consumers.
compilerOptions.languageVersion = usedKotlinVersion

// Validate that we're using the right version.
doFirst {
val api = compilerOptions.apiVersion.get()
val language = compilerOptions.languageVersion.get()
if (api != usedKotlinVersion || language != usedKotlinVersion) {
TODO(
"There's mismatch between configured and actual versions:\n" +
"apiVersion=${api}, languageVersion=${language}, configured=${usedKotlinVersion}."
)
}
}
}
}

tasks {
shadowJar {
exclude("META-INF/maven/**", "META-INF/proguard/**", "META-INF/*.kotlin_module")
manifest {
Expand All @@ -197,25 +215,34 @@ tasks {
dependsOn(shadowJar)
}
pluginUnderTestMetadata {
pluginClasspath.from.clear()
pluginClasspath.from(shadowJar)
pluginClasspath.setFrom(shadowJar)
}
register<Test>("e2eTest") {
description = "Run E2E tests."
group = "Verification"
testClassesDirs = e2eTest.output.classesDirs
classpath = e2eTest.runtimeClasspath
//pass E2E releasing properties to tests. Prefer environment variables which are not displayed with --info
// (unless non CI friendly properties with "." are used)
listOf("sonatypeUsername", "sonatypePassword", "signingKey", "signingPassword", "signing.gnupg.homeDir", "signing.gnupg.keyName", "signing.gnupg.passphrase").forEach {
// Pass E2E releasing properties to tests.
// Prefer environment variables which are not displayed with --info
// (unless non CI friendly properties with "." are used).
listOf(
"sonatypeUsername",
"sonatypePassword",
"signingKey",
"signingPassword",
"signing.gnupg.homeDir",
"signing.gnupg.keyName",
"signing.gnupg.passphrase"
).forEach {
val e2eName = "${it}E2E"
val e2eEnvName = "${ENV_PROJECT_PROPERTIES_PREFIX}${e2eName}"
//properties defined using ORG_GRADLE_PROJECT_ are accessible in child process anyway
// Properties defined using ORG_GRADLE_PROJECT_ are accessible in child process anyway.
if (project.hasProperty(e2eName) && System.getenv(e2eEnvName) == null) {
val e2eValue = project.property(e2eName)!!
if (e2eName.contains(".")) {
systemProperties.put("${SYSTEM_PROJECT_PROPERTIES_PREFIX}${e2eName}", project.property(e2eName))
systemProperty("${SYSTEM_PROJECT_PROPERTIES_PREFIX}${e2eName}", e2eValue)
} else {
environment("$ENV_PROJECT_PROPERTIES_PREFIX${e2eName}", project.property(e2eName)!!)
environment("${ENV_PROJECT_PROPERTIES_PREFIX}${e2eName}", e2eValue)
}
}
}
Expand All @@ -228,7 +255,7 @@ tasks {
withType<Test>().configureEach {
dependsOn(shadowJar)
useJUnitPlatform()
maxParallelForks = 8
maxParallelForks = if (name.startsWith("compatTest")) 1 else 8
}
withType<Test>().matching { it.name.startsWith("compatTest") }.configureEach {
systemProperty("plugin.version", project.version)
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configuration-cache=true
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id("com.gradle.enterprise") version "3.13.3"
id("com.gradle.enterprise") version "3.13.4"
id("org.gradle.toolchains.foojay-resolver-convention") version "0.6.0"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.gradle.util.GradleVersion
import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -60,7 +59,7 @@ abstract class BaseNexusPublishPluginTests {

private val gson = Gson()

protected val gradleVersion =
protected val gradleVersion: GradleVersion =
System.getProperty("compat.gradle.version")?.let { GradleVersion.version(it) } ?: GradleVersion.current()

private val gradleRunner = GradleRunner.create()
Expand Down Expand Up @@ -540,11 +539,6 @@ abstract class BaseNexusPublishPluginTests {
@Test
@Disabled("Fails on my Fedora...")
fun `uses configured connect timeout`() {
Assumptions.assumeTrue(
gradleVersion >= GradleVersion.version("5.0"),
"Task timeouts were added in Gradle 5.0"
)

// Taken from https://stackoverflow.com/a/904609/5866817
val nonRoutableAddress = "10.255.255.1"

Expand Down Expand Up @@ -1037,15 +1031,10 @@ abstract class BaseNexusPublishPluginTests {
gradleRunner(*arguments).buildAndFail()

private fun gradleRunner(vararg arguments: String): GradleRunner {
val warnings = when {
// Failing only became an option at Gradle 5.6.
gradleVersion >= GradleVersion.version("5.6") -> "fail"
else -> "all"
}
return gradleRunner
// .withDebug(true)
// .withDebug(true)
.withProjectDir(projectDir.toFile())
.withArguments(*arguments, "--stacktrace", "--warning-mode=$warnings")
.withArguments(*arguments, "--stacktrace", "--warning-mode=fail")
.forwardOutput()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import java.nio.file.Files
import java.nio.file.Path

fun Path.write(text: String): Path {
Files.createDirectories(parent)
toFile().writeText(text)
Files.createDirectories(this.parent)
this.toFile().writeText(text)
return this
}

fun Path.append(text: String): Path {
Files.createDirectories(parent)
toFile().appendText(text)
Files.createDirectories(this.parent)
this.toFile().appendText(text)
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package io.github.gradlenexus.publishplugin

import io.github.gradlenexus.publishplugin.internal.InvalidatingStagingRepositoryDescriptorRegistry
import io.github.gradlenexus.publishplugin.internal.NexusClient
import io.github.gradlenexus.publishplugin.internal.StagingRepositoryDescriptorRegistryBuildService
import org.gradle.api.GradleException
import org.gradle.api.Incubating
import org.gradle.api.provider.Property
Expand All @@ -30,7 +30,8 @@ import org.gradle.api.tasks.TaskAction
abstract class FindStagingRepository : AbstractNexusStagingRepositoryTask() {

@get:Internal
abstract val registry: Property<InvalidatingStagingRepositoryDescriptorRegistry>
// TODO use @ServiceReference instead of @Internal when minimum is Gradle 8.0.
abstract val registry: Property<StagingRepositoryDescriptorRegistryBuildService>

@get:Optional
@get:Input
Expand Down Expand Up @@ -59,7 +60,7 @@ abstract class FindStagingRepository : AbstractNexusStagingRepositoryTask() {
val descriptor = client.findStagingRepository(stagingProfileId, Regex(descriptionRegex))
logger.lifecycle("Staging repository for {} at {}, stagingProfileId '{}', descriptionRegex '{}' is '{}'", repository.name, serverUrl, stagingProfileId, descriptionRegex, descriptor.stagingRepositoryId)
stagingRepositoryId.set(descriptor.stagingRepositoryId)
registry.get()[repository.name] = descriptor
registry.get().registry[repository.name] = descriptor
}

// TODO: Duplication with InitializeNexusStagingRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package io.github.gradlenexus.publishplugin

import io.github.gradlenexus.publishplugin.internal.InvalidatingStagingRepositoryDescriptorRegistry
import io.github.gradlenexus.publishplugin.internal.NexusClient
import io.github.gradlenexus.publishplugin.internal.StagingRepositoryDescriptorRegistryBuildService
import okhttp3.HttpUrl
import org.gradle.api.GradleException
import org.gradle.api.provider.Property
Expand All @@ -29,7 +29,8 @@ import org.gradle.api.tasks.TaskAction
abstract class InitializeNexusStagingRepository : AbstractNexusStagingRepositoryTask() {

@get:Internal
abstract val registry: Property<InvalidatingStagingRepositoryDescriptorRegistry>
// TODO use @ServiceReference instead of @Internal when minimum is Gradle 8.0.
abstract val registry: Property<StagingRepositoryDescriptorRegistryBuildService>

@get:Optional
@get:Input
Expand All @@ -45,7 +46,7 @@ abstract class InitializeNexusStagingRepository : AbstractNexusStagingRepository
val descriptor = client.createStagingRepository(stagingProfileId, repositoryDescription.get())
val consumerUrl = HttpUrl.get(serverUrl)!!.newBuilder().addEncodedPathSegments("repositories/${descriptor.stagingRepositoryId}/content/").build()
logger.lifecycle("Created staging repository '{}' at {}", descriptor.stagingRepositoryId, consumerUrl)
registry.get()[repository.name] = descriptor
registry.get().registry[repository.name] = descriptor
}

private fun determineStagingProfileId(repository: NexusRepository, client: NexusClient): String {
Expand Down
Loading