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

[#4][#6] Close and release tasks #9

Merged
merged 20 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 4 additions & 0 deletions .stutter/java11.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# DO NOT MODIFY: Generated by Stutter plugin.
4.10.3
5.0
5.6.3
7 changes: 1 addition & 6 deletions .stutter/java8.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
# DO NOT MODIFY: Generated by Stutter plugin.
4.10.3
5.0
5.1.1
5.2.1
5.3.1
5.4.1
5.5.1
5.6.2
5.6.3
6 changes: 5 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ repositories {
val licenseHeaderFile = file("gradle/license-header.txt")
spotless {
kotlin {
ktlint()
ktlint().userData(mapOf("disabled_rules" to "comment-spacing"))
licenseHeaderFile(licenseHeaderFile)
}
}
Expand Down Expand Up @@ -98,9 +98,13 @@ dependencies {
}

stutter {
isSparse = (findProperty("stutter.sparce")?.toString()?.toBoolean()) ?: true
java(8) {
compatibleRange("4.10")
}
java(11) {
compatibleRange("4.10")
}
szpak marked this conversation as resolved.
Show resolved Hide resolved
}

configurations {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FAILED
import org.gradle.testkit.runner.TaskOutcome.SKIPPED
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.gradle.util.GradleVersion
import org.gradle.util.VersionNumber
import org.junit.jupiter.api.Assumptions.assumeTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir
Expand All @@ -45,6 +47,7 @@ import ru.lanwen.wiremock.ext.WiremockResolver.Wiremock
import java.nio.file.Files
import java.nio.file.Path

@Suppress("FunctionName") // TODO: How to suppress "kotlin:S100" from SonarLint?
@ExtendWith(WiremockResolver::class)
class NexusPublishPluginTests {

Expand All @@ -55,7 +58,7 @@ class NexusPublishPluginTests {

private val gson = Gson()

private val gradleVersion = System.getProperty("compat.gradle.version")
private val gradleVersion = System.getProperty("compat.gradle.version") ?: GradleVersion.current().version
marcphilipp marked this conversation as resolved.
Show resolved Hide resolved

private val gradleRunner = GradleRunner.create()
.withPluginClasspath()
Expand All @@ -67,6 +70,13 @@ class NexusPublishPluginTests {
@TempDir
lateinit var projectDir: Path

lateinit var buildGradle: Path

@BeforeEach
internal fun setUp() {
buildGradle = projectDir.resolve("build.gradle")
}

@Test
fun `publish task depends on correct tasks`() {
projectDir.resolve("settings.gradle").write("""
Expand Down Expand Up @@ -615,6 +625,94 @@ class NexusPublishPluginTests {
.contains("b.snapshotRepositoryUrl = https://oss.sonatype.org/content/repositories/snapshots/")
}

@Test
fun `should close staging repository`(@Wiremock server: WireMockServer) {
writeDefaultSingleProjectConfiguration()
buildGradle.append("""
nexusPublishing {
repositories {
sonatype {
nexusUrl = uri('${server.baseUrl()}')
stagingProfileId = '$STAGING_PROFILE_ID'
}
}
}
""")

stubCreateStagingRepoRequest(server, "/staging/profiles/$STAGING_PROFILE_ID/start", STAGED_REPOSITORY_ID)
server.stubFor(post(urlEqualTo("/staging/bulk/close"))
.withRequestBody(matchingJsonPath("\$.data[?(@.stagedRepositoryIds[0] == '$STAGED_REPOSITORY_ID')]"))
.withRequestBody(matchingJsonPath("\$.data[?(@.autoDropAfterRelease == true)]"))
.willReturn(aResponse().withHeader("Content-Type", "application/json").withBody("{}")))
server.stubFor(get(urlEqualTo("/staging/repository/$STAGED_REPOSITORY_ID"))
.willReturn(aResponse().withHeader("Content-Type", "application/json").withBody("{\"transitioning\":false,\"type\":\"CLOSED\"}")))

val result = run("initializeSonatypeStagingRepository", "closeSonatypeStagingRepository")

assertSuccess(result, ":initializeSonatypeStagingRepository")
assertSuccess(result, ":closeSonatypeStagingRepository")
assertCloseOfStagingRepo(server)
}

@Test
fun `should close and release staging repository`(@Wiremock server: WireMockServer) {
writeDefaultSingleProjectConfiguration()
buildGradle.append("""
nexusPublishing {
repositories {
sonatype {
nexusUrl = uri('${server.baseUrl()}')
stagingProfileId = '$STAGING_PROFILE_ID'
}
}
}
""")

stubCreateStagingRepoRequest(server, "/staging/profiles/$STAGING_PROFILE_ID/start", STAGED_REPOSITORY_ID)
server.stubFor(post(urlEqualTo("/staging/bulk/promote"))
.withRequestBody(matchingJsonPath("\$.data[?(@.stagedRepositoryIds[0] == '$STAGED_REPOSITORY_ID')]"))
.withRequestBody(matchingJsonPath("\$.data[?(@.autoDropAfterRelease == true)]"))
.willReturn(aResponse().withHeader("Content-Type", "application/json").withBody("{}")))
server.stubFor(get(urlEqualTo("/staging/repository/$STAGED_REPOSITORY_ID"))
.willReturn(aResponse().withHeader("Content-Type", "application/json").withBody("{\"transitioning\":false,\"type\":\"RELEASED\"}")))

val result = run("tasks", "initializeSonatypeStagingRepository", "releaseSonatypeStagingRepository")

assertSuccess(result, ":initializeSonatypeStagingRepository")
assertSuccess(result, ":releaseSonatypeStagingRepository")
assertReleaseOfStagingRepo(server)
}

// TODO: To be used also in other tests
private fun writeDefaultSingleProjectConfiguration() {
projectDir.resolve("settings.gradle").write("""
rootProject.name = 'sample'
""")
projectDir.resolve("build.gradle").write("""
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath files($pluginClasspathAsString)
}
}
plugins {
id('java-library')
}
apply plugin: 'io.github.gradle-nexus.publish-plugin'
group = 'org.example'
version = '0.0.1'
publishing {
publications {
mavenJava(MavenPublication) {
from(components.java)
}
}
}
""")
}

private fun run(vararg arguments: String): BuildResult {
return gradleRunner(*arguments).build()
}
Expand Down Expand Up @@ -673,7 +771,15 @@ class NexusPublishPluginTests {
}

private fun assertCloseOfStagingRepo(server: WireMockServer) {
server.verify(postRequestedFor(urlMatching("/staging/bulk/close"))
assertGivenTransitionOperationOfStagingRepo(server, "close")
}

private fun assertReleaseOfStagingRepo(server: WireMockServer) {
assertGivenTransitionOperationOfStagingRepo(server, "promote")
}

private fun assertGivenTransitionOperationOfStagingRepo(server: WireMockServer, transitionOperation: String) {
server.verify(postRequestedFor(urlMatching("/staging/bulk/$transitionOperation"))
.withRequestBody(matchingJsonPath("\$.data[?(@.stagedRepositoryIds[0] == '$STAGED_REPOSITORY_ID')]")))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ package io.github.gradlenexus.publishplugin
import java.nio.file.Files
import java.nio.file.Path

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

fun Path.append(text: String): Path {
Files.createDirectories(parent)
toFile().appendText(text)
return this
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.github.gradlenexus.publishplugin

import io.github.gradlenexus.publishplugin.internal.NexusClient
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.kotlin.dsl.property
import java.net.URI
import java.time.Duration
import javax.inject.Inject

@Suppress("UnstableApiUsage")
abstract class BaseOperationOnNexusStagingRepository @Inject
szpak marked this conversation as resolved.
Show resolved Hide resolved
constructor(objects: ObjectFactory, extension: NexusPublishExtension, repository: NexusRepository) : DefaultTask() {

@get:Input
protected val serverUrl: Property<URI> = objects.property()

@get:Optional
@get:Input
protected val username: Property<String> = objects.property()

@get:Optional
@get:Input
protected val password: Property<String> = objects.property()

@get:Optional
@get:Input
protected val packageGroup: Property<String> = objects.property()

@get:Optional
@get:Input
protected val stagingProfileId: Property<String> = objects.property()

@get:Input
protected val repositoryName: Property<String> = objects.property()

@get:Internal
protected val clientTimeout: Property<Duration> = objects.property()

@get:Internal
protected val connectTimeout: Property<Duration> = objects.property()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Maybe we should extract these properties into a @Nested NexusConfiguration object instead of using a base class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could want to have a separated timeout per repository. However, there still should be an easy way to set it globally (as in most of the cases it is enough - in fact most of the users (except @vlsi ;) ) will only use one repository per build).


init {
serverUrl.set(repository.nexusUrl)
username.set(repository.username)
password.set(repository.password)
packageGroup.set(extension.packageGroup)
stagingProfileId.set(repository.stagingProfileId)
repositoryName.set(repository.name)
clientTimeout.set(extension.clientTimeout)
connectTimeout.set(extension.connectTimeout)
this.onlyIf { extension.useStaging.getOrElse(false) }
}

protected fun determineStagingProfileId(client: NexusClient): String {
var stagingProfileId = stagingProfileId.orNull
if (stagingProfileId == null) {
val packageGroup = packageGroup.get()
logger.debug("No stagingProfileId set, querying for packageGroup '{}'", packageGroup)
stagingProfileId = client.findStagingProfileId(packageGroup)
?: throw GradleException("Failed to find staging profile for package group: $packageGroup")
}
return stagingProfileId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.github.gradlenexus.publishplugin

import io.github.gradlenexus.publishplugin.internal.NexusClient
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import javax.inject.Inject

@Suppress("UnstableApiUsage")
open class CloseNexusStagingRepository @Inject
constructor(objects: ObjectFactory, extension: NexusPublishExtension, repository: NexusRepository) :
BaseOperationOnNexusStagingRepository(objects, extension, repository) {

@get:Input
val stagingRepositoryId: Property<String> = objects.property()
szpak marked this conversation as resolved.
Show resolved Hide resolved

init {
stagingRepositoryId.set(repository.stagingRepositoryId)
}

@TaskAction
fun closeStagingRepo() {
val client = NexusClient(serverUrl.get(), username.orNull, password.orNull, clientTimeout.orNull, connectTimeout.orNull)
val stagingProfileId = determineStagingProfileId(client)
logger.info("Closing staging repository with id '{}' for stagingProfileId '{}'", stagingRepositoryId.get(), stagingProfileId)
client.closeStagingRepository(stagingRepositoryId.get())
// TODO: Broken with real Nexus - waiting for effective execution is also required https://github.com/gradle-nexus/publish-plugin/issues/7
}
}
Loading