Skip to content

Commit

Permalink
Use Gradle Nexus Publish Plugin (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipp94831 authored Feb 27, 2024
1 parent b39f483 commit 9685e4a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 81 deletions.
3 changes: 1 addition & 2 deletions sonatype/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ description = "Sets up nexusPublish and closeAndReleaseRepository tasks to push

dependencies {
implementation("au.com.console:kassava:1.0.0")
implementation("io.codearte.gradle.nexus", "gradle-nexus-staging-plugin", "0.30.0")
implementation("de.marcphilipp.gradle", "nexus-publish-plugin", "0.4.0")
implementation("io.github.gradle-nexus", "publish-plugin", "1.3.0")

testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.3.0")
testImplementation("com.github.tomakehurst:wiremock:2.20.0")
Expand Down
110 changes: 42 additions & 68 deletions sonatype/src/main/kotlin/com/bakdata/gradle/SonatypePlugin.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2019 bakdata GmbH
* Copyright (c) 2024 bakdata GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -24,13 +24,14 @@

package com.bakdata.gradle

import de.marcphilipp.gradle.nexus.NexusPublishExtension
import io.codearte.gradle.nexus.NexusStagingExtension
import io.github.gradlenexus.publishplugin.InitializeNexusStagingRepository
import io.github.gradlenexus.publishplugin.NexusPublishExtension
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.UnknownTaskException
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
Expand Down Expand Up @@ -60,13 +61,21 @@ class SonatypePlugin : Plugin<Project> {
extensions.create<SonatypeSettings>("sonatype", this)
}

// note that we need to use adjustConfiguration before applying the plugins (nexus-staging, nexus-publish),
// note that we need to use adjustConfiguration before applying the plugin (publish-plugin),
// so we can adjust the respective settings of the used plugins
// (they use afterEvaluate to apply their settings in turn which is registered after ours)
adjustConfiguration()

plugins.apply("base")
plugins.apply("io.codearte.nexus-staging")
plugins.apply("io.github.gradle-nexus.publish-plugin")

configure<NexusPublishExtension> {
// create default repository called 'nexus' and set the corresponding default urls
repositories.create("nexus") {
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}

allprojects {
components.matching { it.name == "java" }.configureEach {
Expand All @@ -77,39 +86,7 @@ class SonatypePlugin : Plugin<Project> {
}
}

addParentPublishToNexusTasks()

disallowPublishTasks()

afterEvaluate {
tasks.named("closeRepository") {
mustRunAfter("publishToNexus")
}
}
}
}

/**
* Recursively add publishToNexus (if not exists) which depends on the children.
*/
private fun Project.addParentPublishToNexusTasks() {
allprojects {
val parent = project.parent
if (parent != null) {
tasks.matching { it.name == "publishToNexus" }.configureEach {
val parentProvider =
try {
parent.tasks.named("publishToNexus")
} catch (e: UnknownTaskException) {
parent.tasks.register("publishToNexus")
}
this.let { childTask ->
parentProvider.configure {
dependsOn(childTask)
}
}
}
}
}
}

Expand All @@ -123,12 +100,15 @@ class SonatypePlugin : Plugin<Project> {
// lazy execution, so that settings configurations are actually used
afterEvaluate {
// first try to set all settings, even if not given (yet)
project.configure<NexusStagingExtension> {
packageGroup = "com.bakdata"
stagingProfileId = "8412378836ed9c"
username = getOverriddenSetting(SonatypeSettings::osshrUsername)
password = getOverriddenSetting(SonatypeSettings::osshrPassword)
getOverriddenSetting(SonatypeSettings::nexusUrl)?.let { serverUrl = it }
project.configure<NexusPublishExtension> {
packageGroup.set("com.bakdata")

repositories["nexus"].apply {
stagingProfileId.set("746f6fd1d91a4")
username.set(getOverriddenSetting(SonatypeSettings::osshrUsername))
password.set(getOverriddenSetting(SonatypeSettings::osshrPassword))
getOverriddenSetting(SonatypeSettings::nexusUrl)?.let { nexusUrl.set(uri(it)) }
}
}
}

Expand Down Expand Up @@ -157,14 +137,16 @@ class SonatypePlugin : Plugin<Project> {
}

if (this.allTasks.any { it is AbstractPublishToMaven }) {
project.configure<NexusStagingExtension> {
if (username == null) {
missingProps.add(SonatypeSettings::osshrUsername)
}
if (password == null) {
missingProps.add(SonatypeSettings::osshrPassword)
project.configure<NexusPublishExtension> {
repositories["nexus"].apply {
if (!username.isPresent) {
missingProps.add(SonatypeSettings::osshrUsername)
}
if (!password.isPresent) {
missingProps.add(SonatypeSettings::osshrPassword)
}
getOverriddenSetting(SonatypeSettings::nexusUrl)?.let { nexusUrl.set(uri(it)) }
}
getOverriddenSetting(SonatypeSettings::nexusUrl)?.let { serverUrl = it }
}

allprojects {
Expand Down Expand Up @@ -220,7 +202,7 @@ class SonatypePlugin : Plugin<Project> {
if (hasTask(":release") && System.getenv("CI") == null) {
throw GradleException("Release is only supported through CI (e.g., Travis)")
}
if (hasTask(":closeAndReleaseRepository") && System.getenv("CI") == null) {
if (hasTask(":closeAndReleaseStagingRepository") && System.getenv("CI") == null) {
throw GradleException("Closing a release is only supported through CI (e.g., Travis)")
}
}
Expand All @@ -231,31 +213,23 @@ class SonatypePlugin : Plugin<Project> {
private fun addPublishTasks(project: Project) {
with(project) {
apply(plugin = "signing")
apply(plugin = "de.marcphilipp.nexus-publish")
apply(plugin = "org.gradle.maven-publish")

val javadocJar by tasks.creating(Jar::class) {
archiveClassifier.set("javadoc")
from(tasks.findByName("javadoc") ?: tasks.findByName("dokka"))
tasks.findByName("dokka")?.apply {
tasks.creating(Jar::class) {
archiveClassifier.set("javadoc")
from(this)
}
}

val sourcesJar by tasks.creating(Jar::class) {
archiveClassifier.set("sources")
from(project.the<SourceSetContainer>()["main"].allSource)
configure<JavaPluginExtension> {
withSourcesJar()
withJavadocJar()
}

configure<PublishingExtension> {
publications.create<MavenPublication>("sonatype") {
from(components["java"])
artifact(sourcesJar).classifier = "sources"
artifact(javadocJar).classifier = "javadoc"
}
}

configure<NexusPublishExtension> {
// create default repository called 'nexus' and set the corresponding default urls
repositories.create("nexus") {
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}

Expand Down
17 changes: 10 additions & 7 deletions sonatype/src/test/kotlin/com/bakdata/gradle/SonatypePluginIT.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ internal class SonatypePluginIT {

val result = GradleRunner.create()
.withProjectDir(testProjectDir.toFile())
.withArguments("publishToNexus", "closeAndReleaseRepository", "--stacktrace", "--info")
.withArguments("publishToNexus", "closeAndReleaseStagingRepository", "--stacktrace", "--info")
.withProjectPluginClassPath()
.build()

SoftAssertions.assertSoftly { softly ->
softly.assertThat(result.tasks)
.haveExactly(1, taskWithPathAndOutcome(":signSonatypePublication", TaskOutcome.SUCCESS))
.haveExactly(1, taskWithPathAndOutcome(":publishSonatypePublicationToNexusRepository", TaskOutcome.SUCCESS))
.haveExactly(1, taskWithPathAndOutcome(":closeAndReleaseRepository", TaskOutcome.SUCCESS))
.haveExactly(1, taskWithPathAndOutcome(":closeAndReleaseStagingRepository", TaskOutcome.SUCCESS))
}

val projectName = testProjectDir.fileName.toString()
Expand All @@ -118,6 +118,9 @@ internal class SonatypePluginIT {
}

private fun mockNexusProtocol(wiremock: WireMockServer) {
wiremock.addMockServiceRequestListener { request, response ->
println("Answered request ${request.method} ${request.url} with response ${response.status}")
}
wiremock.stubFor(get(urlMatching("/staging/profiles"))
.willReturn(okJson("""{"data": [{"id": $PROFILE_ID, "name": "${TEST_GROUP}"}]}""")))
wiremock.stubFor(post(urlMatching("/staging/profiles/$PROFILE_ID/start"))
Expand All @@ -135,14 +138,14 @@ internal class SonatypePluginIT {
wiremock.stubFor(post(urlMatching("/staging/bulk/close"))
.willReturn(okJson("{}")))
wiremock.stubFor(get(urlMatching("/staging/repository/$STAGING_ID"))
.willReturn(okJson("""{"type": "closed", "transitioning": false}""")))
.willReturn(okJson("""{"repositoryId": "$STAGING_ID", "type": "closed", "transitioning": false}""")))
wiremock.stubFor(post(urlMatching("/staging/bulk/promote"))
.inScenario("promoting").whenScenarioStateIs(STARTED)
.willReturn(okJson("{}"))
.willSetStateTo("promoting"))
wiremock.stubFor(get(urlMatching("/staging/repository/$STAGING_ID"))
.inScenario("promoting").whenScenarioStateIs("promoting")
.willReturn(okJson("""{"type": "released", "transitioning": false}""")))
.willReturn(okJson("""{"repositoryId": "$STAGING_ID", "type": "released", "transitioning": false}""")))
}

@Test
Expand Down Expand Up @@ -189,7 +192,7 @@ internal class SonatypePluginIT {

val result = GradleRunner.create()
.withProjectDir(testProjectDir.toFile())
.withArguments("publishToNexus", "closeAndReleaseRepository", "--stacktrace", "--info")
.withArguments("publishToNexus", "closeAndReleaseStagingRepository", "--stacktrace", "--info")
.withProjectPluginClassPath()
.build()

Expand All @@ -202,7 +205,7 @@ internal class SonatypePluginIT {
softly.assertThat(result.tasks)
.haveExactly(0, taskWithPathAndOutcome(":signSonatypePublication", TaskOutcome.SUCCESS))
.haveExactly(0, taskWithPathAndOutcome(":publishSonatypePublicationToNexusRepository", TaskOutcome.SUCCESS))
.haveExactly(1, taskWithPathAndOutcome(":closeAndReleaseRepository", TaskOutcome.SUCCESS))
.haveExactly(1, taskWithPathAndOutcome(":closeAndReleaseStagingRepository", TaskOutcome.SUCCESS))
}

val expectedUploads = listOf(".jar", ".pom", "-javadoc.jar", "-sources.jar", ".module")
Expand All @@ -216,7 +219,7 @@ internal class SonatypePluginIT {
companion object {
private const val TEST_GROUP: String = "com.bakdata"

private const val PROFILE_ID: String = "8412378836ed9c"
private const val PROFILE_ID: String = "746f6fd1d91a4"

private const val STAGING_ID: Int = 5

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal class SonatypePluginTest {
.haveExactly(1, taskWithName("signSonatypePublication"))
.haveExactly(1, taskWithName("publish"))
.haveExactly(1, taskWithName("publishToNexus"))
.haveExactly(1, taskWithName("closeAndReleaseRepository"))
.haveExactly(1, taskWithName("closeAndReleaseStagingRepository"))
}
}

Expand Down Expand Up @@ -94,16 +94,16 @@ internal class SonatypePluginTest {
.haveExactly(1, taskWithName("signSonatypePublication"))
.haveExactly(1, taskWithName("publish"))
.haveExactly(1, taskWithName("publishToNexus"))
.haveExactly(0, taskWithName("closeAndReleaseRepository"))
.haveExactly(0, taskWithName("closeAndReleaseStagingRepository"))
}
}

assertSoftly { softly ->
softly.assertThat(parent.tasks)
.haveExactly(0, taskWithName("signSonatypePublication"))
.haveExactly(0, taskWithName("publish"))
.haveExactly(1, taskWithName("publishToNexus"))
.haveExactly(1, taskWithName("closeAndReleaseRepository"))
.haveExactly(0, taskWithName("publishToNexus"))
.haveExactly(1, taskWithName("closeAndReleaseStagingRepository"))
}
}

Expand Down

0 comments on commit 9685e4a

Please sign in to comment.