From 7804933b21d69a3c61a3198fe297f1f2e65ff38c Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Thu, 3 Dec 2020 03:24:55 -0600 Subject: [PATCH] Attempt to keep Kotlin totally off the classpath (#1558) * Attempt to keep Kotlin totally off the classpath * Add version header for new class * Fix a few bugs with the samples and provide better debugging output for javaexec * Use main consistently * Fix Gradle's published pom and use maven-publish plugin consistently throughout plugin and samples * Re-add groovydoc jar * Have assemble create the groovydoc jar * Switch to publishToMavenLocal * Sync dependencies with pom.xml and fix null provider for Gradle versions less than 6.2 * Perform quoting conditionally for Windows only * Add jsch, aether, and shaded libraries to round out compatibility * Ensure contract tests are generated before kotlin compile task --- .../standalone/dsl/http-client/build.gradle | 21 +-- .../dsl/http-client/settings.gradle | 14 ++ .../standalone/dsl/http-server/build.gradle | 51 +----- .../dsl/http-server/settings.gradle | 24 +++ .../restdocs/http-server/build.gradle | 75 +++++---- .../standalone/restdocs/http-server/pom.xml | 3 +- .../restdocs/http-server/settings.gradle | 23 ++- .../com/example/fraud/StubGeneratorTests.java | 2 +- .../webclient/http-client/build.gradle | 25 +-- .../webclient/http-client/settings.gradle | 15 +- .../webclient/http-server/build.gradle | 12 +- .../standalone/webclient/http-server/pom.xml | 3 +- .../webclient/http-server/settings.gradle | 24 ++- scripts/gradleOnly.sh | 4 +- .../RecursiveFilesConverterApplication.java | 46 ++++++ .../build.gradle | 29 +++- .../gradle/release.gradle | 147 ++++++++---------- .../pom.xml | 43 ++++- .../verifier/plugin/ContractsCopyTask.java | 39 ++++- .../GenerateClientStubsFromDslTask.java | 53 +++++-- .../plugin/GenerateServerTestsTask.java | 56 +++++-- ...ringCloudContractVerifierGradlePlugin.java | 77 ++++++--- .../verifier/plugin/VersionExtractor.java | 54 +++++++ .../verifier/TestGeneratorApplication.java | 43 +++++ .../converter/ToYamlConverterApplication.java | 36 +++++ 25 files changed, 644 insertions(+), 275 deletions(-) create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/java/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverterApplication.java create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/VersionExtractor.java create mode 100644 spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/TestGeneratorApplication.java create mode 100644 spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ToYamlConverterApplication.java diff --git a/samples/standalone/dsl/http-client/build.gradle b/samples/standalone/dsl/http-client/build.gradle index 6eb44116d4..738de5afe7 100644 --- a/samples/standalone/dsl/http-client/build.gradle +++ b/samples/standalone/dsl/http-client/build.gradle @@ -1,14 +1,8 @@ -buildscript { - repositories { - mavenCentral() - mavenLocal() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/release" } - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${findProperty('bootVersion') ?: bootVersion}" - } +plugins { + id "groovy" + id "org.springframework.boot" + id "io.spring.dependency-management" + id "maven-publish" } group = 'com.example' @@ -22,11 +16,6 @@ repositories { maven { url "https://repo.spring.io/release" } } -apply plugin: 'groovy' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' -apply plugin: 'maven-publish' - dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" diff --git a/samples/standalone/dsl/http-client/settings.gradle b/samples/standalone/dsl/http-client/settings.gradle index 35a147b560..b997608069 100644 --- a/samples/standalone/dsl/http-client/settings.gradle +++ b/samples/standalone/dsl/http-client/settings.gradle @@ -1 +1,15 @@ +pluginManagement { + repositories { + mavenLocal() + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://repo.spring.io/milestone" } + maven { url "https://repo.spring.io/release" } + gradlePluginPortal() + } + plugins { + id "org.springframework.boot" version "${bootVersion}" + id "io.spring.dependency-management" version "1.0.10.RELEASE" + } +} + rootProject.name = 'http-client-dsl-gradle' diff --git a/samples/standalone/dsl/http-server/build.gradle b/samples/standalone/dsl/http-server/build.gradle index 0295955156..b62fa49a03 100644 --- a/samples/standalone/dsl/http-server/build.gradle +++ b/samples/standalone/dsl/http-server/build.gradle @@ -1,42 +1,9 @@ -// tag::repos[] -/* - We need to use the [buildscript {}] section when we have to modify - the classpath for the plugins. If that's not the case this section - can be skipped. - - If you don't need to modify the classpath (e.g. add a Pact dependency), - then you can just set the [pluginManagement {}] section in [settings.gradle] file. - - // settings.gradle - pluginManagement { - repositories { - // for snapshots - maven {url "https://repo.spring.io/snapshot"} - // for milestones - maven {url "https://repo.spring.io/milestone"} - // for GA versions - gradlePluginPortal() - } - } - - */ -buildscript { - repositories { - mavenCentral() - mavenLocal() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/release" } - } -// end::repos[] - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${findProperty('bootVersion') ?: bootVersion}" - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" - //tag::pact_dependency[] - // if additional dependencies are needed e.g. for Pact - classpath "org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}" - //end::pact_dependency[] - } +plugins { + id "java" + id "org.springframework.boot" + id "io.spring.dependency-management" + id "org.springframework.cloud.contract" + id "maven-publish" } group = 'com.example' @@ -52,12 +19,6 @@ repositories { } // end::deps_repos[] -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' -apply plugin: 'spring-cloud-contract' -apply plugin: 'maven-publish' - dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" diff --git a/samples/standalone/dsl/http-server/settings.gradle b/samples/standalone/dsl/http-server/settings.gradle index 90a672de8a..166b851f6f 100644 --- a/samples/standalone/dsl/http-server/settings.gradle +++ b/samples/standalone/dsl/http-server/settings.gradle @@ -1 +1,25 @@ +// tag:repos[] +pluginManagement { + repositories { + mavenLocal() + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://repo.spring.io/milestone" } + maven { url "https://repo.spring.io/release" } + gradlePluginPortal() + } +// end:repos[] + plugins { + id "org.springframework.boot" version "${bootVersion}" + id "io.spring.dependency-management" version "1.0.10.RELEASE" + } + resolutionStrategy { + eachPlugin { + // protects us in case the gradle-portal-plugin hasn't yet been published by Maven + if (requested.id.id == 'org.springframework.cloud.contract') { + useModule("org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}") + } + } + } +} + rootProject.name = 'http-server-dsl-gradle' diff --git a/samples/standalone/restdocs/http-server/build.gradle b/samples/standalone/restdocs/http-server/build.gradle index 66a3f7c2ce..de20d29621 100644 --- a/samples/standalone/restdocs/http-server/build.gradle +++ b/samples/standalone/restdocs/http-server/build.gradle @@ -1,15 +1,9 @@ -buildscript { - repositories { - mavenCentral() - mavenLocal() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/release" } - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${findProperty('bootVersion') ?: bootVersion}" - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${project.findProperty('verifierVersion') ?: verifierVersion}" - } +plugins { + id "groovy" + id "org.springframework.boot" + id "io.spring.dependency-management" + id "org.springframework.cloud.contract" + id "maven-publish" } group = 'com.example' @@ -23,17 +17,10 @@ repositories { maven { url "https://repo.spring.io/release" } } -apply plugin: 'groovy' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' -apply plugin: 'maven-publish' -apply plugin: 'maven' -apply plugin: 'spring-cloud-contract' - dependencyManagement { imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" - mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${project.findProperty('verifierVersion') ?: verifierVersion}" + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${BOM_VERSION}" + mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifierVersion}" } } @@ -50,7 +37,17 @@ dependencies { // end::dependencies[] } +contracts { + baseClassMappings { + baseClassMapping('.*standalone.*', 'com.example.fraud.FraudBaseWithStandaloneSetup') + baseClassMapping('.*webapp.*', 'com.example.fraud.FraudBaseWithWebAppSetup') + } +} + +generateClientStubs.enabled = false + test { + useJUnitPlatform() systemProperty 'spring.profiles.active', 'gradle' testLogging { exceptionFormat = 'full' @@ -65,29 +62,12 @@ test { } } -contracts { - baseClassMappings { - baseClassMapping('.*standalone.*', 'com.example.fraud.FraudBaseWithStandaloneSetup') - baseClassMapping('.*webapp.*', 'com.example.fraud.FraudBaseWithWebAppSetup') - } -} - -generateClientStubs.enabled = false - task stubsJar(type: Jar, dependsOn: ['copySnippets', 'copySources', 'copyClasses']) { baseName = project.name classifier = 'stubs' from project.file("${project.buildDir}/stubs") } -artifacts { - archives stubsJar -} - -test { - useJUnitPlatform() -} - task copySnippets(type: Copy, dependsOn: test) { from "target/snippets/stubs" into "${project.buildDir}/stubs/META-INF/${project.group}/${project.name}/${project.version}/mappings" @@ -105,6 +85,25 @@ task copyClasses(type: Copy) { into "${project.buildDir}/stubs/" } +publishing { + publications { + maven(MavenPublication) { + artifact bootJar + artifact stubsJar + + // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/273 + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + } + } +} + clean.doFirst { delete 'target/snippets/stubs' delete "~/.m2/repository/com/example/http-server-restdocs-gradle" diff --git a/samples/standalone/restdocs/http-server/pom.xml b/samples/standalone/restdocs/http-server/pom.xml index 7e2ef914f1..afab2833c8 100644 --- a/samples/standalone/restdocs/http-server/pom.xml +++ b/samples/standalone/restdocs/http-server/pom.xml @@ -225,8 +225,7 @@ clean build - - install + publishToMavenLocal -PverifierVersion=${spring-cloud-contract.version} diff --git a/samples/standalone/restdocs/http-server/settings.gradle b/samples/standalone/restdocs/http-server/settings.gradle index 06780ec1e9..016c859d87 100644 --- a/samples/standalone/restdocs/http-server/settings.gradle +++ b/samples/standalone/restdocs/http-server/settings.gradle @@ -1 +1,22 @@ -rootProject.name = 'http-server-restdocs' +pluginManagement { + repositories { + mavenLocal() + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://repo.spring.io/milestone" } + gradlePluginPortal() + } + plugins { + id 'org.springframework.boot' version "${bootVersion}" + id "io.spring.dependency-management" version "1.0.10.RELEASE" + } + resolutionStrategy { + eachPlugin { + // protects us in case the gradle-portal-plugin hasn't yet been published by Maven + if (requested.id.id == 'org.springframework.cloud.contract') { + useModule("org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}") + } + } + } +} + +rootProject.name = 'http-server-restdocs-gradle' diff --git a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java index 41e5d826f6..e159ec787a 100644 --- a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java +++ b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java @@ -40,7 +40,7 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.docu import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @SpringBootTest(classes = Application.class) -@AutoConfigureRestDocs +@AutoConfigureRestDocs(outputDir = "target/snippets") @AutoConfigureMockMvc @AutoConfigureJsonTesters public class StubGeneratorTests { diff --git a/samples/standalone/webclient/http-client/build.gradle b/samples/standalone/webclient/http-client/build.gradle index 7599fceac6..16b81fad0b 100644 --- a/samples/standalone/webclient/http-client/build.gradle +++ b/samples/standalone/webclient/http-client/build.gradle @@ -1,14 +1,8 @@ -buildscript { - repositories { - mavenCentral() - mavenLocal() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/release" } - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${findProperty('bootVersion') ?: bootVersion}" - } +plugins { + id "groovy" + id "org.springframework.boot" + id "io.spring.dependency-management" + id "maven-publish" } group = 'com.example' @@ -22,15 +16,10 @@ repositories { maven { url "https://repo.spring.io/release" } } -apply plugin: 'groovy' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' -apply plugin: 'maven-publish' - dependencyManagement { imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" - mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${project.findProperty('verifierVersion') ?: verifierVersion}" + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${BOM_VERSION}" + mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifierVersion}" } } diff --git a/samples/standalone/webclient/http-client/settings.gradle b/samples/standalone/webclient/http-client/settings.gradle index 62eb1783cb..a88abb180b 100644 --- a/samples/standalone/webclient/http-client/settings.gradle +++ b/samples/standalone/webclient/http-client/settings.gradle @@ -1 +1,14 @@ -rootProject.name = 'http-client-webclient' +pluginManagement { + repositories { + mavenLocal() + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://repo.spring.io/milestone" } + gradlePluginPortal() + } + plugins { + id 'org.springframework.boot' version "${bootVersion}" + id "io.spring.dependency-management" version "1.0.10.RELEASE" + } +} + +rootProject.name = 'http-client-webclient-gradle' diff --git a/samples/standalone/webclient/http-server/build.gradle b/samples/standalone/webclient/http-server/build.gradle index d6318876c7..dab95ce0cc 100644 --- a/samples/standalone/webclient/http-server/build.gradle +++ b/samples/standalone/webclient/http-server/build.gradle @@ -26,8 +26,8 @@ repositories { dependencyManagement { imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" - mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${project.findProperty('verifierVersion') ?: verifierVersion}" + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${BOM_VERSION}" + mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifierVersion}" } } @@ -46,6 +46,8 @@ contracts { failOnNoContracts = false } +generateClientStubs.enabled = false + test { useJUnitPlatform() systemProperty 'spring.profiles.active', 'gradle' @@ -68,10 +70,6 @@ task stubsJar(type: Jar, dependsOn: ['copySnippets', 'copySources', 'copyClasses from project.file("${project.buildDir}/stubs") } -artifacts { - archives stubsJar -} - task copySnippets(type: Copy, dependsOn: test) { from "target/snippets/stubs" into "${project.buildDir}/stubs/META-INF/${project.group}/${project.name}/${project.version}/mappings" @@ -110,7 +108,7 @@ publishing { clean.doFirst { delete 'target/snippets/stubs' - delete "~/.m2/repository/com/example/http-server-restdocs-gradle" + delete "~/.m2/repository/com/example/http-server-webclient-gradle" } task resolveDependencies { diff --git a/samples/standalone/webclient/http-server/pom.xml b/samples/standalone/webclient/http-server/pom.xml index d8e71a7dbb..bfb3f1b98e 100644 --- a/samples/standalone/webclient/http-server/pom.xml +++ b/samples/standalone/webclient/http-server/pom.xml @@ -196,8 +196,7 @@ clean build - - install + publishToMavenLocal -PverifierVersion=${spring-cloud-contract.version} diff --git a/samples/standalone/webclient/http-server/settings.gradle b/samples/standalone/webclient/http-server/settings.gradle index 1ea2676b79..781e7c3f9d 100644 --- a/samples/standalone/webclient/http-server/settings.gradle +++ b/samples/standalone/webclient/http-server/settings.gradle @@ -1,26 +1,22 @@ pluginManagement { - plugins { - id 'org.springframework.boot' - id "org.springframework.cloud.contract" version "${verifierVersion}" - id "io.spring.dependency-management" version "1.0.8.RELEASE" - } repositories { - // to pick from local .m2 mavenLocal() - // for snapshots - maven { url "https://repo.spring.io/libs-snapshot-local" } - // for milestones - maven { url "https://repo.spring.io/libs-milestone-local" } - // for GA versions + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://repo.spring.io/milestone" } gradlePluginPortal() } + plugins { + id 'org.springframework.boot' version "${bootVersion}" + id "io.spring.dependency-management" version "1.0.10.RELEASE" + } resolutionStrategy { eachPlugin { - if (requested.id.id == 'org.springframework.boot') { - useModule("org.springframework.boot:spring-boot-gradle-plugin:${bootVersion}") + // protects us in case the gradle-portal-plugin hasn't yet been published by Maven + if (requested.id.id == 'org.springframework.cloud.contract') { + useModule("org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}") } } } } -rootProject.name = 'http-server-webclient' +rootProject.name = 'http-server-webclient-gradle' diff --git a/scripts/gradleOnly.sh b/scripts/gradleOnly.sh index acd2f993d5..5d5808c0e5 100755 --- a/scripts/gradleOnly.sh +++ b/scripts/gradleOnly.sh @@ -7,5 +7,5 @@ FOLDER=`pwd` set -e cd "${FOLDER}/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin" - ./gradlew clean build install -cd "${FOLDER}" \ No newline at end of file + ./gradlew clean build publishToMavenLocal +cd "${FOLDER}" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/java/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverterApplication.java b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/java/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverterApplication.java new file mode 100644 index 0000000000..25683424f7 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/java/org/springframework/cloud/contract/verifier/converter/RecursiveFilesConverterApplication.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-2020 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 + * + * https://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 org.springframework.cloud.contract.verifier.converter; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import org.springframework.util.StringUtils; + +public final class RecursiveFilesConverterApplication { + + private RecursiveFilesConverterApplication() { + } + + public static void main(String[] args) { + if (args.length != 5) { + throw new RuntimeException("Invalid number of arguments"); + } + + File stubsOutputDir = new File(args[0]); + File contractsDslDir = new File(args[1]); + List excludedFiles = Arrays.asList(StringUtils.commaDelimitedListToStringArray(args[2])); + String includedContracts = args[3]; + boolean excludeBuildFolders = Boolean.parseBoolean(args[4]); + + RecursiveFilesConverter converter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, excludedFiles, + includedContracts, excludeBuildFolders); + converter.processFiles(); + } + +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index cf02ad05cf..a7e6ddbd6b 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -62,17 +62,24 @@ configurations { dependencies { compileOnly gradleApi() - implementation "org.eclipse.aether:aether-api:${aetherVersion}" - implementation("org.springframework.cloud:spring-cloud-contract-converters:${project.version}") { - exclude(group: 'org.codehaus.groovy') + // provide version alignment with project + implementation(platform("org.springframework.cloud:spring-cloud-contract-tools:${project.version}")) + + implementation "org.apache.maven.resolver:maven-resolver-api" + implementation "org.eclipse.jgit:org.eclipse.jgit" + implementation "com.jcraft:jsch.agentproxy.jsch" + implementation "com.fasterxml.jackson.core:jackson-databind" + + implementation "org.springframework:spring-core" + implementation("org.springframework.cloud:spring-cloud-contract-shade") + api("org.springframework.cloud:spring-cloud-contract-stub-runner") { + exclude(group: '*') } - api("org.springframework.cloud:spring-cloud-contract-stub-runner:${project.version}") { - exclude(group: 'org.codehaus.groovy') - } - api("org.springframework.cloud:spring-cloud-starter-contract-verifier:${project.version}") { - exclude(group: 'org.codehaus.groovy') + api("org.springframework.cloud:spring-cloud-contract-verifier") { + exclude(group: '*') } + testImplementation('org.spockframework:spock-core:1.3-groovy-2.5') { exclude(group: 'org.codehaus.groovy') } @@ -114,6 +121,12 @@ jacocoTestReport { } }*/ +jar { + manifest { + attributes 'Implementation-Version': project.version + } +} + task resolveDependencies { doLast { project.rootProject.allprojects.each { subProject -> diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/release.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/release.gradle index 03729ceeea..2836c62280 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/release.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle/release.gradle @@ -1,11 +1,11 @@ -apply plugin: 'maven' -//apply plugin: 'signing' +apply plugin: 'maven-publish' +apply plugin: 'signing' -uploadArchives.dependsOn { [check] } +publish.dependsOn(check) -task sourcesJar(type: Jar) { - classifier 'sources' - from sourceSets.main.allSource +java { + withSourcesJar() + withJavadocJar() } task groovydocJar(type: Jar, dependsOn: groovydoc) { @@ -13,16 +13,7 @@ task groovydocJar(type: Jar, dependsOn: groovydoc) { from groovydoc.destinationDir } -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives groovydocJar - archives javadocJar -} +assemble.dependsOn(groovydocJar) ext { resolveRepoName = { Project project -> @@ -50,12 +41,69 @@ ext { repoUrl = isReleaseToCentral ? "https://oss.sonatype.org/service/local/staging/deploy/maven2/" : "https://repo.spring.io/${resolveRepoName(project)}" + repoUserName = isReleaseToCentral ? getProp("SONATYPE_USER") : repoUser + repoPass = isReleaseToCentral ? getProp("SONATYPE_PASSWORD") : repoPass +} + +publishing { + repositories { + maven { + url = repoUrl + credentials { + username = repoUserName + password = repoPass + } + } + } + publications { + pluginMaven(MavenPublication) { + artifact groovydocJar + pom { + name = project.name + packaging = 'jar' + description = 'Spring Cloud Contract Gradle Plugin' + url = 'https://github.com/spring-cloud/spring-cloud-contract' + inceptionYear = '2014' + + scm { + connection = 'scm:git:git@github.com:spring-cloud/spring-cloud-contract.git' + developerConnection = 'scm:git:git@github.com:spring-cloud/spring-cloud-contract.git' + url = 'https://github.com/spring-cloud/spring-cloud-contract' + } + + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + + developers { + developer { + id = 'jkubrynski' + name = 'Jakub Kubrynski' + email = 'jk ATT codearte DOTT io' + } + developer { + id = 'marcingrzejszczak' + name = 'Marcin Grzejszczak' + email = 'mgrzejszczak ATT pivotal DOTT io' + } + developer { + id = 'dsyer' + name = 'Dave Syer' + email = 'dsyer ATT pivotal DOTT io' + } + } + } + } + } } if (isReleaseToCentral) { - /*signing { - sign configurations.archives - }*/ + signing { + sign publishing.publications.maven + } apply plugin: 'com.gradle.plugin-publish' @@ -79,65 +127,6 @@ if (isReleaseToCentral) { } } -afterEvaluate { - uploadArchives { - repositories { - mavenDeployer { - // POM signature - if (isReleaseVersion && isReleaseToCentral) { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - } - // Target repository - String repoUserName = isReleaseToCentral ? getProp("SONATYPE_USER") : repoUser - String repoPass = isReleaseToCentral ? getProp("SONATYPE_PASSWORD") : repoPass - repository(url: repoUrl) { - authentication(userName: repoUserName, password: repoPass) - } - pom.project { - name "$project.name" - packaging 'jar' - description 'Spring Cloud Contract Gradle Plugin' - url 'https://github.com/spring-cloud/spring-cloud-contract' - inceptionYear '2014' - - scm { - connection 'scm:git:git@github.com:spring-cloud/spring-cloud-contract.git' - developerConnection 'scm:git:git@github.com:spring-cloud/spring-cloud-contract.git' - url 'https://github.com/spring-cloud/spring-cloud-contract' - } - - licenses { - license { - name 'The Apache License, Version 2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - - developers { - developer { - id 'jkubrynski' - name 'Jakub Kubrynski' - email 'jk ATT codearte DOTT io' - } - developer { - id 'marcingrzejszczak' - name 'Marcin Grzejszczak' - email 'mgrzejszczak ATT pivotal DOTT io' - } - developer { - id 'dsyer' - name 'Dave Syer' - email 'dsyer ATT pivotal DOTT io' - } - } - } - } - } - } -} - String getProp(String propName) { - return hasProperty(propName) ? - (getProperty(propName) ?: System.properties[propName]) : System.properties[propName] ?: - System.getenv(propName) + return (findProperty(propName) ?: System.properties[propName]) ?: System.getenv(propName) } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml index 45267c18a4..c3390cb662 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml @@ -25,18 +25,59 @@ - org.springframework.cloud spring-cloud-contract-verifier + + + * + * + + org.springframework.cloud spring-cloud-contract-converters + + + * + * + + org.springframework.cloud spring-cloud-contract-stub-runner + + + * + * + + + + + org.springframework.cloud + spring-cloud-contract-shade + + + org.apache.maven.resolver + maven-resolver-api + + + org.eclipse.jgit + org.eclipse.jgit + + + com.jcraft + jsch.agentproxy.jsch + + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework + spring-core diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java index 5830dc60eb..e6baf7271f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java @@ -16,7 +16,9 @@ package org.springframework.cloud.contract.verifier.plugin; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.OutputStream; import java.net.URI; import java.util.Collection; import java.util.Map; @@ -27,14 +29,17 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.util.io.NullOutputStream; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.Internal; @@ -53,7 +58,6 @@ import org.springframework.cloud.contract.stubrunner.StubRunnerOptions; import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder; import org.springframework.cloud.contract.stubrunner.StubRunnerPropertyUtils; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; -import org.springframework.cloud.contract.verifier.converter.ToYamlConverter; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; @@ -103,6 +107,8 @@ class ContractsCopyTask extends DefaultTask { */ private final Property deleteStubsAfterTest; + private final ConfigurableFileCollection classpath; + // outputs private final DirectoryProperty copiedContractsFolder; @@ -129,6 +135,7 @@ class ContractsCopyTask extends DefaultTask { contractsPath = objects.property(String.class); excludeBuildFolders = objects.property(Boolean.class); deleteStubsAfterTest = objects.property(Boolean.class); + classpath = objects.fileCollection(); copiedContractsFolder = objects.directoryProperty(); backupContractsFolder = objects.directoryProperty(); @@ -173,7 +180,22 @@ class ContractsCopyTask extends DefaultTask { private void convertContractsToYaml(File file, String antPattern, String slashSeparatedAntPattern, File outputContractsFolder, boolean excludeBuildFolders) { sync(file, antPattern, slashSeparatedAntPattern, excludeBuildFolders, backupContractsFolder.get().getAsFile()); - ToYamlConverter.replaceContractWithYaml(outputContractsFolder); + OutputStream os; + if (getLogger().isDebugEnabled()) { + os = new ByteArrayOutputStream(); + } else { + os = NullOutputStream.INSTANCE; + } + getProject().javaexec(exec -> { + exec.setMain("org.springframework.cloud.contract.verifier.converter.ToYamlConverterApplication"); + exec.classpath(classpath); + exec.args(quoteAndEscape(outputContractsFolder.getAbsolutePath())); + exec.setStandardOutput(os); + exec.setErrorOutput(os); + }); + if (getLogger().isDebugEnabled()) { + getLogger().debug(os.toString()); + } getLogger().info("Replaced DSL files with their YAML representation at [{}]", outputContractsFolder); } @@ -434,6 +456,11 @@ class ContractsCopyTask extends DefaultTask { return deleteStubsAfterTest; } + @Classpath + ConfigurableFileCollection getClasspath() { + return classpath; + } + @OutputDirectory DirectoryProperty getCopiedContractsFolder() { return copiedContractsFolder; @@ -502,4 +529,12 @@ class ContractsCopyTask extends DefaultTask { return null; } + // See: https://github.com/gradle/gradle/issues/6072 + private String quoteAndEscape(String str) { + if (System.getProperty("os.name").contains("Windows")) { + return "\"" + str.replace("\"", "\\\"") + "\""; + } + return str; + } + } \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java index 6d1caef095..f5a71df40a 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java @@ -16,18 +16,22 @@ package org.springframework.cloud.contract.verifier.plugin; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.util.List; +import java.io.OutputStream; import javax.inject.Inject; +import org.eclipse.jgit.util.io.NullOutputStream; import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.OutputDirectory; @@ -35,7 +39,7 @@ import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; -import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter; +import org.springframework.util.StringUtils; //TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ? /** @@ -52,19 +56,22 @@ class GenerateClientStubsFromDslTask extends DefaultTask { static final String TASK_NAME = "generateClientStubs"; static final String DEFAULT_MAPPINGS_FOLDER = "mappings"; - private Property contractsDslDir; + private final Property contractsDslDir; - private ListProperty excludedFiles; + private final ListProperty excludedFiles; - private Property excludeBuildFolders; + private final Property excludeBuildFolders; - private DirectoryProperty stubsOutputDir; + private final ConfigurableFileCollection classpath; + + private final DirectoryProperty stubsOutputDir; @Inject public GenerateClientStubsFromDslTask(ObjectFactory objects) { contractsDslDir = objects.directoryProperty(); excludedFiles = objects.listProperty(String.class); excludeBuildFolders = objects.property(Boolean.class); + classpath = objects.fileCollection(); stubsOutputDir = objects.directoryProperty(); } @@ -75,10 +82,23 @@ class GenerateClientStubsFromDslTask extends DefaultTask { getLogger().info("Stubs output dir [{}]", output); getLogger().info("Spring Cloud Contract Verifier Plugin: Invoking DSL to client stubs conversion"); getLogger().info("Contracts dir is [{}] output stubs dir is [{}]", contractsDslDir.get().getAsFile(), output); - List excludedFiles = this.excludedFiles.get(); - RecursiveFilesConverter converter = new RecursiveFilesConverter(output, contractsDslDir.get().getAsFile(), - excludedFiles, ".*", excludeBuildFolders.get()); - converter.processFiles(); + OutputStream os; + if (getLogger().isDebugEnabled()) { + os = new ByteArrayOutputStream(); + } else { + os = NullOutputStream.INSTANCE; + } + getProject().javaexec(exec -> { + exec.setMain("org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverterApplication"); + exec.classpath(classpath); + exec.args(quoteAndEscape(output.getAbsolutePath()), quoteAndEscape(contractsDslDir.get().getAsFile().getAbsolutePath()), + quoteAndEscape(StringUtils.collectionToCommaDelimitedString(excludedFiles.get())), quoteAndEscape(".*"), excludeBuildFolders.get()); + exec.setStandardOutput(os); + exec.setErrorOutput(os); + }); + if (getLogger().isDebugEnabled()) { + getLogger().debug(os.toString()); + } } @InputDirectory @@ -98,9 +118,22 @@ class GenerateClientStubsFromDslTask extends DefaultTask { return excludeBuildFolders; } + @Classpath + public ConfigurableFileCollection getClasspath() { + return classpath; + } + @OutputDirectory public Property getStubsOutputDir() { return stubsOutputDir; } + // See: https://github.com/gradle/gradle/issues/6072 + private String quoteAndEscape(String str) { + if (System.getProperty("os.name").contains("Windows")) { + return "\"" + str.replace("\"", "\\\"") + "\""; + } + return str; + } + } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java index ec4649b28a..5d1691c0b4 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java @@ -16,14 +16,24 @@ package org.springframework.cloud.contract.verifier.plugin; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.OutputStream; +import java.util.List; + +import javax.inject.Inject; + +import org.eclipse.jgit.util.io.NullOutputStream; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.Optional; @@ -32,15 +42,11 @@ import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; -import org.springframework.cloud.contract.spec.ContractVerifierException; -import org.springframework.cloud.contract.verifier.TestGenerator; import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties; import org.springframework.cloud.contract.verifier.config.TestFramework; import org.springframework.cloud.contract.verifier.config.TestMode; -import javax.inject.Inject; -import java.io.File; -import java.util.List; +import com.fasterxml.jackson.databind.ObjectMapper; /** * Task used to generate server side tests @@ -85,6 +91,8 @@ class GenerateServerTestsTask extends DefaultTask { private final Property failOnInProgress; + private final ConfigurableFileCollection classpath; + private final DirectoryProperty generatedTestSourcesDir; private final DirectoryProperty generatedTestResourcesDir; @@ -106,6 +114,7 @@ class GenerateServerTestsTask extends DefaultTask { this.baseClassMappings = objects.mapProperty(String.class, String.class); this.assertJsonSize = objects.property(Boolean.class); this.failOnInProgress = objects.property(Boolean.class); + this.classpath = objects.fileCollection(); this.generatedTestSourcesDir = objects.directoryProperty(); this.generatedTestResourcesDir = objects.directoryProperty(); } @@ -121,13 +130,27 @@ class GenerateServerTestsTask extends DefaultTask { getLogger().info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation"); getLogger().info("Contracts are unpacked to [{}]", contractsDslDir); getLogger().info("Included contracts are [{}]", includedContracts); + ContractVerifierConfigProperties properties = toConfigProperties(contractsDslDir, includedContracts, generatedTestSources, generatedTestResources); try { - TestGenerator generator = new TestGenerator(toConfigProperties(contractsDslDir, includedContracts, - generatedTestSources, generatedTestResources)); - int generatedClasses = generator.generate(); - getLogger().info("Generated {} test classes", generatedClasses); + String propertiesJson = new ObjectMapper().writeValueAsString(properties); + OutputStream os; + if (getLogger().isDebugEnabled()) { + os = new ByteArrayOutputStream(); + } else { + os = NullOutputStream.INSTANCE; + } + getProject().javaexec(exec -> { + exec.setMain("org.springframework.cloud.contract.verifier.TestGeneratorApplication"); + exec.classpath(classpath); + exec.args(quoteAndEscape(propertiesJson)); + exec.setStandardOutput(os); + exec.setErrorOutput(os); + }); + if (getLogger().isDebugEnabled()) { + getLogger().debug(os.toString()); + } } - catch (ContractVerifierException e) { + catch (Exception e) { throw new GradleException("Spring Cloud Contract Verifier Plugin exception: " + e.getMessage(), e); } } @@ -213,6 +236,11 @@ class GenerateServerTestsTask extends DefaultTask { return failOnInProgress; } + @Classpath + ConfigurableFileCollection getClasspath() { + return classpath; + } + @OutputDirectory DirectoryProperty getGeneratedTestSourcesDir() { return generatedTestSourcesDir; @@ -253,4 +281,12 @@ class GenerateServerTestsTask extends DefaultTask { return properties; } + // See: https://github.com/gradle/gradle/issues/6072 + private String quoteAndEscape(String str) { + if (System.getProperty("os.name").contains("Windows")) { + return "\"" + str.replace("\"", "\\\"") + "\""; + } + return str; + } + } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java index 8ad6ca5755..b90709075d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java @@ -16,12 +16,14 @@ package org.springframework.cloud.contract.verifier.plugin; -import java.io.File; - import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.attributes.Bundling; +import org.gradle.api.attributes.Category; +import org.gradle.api.attributes.LibraryElements; +import org.gradle.api.attributes.Usage; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileCollection; @@ -44,6 +46,8 @@ import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.testing.Test; import org.springframework.cloud.contract.verifier.config.TestFramework; +import java.io.File; + /** * Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can *
    @@ -59,6 +63,8 @@ import org.springframework.cloud.contract.verifier.config.TestFramework; */ public class SpringCloudContractVerifierGradlePlugin implements Plugin { + private static final String SPRING_CLOUD_VERSION = VersionExtractor.forClass(SpringCloudContractVerifierGradlePlugin.class); + private static final String GROUP_NAME = "Verification"; private static final String EXTENSION_NAME = "contracts"; @@ -71,6 +77,10 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin private static final String CONTRACT_TEST_RUNTIME_ONLY_CONFIGURATION_NAME = "contractTestRuntimeOnly"; + private static final String CONTRACT_TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "contractTestRuntimeClasspath"; + + private static final String CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "contractTestGeneratorRuntimeClasspath"; + private static final String VERIFIER_STUBS_JAR_TASK_NAME = "verifierStubsJar"; private static final String CONTRACT_TEST_TASK_NAME = "contractTest"; @@ -97,6 +107,8 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin createGenerateTestsTask(extension, contractTestSourceSet, copyContracts); createAndConfigurePublishStubsToScmTask(extension, generateClientStubs); + project.getDependencies().add(CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME, "org.springframework.cloud:spring-cloud-contract-converters:" + SPRING_CLOUD_VERSION); + project.afterEvaluate(inner -> { DirectoryProperty generatedTestSourcesDir = extension.getGeneratedTestSourcesDir(); if (generatedTestSourcesDir.isPresent()) { @@ -149,6 +161,19 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin contractTestCompileOnly.extendsFrom(testCompileOnly); contractTestImplementation.extendsFrom(testImplementation); contractTestRuntimeOnly.extendsFrom(testRuntimeOnly); + + configurations.create(CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME, conf -> { + conf.setVisible(false); + conf.setCanBeResolved(true); + conf.setCanBeConsumed(false); + conf.attributes(attributes -> { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); + attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY)); + attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); + attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); + }); + conf.extendsFrom(configurations.getByName(CONTRACT_TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME)); + }); } private void registerContractTestTask(SourceSet contractTestSourceSet) { @@ -190,6 +215,8 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin .convention(extension.getBaseClassMappings().getBaseClassMappings()); generateServerTestsTask.getAssertJsonSize().convention(extension.getAssertJsonSize()); generateServerTestsTask.getFailOnInProgress().convention(extension.getFailOnInProgress()); + generateServerTestsTask.getClasspath() + .from(project.getConfigurations().getByName(CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME)); generateServerTestsTask.getGeneratedTestSourcesDir() .convention(extension.getTestFramework().flatMap(testFramework -> { Property correctSourceSetDir; @@ -205,14 +232,22 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin generateServerTestsTask.dependsOn(copyContracts); }); - project.getTasks().named(contractTestSourceSet.getCompileJavaTaskName(), compileContractTestJava -> { - compileContractTestJava.dependsOn(task); - }); + project.getTasks().named(contractTestSourceSet.getCompileJavaTaskName(), compileContractTestJava -> compileContractTestJava.dependsOn(task)); project.getPlugins().withType(GroovyPlugin.class, groovyPlugin -> { project.getTasks().named(contractTestSourceSet.getCompileTaskName("groovy"), compileContractTestGroovy -> { compileContractTestGroovy.dependsOn(task); }); }); + project.getPlugins().withId("kotlin", kotlinPlugin -> { + project.getTasks().named(contractTestSourceSet.getCompileTaskName("kotlin"), compileContractTestKotlin -> { + compileContractTestKotlin.dependsOn(task); + }); + }); + project.getPlugins().withId("org.jetbrains.kotlin.jvm", kotlinJvmPlugin -> { + project.getTasks().named(contractTestSourceSet.getCompileTaskName("kotlin"), compileContractTestKotlin -> { + compileContractTestKotlin.dependsOn(task); + }); + }); } private void createAndConfigurePublishStubsToScmTask(ContractVerifierExtension extension, @@ -258,6 +293,8 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin .convention(copyContracts.flatMap(ContractsCopyTask::getCopiedContractsFolder)); generateClientStubs.getExcludedFiles().convention(extension.getExcludedFiles()); generateClientStubs.getExcludeBuildFolders().convention(extension.getExcludeBuildFolders()); + generateClientStubs.getClasspath() + .from(project.getConfigurations().getByName(CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME)); generateClientStubs.getStubsOutputDir().convention(extension.getStubsOutputDir() .dir(buildRootPath(GenerateClientStubsFromDslTask.DEFAULT_MAPPINGS_FOLDER))); @@ -343,22 +380,24 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin contractsCopyTask.getConvertToYaml().convention(extension.getConvertToYaml()); contractsCopyTask.getFailOnNoContracts().convention(extension.getFailOnNoContracts()); contractsCopyTask.getContractsDirectory() - .convention(extension.getContractsDslDir().map(contractsDslDir -> { - if (contractsDslDir.getAsFile().exists()) { - return contractsDslDir; - } - else { - Directory legacyContractsDslDir = project.getLayout().getProjectDirectory() - .dir("src/test/resources/contracts"); - if (legacyContractsDslDir.getAsFile().exists()) { - project.getLogger().warn( - "Spring Cloud Contract Verifier Plugin: Falling back to legacy contracts directory in 'test' source set. Please switch to 'contractTest' source set as this will be removed in a future release."); - return legacyContractsDslDir; + .convention(extension.getContractsDslDir().flatMap(contractsDslDir -> { + return project.provider(() -> { + if (contractsDslDir.getAsFile().exists()) { + return contractsDslDir; } else { - return null; + Directory legacyContractsDslDir = project.getLayout().getProjectDirectory() + .dir("src/test/resources/contracts"); + if (legacyContractsDslDir.getAsFile().exists()) { + project.getLogger().warn( + "Spring Cloud Contract Verifier Plugin: Falling back to legacy contracts directory in 'test' source set. Please switch to 'contractTest' source set as this will be removed in a future release."); + return legacyContractsDslDir; + } + else { + return null; + } } - } + }); })); contractsCopyTask.getContractDependency().getGroupId() .convention(extension.getContractDependency().getGroupId()); @@ -385,6 +424,8 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin contractsCopyTask.getContractsPath().convention(extension.getContractsPath()); contractsCopyTask.getExcludeBuildFolders().convention(extension.getExcludeBuildFolders()); contractsCopyTask.getDeleteStubsAfterTest().convention(extension.getDeleteStubsAfterTest()); + contractsCopyTask.getClasspath() + .from(project.getConfigurations().getByName(CONTRACT_TEST_GENERATOR_RUNTIME_CLASSPATH_CONFIGURATION_NAME)); contractsCopyTask.getCopiedContractsFolder() .convention(extension.getStubsOutputDir().dir(buildRootPath(ContractsCopyTask.CONTRACTS))); diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/VersionExtractor.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/VersionExtractor.java new file mode 100644 index 0000000000..296c7c08ec --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/VersionExtractor.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2020 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 + * + * https://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 org.springframework.cloud.contract.verifier.plugin; + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +final class VersionExtractor { + private VersionExtractor() { + } + + static String forClass(Class cls) { + String implementationVersion = cls.getPackage().getImplementationVersion(); + if (implementationVersion != null) { + return implementationVersion; + } + URL codeSourceLocation = cls.getProtectionDomain().getCodeSource().getLocation(); + try { + URLConnection connection = codeSourceLocation.openConnection(); + if (connection instanceof JarURLConnection) { + return getImplementationVersion(((JarURLConnection) connection).getJarFile()); + } + try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) { + return getImplementationVersion(jarFile); + } + } + catch (Exception e) { + return null; + } + } + + private static String getImplementationVersion(JarFile jarFile) throws IOException { + return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } +} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/TestGeneratorApplication.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/TestGeneratorApplication.java new file mode 100644 index 0000000000..52b4c6eaf4 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/TestGeneratorApplication.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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 + * + * https://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 org.springframework.cloud.contract.verifier; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties; + +public final class TestGeneratorApplication { + + private TestGeneratorApplication() { + } + + public static void main(String[] args) throws JsonProcessingException { + if (args.length != 1) { + throw new RuntimeException("Invalid number of arguments"); + } + + ObjectMapper objectMapper = new ObjectMapper(); + + ContractVerifierConfigProperties configProperties = objectMapper.readValue(args[0], + ContractVerifierConfigProperties.class); + + TestGenerator generator = new TestGenerator(configProperties); + generator.generate(); + } + +} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ToYamlConverterApplication.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ToYamlConverterApplication.java new file mode 100644 index 0000000000..c9755dc6af --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/converter/ToYamlConverterApplication.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013-2020 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 + * + * https://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 org.springframework.cloud.contract.verifier.converter; + +import java.io.File; + +public final class ToYamlConverterApplication { + + private ToYamlConverterApplication() { + } + + public static void main(String[] args) { + if (args.length != 1) { + throw new RuntimeException("Invalid number of arguments"); + } + + File copiedContractsFolder = new File(args[0]); + + ToYamlConverter.replaceContractWithYaml(copiedContractsFolder); + } + +}