-
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 d9dffd3
Showing
2 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
273 changes: 273 additions & 0 deletions
273
...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,273 @@ | ||
/* | ||
* Copyright 2024 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.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.