From eb66dca9304292a9edf0e6c08d0966a3209dd188 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Tue, 10 Nov 2020 01:20:33 -0600 Subject: [PATCH] Fixes for remote contracts (#1554) * Make contractsDslDir return null if neither directory exists at task configuration time * Fixup docs type and make the wording a little more clear * Allow username to be null for ls-remote command. * Rework git commit id discovery to handle multiple uri schemes as well as fixing the bug with the MapProperty becoming finalized on first read and then not being able to further edit it (eg: in order to add git.commit) --- .../asciidoc/_project-features-contract.adoc | 4 +- .../verifier/plugin/ContractsCopyTask.java | 94 ++++++++++++------- ...ringCloudContractVerifierGradlePlugin.java | 12 ++- 3 files changed, 70 insertions(+), 40 deletions(-) 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()