-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Gradle Enterprise to Develocity 3.17+ migration recipe for Gradle
- Loading branch information
1 parent
1fc661b
commit adab889
Showing
2 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
258 changes: 258 additions & 0 deletions
258
...dle/src/main/java/org/openrewrite/gradle/plugins/MigrateGradleEnterpriseToDevelocity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
package org.openrewrite.gradle.plugins; | ||
|
||
import lombok.EqualsAndHashCode; | ||
import lombok.Value; | ||
import org.openrewrite.*; | ||
import org.openrewrite.gradle.GradleParser; | ||
import org.openrewrite.gradle.IsSettingsGradle; | ||
import org.openrewrite.groovy.GroovyIsoVisitor; | ||
import org.openrewrite.groovy.tree.G; | ||
import org.openrewrite.internal.ListUtils; | ||
import org.openrewrite.internal.lang.Nullable; | ||
import org.openrewrite.java.style.IntelliJ; | ||
import org.openrewrite.java.style.TabsAndIndentsStyle; | ||
import org.openrewrite.java.tree.*; | ||
import org.openrewrite.marker.Markers; | ||
import org.openrewrite.semver.Semver; | ||
import org.openrewrite.semver.VersionComparator; | ||
|
||
import java.nio.file.Paths; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
import static java.util.Collections.singletonList; | ||
|
||
@Value | ||
@EqualsAndHashCode(callSuper = false) | ||
public class MigrateGradleEnterpriseToDevelocity extends Recipe { | ||
@Option(displayName = "Plugin 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 = "3.x", | ||
required = false) | ||
@Nullable | ||
String version; | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return "Migrate from Gradle Enterprise to Develocity"; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return "Migrate from the Gradle Enterprise Gradle plugin to the Develocity Gradle plugin."; | ||
} | ||
|
||
@Override | ||
public Validated<Object> validate() { | ||
Validated<Object> validated = super.validate(); | ||
if (version != null) { | ||
VersionComparator versionComparator = Objects.requireNonNull(Semver.validate("[3.17,)", null).getValue()); | ||
validated = validated.and(Validated.test("version", "Version must be 3.17+", version, v -> versionComparator.isValid(null, v))); | ||
} | ||
return validated; | ||
} | ||
|
||
@Override | ||
public TreeVisitor<?, ExecutionContext> getVisitor() { | ||
return Preconditions.check( | ||
new IsSettingsGradle<>(), | ||
new GroovyIsoVisitor<ExecutionContext>() { | ||
@Override | ||
public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { | ||
G.CompilationUnit g = cu; | ||
g = (G.CompilationUnit) new ChangePlugin("com.gradle.enterprise", "com.gradle.develocity", version).getVisitor() | ||
.visitNonNull(g, ctx); | ||
g = (G.CompilationUnit) new ChangePluginVersion("com.gradle.common-custom-user-data-gradle-plugin", "2.x", null).getVisitor() | ||
.visitNonNull(g, ctx); | ||
g = (G.CompilationUnit) new MigrateConfigurationVisitor().visitNonNull(g, ctx); | ||
return g; | ||
} | ||
} | ||
); | ||
} | ||
|
||
private static class MigrateConfigurationVisitor extends GroovyIsoVisitor<ExecutionContext> { | ||
@Override | ||
public @Nullable J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { | ||
J.MethodInvocation m = super.visitMethodInvocation(method, ctx); | ||
if (m.getSimpleName().equals("gradleEnterprise") && m.getArguments().size() == 1 && m.getArguments().get(0) instanceof J.Lambda) { | ||
return m.withName(m.getName().withSimpleName("develocity")); | ||
} | ||
|
||
if (m.getSimpleName().startsWith("publishAlways") && withinMethodInvocations(Arrays.asList("gradleEnterprise", "buildScan"))) { | ||
if (m.getSimpleName().equals("publishAlways") && noArguments(m.getArguments())) { | ||
// As of 3.17+, `publishAlways` is the default, so it is recommended to not configure anything | ||
return null; | ||
} | ||
|
||
if (m.getSimpleName().equals("publishAlwaysIf")) { | ||
J.MethodInvocation publishingTemplate = develocityPublishAlwaysIfDsl(getIndent(getCursor().firstEnclosing(G.CompilationUnit.class)), ctx); | ||
if (publishingTemplate == null) { | ||
return m; | ||
} | ||
|
||
return publishingTemplate.withArguments(ListUtils.mapFirst(publishingTemplate.getArguments(), arg -> { | ||
if (arg instanceof J.Lambda) { | ||
J.Lambda lambda = (J.Lambda) arg; | ||
J.Block block = (J.Block) lambda.getBody(); | ||
return lambda.withBody(block.withStatements(ListUtils.mapFirst(block.getStatements(), s -> { | ||
if (s instanceof J.Return) { | ||
J.Return _return = (J.Return) s; | ||
return _return.withExpression(m.getArguments().get(0)); | ||
} | ||
return s; | ||
}))); | ||
} | ||
return arg; | ||
})); | ||
} | ||
} | ||
|
||
if (m.getSimpleName().startsWith("publishOnFailure") && withinMethodInvocations(Arrays.asList("gradleEnterprise", "buildScan"))) { | ||
J.MethodInvocation publishingTemplate = develocityPublishOnFailureIfDsl(getIndent(getCursor().firstEnclosing(G.CompilationUnit.class)), ctx); | ||
if (publishingTemplate == null) { | ||
return m; | ||
} | ||
|
||
if (m.getSimpleName().equals("publishOnFailure") && noArguments(m.getArguments())) { | ||
return publishingTemplate; | ||
} | ||
|
||
if (m.getSimpleName().equals("publishOnFailureIf") && m.getArguments().size() == 1 && m.getArguments().get(0) instanceof J.Binary) { | ||
return publishingTemplate.withArguments(ListUtils.mapFirst(publishingTemplate.getArguments(), arg -> { | ||
if (arg instanceof J.Lambda) { | ||
J.Lambda lambda = (J.Lambda) arg; | ||
J.Block block = (J.Block) lambda.getBody(); | ||
return lambda.withBody(block.withStatements(ListUtils.mapFirst(block.getStatements(), s -> { | ||
if (s instanceof J.Return && ((J.Return) s).getExpression() instanceof J.Unary) { | ||
J.Return _return = (J.Return) s; | ||
return _return.withExpression(new J.Binary( | ||
Tree.randomId(), | ||
Space.EMPTY, | ||
Markers.EMPTY, | ||
_return.getExpression(), | ||
JLeftPadded.build(J.Binary.Type.And).withBefore(Space.SINGLE_SPACE), | ||
Space.formatFirstPrefix(m.getArguments(), Space.SINGLE_SPACE).get(0), | ||
JavaType.Primitive.Boolean | ||
)); | ||
} | ||
return s; | ||
}))); | ||
} | ||
return arg; | ||
})); | ||
} | ||
} | ||
|
||
return m; | ||
} | ||
|
||
@Override | ||
public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ctx) { | ||
J.Assignment a = super.visitAssignment(assignment, ctx); | ||
|
||
if (a.getVariable() instanceof J.Identifier && ((J.Identifier) a.getVariable()).getSimpleName().equals("taskInputFiles") && withinMethodInvocations(Arrays.asList("gradleEnterprise", "buildScan", "capture"))) { | ||
return a.withVariable(((J.Identifier) a.getVariable()).withSimpleName("fileFingerprints")); | ||
} | ||
|
||
return a; | ||
} | ||
|
||
private boolean noArguments(List<Expression> arguments) { | ||
return arguments.isEmpty() || (arguments.size() == 1 && arguments.get(0) instanceof J.Empty); | ||
} | ||
|
||
private boolean withinMethodInvocations(List<String> methods) { | ||
Cursor current = getCursor().getParent(); | ||
for (int i = methods.size() - 1; i >= 0; i--) { | ||
current = findMethodInvocation(current); | ||
if (current == null) { | ||
return false; | ||
} | ||
|
||
if (!((J.MethodInvocation) current.getValue()).getSimpleName().equals(methods.get(i))) { | ||
return false; | ||
} | ||
|
||
current = current.getParent(); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private @Nullable Cursor findMethodInvocation(@Nullable Cursor start) { | ||
if (start == null) { | ||
return null; | ||
} | ||
|
||
Cursor current = start; | ||
while (current.getParent() != null) { | ||
if (current.getValue() instanceof J.MethodInvocation) { | ||
return current; | ||
} | ||
|
||
current = current.getParent(); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@Nullable | ||
private J.MethodInvocation develocityPublishAlwaysIfDsl(String indent, ExecutionContext ctx) { | ||
StringBuilder ge = new StringBuilder("\ndevelocity {\n"); | ||
ge.append(indent).append("buildScan {\n"); | ||
ge.append(indent).append(indent).append("publishing.onlyIf { true }\n"); | ||
ge.append(indent).append("}\n"); | ||
ge.append("}\n"); | ||
|
||
G.CompilationUnit cu = GradleParser.builder().build() | ||
.parseInputs(singletonList( | ||
Parser.Input.fromString(Paths.get("settings.gradle"), ge.toString())), null, ctx) | ||
.map(G.CompilationUnit.class::cast) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("Could not parse as Gradle")); | ||
|
||
J.MethodInvocation develocity = (J.MethodInvocation) cu.getStatements().get(0); | ||
J.MethodInvocation buildScan = (J.MethodInvocation) ((J.Return) ((J.Block) ((J.Lambda) develocity.getArguments().get(0)).getBody()).getStatements().get(0)).getExpression(); | ||
return (J.MethodInvocation) ((J.Return) ((J.Block) ((J.Lambda) buildScan.getArguments().get(0)).getBody()).getStatements().get(0)).getExpression(); | ||
} | ||
|
||
@Nullable | ||
private J.MethodInvocation develocityPublishOnFailureIfDsl(String indent, ExecutionContext ctx) { | ||
StringBuilder ge = new StringBuilder("\ndevelocity {\n"); | ||
ge.append(indent).append("buildScan {\n"); | ||
ge.append(indent).append(indent).append("publishing.onlyIf { !it.buildResult.failures.empty }\n"); | ||
ge.append(indent).append("}\n"); | ||
ge.append("}\n"); | ||
|
||
G.CompilationUnit cu = GradleParser.builder().build() | ||
.parseInputs(singletonList( | ||
Parser.Input.fromString(Paths.get("settings.gradle"), ge.toString())), null, ctx) | ||
.map(G.CompilationUnit.class::cast) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("Could not parse as Gradle")); | ||
|
||
J.MethodInvocation develocity = (J.MethodInvocation) cu.getStatements().get(0); | ||
J.MethodInvocation buildScan = (J.MethodInvocation) ((J.Return) ((J.Block) ((J.Lambda) develocity.getArguments().get(0)).getBody()).getStatements().get(0)).getExpression(); | ||
return (J.MethodInvocation) ((J.Return) ((J.Block) ((J.Lambda) buildScan.getArguments().get(0)).getBody()).getStatements().get(0)).getExpression(); | ||
} | ||
|
||
private String getIndent(G.CompilationUnit cu) { | ||
TabsAndIndentsStyle style = cu.getStyle(TabsAndIndentsStyle.class, IntelliJ.tabsAndIndents()); | ||
if (style.getUseTabCharacter()) { | ||
return "\t"; | ||
} else { | ||
StringBuilder sb = new StringBuilder(); | ||
for (int i = 0; i < style.getIndentSize(); i++) { | ||
sb.append(" "); | ||
} | ||
return sb.toString(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.