All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.gradle.UpgradeDependencyVersion Maven / Gradle / Ivy

There is a newer version: 8.40.2
Show newest version
/*
 * Copyright 2022 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.openrewrite.gradle; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; import org.openrewrite.groovy.GroovyIsoVisitor; import org.openrewrite.groovy.GroovyVisitor; import org.openrewrite.groovy.tree.G; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaSourceFile; import org.openrewrite.java.tree.JavaType; import org.openrewrite.marker.Markup; import org.openrewrite.maven.MavenDownloadingException; import org.openrewrite.maven.MavenDownloadingExceptions; import org.openrewrite.maven.internal.MavenPomDownloader; import org.openrewrite.maven.table.MavenMetadataFailures; import org.openrewrite.maven.tree.*; import org.openrewrite.properties.PropertiesVisitor; import org.openrewrite.properties.tree.Properties; import org.openrewrite.semver.DependencyMatcher; import org.openrewrite.semver.Semver; import org.openrewrite.semver.VersionComparator; import java.util.*; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @Value @EqualsAndHashCode(callSuper = false) public class UpgradeDependencyVersion extends ScanningRecipe { private static final String VERSION_VARIABLE_KEY = "VERSION_VARIABLE"; private static final String NEW_VERSION_KEY = "NEW_VERSION"; private static final String GRADLE_PROPERTIES_FILE_NAME = "gradle.properties"; @EqualsAndHashCode.Exclude transient MavenMetadataFailures metadataFailures = new MavenMetadataFailures(this); @Option(displayName = "Group", description = "The first part of a dependency coordinate `com.google.guava:guava:VERSION`. This can be a glob expression.", example = "com.fasterxml.jackson*") String groupId; @Option(displayName = "Artifact", description = "The second part of a dependency coordinate `com.google.guava:guava:VERSION`. This can be a glob expression.", example = "jackson-module*") String artifactId; @Option(displayName = "New version", description = "An exact version number or node-style semver selector used to select the version number. " + "You can also use `latest.release` for the latest available version and `latest.patch` if " + "the current version is a valid semantic version. For more details, you can look at the documentation " + "page of [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors). " + "Defaults to `latest.release`.", example = "29.X", required = false) @Nullable String newVersion; @Option(displayName = "Version pattern", description = "Allows version selection to be extended beyond the original Node Semver semantics. So for example," + "Setting 'newVersion' to \"25-29\" can be paired with a metadata pattern of \"-jre\" to select Guava 29.0-jre", example = "-jre", required = false) @Nullable String versionPattern; @Override public String getDisplayName() { return "Upgrade Gradle dependency versions"; } @Override public String getInstanceNameSuffix() { return String.format("`%s:%s`", groupId, artifactId); } @Override public String getDescription() { //language=markdown return "Upgrade the version of a dependency in a build.gradle file. " + "Supports updating dependency declarations of various forms:\n" + "* `String` notation: `\"group:artifact:version\"` \n" + "* `Map` notation: `group: 'group', name: 'artifact', version: 'version'`\n" + "Can update version numbers which are defined earlier in the same file in variable declarations."; } @Override public Validated validate() { Validated validated = super.validate(); if (newVersion != null) { validated = validated.and(Semver.validate(newVersion, versionPattern)); } return validated; } private static final String UPDATE_VERSION_ERROR_KEY = "UPDATE_VERSION_ERROR_KEY"; @Value public static class DependencyVersionState { Map versionPropNameToGA = new HashMap<>(); /** * The value is either a String representing the resolved version * or a MavenDownloadingException representing an error during resolution. */ Map gaToNewVersion = new HashMap<>(); } @Override public DependencyVersionState getInitialValue(ExecutionContext ctx) { return new DependencyVersionState(); } private static final MethodMatcher DEPENDENCY_DSL_MATCHER = new MethodMatcher("DependencyHandlerSpec *(..)"); @Override public TreeVisitor getScanner(DependencyVersionState acc) { return new GroovyVisitor() { @Nullable GradleProject gradleProject; @Override public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { if (!cu.getSourcePath().toString().endsWith(".gradle")) { return cu; } gradleProject = cu.getMarkers().findFirst(GradleProject.class).orElse(null); return super.visitCompilationUnit(cu, ctx); } @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); if (gradleDependencyMatcher.get(getCursor()).isPresent() || DEPENDENCY_DSL_MATCHER.matches(m)) { if (m.getArguments().get(0) instanceof G.MapEntry) { String groupId = null; String artifactId = null; String version = null; for (Expression e : m.getArguments()) { if (!(e instanceof G.MapEntry)) { continue; } G.MapEntry arg = (G.MapEntry) e; if (!(arg.getKey() instanceof J.Literal)) { continue; } J.Literal key = (J.Literal) arg.getKey(); String valueValue = null; if (arg.getValue() instanceof J.Literal) { J.Literal value = (J.Literal) arg.getValue(); if (value.getValue() instanceof String) { valueValue = (String) value.getValue(); } } else if (arg.getValue() instanceof J.Identifier) { J.Identifier value = (J.Identifier) arg.getValue(); valueValue = value.getSimpleName(); } else if (arg.getValue() instanceof G.GString) { G.GString value = (G.GString) arg.getValue(); List strings = value.getStrings(); if (!strings.isEmpty() && strings.get(0) instanceof G.GString.Value) { G.GString.Value versionGStringValue = (G.GString.Value) strings.get(0); if (versionGStringValue.getTree() instanceof J.Identifier) { valueValue = ((J.Identifier) versionGStringValue.getTree()).getSimpleName(); } } } if (!(key.getValue() instanceof String)) { continue; } String keyValue = (String) key.getValue(); switch (keyValue) { case "group": groupId = valueValue; break; case "name": artifactId = valueValue; break; case "version": version = valueValue; break; } } if (groupId == null || artifactId == null || version == null) { return m; } String versionVariableName = version; GroupArtifact ga = new GroupArtifact(groupId, artifactId); if (acc.gaToNewVersion.containsKey(ga)) { return m; } try { String resolvedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(new GroupArtifact(groupId, artifactId), m.getSimpleName(), newVersion, versionPattern, ctx); acc.versionPropNameToGA.put(versionVariableName, ga); // It is fine for this value to be null, record it in the map to avoid future lookups //noinspection DataFlowIssue acc.gaToNewVersion.put(ga, resolvedVersion); } catch (MavenDownloadingException e) { acc.gaToNewVersion.put(ga, e); return m; } } else { for (Expression depArg : m.getArguments()) { if (depArg instanceof G.GString) { G.GString gString = (G.GString) depArg; List strings = gString.getStrings(); if (strings.size() != 2 || !(strings.get(0) instanceof J.Literal) || !(strings.get(1) instanceof G.GString.Value)) { continue; } J.Literal groupArtifact = (J.Literal) strings.get(0); G.GString.Value versionValue = (G.GString.Value) strings.get(1); if (!(versionValue.getTree() instanceof J.Identifier) || !(groupArtifact.getValue() instanceof String)) { continue; } Dependency dep = DependencyStringNotationConverter.parse((String) groupArtifact.getValue()); if (dep == null) { continue; } String versionVariableName = ((J.Identifier) versionValue.getTree()).getSimpleName(); GroupArtifact ga = new GroupArtifact(dep.getGroupId(), dep.getArtifactId()); if (acc.gaToNewVersion.containsKey(ga)) { continue; } try { String resolvedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(new GroupArtifact(dep.getGroupId(), dep.getArtifactId()), m.getSimpleName(), newVersion, versionPattern, ctx); if (resolvedVersion != null) { acc.versionPropNameToGA.put(versionVariableName, ga); acc.gaToNewVersion.put(ga, resolvedVersion); } } catch (MavenDownloadingException e) { acc.gaToNewVersion.put(ga, e); } } } } } return m; } }; } @Override public TreeVisitor getVisitor(DependencyVersionState acc) { return new TreeVisitor() { private final UpdateGroovy updateGroovy = new UpdateGroovy(acc); private final UpdateProperties updateProperties = new UpdateProperties(acc); @Override public boolean isAcceptable(SourceFile sf, ExecutionContext ctx) { return updateProperties.isAcceptable(sf, ctx) || updateGroovy.isAcceptable(sf, ctx); } @Override public @Nullable Tree visit(@Nullable Tree t, ExecutionContext ctx) { if (t instanceof SourceFile) { SourceFile sf = (SourceFile) t; if (updateProperties.isAcceptable(sf, ctx)) { t = updateProperties.visitNonNull(t, ctx); } else if (updateGroovy.isAcceptable(sf, ctx)) { t = updateGroovy.visitNonNull(t, ctx); } } return t; } }; } @RequiredArgsConstructor private class UpdateProperties extends PropertiesVisitor { final DependencyVersionState acc; final DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); @Override public Properties visitFile(Properties.File file, ExecutionContext ctx) { if (!file.getSourcePath().endsWith(GRADLE_PROPERTIES_FILE_NAME)) { return file; } return super.visitFile(file, ctx); } @Override public org.openrewrite.properties.tree.Properties visitEntry(Properties.Entry entry, ExecutionContext ctx) { if (acc.versionPropNameToGA.containsKey(entry.getKey())) { GroupArtifact ga = acc.versionPropNameToGA.get(entry.getKey()); if (ga == null || !dependencyMatcher.matches(ga.getGroupId(), ga.getArtifactId())) { return entry; } Object result = acc.gaToNewVersion.get(ga); if (result == null || result instanceof Exception) { return entry; } VersionComparator versionComparator = Semver.validate(StringUtils.isBlank(newVersion) ? "latest.release" : newVersion, versionPattern).getValue(); if (versionComparator == null) { return entry; } Optional finalVersion = versionComparator.upgrade(entry.getValue().getText(), singletonList((String) result)); return finalVersion.map(v -> entry.withValue(entry.getValue().withText(v))).orElse(entry); } return entry; } } @RequiredArgsConstructor private class UpdateGroovy extends GroovyVisitor { final DependencyVersionState acc; @Nullable GradleProject gradleProject; final DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); @Override public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { gradleProject = cu.getMarkers().findFirst(GradleProject.class) .orElse(null); return super.visitCompilationUnit(cu, ctx); } @Override public J postVisit(J tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) tree; Map>> variableNames = getCursor().getMessage(VERSION_VARIABLE_KEY); if (variableNames != null) { cu = (JavaSourceFile) new UpdateVariable(variableNames, gradleProject).visitNonNull(cu, ctx); } Map> versionUpdates = getCursor().getMessage(NEW_VERSION_KEY); if (versionUpdates != null && gradleProject != null) { GradleProject newGp = gradleProject; for (Map.Entry> gavToConfigurations : versionUpdates.entrySet()) { newGp = replaceVersion(newGp, ctx, gavToConfigurations.getKey(), gavToConfigurations.getValue()); } cu = cu.withMarkers(cu.getMarkers().removeByType(GradleProject.class).add(newGp)); } return cu; } return tree; } @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { if ("constraints".equals(method.getSimpleName())) { // don't mess with anything inside a constraints block, leave that to UpgradeTransitiveDependency version recipe return method; } J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); if (gradleDependencyMatcher.get(getCursor()).isPresent() || DEPENDENCY_DSL_MATCHER.matches(m)) { List depArgs = m.getArguments(); if (depArgs.get(0) instanceof J.Literal || depArgs.get(0) instanceof G.GString || depArgs.get(0) instanceof G.MapEntry) { m = updateDependency(m, ctx); } else if (depArgs.get(0) instanceof J.MethodInvocation && (((J.MethodInvocation) depArgs.get(0)).getSimpleName().equals("platform") || ((J.MethodInvocation) depArgs.get(0)).getSimpleName().equals("enforcedPlatform"))) { m = m.withArguments(ListUtils.mapFirst(depArgs, platform -> updateDependency((J.MethodInvocation) platform, ctx))); } } return m; } private J.MethodInvocation updateDependency(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = method; m = m.withArguments(ListUtils.map(m.getArguments(), arg -> { if (arg instanceof G.GString) { G.GString gString = (G.GString) arg; List strings = gString.getStrings(); if (strings.size() != 2 || !(strings.get(0) instanceof J.Literal) || !(strings.get(1) instanceof G.GString.Value)) { return arg; } J.Literal groupArtifact = (J.Literal) strings.get(0); G.GString.Value versionValue = (G.GString.Value) strings.get(1); if (!(versionValue.getTree() instanceof J.Identifier) || !(groupArtifact.getValue() instanceof String)) { return arg; } Dependency dep = DependencyStringNotationConverter.parse((String) groupArtifact.getValue()); if (dep != null && dependencyMatcher.matches(dep.getGroupId(), dep.getArtifactId())) { Object scanResult = acc.gaToNewVersion.get(new GroupArtifact(dep.getGroupId(), dep.getArtifactId())); if (scanResult instanceof Exception) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, scanResult); return arg; } String versionVariableName = ((J.Identifier) versionValue.getTree()).getSimpleName(); getCursor().dropParentUntil(p -> p instanceof SourceFile) .computeMessageIfAbsent(VERSION_VARIABLE_KEY, v -> new HashMap>>()) .computeIfAbsent(versionVariableName, it -> new HashMap<>()) .computeIfAbsent(new GroupArtifact(dep.getGroupId(), dep.getArtifactId()), it -> new HashSet<>()) .add(method.getSimpleName()); } } else if (arg instanceof J.Literal) { J.Literal literal = (J.Literal) arg; if (literal.getType() != JavaType.Primitive.String) { return arg; } String gav = (String) literal.getValue(); if (gav == null) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, new IllegalStateException("Unable to update version")); return arg; } Dependency dep = DependencyStringNotationConverter.parse(gav); if (dep != null && dependencyMatcher.matches(dep.getGroupId(), dep.getArtifactId()) && dep.getVersion() != null && !dep.getVersion().startsWith("$")) { Object scanResult = acc.gaToNewVersion.get(new GroupArtifact(dep.getGroupId(), dep.getArtifactId())); if (scanResult instanceof Exception) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, scanResult); return arg; } try { String selectedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(dep.getGav(), method.getSimpleName(), newVersion, versionPattern, ctx); if (selectedVersion == null || dep.getVersion().equals(selectedVersion)) { return arg; } getCursor().dropParentUntil(p -> p instanceof SourceFile) .computeMessageIfAbsent(NEW_VERSION_KEY, it -> new HashMap>()) .computeIfAbsent(new GroupArtifactVersion(dep.getGroupId(), dep.getArtifactId(), selectedVersion), it -> new HashSet<>()) .add(method.getSimpleName()); String newGav = dep .withVersion(selectedVersion) .toStringNotation(); return literal .withValue(newGav) .withValueSource(literal.getValueSource() == null ? newGav : literal.getValueSource().replace(gav, newGav)); } catch (MavenDownloadingException e) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, e); } } } return arg; })); Exception err = getCursor().pollMessage(UPDATE_VERSION_ERROR_KEY); if (err != null) { m = Markup.warn(m, err); } List depArgs = m.getArguments(); if (depArgs.size() >= 3 && depArgs.get(0) instanceof G.MapEntry && depArgs.get(1) instanceof G.MapEntry && depArgs.get(2) instanceof G.MapEntry) { Expression groupValue = ((G.MapEntry) depArgs.get(0)).getValue(); Expression artifactValue = ((G.MapEntry) depArgs.get(1)).getValue(); if (!(groupValue instanceof J.Literal) || !(artifactValue instanceof J.Literal)) { return m; } J.Literal groupLiteral = (J.Literal) groupValue; J.Literal artifactLiteral = (J.Literal) artifactValue; if (groupLiteral.getValue() == null || artifactLiteral.getValue() == null || !dependencyMatcher.matches((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())) { return m; } Object scanResult = acc.gaToNewVersion.get(new GroupArtifact((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())); if (scanResult instanceof Exception) { return Markup.warn(m, (Exception) scanResult); } G.MapEntry versionEntry = (G.MapEntry) depArgs.get(2); Expression versionExp = versionEntry.getValue(); if (versionExp instanceof J.Literal && ((J.Literal) versionExp).getValue() instanceof String) { J.Literal versionLiteral = (J.Literal) versionExp; String version = (String) versionLiteral.getValue(); if (version.startsWith("$")) { return m; } String selectedVersion; try { GroupArtifactVersion gav = new GroupArtifactVersion((String) groupLiteral.getValue(), (String) artifactLiteral.getValue(), version); selectedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(gav, m.getSimpleName(), newVersion, versionPattern, ctx); } catch (MavenDownloadingException e) { return e.warn(m); } if (selectedVersion == null || version.equals(selectedVersion)) { return m; } List newArgs = new ArrayList<>(3); newArgs.add(depArgs.get(0)); newArgs.add(depArgs.get(1)); newArgs.add(versionEntry.withValue( versionLiteral .withValueSource(versionLiteral.getValueSource() == null ? selectedVersion : versionLiteral.getValueSource().replace(version, selectedVersion)) .withValue(selectedVersion))); newArgs.addAll(depArgs.subList(3, depArgs.size())); return m.withArguments(newArgs); } else if (versionExp instanceof J.Identifier) { String versionVariableName = ((J.Identifier) versionExp).getSimpleName(); getCursor().dropParentUntil(p -> p instanceof SourceFile) .computeMessageIfAbsent(VERSION_VARIABLE_KEY, v -> new HashMap>>()) .computeIfAbsent(versionVariableName, it -> new HashMap<>()) .computeIfAbsent(new GroupArtifact((String) groupLiteral.getValue(), (String) artifactLiteral.getValue()), it -> new HashSet<>()) .add(m.getSimpleName()); } } return m; } } @AllArgsConstructor private class UpdateVariable extends GroovyIsoVisitor { private final Map>> versionVariableNames; @Nullable private final GradleProject gradleProject; @Override public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, ctx); boolean noneMatch = true; Map> gaToConfigurations = null; for (Map.Entry>> versionVariableNameEntry : versionVariableNames.entrySet()) { if (versionVariableNameEntry.getKey().equals((v.getSimpleName()))) { noneMatch = false; gaToConfigurations = versionVariableNameEntry.getValue(); break; } } if (noneMatch) { return v; } if (!(v.getInitializer() instanceof J.Literal)) { return v; } J.Literal initializer = (J.Literal) v.getInitializer(); if (initializer.getType() != JavaType.Primitive.String) { return v; } String version = (String) initializer.getValue(); if (version == null) { return v; } try { for (Map.Entry> gaEntry : gaToConfigurations.entrySet()) { GroupArtifact ga = gaEntry.getKey(); GroupArtifactVersion gav = new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), version); DependencyVersionSelector selector = new DependencyVersionSelector(metadataFailures, gradleProject, null); String selectedVersion = Optional.ofNullable(selector.select(gav, null, newVersion, versionPattern, ctx)) .orElse(selector.select(gav, "classpath", newVersion, versionPattern, ctx)); if (selectedVersion == null) { return v; } getCursor().dropParentUntil(p -> p instanceof SourceFile) .computeMessageIfAbsent(NEW_VERSION_KEY, m -> new HashMap>()) .computeIfAbsent(new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), selectedVersion), it -> new HashSet<>()) .addAll(gaEntry.getValue()); J.Literal newVersionLiteral = ChangeStringLiteral.withStringValue(initializer, selectedVersion); v = v.withInitializer(newVersionLiteral); } } catch (MavenDownloadingException e) { return e.warn(v); } return v; } @Override public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ctx) { J.Assignment a = super.visitAssignment(assignment, ctx); if (!(a.getVariable() instanceof J.Identifier)) { return a; } J.Identifier identifier = (J.Identifier) a.getVariable(); Map> gaToConfigurations = null; boolean noneMatch = true; for (Map.Entry>> versionVariableNameEntry : versionVariableNames.entrySet()) { if (versionVariableNameEntry.getKey().equals(identifier.getSimpleName())) { noneMatch = false; gaToConfigurations = versionVariableNameEntry.getValue(); break; } } if (noneMatch) { return a; } if (!(a.getAssignment() instanceof J.Literal)) { return a; } J.Literal literal = (J.Literal) a.getAssignment(); if (literal.getType() != JavaType.Primitive.String) { return a; } String version = (String) literal.getValue(); if (version == null) { return a; } try { for (Map.Entry> gaEntry : gaToConfigurations.entrySet()) { GroupArtifact ga = gaEntry.getKey(); GroupArtifactVersion gav = new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), version); DependencyVersionSelector selector = new DependencyVersionSelector(metadataFailures, gradleProject, null); String selectedVersion = Optional.ofNullable(selector.select(gav, null, newVersion, versionPattern, ctx)) .orElse(selector.select(gav, "classpath", newVersion, versionPattern, ctx)); if (selectedVersion == null) { return a; } getCursor().dropParentUntil(p -> p instanceof SourceFile) .computeMessageIfAbsent(NEW_VERSION_KEY, m -> new HashMap>()) .computeIfAbsent(new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), selectedVersion), it -> new HashSet<>()) .addAll(gaEntry.getValue()); J.Literal newVersionLiteral = ChangeStringLiteral.withStringValue(literal, selectedVersion); a = a.withAssignment(newVersionLiteral); } } catch (MavenDownloadingException e) { return e.warn(a); } return a; } } public static GradleProject replaceVersion(GradleProject gp, ExecutionContext ctx, GroupArtifactVersion gav, Set configurations) { try { //noinspection ConstantValue if (gav.getGroupId() == null || gav.getArtifactId() == null) { return gp; } Set remainingConfigurations = new HashSet<>(configurations); remainingConfigurations.remove("classpath"); if (remainingConfigurations.isEmpty()) { return gp; } MavenPomDownloader mpd = new MavenPomDownloader(ctx); Pom pom = mpd.download(gav, null, null, gp.getMavenRepositories()); ResolvedPom resolvedPom = pom.resolve(emptyList(), mpd, gp.getMavenRepositories(), ctx); List transitiveDependencies = resolvedPom.resolveDependencies(Scope.Runtime, mpd, ctx); org.openrewrite.maven.tree.Dependency newRequested = org.openrewrite.maven.tree.Dependency.builder() .gav(gav) .build(); ResolvedDependency newDep = ResolvedDependency.builder() .gav(resolvedPom.getGav()) .requested(newRequested) .dependencies(transitiveDependencies) .build(); Map nameToConfiguration = gp.getNameToConfiguration(); Map newNameToConfiguration = new HashMap<>(nameToConfiguration.size()); boolean anyChanged = false; for (GradleDependencyConfiguration gdc : nameToConfiguration.values()) { GradleDependencyConfiguration newGdc = gdc .withRequested(ListUtils.map(gdc.getRequested(), requested -> maybeUpdateDependency(requested, newRequested))) .withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> maybeUpdateResolvedDependency(resolved, newDep, new HashSet<>()))); anyChanged |= newGdc != gdc; newNameToConfiguration.put(newGdc.getName(), newGdc); } if (anyChanged) { gp = gp.withNameToConfiguration(newNameToConfiguration); } } catch (MavenDownloadingException | MavenDownloadingExceptions e) { return gp; } return gp; } private static org.openrewrite.maven.tree.Dependency maybeUpdateDependency( org.openrewrite.maven.tree.Dependency dep, org.openrewrite.maven.tree.Dependency newDep) { if (Objects.equals(dep.getGroupId(), newDep.getGroupId()) && Objects.equals(dep.getArtifactId(), newDep.getArtifactId())) { return newDep; } return dep; } private static ResolvedDependency maybeUpdateResolvedDependency( ResolvedDependency dep, ResolvedDependency newDep, Set traversalHistory) { if (traversalHistory.contains(dep)) { return dep; } if (Objects.equals(dep.getGroupId(), newDep.getGroupId()) && Objects.equals(dep.getArtifactId(), newDep.getArtifactId())) { return newDep; } traversalHistory.add(dep); return dep.withDependencies(ListUtils.map(dep.getDependencies(), d -> maybeUpdateResolvedDependency(d, newDep, new HashSet<>(traversalHistory)))); } }