diff --git a/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/CannotRequestConsentWithinIdeException.kt b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/CannotRequestConsentWithinIdeException.kt new file mode 100644 index 00000000000..805e0308105 --- /dev/null +++ b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/CannotRequestConsentWithinIdeException.kt @@ -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() +) \ No newline at end of file diff --git a/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/ConsentManager.kt b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/ConsentManager.kt index 6d03c69f973..3bf990fc785 100644 --- a/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/ConsentManager.kt +++ b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/ConsentManager.kt @@ -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,30 +52,40 @@ 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)) } + 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) + } else { + USER_CONSENT_MARKER + } + ) + } else { + output.println("You've refused to give the consent for the automatic configuration of local.properties") + modifier.putLine(USER_REFUSAL_MARKER) + } + return consentGiven + } + + fun askForConsent(consentDetailsLink: String? = null): Boolean { + printConsentRequest(consentDetailsLink) repeat(MAX_REQUEST_ATTEMPTS) { output.println(PROMPT_REQUEST) - when (input.readLine()) { - "yes" -> { - output.println("You've given the consent") - modifier.putLine( - if (consentDetailsLink != null) { - USER_CONSENT_MARKER_WITH_DETAILS_LINK.formatWithLink(consentDetailsLink) - } else { - USER_CONSENT_MARKER - } - ) - return true - } - "no" -> { - output.println("You've refused to give the consent") - modifier.putLine(USER_REFUSAL_MARKER) - return false - } + 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 diff --git a/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/InternalGradleSetupSettingsPlugin.kt b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/InternalGradleSetupSettingsPlugin.kt index 946ed786936..71d37f5a589 100644 --- a/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/InternalGradleSetupSettingsPlugin.kt +++ b/repo/gradle-settings-conventions/internal-gradle-setup/src/main/kotlin/InternalGradleSetupSettingsPlugin.kt @@ -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 { private val log = Logging.getLogger(javaClass) @@ -34,7 +39,8 @@ abstract class InternalGradleSetupSettingsPlugin : Plugin { } 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 { val setupFile = connection.getInputStream().buffered().use { parseSetupFile(it) } - if (initialDecision == null && !consentManager.askForConsent(setupFile.consentDetailsLink)) { - log.debug("Skipping automatic local.properties configuration as the consent wasn't given") - return + 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) {