Repository: runningcode/gradle-doctor (kotlin)

Issue:
JAVA_HOME captured in configuration cache even when `ensureJavaHomeMatches = false`
This check is disabled in one of our builds but still causes the variable to be captured in configuration cache.

Hermit manages the jdk and gradle in this build, and the JAVA_HOME variable set by the shell is not used. We found that IDE builds triggered by hermit don't seem to override this environment variable for the gradle execution, so when we try to pre-warm the configuration cache for an IDE build from outside the IDE, we get CC input mismatches caused by the gradle-doctor plugin.

Pull request:
Fixes https://github.com/runningcode/gradle-doctor/issues/408

This PR updates `JavaHomeCheck.kt` to use the Provider API to lazily read the JAVA_HOME environment variable. This prevents JAVA_HOME to be configuration input if `ensureJavaHomeMatches` and `ensureJavaHomeIsSet` are set to false.

Patch:
diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt
index bc89943..1aa2b76 100644
--- a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt
+++ b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt
@@ -11,6 +11,7 @@ import com.osacky.doctor.internal.PillBoxPrinter
 import com.osacky.doctor.internal.SystemClock
 import com.osacky.doctor.internal.UnixDaemonChecker
 import com.osacky.doctor.internal.UnsupportedOsDaemonChecker
+import com.osacky.doctor.internal.isGradle74OrNewer
 import org.gradle.api.Action
 import org.gradle.api.GradleException
 import org.gradle.api.Plugin
@@ -41,7 +42,7 @@ class DoctorPlugin : Plugin<Project> {
         val intervalMeasurer = IntervalMeasurer()
         val pillBoxPrinter = PillBoxPrinter(target.logger)
         val daemonChecker = BuildDaemonChecker(extension, createDaemonChecker(os, cliCommandExecutor), pillBoxPrinter)
-        val javaHomeCheck = createJavaHomeCheck(extension, pillBoxPrinter)
+        val javaHomeCheck = createJavaHomeCheck(extension, pillBoxPrinter, target)
         val appleRosettaTranslationCheck =
             AppleRosettaTranslationCheck(
                 os,
@@ -139,9 +140,18 @@ class DoctorPlugin : Plugin<Project> {
     private fun createJavaHomeCheck(
         extension: DoctorExtension,
         pillBoxPrinter: PillBoxPrinter,
+        project: Project,
     ): JavaHomeCheck {
         val jvmVariables =
-            JvmVariables(environmentJavaHome = System.getenv(JAVA_HOME), gradleJavaHome = Jvm.current().javaHome.path)
+            JvmVariables(
+                environmentJavaHomeProvider =
+                    if (!isGradle74OrNewer()) {
+                        project.providers.environmentVariable(JAVA_HOME).forUseAtConfigurationTime()
+                    } else {
+                        project.providers.environmentVariable(JAVA_HOME)
+                    },
+                gradleJavaHome = Jvm.current().javaHome.path,
+            )
         return JavaHomeCheck(jvmVariables, extension.javaHomeHandler, pillBoxPrinter)
     }
 
diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/JavaHomeCheck.kt b/doctor-plugin/src/main/java/com/osacky/doctor/JavaHomeCheck.kt
index 6e48614..e372fa8 100644
--- a/doctor-plugin/src/main/java/com/osacky/doctor/JavaHomeCheck.kt
+++ b/doctor-plugin/src/main/java/com/osacky/doctor/JavaHomeCheck.kt
@@ -6,6 +6,7 @@ import com.osacky.doctor.internal.JAVA_HOME_TAG
 import com.osacky.doctor.internal.JavaHomeCheckPrescriptionsGenerator
 import com.osacky.doctor.internal.PillBoxPrinter
 import org.gradle.api.GradleException
+import org.gradle.api.provider.Provider
 import java.io.File
 import java.nio.file.NoSuchFileException
 import java.nio.file.Path
@@ -28,7 +29,9 @@ class JavaHomeCheck(
 ) : BuildStartFinishListener,
     HasBuildScanTag {
     private val gradleJavaExecutablePath by lazy { resolveExecutableJavaPath(jvmVariables.gradleJavaHome) }
-    private val environmentJavaExecutablePath by lazy { resolveEnvironmentJavaHome(jvmVariables.environmentJavaHome) }
+    private val environmentJavaExecutablePath by lazy {
+        resolveEnvironmentJavaHome(jvmVariables.environmentJavaHomeProvider.get())
+    }
     private val recordedErrors = Collections.synchronizedSet(LinkedHashSet<String>())
     private val isGradleUsingJavaHome: Boolean
         get() = gradleJavaExecutablePath == environmentJavaExecutablePath
@@ -95,6 +98,6 @@ class JavaHomeCheck(
 }
 
 data class JvmVariables(
-    val environmentJavaHome: String?,
+    val environmentJavaHomeProvider: Provider<String?>,
     val gradleJavaHome: String,
 )


Test patch:
diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/JavaHomeCheckTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/JavaHomeCheckTest.kt
index fafae73..5661fe2 100644
--- a/doctor-plugin/src/test/java/com/osacky/doctor/JavaHomeCheckTest.kt
+++ b/doctor-plugin/src/test/java/com/osacky/doctor/JavaHomeCheckTest.kt
@@ -26,6 +26,7 @@ import com.osacky.doctor.internal.PillBoxPrinter
 import com.osacky.doctor.internal.SpyJavaHomePrescriptionsGenerator
 import org.gradle.api.GradleException
 import org.gradle.api.provider.Property
+import org.gradle.api.provider.Provider
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -86,6 +87,7 @@ class JavaHomeCheckTest {
     private lateinit var underTest: JavaHomeCheck
 
     private val pillBoxPrinter = mock<PillBoxPrinter>()
+    private val provider = mock<Provider<String?>>()
 
     @Before
     fun setup() {
@@ -96,7 +98,8 @@ class JavaHomeCheckTest {
 
     @Test
     fun `given environment java home path is not set when a full check is performed then there are at least two prescriptions`() {
-        val jvmVariables = setupIdenticalJvmVariables().copy(environmentJavaHome = null)
+        whenever(provider.get()).thenReturn(null)
+        val jvmVariables = setupIdenticalJvmVariables().copy(environmentJavaHomeProvider = provider)
         underTest =
             JavaHomeCheck(
                 jvmVariables,
@@ -191,7 +194,8 @@ class JavaHomeCheckTest {
             .createSymbolicLinkPointingTo(javaExecutableFolder.toAbsolutePath())
 
         // environmentJavaHome=***/Users/doctor/.sdkman/candidates/java/current and gradleJavaHome=***/Users/doctor/.sdkman/candidates/java/17.0.10-zulu/zulu-17.jdk/Contents/Home
-        val jvmVariables = JvmVariables(sdkmanEnvironmentJavaHome.pathString, javaHomePath.pathString)
+        whenever(provider.get()).thenReturn(sdkmanEnvironmentJavaHome.pathString)
+        val jvmVariables = JvmVariables(provider, javaHomePath.pathString)
         underTest = JavaHomeCheck(jvmVariables, javaHomeHandler, pillBoxPrinter, spyPrescriptionsGenerator)
         underTest.onStart()
         val errors = underTest.onFinish()
@@ -214,16 +218,18 @@ class JavaHomeCheckTest {
 
     private fun setupIdenticalJvmVariables(): JvmVariables {
         val legitPath = javaHomePath.pathString
-        return JvmVariables(legitPath, legitPath)
+        val provider = mock<Provider<String?>>().also { whenever(it.get()).thenReturn(legitPath) }
+        return JvmVariables(provider, legitPath)
     }
 
     private fun setupDifferentJvmVariables(): JvmVariables {
         val anotherJavaHomePath = setupJavaHomePathStructure(anotherLegitJavaHomePathFolders)
-        return JvmVariables(javaHomePath.toString(), anotherJavaHomePath.toString())
+        val provider = mock<Provider<String?>>().also { whenever(it.get()).thenReturn(javaHomePath.toString()) }
+        return JvmVariables(provider, anotherJavaHomePath.toString())
     }
 
     private fun verifyJvmVariablesAreUsedForPrescriptionGeneration(jvmVariables: JvmVariables) {
-        assertEquals(jvmVariables.environmentJavaHome, spyPrescriptionsGenerator.capturedJavaHomeLocation)
+        assertEquals(jvmVariables.environmentJavaHomeProvider.get(), spyPrescriptionsGenerator.capturedJavaHomeLocation)
         assertEquals(jvmVariables.gradleJavaHome, spyPrescriptionsGenerator.capturedGradleJavaHomeLocation)
     }
 }