[IGS] Add a non-interactive way to make a consent decision for IDEA builds

When running builds from IDEA, especially tests, the interactive consent request is barely noticeable. Also, some of the tools windows are non-interactive terminals. Thus, we detect the IDEA builds and fail fast to make it noticeable.
^KTI-1443 Fixed
This commit is contained in:
Alexander.Likhachev
2023-11-15 01:52:31 +01:00
committed by Space Team
parent ab5699f106
commit 2bd77d5cdc
3 changed files with 70 additions and 25 deletions
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.build
import org.gradle.api.GradleException
class CannotRequestConsentWithinIdeException(consentDetailsLink: String?) : GradleException(
"""
|$USER_CONSENT_REQUEST
|The consent cannot be requested in interactive mode when running from IDE.
|Please either invoke Gradle from the command line or add -P$CONSENT_DECISION_GRADLE_PROPERTY=(true,false) to the run parameters in order to make a decision
|${if (consentDetailsLink != null) USER_CONSENT_DETAILS_LINK_TEMPLATE.formatWithLink(consentDetailsLink) else ""}
""".trimMargin()
)
@@ -25,10 +25,9 @@ internal val USER_CONSENT_REQUEST = """
! ATTENTION REQUIRED !
Most probably you're a developer from the Kotlin team. We are asking for your consent for automatic configuration of local.properties file
for providing some optimizations and collecting additional debug information.
""".trimIndent()
internal val USER_CONSENT_DETAILS_LINK_TEMPLATE = "You can read more details here: $linkPlaceholder"
internal const val USER_CONSENT_DETAILS_LINK_TEMPLATE = "You can read more details here: $linkPlaceholder"
internal const val PROMPT_REQUEST = "Do you agree with this? Please answer with 'yes' or 'no': "
@@ -53,16 +52,18 @@ internal class ConsentManager(
}
}
fun askForConsent(consentDetailsLink: String? = null): Boolean {
private fun printConsentRequest(consentDetailsLink: String? = null) {
output.println()
output.println(USER_CONSENT_REQUEST)
if (consentDetailsLink != null) {
output.println(USER_CONSENT_DETAILS_LINK_TEMPLATE.formatWithLink(consentDetailsLink))
}
repeat(MAX_REQUEST_ATTEMPTS) {
output.println(PROMPT_REQUEST)
when (input.readLine()) {
"yes" -> {
output.println("You've given the consent")
output.println()
}
fun applyConsentDecision(consentGiven: Boolean, consentDetailsLink: String? = null): Boolean {
if (consentGiven) {
output.println("You've given the consent for the automatic configuration of local.properties")
modifier.putLine(
if (consentDetailsLink != null) {
USER_CONSENT_MARKER_WITH_DETAILS_LINK.formatWithLink(consentDetailsLink)
@@ -70,13 +71,21 @@ internal class ConsentManager(
USER_CONSENT_MARKER
}
)
return true
}
"no" -> {
output.println("You've refused to give the consent")
} else {
output.println("You've refused to give the consent for the automatic configuration of local.properties")
modifier.putLine(USER_REFUSAL_MARKER)
return false
}
return consentGiven
}
fun askForConsent(consentDetailsLink: String? = null): Boolean {
printConsentRequest(consentDetailsLink)
repeat(MAX_REQUEST_ATTEMPTS) {
output.println(PROMPT_REQUEST)
return when (input.readLine()) {
"yes" -> applyConsentDecision(true, consentDetailsLink)
"no" -> applyConsentDecision(false, consentDetailsLink)
else -> return@repeat
}
}
// we didn't receive an answer, let's ask next time
@@ -18,7 +18,12 @@ private const val DOMAIN_NAME = "kotlin-build-properties.labs.jb.gg"
private const val SETUP_JSON_URL = "https://$DOMAIN_NAME/setup.json"
private const val PLUGIN_SWITCH_PROPERTY = "kotlin.build.internal.gradle.setup"
private const val CONSENT_GRADLE_PROPERTY = "kotlin.build.internal.gradle.setup.consent"
private const val GLOBAL_CONSENT_GRADLE_PROPERTY = "kotlin.build.internal.gradle.setup.consent"
private val isInIdea
get() = System.getProperty("idea.active").toBoolean()
internal const val CONSENT_DECISION_GRADLE_PROPERTY = "kotlin.build.internal.gradle.setup.consent.give"
abstract class InternalGradleSetupSettingsPlugin : Plugin<Settings> {
private val log = Logging.getLogger(javaClass)
@@ -34,7 +39,8 @@ abstract class InternalGradleSetupSettingsPlugin : Plugin<Settings> {
}
try {
val modifier = LocalPropertiesModifier(target.rootDir.resolve("local.properties"))
val consentManager = ConsentManager(modifier, target.providers.gradleProperty(CONSENT_GRADLE_PROPERTY).orNull?.toBoolean())
val consentManager =
ConsentManager(modifier, target.providers.gradleProperty(GLOBAL_CONSENT_GRADLE_PROPERTY).orNull?.toBoolean())
val initialDecision = consentManager.getUserDecision()
if (initialDecision == false) {
log.debug("Skipping automatic local.properties configuration as you've opted out")
@@ -44,12 +50,25 @@ abstract class InternalGradleSetupSettingsPlugin : Plugin<Settings> {
val setupFile = connection.getInputStream().buffered().use {
parseSetupFile(it)
}
if (initialDecision == null && !consentManager.askForConsent(setupFile.consentDetailsLink)) {
if (initialDecision == null) {
val nonInteractiveDecision = target.providers.gradleProperty(CONSENT_DECISION_GRADLE_PROPERTY).map { it.toBoolean() }
if (isInIdea && !nonInteractiveDecision.isPresent) {
throw CannotRequestConsentWithinIdeException(setupFile.consentDetailsLink)
}
val consentReceived = if (nonInteractiveDecision.isPresent) {
consentManager.applyConsentDecision(nonInteractiveDecision.get(), setupFile.consentDetailsLink)
} else {
consentManager.askForConsent(setupFile.consentDetailsLink)
}
if (!consentReceived) {
log.debug("Skipping automatic local.properties configuration as the consent wasn't given")
return
}
}
modifier.applySetup(setupFile)
log.info("Automatic local.properties setup has been applied.")
} catch (e: CannotRequestConsentWithinIdeException) {
throw e
} catch (e: UnknownHostException) {
log.debug("Cannot connect to the internal properties storage", e)
} catch (e: SocketTimeoutException) {