Skip to content

Commit

Permalink
Add recipe to enable Develocity build cache for Gradle (#4859)
Browse files Browse the repository at this point in the history
* Add enable buildcache recipe for gradle

* Fix description

* Update test cases and rename config

* Add initial version of visitor

* Update rewrite-gradle/src/main/java/org/openrewrite/gradle/EnableDevelocityBuildCache.java

Co-authored-by: Jacob van Lingen <[email protected]>

* Indent fix

* Polish

* Change validation

* Remove linebreaks

* Simplify how build cache is created and extracted

* Only look for develocity at the root, and buildCache within that

* Show a richer example for `remotePushEnabled`

---------

Co-authored-by: Jacob van Lingen <[email protected]>
Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
3 people authored Jan 10, 2025
1 parent 6609c8c commit 6ff7a03
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2025 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;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.groovy.GroovyIsoVisitor;
import org.openrewrite.groovy.tree.G;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.java.tree.J;

import java.util.concurrent.atomic.AtomicBoolean;

@Value
@EqualsAndHashCode(callSuper = false)
public class EnableDevelocityBuildCache extends Recipe {

@Override
public String getDisplayName() {
return "Enable Develocity build cache";
}

@Override
public String getDescription() {
return "Adds `buildCache` configuration to `develocity` where not yet present.";
}

@Option(displayName = "Enable remote build cache",
description = "Value for `//develocity/buildCache/remote/enabled`.",
example = "true",
required = false)
@Nullable
String remoteEnabled;

@Option(displayName = "Enable remote build cache push",
description = "Value for `//develocity/buildCache/remote/storeEnabled`.",
example = "System.getenv(\"CI\") != null",
required = false)
@Nullable
String remotePushEnabled;

@Override
public Validated<Object> validate(ExecutionContext ctx) {
return super.validate(ctx)
.and(Validated.notBlank("remoteEnabled", remoteEnabled)
.or(Validated.notBlank("remotePushEnabled", remotePushEnabled)));
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new IsSettingsGradle<>(), new GroovyIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
if ("develocity".equals(method.getSimpleName()) && !hasBuildCache(method)) {
J.MethodInvocation buildCache = createBuildCache();
return maybeAutoFormat(method, method.withArguments(ListUtils.mapFirst(method.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.concat(block.getStatements(), buildCache)));
}
return arg;
})), ctx);
}
return method;
}

private boolean hasBuildCache(J.MethodInvocation m) {
return new GroovyIsoVisitor<AtomicBoolean>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBoolean) {
if ("buildCache".equals(method.getSimpleName())) {
atomicBoolean.set(true);
return method;
}
return super.visitMethodInvocation(method, atomicBoolean);
}
}.reduce(m, new AtomicBoolean(false), getCursor().getParentTreeCursor()).get();
}
});
}

private J.MethodInvocation createBuildCache() {
String conf = "buildCache {\n" +
" remote(develocity.buildCache) {\n";
if (!StringUtils.isBlank(remoteEnabled)) {
conf += " enabled = " + remoteEnabled + "\n";
}
if (!StringUtils.isBlank(remotePushEnabled)) {
conf += " push = " + remotePushEnabled + "\n";
}
conf += " }" +
"}";
return (J.MethodInvocation) GradleParser.builder().build()
.parse(conf)
.map(G.CompilationUnit.class::cast)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Could not parse as Gradle"))
.getStatements()
.get(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2025 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;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.gradle.Assertions.settingsGradle;

class EnableDevelocityBuildCacheTest implements RewriteTest {

@Test
@DocumentExample
void addBuildCacheRemoteConfig() {
rewriteRun(spec -> spec.recipe(new EnableDevelocityBuildCache("true", "System.getenv(\"CI\") != null")),
settingsGradle(
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
}
""",
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
buildCache {
remote(develocity.buildCache) {
enabled = true
push = System.getenv("CI") != null
}
}
}
"""
)
);
}

@Test
void addBuildCacheRemoteConfigExtendedConfig() {
rewriteRun(spec -> spec.recipe(new EnableDevelocityBuildCache("true", "System.getenv(\"CI\") != null")),
settingsGradle(
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
buildScan {
uploadInBackground = true
}
}
""",
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
buildScan {
uploadInBackground = true
}
buildCache {
remote(develocity.buildCache) {
enabled = true
push = System.getenv("CI") != null
}
}
}
"""
)
);
}

@Test
void addBuildCacheRemoteConfigWithOnlyPush() {
rewriteRun(spec -> spec.recipe(new EnableDevelocityBuildCache(null, "true")),
settingsGradle(
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
}
""",
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
buildCache {
remote(develocity.buildCache) {
push = true
}
}
}
"""
)
);
}

@Test
void shouldNotModifyBuildCacheConfig() {
rewriteRun(spec -> spec.recipe(new EnableDevelocityBuildCache(null, "#{isTrue(env['CI'])}")),
settingsGradle(
"""
plugins {
id 'com.gradle.develocity' version '3.17.6'
}
develocity {
server = 'https://dev.example.com/'
buildCache {
remote(develocity.buildCache) {
push = false
}
}
}
"""
)
);
}
}

0 comments on commit 6ff7a03

Please sign in to comment.