Skip to content

Commit

Permalink
Merge branch 'devel'
Browse files Browse the repository at this point in the history
  • Loading branch information
szpak committed Mar 19, 2017
2 parents 02d2c8a + af3c994 commit ef79f0c
Show file tree
Hide file tree
Showing 25 changed files with 433 additions and 157 deletions.
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EditorConfig: http://EditorConfig.org
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.json]
indent_size = 2
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
0.6.0 - 2017-03-19

- Consider state trying to find just one repository in given state - [#36](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/36) - contribution by [strelok1](https://github.com/strelok1)
- Better error message in case of HTTP request failure - [#5](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/5) - contribution by [deanhiller](https://github.com/deanhiller)
- Add EditorConfig configuration to better deal with spaces vs tabs - [#33](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/33)

0.5.3 - 2015-06-13

- `packageGroup` should be taken from project.group by default - [#11](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/11)

0.5.2 - 2015-06-09

- Provide single task to close and promote repository - [#9](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/9)
- `getStagingProfile` task should display output without `--info` switch - [#8](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/8)

0.5.1 - 2015-03-08

- Credentials should be automatically fetched from configured deployer - [#7](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/7)
- Credentials should be automatically fetched from Gradle properties (when available) - [#6](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/6)

0.5.0 - 2015-03-02

- Wait given time period when repositories are not yet available - [#3](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/3)
- Use configured stagingProfileId when available - [#2](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/2)
- nexusUrl by default should use Sonatype OSSRH - [#1](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/1)

0.4.0 - 2015-02-27

- Initial release
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import io.codearte.gradle.nexus.logic.OperationRetrier
import io.codearte.gradle.nexus.logic.RepositoryCloser
import io.codearte.gradle.nexus.logic.RepositoryFetcher
import io.codearte.gradle.nexus.logic.StagingProfileFetcher
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction

@CompileStatic
public class CloseRepositoryTask extends BaseStagingTask {
class CloseRepositoryTask extends BaseStagingTask {

@Input
@Optional
String stagingRepositoryId

@TaskAction
void doAction() {
Expand All @@ -19,6 +25,7 @@ public class CloseRepositoryTask extends BaseStagingTask {

String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher)
String repositoryId = retrier.doWithRetry { repositoryFetcher.getOpenRepositoryIdForStagingProfileId(stagingProfileId) }
stagingRepositoryId = repositoryId
repositoryCloser.closeRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,42 @@ import io.codearte.gradle.nexus.logic.StagingProfileFetcher
import org.gradle.api.tasks.TaskAction

@CompileStatic
public class PromoteRepositoryTask extends BaseStagingTask {
class PromoteRepositoryTask extends BaseStagingTask {

@TaskAction
void doAction() {
StagingProfileFetcher stagingProfileFetcher = createFetcherWithGivenClient(createClient())
RepositoryFetcher repositoryFetcher = createRepositoryFetcherWithGivenClient(createClient())
RepositoryPromoter repositoryPromoter = createRepositoryPromoterWithGivenClient(createClient())
OperationRetrier<String> retrier = createOperationRetrier()

tryToTakeStagingProfileIdFromCloseRepositoryTask()
String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher)
String repositoryId = retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) }

String repositoryId = getRepositoryIdFromCloseTaskOrFromServer(stagingProfileId, repositoryFetcher)
repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId)
}

private void tryToTakeStagingProfileIdFromCloseRepositoryTask() {
if (getStagingProfileId() != null) {
return
}
String stagingProfileIdFromCloseRepositoryTask = project.tasks.withType(CloseRepositoryTask)[0].getStagingProfileId()
String stagingProfileIdFromCloseRepositoryTask = getCloseRepositoryTask().stagingProfileId

if (stagingProfileIdFromCloseRepositoryTask != null) {
logger.debug("Reusing staging profile id from closeRepository task: $stagingProfileIdFromCloseRepositoryTask")
setStagingProfileId(stagingProfileIdFromCloseRepositoryTask)
}
}

private CloseRepositoryTask getCloseRepositoryTask() {
return project.tasks.withType(CloseRepositoryTask)[0]
}

private String getRepositoryIdFromCloseTaskOrFromServer(String stagingProfileId, RepositoryFetcher repositoryFetcher) {
//TODO: Add debug statement
OperationRetrier<String> retrier = createOperationRetrier()
String repositoryId = getCloseRepositoryTask().stagingRepositoryId ?:
retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) }
return repositoryId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.codearte.gradle.nexus.infra

import groovy.transform.CompileStatic

/**
* Custom exception to propagate server errors.
*
* Created as groovyx.net.http.HttpResponseException contains in a message only a reason phrase (e.g. Server Error) without response body
* which in many cases is crucial to determine the resons why error was returned.
*
* It may be made redundant once migrated to other HTTP library.
*/
@CompileStatic
class NexusHttpResponseException extends NexusStagingException {

final int statusCode

NexusHttpResponseException(int statusCode, String message, Throwable cause) {
super(message, cause)
this.statusCode = statusCode
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import groovyx.net.http.ContentType
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient

/**
Expand Down Expand Up @@ -50,9 +51,17 @@ class SimplifiedHttpJsonRestClient {
setUriAndAuthentication(uri)
Map params = createAndInitializeCallParametersMap()
params.body = content
log.debug("POST request content: $content")
//TODO: Add better error handling (e.g. display error message received from server, not only 500 + not fail on 404 in 'text/html')
HttpResponseDecorator response = (HttpResponseDecorator)restClient.post(params)
log.warn("POST response data: ${response.data}")
try {
log.debug("POST request content: $content")
HttpResponseDecorator response = (HttpResponseDecorator) restClient.post(params)
log.debug("POST response status ${response.status}, data: ${response.data}")
} catch (HttpResponseException e) {
//Enhance rethrown exception to contain also response body - #5
//TODO: Still better handle response content type on 404 and 50x - server returns 'text/plain', but RESTClient from Groovy Builder tries to parse it as JSON
HttpResponseDecorator resp = e.getResponse();
String message = "${resp.statusLine.statusCode}: ${resp.statusLine.reasonPhrase}, body: ${resp.data}"
log.warn("POST response failed. ${message}")
throw new NexusHttpResponseException(e.getStatusCode(), message, e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import groovy.transform.InheritConstructors
@CompileStatic
abstract class AbstractStagingOperationExecutor extends BaseOperationExecutor {

protected Map prepareStagingPostContentWithGivenRepositoryIdAndStagingId(String repositoryId, String stagingProfileId) {
protected Map<String, Map> prepareStagingPostContentWithGivenRepositoryIdAndStagingId(String repositoryId, String stagingProfileId) {
return [data: [
stagedRepositoryId: repositoryId,
description: 'Automatically released/promoted with gradle-nexus-staging-plugin!',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class OperationRetrier<T> {
this.delayBetweenRetries = delayBetweenRetries
}

public T doWithRetry(Closure<T> operation) {
T doWithRetry(Closure<T> operation) {
int counter = 0
int numberOfAttempts = numberOfRetries + 1
while (true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class RepositoryCloser extends AbstractStagingOperationExecutor {

void closeRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) {
log.info("Closing repository '$repositoryId' with staging profile '$stagingProfileId'")
Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId)
Map<String, Map> postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId)
client.post(nexusUrl + "/staging/profiles/$stagingProfileId/finish", postContent)
log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been closed")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.codearte.gradle.nexus.logic

import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
import groovy.util.logging.Slf4j
import org.gradle.api.Incubating

@CompileStatic
@InheritConstructors
@Slf4j
@Incubating
class RepositoryDropper extends AbstractStagingOperationExecutor {

void dropRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) {
log.info("Droping repository '$repositoryId' with staging profile '$stagingProfileId'")
Map<String, Map> postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId)
client.post(nexusUrl + "/staging/profiles/$stagingProfileId/drop", postContent)
log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been dropped")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,17 @@ class RepositoryFetcher extends BaseOperationExecutor {
}

private String parseResponseAndGetRepositoryIdInGivenState(Map responseAsMap, String repositoryState) {
def repository = verifyThatOneRepositoryAndReturnIt(responseAsMap, repositoryState)
verifyReceivedRepositoryState(repository, repositoryState)
Map repository = verifyThatOneRepositoryAndReturnIt(responseAsMap, repositoryState)
log.debug("Received 1 '$repositoryState' repository with id: ${repository.repositoryId}")
return repository.repositoryId
}

private Map verifyThatOneRepositoryAndReturnIt(Map responseAsMap, String repositoryState) {
int numberOfRespositories = responseAsMap.data.size()
if (numberOfRespositories != 1) {
throw new WrongNumberOfRepositories(numberOfRespositories, repositoryState)
}
Map repository = responseAsMap.data[0] as Map
return repository
}

private void verifyReceivedRepositoryState(Map repository, String expectedRepositoryState) {
if (repository.type != expectedRepositoryState) {
throw new IllegalArgumentException(
"Unexpected state of received repository. Received ${repository.type}, expected $expectedRepositoryState")
Closure repositoryInGivenState = { it.type == repositoryState }
int numberOfRepositories = responseAsMap.data.count(repositoryInGivenState)
if (numberOfRepositories != 1) {
throw new WrongNumberOfRepositories(numberOfRepositories, repositoryState)
}
return responseAsMap.data.find(repositoryInGivenState) as Map
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class RepositoryPromoter extends AbstractStagingOperationExecutor {

void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) {
log.info("Promoting repository '$repositoryId' with staging profile '$stagingProfileId'")
Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId)
Map<String, Map> postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId)
client.post(nexusUrl + "/staging/profiles/$stagingProfileId/promote", postContent)
log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promotted")
log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promoted")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.codearte.gradle.nexus

//Separate interface as there is problem with constants visibility in traits
interface FunctionalTestConstants {

public static final String E2E_SERVER_BASE_PATH = "https://oss.sonatype.org/service/local/"
public static final String E2E_PACKAGE_GROUP = 'io.gitlab.nexus-at'
public static final String E2E_STAGING_PROFILE_ID = "5027d084a01a3a"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.codearte.gradle.nexus

import groovy.transform.CompileStatic

@CompileStatic
trait FunctionalTestHelperTrait implements FunctionalTestConstants {

private static final String NEXUS_USERNAME_AT_ENVIRONMENT_VARIABLE_NAME = 'nexusUsernameAT'
private static final String NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME = 'nexusPasswordAT'

String getNexusUsernameAT() {
return System.getenv(NEXUS_USERNAME_AT_ENVIRONMENT_VARIABLE_NAME) ?: 'nexus-at'
}

//Temporary hack to read nexus password in e2e tests
String tryToReadNexusPasswordAT() {
return System.getenv(NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME) ?: { throw new RuntimeException(
"Nexus password for AT tests is not set in a system variable '$NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME'") }()
}
}
12 changes: 0 additions & 12 deletions src/test/groovy/io/codearte/gradle/nexus/PasswordUtil.groovy

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import nebula.test.IntegrationSpec
class BaseNexusStagingFunctionalSpec extends IntegrationSpec {

protected String nexusPassword
protected String nexusUsername
protected String packageGroup

void setup() {
fork = true //to prevent ClassCastException: org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration
nexusUsername = 'nexus-at'
nexusPassword = ''
packageGroup = 'io.gitlab.nexus-at'
}

protected String getApplyPluginBlock() {
Expand All @@ -26,9 +30,9 @@ class BaseNexusStagingFunctionalSpec extends IntegrationSpec {
protected String getDefaultConfigurationClosure() {
return """
nexusStaging {
username = "codearte"
username = '$nexusUsername'
password = '$nexusPassword'
packageGroup = "io.codearte"
packageGroup = '$packageGroup'
}
"""
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package io.codearte.gradle.nexus.functional

import io.codearte.gradle.nexus.PasswordUtil
import io.codearte.gradle.nexus.FunctionalTestHelperTrait
import spock.lang.Ignore
import spock.lang.IgnoreIf

class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec {
class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec implements FunctionalTestHelperTrait {

@Override
void setup() {
nexusPassword = PasswordUtil.tryToReadNexusPassword()
nexusPassword = tryToReadNexusPasswordAT()
}

@IgnoreIf({ !env.containsKey("nexusPassword") })
@IgnoreIf({ !env.containsKey("nexusPasswordAT") })
def "should run"() {
given:
buildFile << """
Expand All @@ -24,7 +24,7 @@ class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec {
result.wasExecuted(':getStagingProfile')
and:
// println result.standardOutput //TODO: How to redirect stdout to show on console (works with 2.2.1)
result.standardOutput.contains("Received staging profile id: 93c08fdebde1ff")
result.standardOutput.contains("Received staging profile id: $E2E_STAGING_PROFILE_ID")
}

@Ignore
Expand Down Expand Up @@ -54,6 +54,6 @@ class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec {
then:
result.wasExecuted(':promoteRepository')
and:
result.standardOutput.contains("has been promotted") //TODO: Match with regexp
result.standardOutput.contains("has been promoted") //TODO: Match with regexp
}
}
}
Loading

0 comments on commit ef79f0c

Please sign in to comment.