From 866720c003c2429f4c5763fd1234522fe87744e0 Mon Sep 17 00:00:00 2001 From: Jente Sondervorst Date: Wed, 25 Sep 2024 19:30:59 +0200 Subject: [PATCH] Adding gradle wrapper through distribution is now possible (#4491) * Fixes #4490 * Added checksum for custom distributionUri * Fixed test * Removed leftover investigation disabled annotations * Review comments + added scenario's in testcase of migration/addition/removal... * fix test, again... * Apply formatter and collapse imports * Slight polish * review comments --------- Co-authored-by: Jente Sondervorst Co-authored-by: Tim te Beek --- .../gradle/UpdateGradleWrapper.java | 103 ++++--- .../gradle/UpdateGradleWrapperTest.java | 286 +++++++++++++++--- 2 files changed, 299 insertions(+), 90 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java index 67ba93dbf15..84f1a9ad0e5 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java @@ -48,7 +48,6 @@ import static java.util.Objects.requireNonNull; import static org.openrewrite.PathUtils.equalIgnoringSeparators; import static org.openrewrite.gradle.util.GradleWrapper.*; -import static org.openrewrite.internal.StringUtils.formatUriForPropertiesFile; import static org.openrewrite.internal.StringUtils.isBlank; @RequiredArgsConstructor @@ -108,9 +107,23 @@ public String getDescription() { @Nullable final String wrapperUri; + @Getter + @Option(example = "29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda", + description = "The SHA-256 checksum of the Gradle distribution. " + + "If specified, the recipe will add the checksum along with the custom distribution URL.", + required = false) + @Nullable + final String distributionChecksum; + @Override public Validated validate() { Validated validated = super.validate(); + if (wrapperUri != null && (version != null || distribution != null)) { + return Validated.invalid("wrapperUri", wrapperUri, "WrapperUri cannot be used with version and/or distribution parameter"); + } + if (wrapperUri == null && distributionChecksum != null) { + return Validated.invalid("distributionChecksum", distributionChecksum, "DistributionChecksum can only be used with wrapperUri"); + } if (version != null) { validated = validated.and(Semver.validate(version, null)); } @@ -123,37 +136,21 @@ public Validated validate() { private GradleWrapper getGradleWrapper(ExecutionContext ctx) { if (gradleWrapper == null) { + if (wrapperUri != null) { + return gradleWrapper = GradleWrapper.create(URI.create(wrapperUri), ctx); + } try { gradleWrapper = GradleWrapper.create(distribution, version, null, ctx); } catch (Exception e) { - // services.gradle.org is unreachable, possibly because of a firewall - // But if the user specified a wrapperUri to an internal repository things might still be workable - if (wrapperUri == null) { - // If the user didn't specify a wrapperUri, but they did provide a specific version we assume they know this version - // is available from whichever distribution url they were previously using and update the version - if (!StringUtils.isBlank(version) && Semver.validate(version, null).getValue() instanceof ExactVersion) { - return gradleWrapper = new GradleWrapper(version, new DistributionInfos("", null, null)); - } else { - throw new IllegalArgumentException( - "Could not reach services.gradle.org, no alternative wrapper URI is provided and no exact version is provided. " + - "To use this recipe in environments where services.gradle.org is unavailable specify a wrapperUri or exact version.", e); - } + // services.gradle.org is unreachable + // If the user didn't specify a wrapperUri, but they did provide a specific version we assume they know this version + // is available from whichever distribution url they were previously using and update the version + if (!StringUtils.isBlank(version) && Semver.validate(version, null).getValue() instanceof ExactVersion) { + return gradleWrapper = new GradleWrapper(version, new DistributionInfos("", null, null)); } - if (wrapperUri.contains("${version})")) { - if (version == null) { - throw new IllegalArgumentException( - "wrapperUri contains a ${version} interpolation specifier but no version parameter was specified.", e); - } - if (!version.matches("[0-9.]+")) { - throw new IllegalArgumentException( - "Version selectors like \"" + version + "\" are unavailable when services.gradle.org cannot be reached. " + - "Specify an exact, literal version number.", e); - } - } - String effectiveWrapperUri = wrapperUri - .replace("${version}", version == null ? "" : version) - .replace("${distribution}", distribution == null ? "bin" : distribution); - gradleWrapper = GradleWrapper.create(URI.create(effectiveWrapperUri), ctx); + throw new IllegalArgumentException( + "Could not reach services.gradle.org. " + + "To use this recipe in environments where services.gradle.org is unavailable specify a wrapperUri or exact version.", e); } } return gradleWrapper; @@ -222,16 +219,26 @@ public Properties visitEntry(Properties.Entry entry, ExecutionContext ctx) { return entry; } - // Typical example: https://services.gradle.org/distributions/gradle-7.4-all.zip + // Typical example: https://services.gradle.org/distributions/gradle-7.4-all.zip or https://company.com/repo/gradle-8.2-bin.zip String currentDistributionUrl = entry.getValue().getText(); GradleWrapper gradleWrpr = getGradleWrapper(ctx); - if (StringUtils.isBlank(gradleWrpr.getDistributionUrl()) && - !StringUtils.isBlank(version) && Semver.validate(version, null).getValue() instanceof ExactVersion) { + if (StringUtils.isBlank(gradleWrpr.getDistributionUrl()) && !StringUtils.isBlank(version) && + Semver.validate(version, null).getValue() instanceof ExactVersion) { String newDownloadUrl = currentDistributionUrl.replace("\\", "") - .replaceAll("(.*gradle-)(\\d+\\.\\d+(?:\\.\\d+)?)(.*-(?:bin|all).zip)", "$1" + gradleWrapper.getVersion() + "$3"); + .replaceAll("(.*gradle-)(\\d+\\.\\d+(?:\\.\\d+)?)(.*-(?:bin|all).zip)", + "$1" + gradleWrapper.getVersion() + "$3"); gradleWrapper = new GradleWrapper(version, new DistributionInfos(newDownloadUrl, null, null)); } + String wrapperHost = currentDistributionUrl.substring(0, currentDistributionUrl.lastIndexOf("/")) + "/gradle-"; + if (StringUtils.isBlank(wrapperUri) && !StringUtils.isBlank(gradleWrpr.getDistributionUrl()) && + !gradleWrpr.getPropertiesFormattedUrl().startsWith(wrapperHost)) { + String newDownloadUrl = gradleWrpr.getDistributionUrl() + .replace("\\", "") + .replaceAll("(.*gradle-)(\\d+\\.\\d+(?:\\.\\d+)?)(.*-(?:bin|all).zip)", + wrapperHost + gradleWrapper.getVersion() + "$3"); + gradleWrapper = new GradleWrapper(gradleWrpr.getVersion(), new DistributionInfos(newDownloadUrl, null, null)); + } if (!gradleWrapper.getPropertiesFormattedUrl().equals(currentDistributionUrl)) { acc.needsWrapperUpdate = true; @@ -292,12 +299,17 @@ public Collection generate(GradleWrapperState acc, ExecutionContext GradleWrapper gradleWrapper = getGradleWrapper(ctx); if (acc.addGradleWrapperProperties) { + String checksum = gradleWrapper.getDistributionChecksum() == null ? null : gradleWrapper.getDistributionChecksum().getHexValue(); + if (wrapperUri != null && distributionChecksum != null && checksum == null) { + checksum = distributionChecksum; + } + //noinspection UnusedProperty Properties.File gradleWrapperProperties = new PropertiesParser().parse( "distributionBase=GRADLE_USER_HOME\n" + "distributionPath=wrapper/dists\n" + "distributionUrl=" + gradleWrapper.getPropertiesFormattedUrl() + "\n" + - ((gradleWrapper.getDistributionChecksum() == null) ? "" : "distributionSha256Sum=" + gradleWrapper.getDistributionChecksum().getHexValue() + "\n") + + (checksum == null ? "" : "distributionSha256Sum=" + checksum + "\n") + "zipStoreBase=GRADLE_USER_HOME\n" + "zipStorePath=wrapper/dists") .findFirst() @@ -480,6 +492,14 @@ public Properties visitFile(Properties.File file, ExecutionContext ctx) { Properties.Entry entry = new Properties.Entry(Tree.randomId(), "\n", Markers.EMPTY, DISTRIBUTION_SHA_256_SUM_KEY, "", Properties.Entry.Delimiter.EQUALS, propertyValue); List contentList = ListUtils.concat(((Properties.File) p).getContent(), entry); p = ((Properties.File) p).withContent(contentList); + } else if (!checksumKey.isEmpty() && gradleWrapper.getDistributionChecksum() == null) { + List contentList = ListUtils.map(((Properties.File) p).getContent(), c -> { + if (c instanceof Properties.Entry && DISTRIBUTION_SHA_256_SUM_KEY.equals(((Properties.Entry) c).getKey())) { + return null; + } + return c; + }); + p = ((Properties.File) p).withContent(contentList); } return p; } @@ -488,22 +508,7 @@ public Properties visitFile(Properties.File file, ExecutionContext ctx) { public Properties visitEntry(Properties.Entry entry, ExecutionContext ctx) { if ("distributionUrl".equals(entry.getKey())) { Properties.Value value = entry.getValue(); - String currentUrl = value.getText(); - // Prefer wrapperUri specified directly in the recipe over other options - // If that isn't set, prefer the existing artifact repository URL over changing to services.gradle.org - if (!StringUtils.isBlank(wrapperUri)) { - String effectiveWrapperUri = formatUriForPropertiesFile(wrapperUri - .replace("${version}", gradleWrapper.getVersion()) - .replace("${distribution}", distribution == null ? "bin" : distribution)); - return entry.withValue(value.withText(effectiveWrapperUri)); - } else if (currentUrl.startsWith("https\\://services.gradle.org/distributions/")) { - return entry.withValue(value.withText(gradleWrapper.getPropertiesFormattedUrl())); - } else { - String gradleServicesDistributionUrl = gradleWrapper.getDistributionUrl(); - String newDistributionFile = gradleServicesDistributionUrl.substring(gradleServicesDistributionUrl.lastIndexOf('/') + 1); - String repositoryUrlPrefix = currentUrl.substring(0, currentUrl.lastIndexOf('/')); - return entry.withValue(value.withText(repositoryUrlPrefix + "/" + newDistributionFile)); - } + return entry.withValue(value.withText(gradleWrapper.getPropertiesFormattedUrl())); } if (DISTRIBUTION_SHA_256_SUM_KEY.equals(entry.getKey()) && gradleWrapper.getDistributionChecksum() != null) { return entry.withValue(entry.getValue().withText(gradleWrapper.getDistributionChecksum().getHexValue())); diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java index 482ad8cf782..8510cd56d64 100755 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java @@ -43,14 +43,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.gradle.Assertions.buildGradle; import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_BATCH_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_JAR_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_PROPERTIES_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_SCRIPT_LOCATION; +import static org.openrewrite.gradle.util.GradleWrapper.*; import static org.openrewrite.properties.Assertions.properties; -import static org.openrewrite.test.SourceSpecs.dir; -import static org.openrewrite.test.SourceSpecs.other; -import static org.openrewrite.test.SourceSpecs.text; +import static org.openrewrite.test.SourceSpecs.*; @SuppressWarnings("UnusedProperty") class UpdateGradleWrapperTest implements RewriteTest { @@ -66,10 +61,11 @@ class UpdateGradleWrapperTest implements RewriteTest { private final SourceSpecs gradlew = text("", spec -> spec.path(WRAPPER_SCRIPT_LOCATION).after(notEmpty)); private final SourceSpecs gradlewBat = text("", spec -> spec.path(WRAPPER_BATCH_LOCATION).after(notEmpty)); private final SourceSpecs gradleWrapperJarQuark = other("", spec -> spec.path(WRAPPER_JAR_LOCATION)); + private final String wrapperJarChecksum = "29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda"; @Override public void defaults(RecipeSpec spec) { - spec.recipe(new UpdateGradleWrapper("7.4.2", null, null, null)) + spec.recipe(new UpdateGradleWrapper("7.4.2", null, null, null, null)) .beforeRecipe(withToolingApi()); } @@ -238,7 +234,7 @@ void updateChecksumAlreadySet() { @Test void dontAddMissingWrapper() { rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("7.x", null, Boolean.FALSE, null)) + spec -> spec.recipe(new UpdateGradleWrapper("7.x", null, Boolean.FALSE, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .afterRecipe(run -> assertThat(run.getChangeset().getAllResults()).isEmpty()) ); @@ -248,7 +244,7 @@ void dontAddMissingWrapper() { void updateMultipleWrappers() { rewriteRun( spec -> spec.allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) - .recipe(new UpdateGradleWrapper("7.4.2", null, Boolean.FALSE, null)), + .recipe(new UpdateGradleWrapper("7.4.2", null, Boolean.FALSE, null, null)), dir("example1", properties( """ @@ -307,7 +303,7 @@ void updateMultipleWrappers() { @Test void olderThan6_6() { rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("5.6.4", null, null, null)) + spec -> spec.recipe(new UpdateGradleWrapper("5.6.4", null, null, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "4.0"))) .afterRecipe(run -> { var gradleSh = result(run, PlainText.class, "gradlew"); @@ -368,7 +364,7 @@ void doNotDowngrade() { @Test void allowUpdatingDistributionTypeWhenSameVersion() { rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("5.6.x", "bin", null, null)) + spec -> spec.recipe(new UpdateGradleWrapper("5.6.x", "bin", null, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "5.6.4"))) .afterRecipe(run -> { var gradleSh = result(run, PlainText.class, "gradlew"); @@ -409,7 +405,7 @@ void allowUpdatingDistributionTypeWhenSameVersion() { @Test void defaultsToLatestRelease() { rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper(null, null, null, null)) + spec -> spec.recipe(new UpdateGradleWrapper(null, null, null, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .afterRecipe(run -> { var gradleSh = result(run, PlainText.class, "gradlew"); @@ -532,18 +528,16 @@ void skipWorkIfUpdatedEarlier() { ); } - @Issue("https://github.com/openrewrite/rewrite/issues/2651") @Test - void preferExistingDistributionSource() { + void getExactVersionWithPatternFromGradleServices() { rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("8.0.x", null, null, null)) - .expectedCyclesThatMakeChanges(2) + spec -> spec.recipe(new UpdateGradleWrapper("8.0.x", null, null, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))), properties( """ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists - distributionUrl=https\\://company.com/repo/gradle-7.4-bin.zip + distributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists """, @@ -558,7 +552,7 @@ void preferExistingDistributionSource() { return """ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists - distributionUrl=https\\://company.com/repo/gradle-8.0.2-bin.zip + distributionUrl=https\\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionSha256Sum=%s @@ -588,7 +582,7 @@ void preferExistingDistributionSourceWhenServicesGradleOrgUnavailable() { .setHttpSender(unhelpfulSender) .setLargeFileHttpSender(unhelpfulSender); rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("8.10", "bin", false, null)) + spec -> spec.recipe(new UpdateGradleWrapper("8.10", "bin", false, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .executionContext(ctx), properties( @@ -615,11 +609,103 @@ void preferExistingDistributionSourceWhenServicesGradleOrgUnavailable() { } @Test - void customDistributionUri() { + void addWrapperWithCustomDistributionUri() { + HttpSender customDistributionHost = request -> { + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(customDistributionHost) + .setLargeFileHttpSender(customDistributionHost); rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("8.0.x", null, null, "https://company.com/repo/gradle-${version}-${distribution}.zip")) - .expectedCyclesThatMakeChanges(2) - .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))), + spec -> spec + .recipe(new UpdateGradleWrapper(null, null, null, "https://company.com/repo/gradle-8.0.2-bin.zip", null)) + .expectedCyclesThatMakeChanges(1) + .executionContext(ctx) + .afterRecipe(run -> { + assertThat(run.getChangeset().getAllResults()).hasSize(4); + + var gradleSh = result(run, PlainText.class, "gradlew"); + assertThat(gradleSh.getSourcePath()).isEqualTo(WRAPPER_SCRIPT_LOCATION); + assertThat(gradleSh.getFileAttributes()).isNotNull(); + assertThat(gradleSh.getFileAttributes().isReadable()).isTrue(); + assertThat(gradleSh.getFileAttributes().isExecutable()).isTrue(); + + var gradleBat = result(run, PlainText.class, "gradlew.bat"); + assertThat(gradleBat.getSourcePath()).isEqualTo(WRAPPER_BATCH_LOCATION); + + var gradleWrapperProperties = result(run, Properties.File.class, "gradle-wrapper.properties"); + assertThat(gradleWrapperProperties.getSourcePath()).isEqualTo(WRAPPER_PROPERTIES_LOCATION); + + var gradleWrapperJar = result(run, Remote.class, "gradle-wrapper.jar"); + assertThat(gradleWrapperJar.getSourcePath()).isEqualTo(WRAPPER_JAR_LOCATION); + assertThat(gradleWrapperJar.getUri()).isEqualTo(URI.create("https://company.com/repo/gradle-8.0.2-bin.zip")); + assertThat(isValidWrapperJar(gradleWrapperJar)).as("Wrapper jar is not valid").isTrue(); + }), + buildGradle( + """ + plugins { + id "java" + } + """ + ) + ); + } + + @Test + void addWrapperWithCustomDistributionUriAndDistributionChecksum() { + HttpSender customDistributionHost = request -> { + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(customDistributionHost) + .setLargeFileHttpSender(customDistributionHost); + rewriteRun( + spec -> spec + .recipe(new UpdateGradleWrapper(null, null, null, "https://company.com/repo/gradle-8.0.2-bin.zip", wrapperJarChecksum)) + .expectedCyclesThatMakeChanges(1) + .executionContext(ctx) + .afterRecipe(run -> { + var gradleWrapperProperties = result(run, Properties.File.class, "gradle-wrapper.properties"); + assertThat(gradleWrapperProperties.getSourcePath()).isEqualTo(WRAPPER_PROPERTIES_LOCATION); + assertThat(gradleWrapperProperties.getContent().stream() + .filter(Properties.Entry.class::isInstance) + .map(Properties.Entry.class::cast) + .anyMatch(prop -> "distributionSha256Sum".equals(prop.getKey()) && wrapperJarChecksum.equals(prop.getValue().getText()))).isTrue(); + }), + buildGradle( + """ + plugins { + id "java" + } + """ + ) + ); + } + + @Test + void migrateToCustomDistributionUri() { + HttpSender customDistributionHost = request -> { + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(customDistributionHost) + .setLargeFileHttpSender(customDistributionHost); + rewriteRun( + spec -> spec.recipe(new UpdateGradleWrapper(null, null, null, "https://company.com/repo/gradle-8.10-bin.zip", null)) + .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) + .executionContext(ctx), properties( """ distributionBase=GRADLE_USER_HOME @@ -628,23 +714,94 @@ void customDistributionUri() { zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists """, + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.10-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, spec -> spec.path("gradle/wrapper/gradle-wrapper.properties") - .after(after -> { - Matcher checksumMatcher = Pattern.compile("distributionSha256Sum=(.*)").matcher(after); - assertThat(checksumMatcher.find()).isTrue(); - String checksum = checksumMatcher.group(1); - assertThat(checksum).isNotBlank(); + ), + gradlew, + gradlewBat, + gradleWrapperJarQuark + ); + } - // language=properties - return """ - distributionBase=GRADLE_USER_HOME - distributionPath=wrapper/dists - distributionUrl=https\\://company.com/repo/gradle-8.0.2-bin.zip - zipStoreBase=GRADLE_USER_HOME - zipStorePath=wrapper/dists - distributionSha256Sum=%s - """.formatted(checksum); - }) + @Test + void removeShaDuringMigrationToCustomDistributionUri() { + HttpSender customDistributionHost = request -> { + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(customDistributionHost) + .setLargeFileHttpSender(customDistributionHost); + rewriteRun( + spec -> spec.recipe(new UpdateGradleWrapper(null, null, null, "https://company.com/repo/gradle-8.10-bin.zip", null)) + .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) + .executionContext(ctx), + properties( + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://services.gradle.org/distributions/gradle-7.4.2-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda + """, + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.10-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + spec -> spec.path("gradle/wrapper/gradle-wrapper.properties") + ), + gradlew, + gradlewBat, + gradleWrapperJarQuark + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite/issues/2651") + void updateWithCustomDistributionUri() { + HttpSender customDistributionHost = request -> { + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(customDistributionHost) + .setLargeFileHttpSender(customDistributionHost); + rewriteRun( + spec -> spec.recipe(new UpdateGradleWrapper("8.10", null, null, null, null)) + .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) + .executionContext(ctx), + properties( + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.2-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.10-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + spec -> spec.path("gradle/wrapper/gradle-wrapper.properties") ), gradlew, gradlewBat, @@ -664,7 +821,7 @@ void servicesGradleOrgUnavailable() { .setHttpSender(unhelpfulSender) .setLargeFileHttpSender(unhelpfulSender); rewriteRun( - spec -> spec.recipe(new UpdateGradleWrapper("8.6", null, null, "https://artifactory.moderne.ninja/artifactory/gradle-distributions/gradle-${version}-bin.zip")) + spec -> spec.recipe(new UpdateGradleWrapper("8.6", null, null, null, null)) .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .executionContext(ctx), properties( @@ -678,7 +835,49 @@ void servicesGradleOrgUnavailable() { """ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists - distributionUrl=https\\://artifactory.moderne.ninja/artifactory/gradle-distributions/gradle-8.6-bin.zip + distributionUrl=https\\://services.gradle.org/distributions/gradle-8.6-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + spec -> spec.path("gradle/wrapper/gradle-wrapper.properties") + ), + text(GRADLEW_TEXT, spec -> spec.path(WRAPPER_SCRIPT_LOCATION)), + text(GRADLEW_BAT_TEXT, spec -> spec.path(WRAPPER_BATCH_LOCATION)), + gradleWrapperJarQuark + ); + } + + @Test + void servicesGradleOrgUnavailableForCustomDistributionUri() { + HttpSender unhelpfulSender = request -> { + if (request.getUrl().toString().contains("services.gradle.org")) { + throw new RuntimeException("I'm sorry Dave, I'm afraid I can't do that."); + } + if (request.getUrl().toString().contains("company.com")) { + return new HttpSender.Response(200, UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"), () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(unhelpfulSender) + .setLargeFileHttpSender(unhelpfulSender); + rewriteRun( + spec -> spec.recipe(new UpdateGradleWrapper("8.10", null, null, null, null)) + .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) + .executionContext(ctx), + properties( + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.2-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists """, @@ -743,6 +942,11 @@ void updateWrapperInSubDirectory() { ); } + @Test + void failRecipeIfBothVersionAndDistributionUriAreProvided() { + assertThat(new UpdateGradleWrapper("7.4.2", "bin", false, "https://company.com/repo/gradle-7.4.2-bin.zip", null).validate().isInvalid()).isTrue(); + } + private S result(RecipeRun run, Class clazz, String endsWith) { return run.getChangeset().getAllResults().stream() .map(Result::getAfter)