From e65886f0180062ef8b5a55e75eba9364e468f76d Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 14 Mar 2023 10:22:36 -0400 Subject: [PATCH] Changed Configuration Validation --- dependency-guard/gradle.properties | 2 +- .../plugins/dependencyguard/PluginTest.kt | 4 +- .../dependencyguard/DependencyGuardPlugin.kt | 31 +---- .../internal/ConfigurationValidators.kt | 128 ++++++++---------- .../dependencyguard/internal/ProjectExt.kt | 10 +- .../internal/list/DependencyGuardListTask.kt | 33 ++++- .../internal/tree/DependencyTreeDiffTask.kt | 15 +- 7 files changed, 102 insertions(+), 121 deletions(-) diff --git a/dependency-guard/gradle.properties b/dependency-guard/gradle.properties index bbfdd5f..ce1d75f 100644 --- a/dependency-guard/gradle.properties +++ b/dependency-guard/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.dropbox.dependency-guard -VERSION_NAME=0.4.2 +VERSION_NAME=0.4.3-SNAPSHOT POM_ARTIFACT_ID=dependency-guard POM_NAME=Dependency Guard diff --git a/dependency-guard/src/gradleTest/kotlin/com/dropbox/gradle/plugins/dependencyguard/PluginTest.kt b/dependency-guard/src/gradleTest/kotlin/com/dropbox/gradle/plugins/dependencyguard/PluginTest.kt index b6864c6..0c465a0 100644 --- a/dependency-guard/src/gradleTest/kotlin/com/dropbox/gradle/plugins/dependencyguard/PluginTest.kt +++ b/dependency-guard/src/gradleTest/kotlin/com/dropbox/gradle/plugins/dependencyguard/PluginTest.kt @@ -266,7 +266,7 @@ class PluginTest { args = arrayOf(":lib:dependencyGuard") ) - assertThat(result.output).contains("Error: No configurations provided to Dependency Guard Plugin.") + assertThat(result.output).contains("Error: No configurations provided to Dependency Guard Plugin for project :lib") } @ParameterizedPluginTest @@ -283,6 +283,6 @@ class PluginTest { args = arrayOf(":lib:dependencyGuard") ) - assertThat(result.output).contains("Configuration with name releaseCompileClasspath was not found for :lib") + assertThat(result.output).contains("Dependency Guard could not resolve the configurations named [releaseCompileClasspath] for :lib") } } diff --git a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/DependencyGuardPlugin.kt b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/DependencyGuardPlugin.kt index 91c8fe7..2c21e9e 100644 --- a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/DependencyGuardPlugin.kt +++ b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/DependencyGuardPlugin.kt @@ -6,6 +6,7 @@ import com.dropbox.gradle.plugins.dependencyguard.internal.isRootProject import com.dropbox.gradle.plugins.dependencyguard.internal.list.DependencyGuardListTask import com.dropbox.gradle.plugins.dependencyguard.internal.projectConfigurations import com.dropbox.gradle.plugins.dependencyguard.internal.tree.DependencyTreeDiffTask +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider @@ -46,33 +47,6 @@ public class DependencyGuardPlugin : Plugin { target = target, dependencyGuardTask = dependencyGuardTask ) - - validateConfigurationAfterEvaluate(target, extension) - } - - /** - * It's recommended to avoid `afterEvaluate` but in this case we are doing a validation of inputs - * - * Without adding this, there were cases when the `target.configurations` were not all available yet. - */ - private fun validateConfigurationAfterEvaluate(target: Project, extension: DependencyGuardPluginExtension) { - target.afterEvaluate { - val availableConfigurationNames = target.configurations.map { it.name } - val monitoredConfigurationNames = extension.configurations.map { it.configurationName } - ConfigurationValidators.requirePluginConfig( - projectPath = target.path, - isForRootProject = target.isRootProject(), - availableConfigurations = availableConfigurationNames, - monitoredConfigurations = monitoredConfigurationNames, - ) - ConfigurationValidators.validateConfigurationsAreAvailable( - projectPath = target.path, - isForRootProject = target.isRootProject(), - logger = target.logger, - availableConfigurationNames = availableConfigurationNames, - monitoredConfigurationNames = monitoredConfigurationNames - ) - } } private fun attachToCheckTask(target: Project, dependencyGuardTask: TaskProvider) { @@ -110,8 +84,7 @@ public class DependencyGuardPlugin : Plugin { DEPENDENCY_GUARD_TASK_NAME, DependencyGuardListTask::class.java ) { - val task = this - task.setParams( + this.setParams( project = target, extension = extension, shouldBaseline = false diff --git a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ConfigurationValidators.kt b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ConfigurationValidators.kt index c1cf9b7..ac5b579 100644 --- a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ConfigurationValidators.kt +++ b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ConfigurationValidators.kt @@ -1,25 +1,47 @@ package com.dropbox.gradle.plugins.dependencyguard.internal +import com.dropbox.gradle.plugins.dependencyguard.DependencyGuardConfiguration import com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin +import com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPluginExtension import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.initialization.dsl.ScriptHandler -import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging +import org.gradle.api.provider.Provider internal object ConfigurationValidators { private val logger = Logging.getLogger(DependencyGuardPlugin::class.java) - fun requirePluginConfig( + fun validatePluginConfiguration( + project: Project, + extension: DependencyGuardPluginExtension, + resolvedConfigurationsMap: Map> + ) { + val requestedConfigurations = extension.configurations.map { it.configurationName } + val validRequestedConfigurations = resolvedConfigurationsMap.keys.map { it.configurationName } + val allAvailableConfigurations = project.projectConfigurations.map { it.name } + + validatePluginConfiguration( + projectPath = project.path, + isForRootProject = project.isRootProject(), + requestedConfigurations = requestedConfigurations, + validRequestedConfigurations = validRequestedConfigurations, + allAvailableConfigurations = allAvailableConfigurations, + ) + } + + internal fun validatePluginConfiguration( projectPath: String, isForRootProject: Boolean, - availableConfigurations: List, - monitoredConfigurations: List + requestedConfigurations: List, + validRequestedConfigurations: List, + allAvailableConfigurations: List, ) { if (isForRootProject) { - logger.info("Configured for Root Project") - if (monitoredConfigurations.isNotEmpty() && monitoredConfigurations.first() != ScriptHandler.CLASSPATH_CONFIGURATION) { - logger.error("If you wish to use dependency guard on your root project, use the following config:") + if (requestedConfigurations != listOf(ScriptHandler.CLASSPATH_CONFIGURATION)) { + logger.error("If you wish to use Dependency Guard on your root project, use the following config:") throw GradleException( """ dependencyGuard { @@ -29,35 +51,36 @@ internal object ConfigurationValidators { ) } } else { - val availableConfigNames = availableConfigurations - .filter { isClasspathConfig(it) } + if (requestedConfigurations.isEmpty()) { + throw GradleException(buildString { + appendLine("Error: No configurations provided to Dependency Guard Plugin for project $projectPath") + appendLine(configurationsYouCouldUseMessage(allAvailableConfigurations)) + }) + } - require(availableConfigNames.isNotEmpty()) { - buildString { - appendLine("Error: The Dependency Guard Plugin can not find any configurations ending in 'RuntimeClasspath' or 'CompileClasspath'.") - appendLine("Suggestion: Move your usage of Dependency Guard as access to these Classpath configurations are required to configure Dependency Guard correctly.") - appendLine("Why: Either you have applied the plugin too early or your project just doesn't have any matching Classpath configurations.") - appendLine("All available Gradle configurations for project $projectPath at this point of time:") - availableConfigurations.forEach { - appendLine("* $it") + val unresolvedConfigurations = requestedConfigurations.minus(validRequestedConfigurations.toSet()) + if (unresolvedConfigurations.isNotEmpty()) { + throw GradleException( + buildString { + appendLine("Dependency Guard could not resolve the configurations named $unresolvedConfigurations for $projectPath") + appendLine(configurationsYouCouldUseMessage(allAvailableConfigurations)) } - } + ) } + } + } - val configurationNames = monitoredConfigurations.map { it } - require(configurationNames.isNotEmpty() && configurationNames[0].isNotBlank()) { - buildString { - appendLine("Error: No configurations provided to Dependency Guard Plugin.") - appendLine("Here are some valid configurations you could use.") - appendLine("") + private fun configurationsYouCouldUseMessage(availableConfigurations: List): String = buildString { + val availableConfigNames = availableConfigurations.filter { isClasspathConfig(it) } + if (availableConfigNames.isNotEmpty()) { + appendLine("Here are some valid configurations you could use.") + appendLine("") - appendLine("dependencyGuard {") - availableConfigNames.forEach { - appendLine(""" configuration("$it")""") - } - appendLine("}") - } + appendLine("dependencyGuard {") + availableConfigNames.forEach { + appendLine(""" configuration("$it")""") } + appendLine("}") } } @@ -70,49 +93,4 @@ internal object ConfigurationValidators { ignoreCase = true ) } - - - fun validateConfigurationsAreAvailable( - isForRootProject: Boolean, - projectPath: String, - logger: Logger, - availableConfigurationNames: Collection, - monitoredConfigurationNames: List - ) { - - if (isForRootProject) { - if (monitoredConfigurationNames != listOf(ScriptHandler.CLASSPATH_CONFIGURATION)) { - throw GradleException( - """ - For the root project, the only allowed configuration is ${ScriptHandler.CLASSPATH_CONFIGURATION}. - Use the following config: - dependencyGuard { - configuration("classpath") - } - """.trimIndent() - ) - } - return - } - - monitoredConfigurationNames.forEach { monitoredConfigurationName -> - availableConfigurationNames - .firstOrNull { it == monitoredConfigurationName } - ?: run { - val availableClasspathConfigs = availableConfigurationNames - .filter { - isClasspathConfig(it) - } - if (availableClasspathConfigs.isNotEmpty()) { - logger.quiet("Configuration with name $monitoredConfigurationName was not found. Available Configurations for $projectPath are:") - availableClasspathConfigs.forEach { - logger.quiet("* $it") - } - } - throw GradleException( - "Configuration with name $monitoredConfigurationName was not found for $projectPath" - ) - } - } - } } diff --git a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ProjectExt.kt b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ProjectExt.kt index 7acd5ef..f4e0ad5 100644 --- a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ProjectExt.kt +++ b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/ProjectExt.kt @@ -20,8 +20,8 @@ internal val Project.projectConfigurations: ConfigurationContainer configurations } -internal fun ConfigurationContainer.getResolvedComponentResult(name: String): Provider = this - .getByName(name) - .incoming - .resolutionResult - .rootComponent \ No newline at end of file +internal fun ConfigurationContainer.getResolvedComponentResult(name: String): Provider? = this + .findByName(name) + ?.incoming + ?.resolutionResult + ?.rootComponent diff --git a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/list/DependencyGuardListTask.kt b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/list/DependencyGuardListTask.kt index fb16f74..6dcd294 100644 --- a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/list/DependencyGuardListTask.kt +++ b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/list/DependencyGuardListTask.kt @@ -158,9 +158,20 @@ internal abstract class DependencyGuardListTask : DefaultTask() { extension: DependencyGuardPluginExtension, shouldBaseline: Boolean ) { + val resolvedConfigurationsMap = resolveMonitoredConfigurationsMap( + project = project, + monitoredConfigurations = extension.configurations + ) + + ConfigurationValidators.validatePluginConfiguration( + project = project, + extension = extension, + resolvedConfigurationsMap = resolvedConfigurationsMap, + ) + this.forRootProject.set(project.isRootProject()) this.projectPath.set(project.path) - this.monitoredConfigurationsMap.set(resolveMonitoredConfigurationsMap(project, extension.configurations)) + this.monitoredConfigurationsMap.set(resolvedConfigurationsMap) this.shouldBaseline.set(shouldBaseline) val projectDirDependenciesDir = OutputFileUtils.projectDirDependenciesDir(project) this.projectDirectoryDependenciesDir.set(projectDirDependenciesDir) @@ -168,14 +179,24 @@ internal abstract class DependencyGuardListTask : DefaultTask() { declareCompatibilities() } - private fun resolveMonitoredConfigurationsMap( + /** + * Attempts to resolve requested configurations. + * + * Will throw Exception if unable to resolve any requested configurations. + */ + internal fun resolveMonitoredConfigurationsMap( project: Project, monitoredConfigurations: Collection, ): Map> { - val configurationContainer = project.projectConfigurations - - return monitoredConfigurations.associateWith { - configurationContainer.getResolvedComponentResult(it.configurationName) + val resolvedConfigurationsMap = mutableMapOf>() + monitoredConfigurations.forEach { monitoredConfiguration -> + val resolvedComponentResultForConfiguration = + project.projectConfigurations.getResolvedComponentResult(monitoredConfiguration.configurationName) + if (resolvedComponentResultForConfiguration != null) { + resolvedConfigurationsMap[monitoredConfiguration] = resolvedComponentResultForConfiguration + } } + return resolvedConfigurationsMap } + } diff --git a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/tree/DependencyTreeDiffTask.kt b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/tree/DependencyTreeDiffTask.kt index 003354e..3fd2a22 100644 --- a/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/tree/DependencyTreeDiffTask.kt +++ b/dependency-guard/src/main/kotlin/com/dropbox/gradle/plugins/dependencyguard/internal/tree/DependencyTreeDiffTask.kt @@ -8,6 +8,7 @@ import com.dropbox.gradle.plugins.dependencyguard.internal.utils.OutputFileUtils import com.dropbox.gradle.plugins.dependencyguard.internal.utils.Tasks.declareCompatibilities import java.io.File import org.gradle.api.DefaultTask +import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.provider.Property @@ -46,10 +47,18 @@ internal abstract class DependencyTreeDiffTask : DefaultTask() { shouldBaseline: Boolean, ) { val projectDependenciesDir = OutputFileUtils.projectDirDependenciesDir(project) - val projectDirOutputFile: File = DependencyGuardTreeDiffer.projectDirOutputFile(projectDependenciesDir, configurationName) - val buildDirOutputFile: File = DependencyGuardTreeDiffer.buildDirOutputFile(project.layout.buildDirectory.get(), configurationName) + val projectDirOutputFile: File = + DependencyGuardTreeDiffer.projectDirOutputFile(projectDependenciesDir, configurationName) + val buildDirOutputFile: File = + DependencyGuardTreeDiffer.buildDirOutputFile(project.layout.buildDirectory.get(), configurationName) val projectPath = project.path - val resolvedComponentResult = project.projectConfigurations.getResolvedComponentResult(configurationName) + + val resolvedComponentResult: Provider = + project.projectConfigurations.getResolvedComponentResult(configurationName) + ?: throw GradleException(buildString { + appendLine("Dependency Guard could not resolve configuration $configurationName for $projectPath.") + appendLine("Please update your configuration and try again.") + }) this.resolvedComponentResult.set(resolvedComponentResult) this.projectDirOutputFile.set(projectDirOutputFile)