Browse Source

Run all Gradle tests with configuration cache

gradle-config-cache-issues
Alexey Tsvetkov 2 years ago
parent
commit
288eb62882
  1. 9
      gradle-plugins/compose/build.gradle.kts
  2. 122
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt
  3. 39
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/GradlePluginTest.kt
  4. 11
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/KotlinCompatabilityTest.kt
  5. 37
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProject.kt
  6. 3
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProperties.kt
  7. 21
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt
  8. 2
      gradle-plugins/gradle.properties

9
gradle-plugins/compose/build.gradle.kts

@ -109,7 +109,14 @@ tasks.test {
for (gradleVersion in supportedGradleVersions) {
tasks.registerVerificationTask<Test>("testGradle-$gradleVersion") {
classpath = tasks.test.get().classpath
systemProperty("compose.tests.gradle.version", gradleVersion)
val configCacheSuffix = "-with-configuration-cache"
val version = if (gradleVersion.endsWith(configCacheSuffix)) {
systemProperty("compose.tests.gradle.configuration.cache", "true")
gradleVersion.removeSuffix(configCacheSuffix)
} else {
gradleVersion
}
systemProperty("compose.tests.gradle.version", version)
filter {
includeTestsMatching(gradleTestsPattern)
}

122
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt

@ -6,20 +6,18 @@
package org.jetbrains.compose.test.tests.integration
import org.gradle.internal.impldep.org.testng.Assert
import org.gradle.testkit.runner.TaskOutcome
import org.jetbrains.compose.desktop.application.internal.*
import org.jetbrains.compose.internal.uppercaseFirstChar
import org.jetbrains.compose.test.utils.*
import java.io.File
import java.util.*
import java.util.jar.JarFile
import kotlin.collections.HashSet
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import java.util.jar.JarFile
class DesktopApplicationTest : GradlePluginTestBase() {
@Test
@ -37,32 +35,32 @@ class DesktopApplicationTest : GradlePluginTestBase() {
}
""".trimIndent()
}
gradle("run").build().let { result ->
assertEquals(TaskOutcome.SUCCESS, result.task(":run")?.outcome)
gradle("run").checks {
check.taskSuccessful(":run")
}
gradle("runDistributable").build().let { result ->
assertEquals(TaskOutcome.SUCCESS, result.task(":createDistributable")!!.outcome)
assertEquals(TaskOutcome.SUCCESS, result.task(":runDistributable")?.outcome)
gradle("runDistributable").checks {
check.taskSuccessful(":createDistributable")
check.taskSuccessful(":runDistributable")
}
}
@Test
fun testRunMpp() = with(testProject(TestProjects.mpp)) {
val logLine = "Kotlin MPP app is running!"
gradle("run").build().checks { check ->
check.taskOutcome(":run", TaskOutcome.SUCCESS)
gradle("run").checks {
check.taskSuccessful(":run")
check.logContains(logLine)
}
gradle("runDistributable").build().checks { check ->
check.taskOutcome(":createDistributable", TaskOutcome.SUCCESS)
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle("runDistributable").checks {
check.taskSuccessful(":createDistributable")
check.taskSuccessful(":runDistributable")
check.logContains(logLine)
}
}
@Test
fun testAndroidxCompiler() = with(testProject(TestProjects.androidxCompiler, defaultAndroidxCompilerEnvironment)) {
gradle(":runDistributable").build().checks { check ->
gradle(":runDistributable").checks {
val actualMainImage = file("main-image.actual.png")
val expectedMainImage = file("main-image.expected.png")
assert(actualMainImage.readBytes().contentEquals(expectedMainImage.readBytes())) {
@ -73,8 +71,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun kotlinDsl(): Unit = with(testProject(TestProjects.jvmKotlinDsl)) {
gradle(":packageDistributionForCurrentOS", "--dry-run").build()
gradle(":packageReleaseDistributionForCurrentOS", "--dry-run").build()
gradle(":packageDistributionForCurrentOS", "--dry-run")
gradle(":packageReleaseDistributionForCurrentOS", "--dry-run")
}
@Test
@ -106,8 +104,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
}
checkImageBeforeBuild()
gradle(":runReleaseDistributable").build().checks { check ->
check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS)
gradle(":runReleaseDistributable").checks {
check.taskSuccessful(":proguardReleaseJars")
checkImageAfterBuild()
assertEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt"))
}
@ -115,8 +113,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
file("build.gradle").modify { "$it\n$enableObfuscation" }
actualMainImage.delete()
checkImageBeforeBuild()
gradle(":runReleaseDistributable").build().checks { check ->
check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS)
gradle(":runReleaseDistributable").checks {
check.taskSuccessful(":proguardReleaseJars")
checkImageAfterBuild()
assertNotEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt"))
}
@ -138,13 +136,13 @@ class DesktopApplicationTest : GradlePluginTestBase() {
}
val packagingTask = ":packageDistributionForCurrentOS"
gradle(packagingTask).build().checks { check ->
check.taskOutcome(packagingTask, TaskOutcome.SUCCESS)
gradle(packagingTask).checks {
check.taskSuccessful(packagingTask)
}
gradle("clean", packagingTask).build().checks { check ->
check.taskOutcome(":checkRuntime", TaskOutcome.FROM_CACHE)
check.taskOutcome(packagingTask, TaskOutcome.SUCCESS)
gradle("clean", packagingTask).checks {
check.taskFromCache(":checkRuntime")
check.taskSuccessful(packagingTask)
}
}
@ -160,7 +158,7 @@ class DesktopApplicationTest : GradlePluginTestBase() {
private fun TestProject.testPackageJvmDistributions() {
val result = gradle(":packageDistributionForCurrentOS").build()
val result = gradle(":packageDistributionForCurrentOS")
val mainClass = file("build/classes").walk().single { it.isFile && it.name == "MainKt.class" }
val bytecodeVersion = readClassFileVersion(mainClass)
@ -196,8 +194,10 @@ class DesktopApplicationTest : GradlePluginTestBase() {
} else {
Assert.assertEquals(packageFile.name, "TestPackage-1.0.0.$ext", "Unexpected package name")
}
assertEquals(TaskOutcome.SUCCESS, result.task(":package${ext.uppercaseFirstChar()}")?.outcome)
assertEquals(TaskOutcome.SUCCESS, result.task(":packageDistributionForCurrentOS")?.outcome)
result.checks {
check.taskSuccessful(":package${ext.uppercaseFirstChar()}")
check.taskSuccessful(":packageDistributionForCurrentOS")
}
}
@Test
@ -238,8 +238,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
}
private fun TestProject.testPackageUberJarForCurrentOS() {
gradle(":packageUberJarForCurrentOS").build().let { result ->
assertEquals(TaskOutcome.SUCCESS, result.task(":packageUberJarForCurrentOS")?.outcome)
gradle(":packageUberJarForCurrentOS").checks {
check.taskSuccessful(":packageUberJarForCurrentOS")
val resultJarFile = file("build/compose/jars/TestPackage-${currentTarget.id}-1.0.0.jar")
resultJarFile.checkExists()
@ -257,9 +257,9 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun testModuleClash() = with(testProject(TestProjects.moduleClashCli)) {
gradle(":app:runDistributable").build().checks { check ->
check.taskOutcome(":app:createDistributable", TaskOutcome.SUCCESS)
check.taskOutcome(":app:runDistributable", TaskOutcome.SUCCESS)
gradle(":app:runDistributable").checks {
check.taskSuccessful(":app:createDistributable")
check.taskSuccessful(":app:runDistributable")
check.logContains("Called lib1#util()")
check.logContains("Called lib2#util()")
}
@ -267,8 +267,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun testJavaLogger() = with(testProject(TestProjects.javaLogger)) {
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle(":runDistributable").checks {
check.taskSuccessful(":runDistributable")
check.logContains("Compose Gradle plugin test log warning!")
}
}
@ -284,8 +284,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
with(testProject(TestProjects.macOptions)) {
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle(":runDistributable").checks {
check.taskSuccessful(":runDistributable")
check.logContains("Hello, from Mac OS!")
val appDir = testWorkDir.resolve("build/compose/binaries/main/app/TestPackage.app/Contents/")
val actualInfoPlist = appDir.resolve("Info.plist").checkExists()
@ -333,8 +333,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
security("default-keychain", "-s", keychain)
security("unlock-keychain", "-p", password, keychain)
gradle(":createDistributable").build().checks { check ->
check.taskOutcome(":createDistributable", TaskOutcome.SUCCESS)
gradle(":createDistributable").checks {
check.taskSuccessful(":createDistributable")
val appDir = testWorkDir.resolve("build/compose/binaries/main/app/TestPackage.app/")
val result = runProcess(MacUtils.codesign, args = listOf("--verify", "--verbose", appDir.absolutePath))
val actualOutput = result.err.trim()
@ -345,8 +345,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
Assert.assertEquals(expectedOutput, actualOutput)
}
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle(":runDistributable").checks {
check.taskSuccessful(":runDistributable")
check.logContains("Signed app successfully started!")
}
}
@ -357,8 +357,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
fun testOptionsWithSpaces() {
with(testProject(TestProjects.optionsWithSpaces)) {
fun testRunTask(runTask: String) {
gradle(runTask).build().checks { check ->
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
gradle(runTask).checks {
check.taskSuccessful(runTask)
check.logContains("Running test options with spaces!")
check.logContains("Arg #1=Value 1!")
check.logContains("Arg #2=Value 2!")
@ -369,8 +369,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
testRunTask(":runDistributable")
testRunTask(":run")
gradle(":packageDistributionForCurrentOS").build().checks { check ->
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
gradle(":packageDistributionForCurrentOS").checks {
check.taskSuccessful(":packageDistributionForCurrentOS")
}
}
}
@ -379,8 +379,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
fun testDefaultArgs() {
with(testProject(TestProjects.defaultArgs)) {
fun testRunTask(runTask: String) {
gradle(runTask).build().checks { check ->
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
gradle(runTask).checks {
check.taskSuccessful(runTask)
check.logContains("compose.application.configure.swing.globals=true")
}
}
@ -388,8 +388,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
testRunTask(":runDistributable")
testRunTask(":run")
gradle(":packageDistributionForCurrentOS").build().checks { check ->
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
gradle(":packageDistributionForCurrentOS").checks {
check.taskSuccessful(":packageDistributionForCurrentOS")
}
}
}
@ -398,8 +398,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
fun testDefaultArgsOverride() {
with(testProject(TestProjects.defaultArgsOverride)) {
fun testRunTask(runTask: String) {
gradle(runTask).build().checks { check ->
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
gradle(runTask).checks {
check.taskSuccessful(runTask)
check.logContains("compose.application.configure.swing.globals=false")
}
}
@ -407,8 +407,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
testRunTask(":runDistributable")
testRunTask(":run")
gradle(":packageDistributionForCurrentOS").build().checks { check ->
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
gradle(":packageDistributionForCurrentOS").checks {
check.taskSuccessful(":packageDistributionForCurrentOS")
}
}
}
@ -416,8 +416,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun testSuggestModules() {
with(testProject(TestProjects.jvm)) {
gradle(":suggestRuntimeModules").build().checks { check ->
check.taskOutcome(":suggestRuntimeModules", TaskOutcome.SUCCESS)
gradle(":suggestRuntimeModules").checks {
check.taskSuccessful(":suggestRuntimeModules")
check.logContains("Suggested runtime modules to include:")
check.logContains("modules(\"java.instrument\", \"jdk.unsupported\")")
}
@ -427,8 +427,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun testUnpackSkiko() {
with(testProject(TestProjects.unpackSkiko)) {
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle(":runDistributable").checks {
check.taskSuccessful(":runDistributable")
val libraryPathPattern = "Read skiko library path: '(.*)'".toRegex()
val m = libraryPathPattern.find(check.log)
@ -450,12 +450,12 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test
fun resources() = with(testProject(TestProjects.resources)) {
gradle(":run").build().checks { check ->
check.taskOutcome(":run", TaskOutcome.SUCCESS)
gradle(":run").checks {
check.taskSuccessful(":run")
}
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
gradle(":runDistributable").checks {
check.taskSuccessful(":runDistributable")
}
}
}

39
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/GradlePluginTest.kt

@ -16,7 +16,6 @@ import java.net.SocketTimeoutException
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.concurrent.thread
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Test
class GradlePluginTest : GradlePluginTestBase() {
@ -30,18 +29,13 @@ class GradlePluginTest : GradlePluginTestBase() {
)
)
) {
gradle(":compileKotlinJs").build().checks { check ->
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
gradle(":compileKotlinJs").checks {
check.taskSuccessful(":compileKotlinJs")
}
}
@Test
fun configurePreviewWithoutConfigurationCache() = configurePreview(withConfigurationCache = false)
@Test
fun configurePreviewWithConfigurationCache() = configurePreview(withConfigurationCache = true)
private fun configurePreview(withConfigurationCache: Boolean) {
fun configurePreview() {
val isAlive = AtomicBoolean(true)
val receivedConfigCount = AtomicInteger(0)
val port = AtomicInteger(-1)
@ -82,7 +76,7 @@ class GradlePluginTest : GradlePluginTestBase() {
}
try {
testConfigureDesktopPreviewImpl(port.get(), withConfigurationCache)
testConfigureDesktopPreviewImpl(port.get())
} finally {
isAlive.set(false)
connectionThread.interrupt()
@ -96,35 +90,20 @@ class GradlePluginTest : GradlePluginTestBase() {
}
}
private fun testConfigureDesktopPreviewImpl(port: Int, withConfigurationCache: Boolean) {
private fun testConfigureDesktopPreviewImpl(port: Int) {
check(port > 0) { "Invalid port: $port" }
with(testProject(TestProjects.jvmPreview)) {
val portProperty = "-Pcompose.desktop.preview.ide.port=$port"
val previewTargetProperty = "-Pcompose.desktop.preview.target=PreviewKt.ExamplePreview"
val jvmTask = ":jvm:configureDesktopPreview"
val configurationCacheArg = "--configuration-cache"
val jvmRunner = if (withConfigurationCache) {
gradle(jvmTask, portProperty, previewTargetProperty, configurationCacheArg)
} else {
gradle(jvmTask, portProperty, previewTargetProperty)
gradle(jvmTask, portProperty, previewTargetProperty).checks {
check.taskSuccessful(jvmTask)
}
jvmRunner
.build()
.checks { check ->
check.taskOutcome(jvmTask, TaskOutcome.SUCCESS)
}
val mppTask = ":mpp:configureDesktopPreviewDesktop"
val mppRunner = if (withConfigurationCache) {
gradle(mppTask, portProperty, previewTargetProperty, configurationCacheArg)
} else {
gradle(mppTask, portProperty, previewTargetProperty)
gradle(mppTask, portProperty, previewTargetProperty).checks {
check.taskSuccessful(mppTask)
}
mppRunner
.build()
.checks { check ->
check.taskOutcome(mppTask, TaskOutcome.SUCCESS)
}
}
}

11
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/KotlinCompatabilityTest.kt

@ -5,13 +5,10 @@
package org.jetbrains.compose.test.tests.integration
import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.UnexpectedBuildFailure
import org.jetbrains.compose.test.utils.GradlePluginTestBase
import org.jetbrains.compose.test.utils.TestProjects
import org.jetbrains.compose.test.utils.checks
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class KotlinCompatabilityTest : GradlePluginTestBase() {
@Test
@ -33,8 +30,8 @@ class KotlinCompatabilityTest : GradlePluginTestBase() {
)
) {
val logLine = "Kotlin MPP app is running!"
gradle("run").build().checks { check ->
check.taskOutcome(":run", TaskOutcome.SUCCESS)
gradle("run").checks {
check.taskSuccessful(":run")
check.logContains(logLine)
}
}
@ -45,8 +42,8 @@ class KotlinCompatabilityTest : GradlePluginTestBase() {
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
)
) {
gradle(":compileKotlinJs").build().checks { check ->
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
gradle(":compileKotlinJs").checks {
check.taskSuccessful(":compileKotlinJs")
}
}
}

37
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProject.kt

@ -5,7 +5,9 @@
package org.jetbrains.compose.test.utils
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.util.GradleVersion
import org.jetbrains.compose.desktop.application.internal.ComposeProperties
import java.io.File
import java.util.Properties
@ -62,7 +64,35 @@ class TestProject(
}
}
fun gradle(vararg args: String): GradleRunner =
internal fun gradle(vararg args: String): BuildResult {
if (TestProperties.gradleConfigurationCache) {
if (GradleVersion.version(TestProperties.gradleVersionForTests) < GradleVersion.version("8.0-rc-1")) {
// Gradle 7.* does not use the configuration cache in the same build.
// In other words, if cache misses, Gradle performs configuration,
// but does not, use the serialized task graph.
// So in order to test the cache, we need to perform dry-run before the actual run.
// This should be fixed in https://github.com/gradle/gradle/issues/21985 (which is planned for 8.0 RC 1)
gradleRunner(args.withDryRun()).build()
}
}
return gradleRunner(args).build()
}
private fun Array<out String>.withDryRun(): Array<String> {
var sawDryRun = false
val dryRunArgs = ArrayList<String>(size)
for (arg in this) {
sawDryRun = sawDryRun || arg.trim() in listOf("-m", "--dry-run")
dryRunArgs.add(arg)
}
if (!sawDryRun) {
dryRunArgs.add("--dry-run")
}
return dryRunArgs.toTypedArray()
}
private fun gradleRunner(args: Array<out String>) =
GradleRunner.create().apply {
withGradleVersion(TestProperties.gradleVersionForTests)
withProjectDir(testEnvironment.workingDir)
@ -70,11 +100,6 @@ class TestProject(
forwardOutput()
}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Do not commit!")
fun gradleDebug(vararg args: String): GradleRunner =
gradle(*args).withDebug(true)
fun file(path: String): File =
testEnvironment.workingDir.resolve(path)

3
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProperties.kt

@ -29,6 +29,9 @@ object TestProperties {
val gradleVersionForTests: String?
get() = System.getProperty("compose.tests.gradle.version")
val gradleConfigurationCache: Boolean
get() = System.getProperty("compose.tests.gradle.configuration.cache") == "true"
val summaryFile: File?
get() = System.getProperty("compose.tests.summary.file")?.let { File(it) }

21
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt

@ -18,10 +18,13 @@ internal fun <T> Collection<T>.checkContains(vararg elements: T) {
}
}
internal fun BuildResult.checks(fn: (BuildResultChecks) -> Unit) {
fn(BuildResultChecks(this))
internal fun BuildResult.checks(fn: ChecksWrapper.() -> Unit) {
fn(ChecksWrapper(BuildResultChecks(this)))
}
@JvmInline
internal value class ChecksWrapper(val check: BuildResultChecks)
internal class BuildResultChecks(private val result: BuildResult) {
val log: String
get() = result.output
@ -32,7 +35,19 @@ internal class BuildResultChecks(private val result: BuildResult) {
}
}
fun taskOutcome(task: String, expectedOutcome: TaskOutcome) {
fun taskSuccessful(task: String) {
taskOutcome(task, TaskOutcome.SUCCESS)
}
fun taskFailed(task: String) {
taskOutcome(task, TaskOutcome.FAILED)
}
fun taskFromCache(task: String) {
taskOutcome(task, TaskOutcome.FROM_CACHE)
}
private fun taskOutcome(task: String, expectedOutcome: TaskOutcome) {
val actualOutcome = result.task(task)?.outcome
if (actualOutcome != expectedOutcome) {
throw AssertionError(

2
gradle-plugins/gradle.properties

@ -15,7 +15,7 @@ compose.tests.js.compiler.compatible.kotlin.version=1.7.20
compose.tests.androidx.compiler.version=1.1.1
compose.tests.androidx.compiler.compatible.kotlin.version=1.6.10
# __SUPPORTED_GRADLE_VERSIONS__
compose.tests.gradle.versions=7.0.2, 7.6
compose.tests.gradle.versions=7.0.2, 7.6-with-configuration-cache
# A version of Gradle plugin, that will be published,
# unless overridden by COMPOSE_GRADLE_PLUGIN_VERSION env var.

Loading…
Cancel
Save