diff --git a/docs/src/main/asciidoc/_project-features-contract.adoc b/docs/src/main/asciidoc/_project-features-contract.adoc index d72eb001be..dd08e3684e 100644 --- a/docs/src/main/asciidoc/_project-features-contract.adoc +++ b/docs/src/main/asciidoc/_project-features-contract.adoc @@ -103,8 +103,8 @@ contracts { To get started with writing contracts in Kotlin, you need to start with a (newly created) Kotlin Script file (`.kts`). As with the Java DSL, you can put your contracts in any directory of your choice. -By default, the Maven plugin looks at `src/test/resources/contracts` and Gradle plugins look at the -`src/contractTest/resources/contracts` directory. +By default, the Maven plugin will look at the `src/test/resources/contracts` directory and Gradle plugin will +look at the `src/contractTest/resources/contracts` directory. NOTE: Since 3.0.0, the Gradle plugin will also look at the legacy directory `src/test/resources/contracts` for migration purposes. When contracts are found in this directory, a warning 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 05013af806..5830dc60eb 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 @@ -17,7 +17,9 @@ package org.springframework.cloud.contract.verifier.plugin; import java.io.File; +import java.net.URI; import java.util.Collection; +import java.util.Map; import javax.inject.Inject; @@ -25,14 +27,13 @@ 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.gradle.api.Action; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; -import org.gradle.api.Task; 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.Input; import org.gradle.api.tasks.InputDirectory; @@ -42,7 +43,6 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; 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.stubrunner.ContractDownloader; import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder; @@ -89,6 +89,8 @@ class ContractsCopyTask extends DefaultTask { private final MapProperty contractsProperties; + private final Provider> allContractsProperties; + private final Property contractsPath; private final Property excludeBuildFolders; @@ -115,6 +117,15 @@ class ContractsCopyTask extends DefaultTask { contractRepository = objects.newInstance(Repository.class); contractsMode = objects.property(StubRunnerProperties.StubsMode.class); contractsProperties = objects.mapProperty(String.class, String.class); + allContractsProperties = contractsProperties.flatMap(contractsProps -> { + MapProperty allProps = objects.mapProperty(String.class, String.class); + allProps.putAll(contractsProps); + String gitCommitId = this.discoverGitCommitId(contractsProps); + if (StringUtils.hasText(gitCommitId)) { + allProps.put("git.commit", gitCommitId); + } + return allProps; + }); contractsPath = objects.property(String.class); excludeBuildFolders = objects.property(Boolean.class); deleteStubsAfterTest = objects.property(Boolean.class); @@ -124,35 +135,6 @@ class ContractsCopyTask extends DefaultTask { this.getOutputs().upToDateWhen(task -> !(this.shouldDownloadContracts() && this.getContractDependency().toStubConfiguration().isVersionChanging())); - // Lambdas break build caching support - this.doFirst(new Action() { - @Override - public void execute(Task inner) { - String repositoryUrl = contractRepository.getRepositoryUrl().getOrNull(); - if (repositoryUrl != null && ScmStubDownloaderBuilder.isProtocolAccepted(repositoryUrl)) { - String branch = StubRunnerPropertyUtils.getProperty(contractsProperties.get(), "git.branch"); - branch = StringUtils.hasText(branch) ? branch : "master"; - UsernamePasswordCredentialsProvider provider = null; - if (StringUtils.hasText(contractRepository.getUsername().get())) { - provider = new UsernamePasswordCredentialsProvider(contractRepository.getUsername().get(), - contractRepository.getPassword().get()); - } - try { - Collection refs = Git.lsRemoteRepository().setRemote(repositoryUrl) - .setCredentialsProvider(provider).call(); - for (Ref ref : refs) { - if (ref.getName().equals(branch) || ref.getName().equals("refs/heads/" + branch) - || ref.getName().equals("refs/tags/" + branch)) { - contractsProperties.put("git.commit", ref.getObjectId().name()); - } - } - } - catch (GitAPIException e) { - ContractsCopyTask.this.getLogger().warn("Unable to determine git repository commit id"); - } - } - } - }); } @TaskAction @@ -282,7 +264,7 @@ class ContractsCopyTask extends DefaultTask { } @InputDirectory - @SkipWhenEmpty + @Optional @PathSensitive(PathSensitivity.RELATIVE) DirectoryProperty getContractsDirectory() { return contractsDirectory; @@ -426,11 +408,16 @@ class ContractsCopyTask extends DefaultTask { return contractsMode; } - @Input + @Internal MapProperty getContractsProperties() { return contractsProperties; } + @Input + Provider> getAllContractsProperties() { + return allContractsProperties; + } + @Input @Optional Property getContractsPath() { @@ -470,7 +457,7 @@ class ContractsCopyTask extends DefaultTask { .withStubRepositoryRoot(contractRepository.repositoryUrl.getOrNull()).withStubsMode(contractsMode.get()) .withUsername(contractRepository.username.getOrNull()) .withPassword(contractRepository.password.getOrNull()) - .withDeleteStubsAfterTest(deleteStubsAfterTest.get()).withProperties(contractsProperties.getOrNull()) + .withDeleteStubsAfterTest(deleteStubsAfterTest.get()).withProperties(allContractsProperties.getOrNull()) .withFailOnNoStubs(failOnNoContracts.get()); if (contractRepository.proxyPort.isPresent()) { options = options.withProxy(contractRepository.proxyHost.getOrNull(), contractRepository.proxyPort.get()); @@ -478,4 +465,41 @@ class ContractsCopyTask extends DefaultTask { return options.build(); } + @Nullable + private String discoverGitCommitId(Map props) { + String repositoryUrl = contractRepository.getRepositoryUrl().getOrNull(); + if (repositoryUrl != null && ScmStubDownloaderBuilder.isProtocolAccepted(repositoryUrl)) { + String branch = StubRunnerPropertyUtils.getProperty(props, "git.branch"); + branch = StringUtils.hasText(branch) ? branch : "master"; + UsernamePasswordCredentialsProvider provider = null; + if (StringUtils.hasText(contractRepository.getUsername().getOrNull())) { + provider = new UsernamePasswordCredentialsProvider(contractRepository.getUsername().get(), + contractRepository.getPassword().get()); + } + try { + String repoUrl; + URI repoUri = URI.create(repositoryUrl); + String part = repoUri.getSchemeSpecificPart(); + if (!StringUtils.hasLength(part)) { + repoUrl = part; + } + else { + repoUrl = part.startsWith("//") ? part.substring(2) : part; + } + Collection refs = Git.lsRemoteRepository().setRemote(repoUrl).setCredentialsProvider(provider) + .call(); + for (Ref ref : refs) { + if (ref.getName().equals(branch) || ref.getName().equals("refs/heads/" + branch) + || ref.getName().equals("refs/tags/" + branch)) { + return ref.getObjectId().name(); + } + } + } + catch (GitAPIException e) { + ContractsCopyTask.this.getLogger().warn("Unable to determine git repository commit id", e); + } + } + return null; + } + } \ 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/SpringCloudContractVerifierGradlePlugin.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java index d4b3726872..84a4209568 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 @@ -350,10 +350,16 @@ public class SpringCloudContractVerifierGradlePlugin implements Plugin return contractsDslDir; } else { - 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 project.getLayout().getProjectDirectory() + 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()