Skip to content

Commit

Permalink
automatically close the created repository and optionally also releas…
Browse files Browse the repository at this point in the history
…e it (#403)
  • Loading branch information
gabrielittner committed Sep 4, 2022
1 parent 482502d commit c143338
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 28 deletions.
42 changes: 23 additions & 19 deletions nexus/src/main/kotlin/com/vanniktech/maven/publish/nexus/Nexus.kt
Expand Up @@ -136,9 +136,18 @@ class Nexus(
return repository
}

private fun closeStagingRepository(stagingRepository: Repository): String {
private fun closeStagingRepository(stagingRepository: Repository) {
val repositoryId = stagingRepository.repositoryId

if (stagingRepository.type == "closed") {
if (stagingRepository.transitioning) {
waitForClose(stagingRepository.repositoryId)
} else {
println("Repository $repositoryId already closed")
}
return
}

if (stagingRepository.type != "open") {
throw IllegalArgumentException("Repository $repositoryId is of type '${stagingRepository.type}' and not 'open'")
}
Expand All @@ -150,8 +159,7 @@ class Nexus(
}

waitForClose(repositoryId)

return repositoryId
println("Repository $repositoryId closed")
}

private fun waitForClose(repositoryId: String) {
Expand Down Expand Up @@ -198,7 +206,18 @@ class Nexus(
}
}

private fun releaseStagingRepository(repositoryId: String) {
fun closeCurrentStagingRepository(): String {
val stagingRepository = findStagingRepository()
closeStagingRepository(stagingRepository)
return stagingRepository.repositoryId
}

fun closeStagingRepository(repositoryId: String) {
val stagingRepository = getStagingRepository(repositoryId)
closeStagingRepository(stagingRepository)
}

fun releaseStagingRepository(repositoryId: String) {
println("Releasing repository: $repositoryId")
val response = service.releaseRepository(
TransitionRepositoryInput(
Expand All @@ -216,21 +235,6 @@ class Nexus(
println("Repository $repositoryId released")
}

private fun closeAndReleaseRepository(stagingRepository: Repository) {
closeStagingRepository(stagingRepository)
releaseStagingRepository(stagingRepository.repositoryId)
}

fun closeAndReleaseCurrentRepository() {
val stagingRepository = findStagingRepository()
closeAndReleaseRepository(stagingRepository)
}

fun closeAndReleaseRepositoryById(repositoryId: String) {
val stagingRepository = getStagingRepository(repositoryId)
closeAndReleaseRepository(stagingRepository)
}

companion object {
private const val PROGRESS_1 = "\u2839"
private const val PROGRESS_2 = "\u2838"
Expand Down
Expand Up @@ -11,6 +11,8 @@ import org.gradle.api.credentials.PasswordCredentials
import org.gradle.api.provider.Property
import org.gradle.api.publish.maven.MavenPom
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.build.event.BuildEventsListenerRegistry
import org.gradle.configurationcache.extensions.serviceOf
import org.gradle.plugins.signing.SigningPlugin

@Incubating
Expand All @@ -25,18 +27,24 @@ abstract class MavenPublishBaseExtension(

/**
* Sets up Maven Central publishing through Sonatype OSSRH by configuring the target repository. Gradle will then
* automatically create a `publishAllPublicationsToMavenRepository` task as well as include it in the general
* `publish` task. If the current version ends with `-SNAPSHOT` the artifacts will be published to Sonatype's snapshot
* automatically create a `publishAllPublicationsToMavenCentralRepository` task as well as include it in the general
* `publish` task. As part of running publish the plugin will automatically create a staging repostory on Sonatype
* to which all artifacts will be published. At the end of the build this staging repository will be automatically
* closed. When the [automaticRelease] parameter is `true` the staging repository will also be released
* automatically afterwards.
* If the current version ends with `-SNAPSHOT` the artifacts will be published to Sonatype's snapshot
* repository instead.
*
* This expects you provide your Sonatype user name and password through Gradle properties called
* This expects you provide your Sonatype username and password through Gradle properties called
* `mavenCentralUsername` and `mavenCentralPassword`.
*
* The `closeAndReleaseRepository` task is automatically configured for Sonatype OSSRH using the same credentials.
*
* @param host the instance of Sonatype OSSRH to use
* @param automaticRelease whether a non SNAPSHOT build should be released automatically at the end of the build
*/
fun publishToMavenCentral(host: SonatypeHost = SonatypeHost.DEFAULT) {
@JvmOverloads
fun publishToMavenCentral(host: SonatypeHost = SonatypeHost.DEFAULT, automaticRelease: Boolean = false) {
sonatypeHost.set(host)
sonatypeHost.finalizeValue()

Expand All @@ -46,7 +54,9 @@ abstract class MavenPublishBaseExtension(
sonatypeHost = sonatypeHost,
repositoryUsername = project.providers.gradleProperty("mavenCentralUsername"),
repositoryPassword = project.providers.gradleProperty("mavenCentralPassword"),
automaticRelease = automaticRelease,
)
project.serviceOf<BuildEventsListenerRegistry>().onTaskCompletion(buildService)

val groupId = project.provider { project.group.toString() }
val versionIsSnapshot = project.provider { project.versionIsSnapshot }
Expand Down
Expand Up @@ -18,8 +18,9 @@ open class MavenPublishPlugin : Plugin<Project> {
project.setCoordinates()

val sonatypeHost = project.findOptionalProperty("SONATYPE_HOST")
if (sonatypeHost != null && sonatypeHost.isNotBlank()) {
baseExtension.publishToMavenCentral(SonatypeHost.valueOf(sonatypeHost))
if (!sonatypeHost.isNullOrBlank()) {
val automaticRelease = project.findOptionalProperty("SONATYPE_AUTOMATIC_RELEASE").toBoolean()
baseExtension.publishToMavenCentral(SonatypeHost.valueOf(sonatypeHost), automaticRelease)
}
val releaseSigning = project.findOptionalProperty("RELEASE_SIGNING_ENABLED")?.toBoolean()
if (releaseSigning == true) {
Expand Down
Expand Up @@ -32,9 +32,11 @@ internal abstract class CloseAndReleaseSonatypeRepositoryTask : DefaultTask() {

val manualStagingRepositoryId = this.manualStagingRepositoryId
if (manualStagingRepositoryId != null) {
service.nexus.closeAndReleaseRepositoryById(manualStagingRepositoryId)
service.nexus.closeStagingRepository(manualStagingRepositoryId)
service.nexus.releaseStagingRepository(manualStagingRepositoryId)
} else {
service.nexus.closeAndReleaseCurrentRepository()
val id = service.nexus.closeCurrentStagingRepository()
service.nexus.releaseStagingRepository(id)
}

service.repositoryClosed = true
Expand Down
Expand Up @@ -7,12 +7,16 @@ import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.services.BuildServiceRegistry
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFailureResult

internal abstract class SonatypeRepositoryBuildService : BuildService<SonatypeRepositoryBuildService.Params> {
internal abstract class SonatypeRepositoryBuildService : BuildService<SonatypeRepositoryBuildService.Params>, AutoCloseable, OperationCompletionListener {
internal interface Params : BuildServiceParameters {
val sonatypeHost: Property<SonatypeHost>
val repositoryUsername: Property<String>
val repositoryPassword: Property<String>
val automaticRelease: Property<Boolean>
}

val nexus = Nexus(
Expand All @@ -36,19 +40,43 @@ internal abstract class SonatypeRepositoryBuildService : BuildService<SonatypeRe
// indicates whether we already closed a staging repository to avoid doing it more than once in a build
var repositoryClosed: Boolean = false

var buildHasFailure: Boolean = false

override fun onFinish(event: FinishEvent) {
if (event.result is TaskFailureResult) {
buildHasFailure = true
}
}

override fun close() {
if (buildHasFailure) {
return
}

val stagingRepositoryId = this.stagingRepositoryId
if (stagingRepositoryId != null) {
nexus.closeStagingRepository(stagingRepositoryId)
if (parameters.automaticRelease.get()) {
nexus.releaseStagingRepository(stagingRepositoryId)
}
}
}

companion object {
private const val NAME = "sonatype-repository-build-service"

fun BuildServiceRegistry.registerSonatypeRepositoryBuildService(
sonatypeHost: Provider<SonatypeHost>,
repositoryUsername: Provider<String>,
repositoryPassword: Provider<String>,
automaticRelease: Boolean,
): Provider<SonatypeRepositoryBuildService> {
return registerIfAbsent(NAME, SonatypeRepositoryBuildService::class.java) {
it.maxParallelUsages.set(1)
it.parameters.sonatypeHost.set(sonatypeHost)
it.parameters.repositoryUsername.set(repositoryUsername)
it.parameters.repositoryPassword.set(repositoryPassword)
it.parameters.automaticRelease.set(automaticRelease)
}
}
}
Expand Down

0 comments on commit c143338

Please sign in to comment.