Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import org.openrewrite.gradle.internal.AddDependencyVisitor;
import org.openrewrite.gradle.marker.GradleDependencyConfiguration;
import org.openrewrite.gradle.marker.GradleProject;
import org.openrewrite.gradle.marker.GradleSettings;
import org.openrewrite.gradle.trait.ExtraProperty;
import org.openrewrite.gradle.trait.GradleDependency;
import org.openrewrite.gradle.trait.GradleMultiDependency;
import org.openrewrite.gradle.trait.GradleVersionCatalog;
import org.openrewrite.gradle.trait.SpringDependencyManagementPluginEntry;
import org.openrewrite.groovy.tree.G;
import org.openrewrite.internal.ListUtils;
Expand Down Expand Up @@ -543,6 +545,9 @@ private class UpdateGradle extends JavaVisitor<ExecutionContext> {
@Nullable
GradleProject gradleProject;

@Nullable
GradleSettings gradleSettings;

@Nullable
List<GroupArtifact> newlyManaged;

Expand All @@ -565,6 +570,8 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
newlyManaged = null;
gradleProject = original.getMarkers().findFirst(GradleProject.class)
.orElse(null);
gradleSettings = original.getMarkers().findFirst(GradleSettings.class)
.orElse(null);
JavaSourceFile sourceFile = applyPluginProvidedDependencies(original, ctx);
JavaSourceFile result = (JavaSourceFile) super.visit(sourceFile, ctx);
if (result != original && gradleProject != null) {
Expand Down Expand Up @@ -749,6 +756,30 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx)
}
}

GradleVersionCatalog catalog = new GradleVersionCatalog.Matcher()
.get(getCursor())
.orElse(null);
if (catalog != null) {
DependencyVersionSelector versionSelector = new DependencyVersionSelector(metadataFailures, gradleProject, gradleSettings);
for (GroupArtifact ga : catalog.getLibraries().keySet()) {
if (dependencyMatcher.matches(ga.getGroupId(), ga.getArtifactId())) {
String currentVersion = catalog.getVersion(ga);
if (currentVersion != null) {
try {
GroupArtifactVersion gav = new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), currentVersion);
String selectedVersion = versionSelector.select(gav, null, newVersion, versionPattern, ctx);
if (selectedVersion != null && !selectedVersion.equals(currentVersion)) {
catalog = catalog.withVersion(ga, selectedVersion);
}
} catch (MavenDownloadingException ignored) {
// leave this library's version unchanged
}
}
}
}
m = catalog.getTree();
}

if ("ext".equals(method.getSimpleName()) && getCursor().firstEnclosingOrThrow(SourceFile.class).getSourcePath().endsWith("settings.gradle")) {
// rare case that gradle versions are set via settings.gradle ext block (only possible for Groovy DSL)
m = (J.MethodInvocation) new JavaIsoVisitor<ExecutionContext>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2026 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.marker;

import lombok.Value;
import lombok.With;
import org.openrewrite.Cursor;
import org.openrewrite.marker.Marker;
import org.openrewrite.maven.tree.GroupArtifact;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
* A snapshot of which libraries declared in a
* {@code org.openrewrite.gradle.trait.GradleVersionCatalog} originally shared each
* {@code versionRef(...)} declaration, taken before any recipe mutates the catalog.
* <p>
* Attached to the version catalog's own root AST node, so downstream recipes can tell whether
* two separately-requested version bumps actually target the same underlying
* {@code version(...)} declaration.
*/
@Value
@With
public class GradleVersionCatalogVersionReferences implements Marker {
UUID id;

/**
* Keyed by a shared {@code version(...)} declaration's own alias. Only references actually
* resolved through by at least one library are recorded.
*/
Map<String, SharedReference> sharedReferencesByAlias;

@Override
public String print(Cursor cursor, UnaryOperator<String> commentWrapper, boolean verbose) {
return verbose ? commentWrapper.apply("(" + this + ")") : "";
}

@Override
public String toString() {
return sharedReferencesByAlias.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey() + "->" + e.getValue())
.collect(Collectors.joining(", "));
}

/**
* The version value a shared reference held when the snapshot was taken, together with the
* group:artifact of every library that originally resolved its version through it.
*/
@Value
public static class SharedReference {
String version;
List<GroupArtifact> groupArtifacts;

@Override
public String toString() {
return version + "@" + groupArtifacts.stream()
.map(ga -> ga.getGroupId() + ":" + ga.getArtifactId())
.collect(Collectors.joining(", ", "[", "]"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.openrewrite.Cursor;
import org.openrewrite.SourceFile;
import org.openrewrite.gradle.marker.GradleProject;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.trait.SimpleTraitMatcher;
import org.openrewrite.trait.Trait;
Expand Down Expand Up @@ -50,4 +51,35 @@ protected boolean withinBlock(Cursor cursor, String name) {

return false;
}

/**
* @return {@code true} if the cursor's tree is itself a statement in an enclosing block,
* rather than a nested expression such as the receiver of a chained method call.
*/
protected boolean isTopLevelStatement(Cursor cursor) {
Cursor parent = cursor.getParentTreeCursor();
if (parent.getValue() instanceof J.Return) {
// Groovy closures implicitly return their last expression through a synthetic Return
parent = parent.getParentTreeCursor();
}
return !parent.isRoot() && parent.getValue() instanceof J.Block;
}

protected static J.@Nullable MethodInvocation asChainedInvocation(J.MethodInvocation m) {
return m.getSelect() instanceof J.MethodInvocation ? (J.MethodInvocation) m.getSelect() : null;
}

/**
* @return the string value of {@code m}'s argument at {@code index}, or {@code null} if
* there's no such argument or it isn't a string literal.
*/
protected static @Nullable String literalArgument(J.MethodInvocation m, int index) {
if (index < m.getArguments().size()) {
Expression argument = m.getArguments().get(index);
if (argument instanceof J.Literal && ((J.Literal) argument).getValue() instanceof String) {
return (String) ((J.Literal) argument).getValue();
}
}
return null;
}
}
Loading