Added support for incremental Gradle builds

fixes gh-1133
This commit is contained in:
Marcin Grzejszczak
2019-07-31 23:26:28 +02:00
parent 746fe40f80
commit 31972419e9
9 changed files with 210 additions and 84 deletions

View File

@@ -35,6 +35,10 @@
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>

View File

@@ -19,8 +19,10 @@ package org.springframework.cloud.contract.verifier.plugin
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.gradle.api.internal.ConventionTask
import org.gradle.api.DefaultTask
import org.gradle.api.logging.Logger
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.WorkResult
@@ -37,22 +39,30 @@ import org.springframework.cloud.contract.verifier.converter.ToYamlConverter
*/
@PackageScope
@CompileStatic
class ContractsCopyTask extends ConventionTask {
class ContractsCopyTask extends DefaultTask {
private static final String ORIGINAL_PATH = "original"
ContractVerifierExtension extension
GradleContractsDownloader downloader
@InputDirectory
File input
@OutputDirectory
File outputContractsFolder
@TaskAction
void copy() {
ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getExtension())
File file = getDownloader().downloadAndUnpackContractsIfRequired(getExtension(), props)
file = contractsSubDirIfPresent(logger, file)
ContractVerifierConfigProperties props = props()
if (logger.isDebugEnabled()) {
logger.debug("Config props [" + props + "]")
}
File file = getInput()
String antPattern = "${props.includedRootFolderAntPattern}*.*"
String slashSeparatedGroupId = project.group.toString().replace(".", File.separator)
String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString())
String root = root(props)
File outputContractsFolder = outputContractsFolder(root)
String root = root()
File outputContractsFolder = getOutputContractsFolder()
project.logger.info("Downloading and unpacking files from [$file] to [$outputContractsFolder]. The inclusion ant patterns are [${antPattern}] and [${slashSeparatedAntPattern}]")
copy(file, antPattern, slashSeparatedAntPattern, props, outputContractsFolder)
if (getExtension().isConvertToYaml()) {
@@ -61,15 +71,26 @@ class ContractsCopyTask extends ConventionTask {
}
private File getInput() {
File file = getDownloader().downloadAndUnpackContractsIfRequired(getExtension(), props())
return contractsSubDirIfPresent(logger, file)
}
private ContractVerifierConfigProperties props() {
return ExtensionToProperties.fromExtension(getExtension())
}
@CompileDynamic
private File outputContractsFolder(String root) {
private File getOutputContractsFolder() {
String root = root()
File outputContractsFolder = outputFolder(root, "contracts")
ext.contractsDslDir = outputContractsFolder
return outputContractsFolder
}
@CompileDynamic
private String root(ContractVerifierConfigProperties props) {
private String root() {
ContractVerifierConfigProperties props = props()
String root = OutputFolderBuilder.buildRootPath(project)
ext.contractVerifierConfigProperties = props
return root

View File

@@ -18,8 +18,10 @@ package org.springframework.cloud.contract.verifier.plugin
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
@@ -33,8 +35,12 @@ import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConve
* @since 2.0.0
*/
@CompileStatic
class GenerateClientStubsFromDslTask extends ConventionTask {
class GenerateClientStubsFromDslTask extends DefaultTask {
@InputDirectory
File contractsDslDir
@OutputDirectory
File stubsOutputDir
ContractVerifierExtension configProperties
@@ -45,9 +51,8 @@ class GenerateClientStubsFromDslTask extends ConventionTask {
logger.info("Stubs output dir [${getStubsOutputDir()}")
Task copyContractsTask = project.getTasksByName(SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME, false).first()
ContractVerifierConfigProperties props = props(copyContractsTask)
File contractsDslDir = contractsDslDir(copyContractsTask, props)
logger.info("Spring Cloud Contract Verifier Plugin: Invoking DSL to client stubs conversion")
props.contractsDslDir = contractsDslDir
props.contractsDslDir = getContractsDslDir()
props.includedContracts = ".*"
File outMappingsDir = OutputFolderBuilder.outputMappingsDir(project, getStubsOutputDir())
logger.info("Contracts dir is [${contractsDslDir}] output stubs dir is [${outMappingsDir}]")
@@ -55,6 +60,15 @@ class GenerateClientStubsFromDslTask extends ConventionTask {
converter.processFiles()
}
private File getContractsDslDir() {
Task copyContractsTask = project.getTasksByName(SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME, false).first()
ContractVerifierConfigProperties props = props(copyContractsTask)
if (logger.isDebugEnabled()) {
logger.debug("Config props [" + props + "]")
}
return contractsDslDir(copyContractsTask, props)
}
@CompileDynamic
private ContractVerifierConfigProperties props(Task task) {
try {

View File

@@ -18,16 +18,16 @@ package org.springframework.cloud.contract.verifier.plugin
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Task
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.springframework.cloud.contract.spec.Contract
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 static org.springframework.cloud.contract.verifier.plugin.SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME
@@ -37,20 +37,22 @@ import static org.springframework.cloud.contract.verifier.plugin.SpringCloudCont
* @since 1.0.0
*/
@CompileStatic
class GenerateServerTestsTask extends ConventionTask {
class GenerateServerTestsTask extends DefaultTask {
@InputDirectory
File contractsDslDir
@OutputDirectory
File generatedTestSourcesDir
//TODO: How to deal with @Input*, @Output* and that domain object?
ContractVerifierExtension configProperties
GradleContractsDownloader downloader
@TaskAction
void generate() {
logger.info("Generated test sources dir [${getGeneratedTestSourcesDir()}]")
Task copyContractsTask = project.getTasksByName(COPY_CONTRACTS_TASK_NAME, false).first()
ContractVerifierConfigProperties props = props(copyContractsTask)
File contractsDslDir = contractsDslDir(copyContractsTask, props)
ContractVerifierConfigProperties props = getProps()
File contractsDslDir = getContractsDslDir()
if (getConfigProperties().getContractDependency()) {
project.logger.debug("Updating the stubs locations for the case where we have a JAR with contracts")
props.contractsDslDir = contractsDslDir
@@ -59,9 +61,6 @@ class GenerateServerTestsTask extends ConventionTask {
project.logger.info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation")
project.logger.info("Contracts are unpacked to [${contractsDslDir}]")
project.logger.info("Included contracts are [${props.includedContracts}]")
def sourceSetType = getConfigProperties().getTestFramework() == TestFramework.SPOCK ?
"groovy" : "java"
applySourceSets(sourceSetType)
try {
props = props ?: ExtensionToProperties.fromExtension(getConfigProperties())
props.contractsDslDir = contractsDslDir
@@ -74,18 +73,9 @@ class GenerateServerTestsTask extends ConventionTask {
}
}
@CompileDynamic
private void applySourceSets(sourceSetType) {
project.sourceSets.test."${sourceSetType}" {
project.logger.
info("Registering ${getConfigProperties().generatedTestSourcesDir} as test source directory")
srcDir getConfigProperties().getGeneratedTestSourcesDir()
}
project.sourceSets.test.resources {
project.logger.
info("Registering ${getConfigProperties().generatedTestResourcesDir} as test resource directory")
srcDir getConfigProperties().getGeneratedTestResourcesDir()
}
private ContractVerifierConfigProperties getProps() {
Task copyContractsTask = project.getTasksByName(COPY_CONTRACTS_TASK_NAME, false).first()
return props(copyContractsTask)
}
@CompileDynamic
@@ -101,6 +91,12 @@ class GenerateServerTestsTask extends ConventionTask {
}
}
private File getContractsDslDir() {
Task copyContractsTask = project.getTasksByName(COPY_CONTRACTS_TASK_NAME, false).first()
ContractVerifierConfigProperties props = props(copyContractsTask)
return contractsDslDir(copyContractsTask, props)
}
@CompileDynamic
private File contractsDslDir(Task task, ContractVerifierConfigProperties props) {
try {

View File

@@ -17,7 +17,8 @@
package org.springframework.cloud.contract.verifier.plugin
import groovy.transform.CompileStatic
import org.gradle.api.internal.ConventionTask
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater
@@ -33,12 +34,17 @@ import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
* @since 2.0.0
*/
@CompileStatic
class PublishStubsToScmTask extends ConventionTask {
File stubsOutputDir
ContractVerifierExtension configProperties
GradleContractsDownloader downloader
class PublishStubsToScmTask extends DefaultTask {
private final ExtensionHolderSpec closureHolder = new ClosureHolder()
@OutputDirectory
File stubsOutputDir
ContractVerifierExtension configProperties
GradleContractsDownloader downloader
@TaskAction
void publishStubsToScm() {
ContractVerifierExtension clonedExtension = modifyExtension()
@@ -67,6 +73,12 @@ class PublishStubsToScmTask extends ConventionTask {
return true
}
/**
* This method can be used to perform additional customization of
* the {@link ContractVerifierExtension}.
*
* @param closure to customize the {@link ContractVerifierExtension}
*/
void customize(@DelegatesTo(ContractVerifierExtension) Closure closure) {
project.logger.debug("Storing the extension closure")
this.closureHolder.extensionClosure = closure

View File

@@ -25,6 +25,9 @@ import org.gradle.api.plugins.GroovyPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.jvm.tasks.Jar
import org.springframework.cloud.contract.verifier.config.TestFramework
/**
* Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can
* <ul>
@@ -63,9 +66,30 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
Task copyContracts = createAndConfigureCopyContractsTask(stubsJar, downloader, extension)
createAndConfigureMavenPublishPlugin(stubsJar, extension)
createGenerateTestsTask(extension, copyContracts, downloader)
createAndConfigureGenerateClientStubs(extension, copyContracts)
createAndConfigureGenerateClientStubs(extension, copyContracts, downloader)
createAndConfigurePublishStubsToScmTask(extension, downloader)
addIdeaTestSources(project, extension)
applyDefaultSourceSets(extension)
}
private void applyDefaultSourceSets(ContractVerifierExtension extension) {
def sourceSetType = extension.getTestFramework() == TestFramework.SPOCK ?
"groovy" : "java"
applySourceSets(extension, sourceSetType)
}
@CompileDynamic
private void applySourceSets(ContractVerifierExtension extension, sourceSetType) {
project.sourceSets.test."${sourceSetType}" {
project.logger.
info("Registering ${extension.generatedTestSourcesDir} as test source directory")
srcDir extension.getGeneratedTestSourcesDir()
}
project.sourceSets.test.resources {
project.logger.
info("Registering ${extension.generatedTestResourcesDir} as test resource directory")
srcDir extension.getGeneratedTestResourcesDir()
}
}
@CompileDynamic
@@ -108,49 +132,46 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
@CompileDynamic
private void createGenerateTestsTask(ContractVerifierExtension extension, Task copyContracts,
GradleContractsDownloader gradleContractsDownloader) {
Task task = project.tasks.create(GENERATE_SERVER_TESTS_TASK_NAME, GenerateServerTestsTask)
GenerateServerTestsTask task = project.tasks.create(GENERATE_SERVER_TESTS_TASK_NAME, GenerateServerTestsTask)
task.description = "Generate server tests from the contracts"
task.group = GROUP_NAME
task.conventionMapping.with {
downloader = { gradleContractsDownloader }
generatedTestSourcesDir = { extension.generatedTestSourcesDir }
configProperties = { extension }
task.with {
downloader = gradleContractsDownloader
generatedTestSourcesDir = extension.generatedTestSourcesDir
configProperties = extension
}
task.enabled = !project.gradle.startParameter.excludedTaskNames.contains("test")
task.dependsOn copyContracts
project.tasks.findByName("compileTestJava").dependsOn(task)
}
@CompileDynamic
private void createAndConfigurePublishStubsToScmTask(ContractVerifierExtension extension,
GradleContractsDownloader gradleContractsDownloader) {
Task task = project.tasks.create(PUBLISH_STUBS_TO_SCM_TASK_NAME, PublishStubsToScmTask)
PublishStubsToScmTask task = project.tasks.create(PUBLISH_STUBS_TO_SCM_TASK_NAME, PublishStubsToScmTask)
task.description = "The generated stubs get committed to the SCM repo and pushed to origin"
task.group = GROUP_NAME
task.conventionMapping.with {
downloader = { gradleContractsDownloader }
configProperties = { extension }
stubsOutputDir = { extension.stubsOutputDir }
task.with {
downloader = gradleContractsDownloader
configProperties = extension
stubsOutputDir = extension.stubsOutputDir
}
task.dependsOn DSL_TO_CLIENT_TASK_NAME
}
@CompileDynamic
private Task createAndConfigureGenerateClientStubs(ContractVerifierExtension extension,
Task copyContracts) {
Task task = project.tasks.create(DSL_TO_CLIENT_TASK_NAME, GenerateClientStubsFromDslTask)
Task copyContracts, GradleContractsDownloader gradleContractsDownloader) {
GenerateClientStubsFromDslTask task = project.tasks.create(DSL_TO_CLIENT_TASK_NAME, GenerateClientStubsFromDslTask)
task.description = "Generate client stubs from the contracts"
task.group = GROUP_NAME
task.conventionMapping.with {
downloader = { gradleContractsDownloader }
stubsOutputDir = { extension.stubsOutputDir }
configProperties = { extension }
task.with {
downloader = gradleContractsDownloader
stubsOutputDir = extension.stubsOutputDir
configProperties = extension
}
task.dependsOn copyContracts
return task
}
@CompileDynamic
private Task createAndConfigureStubsJarTasks(ContractVerifierExtension extension) {
Task task = stubsTask()
if (task) {
@@ -158,21 +179,31 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
return task
}
else {
task = project.tasks.create(type: Jar, name: VERIFIER_STUBS_JAR_TASK_NAME,
dependsOn: DSL_TO_CLIENT_TASK_NAME) {
baseName = project.name
classifier = extension.stubsSuffix
from { extension.stubsOutputDir ?: project.file("${project.buildDir}/stubs") }
}
task = createStubsJarTask(extension)
task.description = "Creates the stubs JAR task"
task.group = GROUP_NAME
project.artifacts {
archives task
}
setArchives(task)
return task
}
}
@CompileDynamic
private Task createStubsJarTask(ContractVerifierExtension extension) {
return project.tasks.create(type: Jar, name: VERIFIER_STUBS_JAR_TASK_NAME,
dependsOn: DSL_TO_CLIENT_TASK_NAME) {
getArchiveBaseName().set(project.name)
getArchiveClassifier().set(extension.stubsSuffix)
from { extension.stubsOutputDir ?: project.file("${project.buildDir}/stubs") }
}
}
@CompileDynamic
private void setArchives(Task task) {
project.artifacts {
archives task
}
}
private Task stubsTask() {
try {
return project.tasks.getByName(VERIFIER_STUBS_JAR_TASK_NAME)
@@ -182,22 +213,20 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
}
}
@CompileDynamic
private Task createAndConfigureCopyContractsTask(Task stubs,
GradleContractsDownloader gradleContractsDownloader,
ContractVerifierExtension contractVerifierExtension) {
Task task = project.tasks.create(COPY_CONTRACTS_TASK_NAME, ContractsCopyTask)
ContractsCopyTask task = project.tasks.create(COPY_CONTRACTS_TASK_NAME, ContractsCopyTask)
task.description = "Copies contracts to the output folder"
task.group = GROUP_NAME
task.conventionMapping.with {
downloader = { gradleContractsDownloader }
extension = { contractVerifierExtension }
task.with {
downloader = gradleContractsDownloader
extension = contractVerifierExtension
}
stubs.dependsOn task
return task
}
@CompileDynamic
private void createAndConfigureMavenPublishPlugin(Task stubsTask, ContractVerifierExtension extension) {
if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) {
project.logger.debug("Maven Publish Plugin is not present - won't add default publication")
@@ -213,12 +242,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
def publishingExtension = project.extensions.findByName('publishing')
if (!hasPublication(publishingExtension)) {
project.logger.debug("Spring Cloud Contract Verifier Plugin: Stubs publication is not present - will create one")
publishingExtension.publications {
stubs(MavenPublication) {
artifactId "${project.name}"
artifact stubsTask
}
}
setPublications(publishingExtension, stubsTask)
}
else {
project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs publication was present - won't create a new one. Remember about passing stubs as artifact")
@@ -227,6 +251,16 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
}
}
@CompileDynamic
private void setPublications(def publishingExtension, Task stubsTask) {
publishingExtension.publications {
stubs(MavenPublication) {
artifactId "${project.name}"
artifact stubsTask
}
}
}
@CompileDynamic
private boolean hasPublication(def publishingExtension) {
try {

View File

@@ -19,6 +19,8 @@ package org.springframework.cloud.contract.verifier.plugin
import org.junit.Ignore
import spock.lang.Stepwise
import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
@Stepwise
@Ignore
class SampleProjectSpec extends ContractVerifierIntegrationSpec {
@@ -32,22 +34,33 @@ class SampleProjectSpec extends ContractVerifierIntegrationSpec {
def "should pass basic flow for Spock"() {
given:
assert fileExists('build.gradle')
expect:
when:
String[] args = ["check", "publishToMavenLocal", "--debug"] as String[]
if (WORK_OFFLINE) {
args << "--offline"
}
runTasksSuccessfully(args)
then:
jarContainsContractVerifierContracts('fraudDetectionService/build/libs')
when: "running generation without change inputs"
def secondExecutionResult = runTasksSuccessfully()
then: "tasks should be up-to-date"
validateTasksOutcome(secondExecutionResult, UP_TO_DATE, 'generateClientStubs', 'generateContractTests', 'copyContracts')
}
def "should pass basic flow for JUnit"() {
given:
switchToJunitTestFramework()
assert fileExists('build.gradle')
expect:
when:
runTasksSuccessfully(checkAndPublishToMavenLocal())
then:
jarContainsContractVerifierContracts('fraudDetectionService/build/libs')
when: "running generation without change inputs"
def secondExecutionResult = runTasksSuccessfully()
then: "tasks should be up-to-date"
validateTasksOutcome(secondExecutionResult, UP_TO_DATE, 'generateClientStubs', 'generateContractTests', 'copyContracts')
}
}

View File

@@ -43,6 +43,7 @@ subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = "1.0.0"
repositories {
mavenCentral()

View File

@@ -18,8 +18,10 @@ package org.springframework.cloud.contract.verifier.config;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -366,4 +368,33 @@ public class ContractVerifierConfigProperties {
this.excludeBuildFolders = excludeBuildFolders;
}
@Override
public String toString() {
return new StringJoiner(", ",
ContractVerifierConfigProperties.class.getSimpleName() + "[", "]")
.add("targetFramework=" + targetFramework)
.add("testFramework=" + testFramework).add("testMode=" + testMode)
.add("basePackageForTests='" + basePackageForTests + "'")
.add("baseClassForTests='" + baseClassForTests + "'")
.add("nameSuffixForTests='" + nameSuffixForTests + "'")
.add("ruleClassForTests='" + ruleClassForTests + "'")
.add("excludedFiles=" + excludedFiles)
.add("includedFiles=" + includedFiles)
.add("ignoredFiles=" + ignoredFiles)
.add("imports=" + Arrays.toString(imports))
.add("staticImports=" + Arrays.toString(staticImports))
.add("contractsDslDir=" + contractsDslDir)
.add("generatedTestSourcesDir=" + generatedTestSourcesDir)
.add("generatedTestResourcesDir=" + generatedTestResourcesDir)
.add("stubsOutputDir=" + stubsOutputDir)
.add("stubsSuffix='" + stubsSuffix + "'")
.add("assertJsonSize=" + assertJsonSize)
.add("includedContracts='" + includedContracts + "'")
.add("includedRootFolderAntPattern='"
+ includedRootFolderAntPattern + "'")
.add("packageWithBaseClasses='" + packageWithBaseClasses + "'")
.add("baseClassMappings=" + baseClassMappings)
.add("excludeBuildFolders=" + excludeBuildFolders).toString();
}
}