From 21b6322e9983521c17cd6ee6e008d1b3d49f8d2b Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Sun, 13 Aug 2023 03:28:51 +0200 Subject: [PATCH] Make the refreshVersions task fully support configuration cache Related to #672 --- .../core/RefreshVersionsCorePlugin.kt | 26 ++++- .../core/RefreshVersionsTask.kt | 39 +++++-- .../core/extensions/gradle/Dependency.kt | 2 + .../internal/ConfigurationLessDependency.kt | 36 ++++-- .../DependencyVersionsFetcher.Companion.kt | 37 ++++++- .../DependencyWithVersionCandidates.kt | 2 +- ...ionsImpl.kt => LookupVersionCandidates.kt} | 34 +----- .../internal/RefreshVersionsConfigHolder.kt | 11 +- .../core/internal/SettingsPluginsUpdater.kt | 24 +--- .../core/internal/UsedPluginsTracker.kt | 2 + .../core/internal/UsedVersionForTracker.kt | 2 + .../core/internal/VersionManagementKind.kt | 3 +- .../dependencies/DependenciesTracker.kt | 104 ++++++++++++++++++ .../core/internal/dependencies/Repo.kt | 31 ++++++ 14 files changed, 267 insertions(+), 86 deletions(-) rename plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/{NewRefreshVersionsImpl.kt => LookupVersionCandidates.kt} (91%) create mode 100644 plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/DependenciesTracker.kt create mode 100644 plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/Repo.kt diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsCorePlugin.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsCorePlugin.kt index f7d62fdf2..8c3381a61 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsCorePlugin.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsCorePlugin.kt @@ -5,7 +5,7 @@ import de.fayard.refreshVersions.core.extensions.gradle.isRootProject import de.fayard.refreshVersions.core.internal.InternalRefreshVersionsApi import de.fayard.refreshVersions.core.internal.OutputFile import de.fayard.refreshVersions.core.internal.RefreshVersionsConfigHolder -import de.fayard.refreshVersions.core.internal.skipConfigurationCache +import de.fayard.refreshVersions.core.internal.VersionsCatalogs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.register @@ -18,15 +18,31 @@ open class RefreshVersionsCorePlugin : Plugin { check(project.isRootProject) { "ERROR: de.fayard.refreshVersions.core should not be applied manually" } if (project.isBuildSrc.not()) { OutputFile.init(project) - // In the case where this runs in includedBuilds, the task configuration lambda may (will) run - // after RefreshVersionsConfigHolder content is cleared (via its ClearStaticStateBuildService), - // so we get the value before. val versionsFileName = RefreshVersionsConfigHolder.versionsPropertiesFile.name + val shouldUpdateVersionCatalogs = VersionsCatalogs.isSupported() && FeatureFlag.VERSIONS_CATALOG.isEnabled project.tasks.register(name = "refreshVersions") { group = "refreshVersions" description = "Search for new dependencies versions and update $versionsFileName" - skipConfigurationCache() + if (shouldUpdateVersionCatalogs) { + this.defaultVersionCatalog = VersionsCatalogs.getDefault(project) + this.defaultVersionCatalogFile = project.file(VersionsCatalogs.LIBS_VERSIONS_TOML) + } + rootProjectSettingsFile = project.file("settings.gradle.kts").let { kotlinDslSettings -> + if (kotlinDslSettings.exists()) kotlinDslSettings else { + project.file("settings.gradle").also { + check(it.exists()) + } + } + } + buildSrcSettingsFile = project.file("buildSrc/settings.gradle.kts").let { kotlinDslSettings -> + if (kotlinDslSettings.exists()) kotlinDslSettings else { + project.file("buildSrc/settings.gradle").takeIf { + it.exists() + } + } + } + RefreshVersionsConfigHolder.dependenciesTracker.recordBuildscriptAndRegularDependencies(project) } project.tasks.register(name = "refreshVersionsCleanup") { diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsTask.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsTask.kt index 363027f51..ebbafdec4 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsTask.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/RefreshVersionsTask.kt @@ -1,8 +1,14 @@ package de.fayard.refreshVersions.core -import de.fayard.refreshVersions.core.internal.* +import de.fayard.refreshVersions.core.internal.OutputFile +import de.fayard.refreshVersions.core.internal.PluginDependencyCompat +import de.fayard.refreshVersions.core.internal.RefreshVersionsConfigHolder import de.fayard.refreshVersions.core.internal.RefreshVersionsConfigHolder.settings -import de.fayard.refreshVersions.core.internal.VersionsCatalogs.LIBS_VERSIONS_TOML +import de.fayard.refreshVersions.core.internal.SettingsPluginsUpdater +import de.fayard.refreshVersions.core.internal.VersionsCatalogUpdater +import de.fayard.refreshVersions.core.internal.VersionsCatalogs +import de.fayard.refreshVersions.core.internal.configureLintIfRunningOnAnAndroidProject +import de.fayard.refreshVersions.core.internal.lookupVersionCandidates import de.fayard.refreshVersions.core.internal.problems.log import de.fayard.refreshVersions.core.internal.versions.VersionsPropertiesModel import de.fayard.refreshVersions.core.internal.versions.writeWithNewVersions @@ -11,11 +17,14 @@ import kotlinx.coroutines.runBlocking import org.gradle.api.DefaultTask import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.artifacts.VersionCatalog import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option import org.gradle.util.GradleVersion +import java.io.File /** * @@ -29,7 +38,6 @@ import org.gradle.util.GradleVersion */ open class RefreshVersionsTask : DefaultTask() { - @Input @Optional @Option(option = "enable", description = "Enable a feature flag") var enableFlag: FeatureFlag? = null @@ -46,6 +54,20 @@ open class RefreshVersionsTask : DefaultTask() { if (value != null) FeatureFlag.userSettings.put(value, false) } + @get:Input + @get:Optional + internal var defaultVersionCatalog: VersionCatalog? = null + + @get:InputFile + internal lateinit var defaultVersionCatalogFile: File + + @get:InputFile + internal lateinit var rootProjectSettingsFile: File + + @get:InputFile + @get:Optional + internal var buildSrcSettingsFile: File? = null + @TaskAction fun taskActionRefreshVersions() { OutputFile.checkWhichFilesExist() @@ -62,7 +84,7 @@ open class RefreshVersionsTask : DefaultTask() { val versionsCatalogLibraries: Set val versionsCatalogPlugins: Set if (shouldUpdateVersionCatalogs) { - val versionCatalog = VersionsCatalogs.getDefault(project) + val versionCatalog = defaultVersionCatalog versionsCatalogLibraries = VersionsCatalogs.libraries(versionCatalog) versionsCatalogPlugins = VersionsCatalogs.plugins(versionCatalog) } else { @@ -80,16 +102,17 @@ open class RefreshVersionsTask : DefaultTask() { }) { httpClient -> lookupVersionCandidates( httpClient = httpClient, - project = project, + dependenciesTracker = RefreshVersionsConfigHolder.dependenciesTracker, versionMap = RefreshVersionsConfigHolder.readVersionsMap(), versionKeyReader = RefreshVersionsConfigHolder.versionKeyReader, versionsCatalogLibraries = versionsCatalogLibraries, versionsCatalogPlugins = versionsCatalogPlugins ) } - VersionsPropertiesModel.writeWithNewVersions(result.dependenciesUpdatesForVersionsProperties) + val versionPropertiesUpdated = VersionsPropertiesModel.writeWithNewVersions(result.dependenciesUpdatesForVersionsProperties) SettingsPluginsUpdater.updateGradleSettingsWithAvailablePluginsUpdates( - rootProject = project, + rootProjectSettingsFile = rootProjectSettingsFile, + buildSrcSettingsFile = buildSrcSettingsFile, settingsPluginsUpdates = result.settingsPluginsUpdates, buildSrcSettingsPluginsUpdates = result.buildSrcSettingsPluginsUpdates ) @@ -104,7 +127,7 @@ open class RefreshVersionsTask : DefaultTask() { if (versionPropertiesUpdated) OutputFile.VERSIONS_PROPERTIES.logFileWasModified() if (shouldUpdateVersionCatalogs) { - val libsToml = project.file(LIBS_VERSIONS_TOML) + val libsToml = defaultVersionCatalogFile if (libsToml.canRead()) { val updated = VersionsCatalogUpdater( file = libsToml, diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/extensions/gradle/Dependency.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/extensions/gradle/Dependency.kt index 8103c604e..a558a0be8 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/extensions/gradle/Dependency.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/extensions/gradle/Dependency.kt @@ -1,12 +1,14 @@ package de.fayard.refreshVersions.core.extensions.gradle import de.fayard.refreshVersions.core.ModuleId +import de.fayard.refreshVersions.core.internal.ConfigurationLessDependency import de.fayard.refreshVersions.core.internal.InternalRefreshVersionsApi import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ExternalDependency @InternalRefreshVersionsApi fun Dependency.moduleId(): ModuleId? = when { + this is ConfigurationLessDependency -> if (isNpm) npmModuleId() else mavenModuleId() this is ExternalDependency -> mavenModuleId() this::class.simpleName == "NpmDependency" -> npmModuleId() else -> null diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/ConfigurationLessDependency.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/ConfigurationLessDependency.kt index 5f77714fc..991adb0e2 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/ConfigurationLessDependency.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/ConfigurationLessDependency.kt @@ -2,13 +2,16 @@ package de.fayard.refreshVersions.core.internal import de.fayard.refreshVersions.core.ModuleId import org.gradle.api.artifacts.Dependency -import org.gradle.api.internal.artifacts.dependencies.AbstractDependency +import org.gradle.api.artifacts.ExternalDependency +import org.gradle.api.artifacts.VersionConstraint -internal class ConfigurationLessDependency( - private val group: String, +internal data class ConfigurationLessDependency( + private val group: String?, private val name: String, - private val version: String? -) : AbstractDependency() { + private val version: String?, + val versionConstraint: VersionConstraint? = null, + val isNpm: Boolean = false +) : Dependency { constructor( moduleId: ModuleId.Maven, @@ -19,6 +22,16 @@ internal class ConfigurationLessDependency( version = version ) + constructor( + otherDependency: Dependency + ) : this( + group = otherDependency.group, + name = otherDependency.name, + version = otherDependency.version, + versionConstraint = if (otherDependency is ExternalDependency) otherDependency.versionConstraint else null, + isNpm = otherDependency::class.simpleName == "NpmDependency" + ) + companion object { operator fun invoke(dependencyNotation: String): ConfigurationLessDependency { val beforeFirstColon = dependencyNotation.substringBefore(':') @@ -37,13 +50,14 @@ internal class ConfigurationLessDependency( override fun getName() = name override fun getVersion() = version - override fun contentEquals(dependency: Dependency): Boolean = throw UnsupportedOperationException() + override fun contentEquals(dependency: Dependency): Boolean = when (dependency) { + is ConfigurationLessDependency -> dependency == this + else -> dependency.contentEquals(this) + } - override fun copy(): Dependency = ConfigurationLessDependency( - group = group, - name = name, - version = version - ) + override fun copy(): Dependency = copy(group = group) + override fun getReason(): String? = null + override fun because(reason: String?) = throw IllegalAccessException("Not editable") override fun toString(): String = if (version == null) "$group:$name" else "$group:$name:$version" } diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyVersionsFetcher.Companion.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyVersionsFetcher.Companion.kt index 0a63deeb7..d9efd424d 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyVersionsFetcher.Companion.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyVersionsFetcher.Companion.kt @@ -3,6 +3,7 @@ package de.fayard.refreshVersions.core.internal import de.fayard.refreshVersions.core.DependencyVersionsFetcher import de.fayard.refreshVersions.core.ModuleId import de.fayard.refreshVersions.core.extensions.gradle.passwordCredentials +import de.fayard.refreshVersions.core.internal.dependencies.Repo import de.fayard.refreshVersions.core.internal.npm.NpmDependencyVersionsFetcherHttp import okhttp3.Credentials import okhttp3.OkHttpClient @@ -38,7 +39,41 @@ internal fun DependencyVersionsFetcher.Companion.forMaven( // https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:supported_transport_protocols // We should trigger a warning that it's not supported yet, with link to relevant issue, // and we should report any transport protocol not known to be supported by Gradle (but not crash - // in case a future Gradle version adds more of them and we're not updated yet to support it). + // in case a future Gradle version adds more of them, and we're not updated yet to support it). + } +} + +internal fun DependencyVersionsFetcher.Companion.forMaven( + httpClient: OkHttpClient, + moduleId: ModuleId.Maven?, + repository: Repo.Maven // TODO: Support Ivy repositories +): DependencyVersionsFetcher? { + moduleId ?: return null + return when (repository.url.scheme) { + "https", "http" -> MavenDependencyVersionsFetcherHttp( + httpClient = httpClient, + moduleId = moduleId, + repoUrl = repository.url.toString().let { if (it.endsWith('/')) it else "$it/" }, + repoAuthorization = repository.passwordCredentials?.let { credentials -> + Credentials.basic( + username = credentials.username, + password = credentials.password + ) + } + ) + "file" -> MavenDependencyVersionsFetcherFile( + moduleId = moduleId, + repoUrl = repository.url.toString().let { if (it.endsWith('/')) it else "$it/" } + ) + "gcs" -> MavenDependencyVersionsFetcherGoogleCloudStorage( + moduleId = moduleId, + repoUrl = repository.url.toString() + ) + else -> null //TODO: Support more transport protocols. Here's what Gradle supports: + // https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:supported_transport_protocols + // We should trigger a warning that it's not supported yet, with link to relevant issue, + // and we should report any transport protocol not known to be supported by Gradle (but not crash + // in case a future Gradle version adds more of them, and we're not updated yet to support it). } } diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyWithVersionCandidates.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyWithVersionCandidates.kt index 9f8c0843f..5b0539e7a 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyWithVersionCandidates.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/DependencyWithVersionCandidates.kt @@ -4,7 +4,7 @@ import de.fayard.refreshVersions.core.DependencyVersionsFetcher import de.fayard.refreshVersions.core.ModuleId import de.fayard.refreshVersions.core.Version -internal class DependencyWithVersionCandidates( +internal data class DependencyWithVersionCandidates( val moduleId: ModuleId, val currentVersion: String, // TODO: Ensure VersionsCatalogUpdater can have the data it needs, and remove this. val versionsCandidates: (currentVersion: Version) -> List, diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/NewRefreshVersionsImpl.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/LookupVersionCandidates.kt similarity index 91% rename from plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/NewRefreshVersionsImpl.kt rename to plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/LookupVersionCandidates.kt index af4d9e860..2dfc4e4c1 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/NewRefreshVersionsImpl.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/LookupVersionCandidates.kt @@ -7,8 +7,8 @@ import de.fayard.refreshVersions.core.ModuleId import de.fayard.refreshVersions.core.Version import de.fayard.refreshVersions.core.extensions.gradle.* import de.fayard.refreshVersions.core.extensions.gradle.hasDynamicVersion -import de.fayard.refreshVersions.core.extensions.gradle.isRootProject import de.fayard.refreshVersions.core.extensions.gradle.npmModuleId +import de.fayard.refreshVersions.core.internal.dependencies.DependenciesTracker import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -25,31 +25,22 @@ import org.gradle.util.GradleVersion internal suspend fun lookupVersionCandidates( httpClient: OkHttpClient, - project: Project, + dependenciesTracker: DependenciesTracker, versionMap: Map, versionKeyReader: ArtifactVersionKeyReader, versionsCatalogLibraries: Set, versionsCatalogPlugins: Set ): VersionCandidatesLookupResult { - require(project.isRootProject) - - val projects = RefreshVersionsConfigHolder.allProjects(project) - val dependenciesFromVersionFor = UsedVersionForTracker.read() return lookupVersionCandidates( dependencyVersionsFetchers = { dependencyFilter -> - projects.flatMap { - it.getDependencyVersionFetchers(httpClient = httpClient, dependencyFilter = dependencyFilter).toList() - }.plus( - UsedPluginsTracker.read().getDependencyVersionsFetchers(httpClient) //TODO: Is this needed? - //TODO: If so, don't we miss passing dependencies through the dependencyFilter? - ).plus( + dependenciesTracker.dependencyVersionsFetchers(httpClient, dependencyFilter).plus( dependenciesFromVersionFor.asSequence().onEach { (dependency, _) -> check(dependencyFilter(dependency)) // Needed because dependencyFilter also tracks dependencies usage. }.getDependencyVersionsFetchers(httpClient) - ).toSet() + ) }, lookupAvailableGradleVersions = { if (GRADLE_UPDATES.isEnabled) lookupAvailableGradleVersions(httpClient) else emptyList() @@ -232,23 +223,6 @@ internal fun Settings.getDependencyVersionFetchers( dependencyFilter = dependencyFilter ) -private fun Project.getDependencyVersionFetchers( - httpClient: OkHttpClient, - dependencyFilter: (Dependency) -> Boolean -): Sequence = getDependencyVersionFetchers( - httpClient = httpClient, - configurations = buildscript.configurations, - repositories = buildscript.repositories.withPluginsRepos(), - dependencyFilter = dependencyFilter -).plus( - getDependencyVersionFetchers( - httpClient = httpClient, - configurations = configurations, - repositories = repositories, - dependencyFilter = dependencyFilter - ) -) - private fun Sequence>>.getDependencyVersionsFetchers( httpClient: OkHttpClient ): Sequence { diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/RefreshVersionsConfigHolder.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/RefreshVersionsConfigHolder.kt index a3d918195..3c410421f 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/RefreshVersionsConfigHolder.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/RefreshVersionsConfigHolder.kt @@ -3,7 +3,7 @@ package de.fayard.refreshVersions.core.internal import de.fayard.refreshVersions.core.DependencySelection import de.fayard.refreshVersions.core.ModuleId import de.fayard.refreshVersions.core.extensions.gradle.isBuildSrc -import de.fayard.refreshVersions.core.extensions.gradle.isRootProject +import de.fayard.refreshVersions.core.internal.dependencies.DependenciesTracker import de.fayard.refreshVersions.core.internal.versions.VersionsPropertiesModel import de.fayard.refreshVersions.core.internal.versions.VersionsPropertiesModel.Section.VersionEntry import de.fayard.refreshVersions.core.internal.versions.readFromFile @@ -32,7 +32,7 @@ object RefreshVersionsConfigHolder { var versionsPropertiesFile: File by resettableDelegates.LateInit() private set - val buildSrc: Project? get() = buildSrcSettings?.gradle?.rootProject + internal val dependenciesTracker: DependenciesTracker by resettableDelegates.Lazy { DependenciesTracker() } /** * Not initialized when the IDE syncs the buildSrc of the project alone (without the host project) @@ -56,12 +56,6 @@ object RefreshVersionsConfigHolder { } } - fun allProjects(project: Project): Sequence { - require(project.isRootProject) - val buildSrcProjects = buildSrc?.allprojects?.asSequence() ?: emptySequence() - return buildSrcProjects + project.allprojects.asSequence() - } - internal val resultMode: VersionCandidatesResultMode = VersionCandidatesResultMode( filterMode = VersionCandidatesResultMode.FilterMode.AllIntermediateVersions, sortingMode = VersionCandidatesResultMode.SortingMode.ByVersion @@ -123,6 +117,7 @@ object RefreshVersionsConfigHolder { // requested? // Might be worth opening an issue on https://youtrack.jetbrains.com to find out. if (versionKeyReaderDelegate.isInitialized) { + dependenciesTracker.recordBuildscriptAndRegularDependencies(settings) persistInitData(settings) } else { runCatching { diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/SettingsPluginsUpdater.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/SettingsPluginsUpdater.kt index 8d70c01ba..1940877ab 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/SettingsPluginsUpdater.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/SettingsPluginsUpdater.kt @@ -1,38 +1,19 @@ package de.fayard.refreshVersions.core.internal -import de.fayard.refreshVersions.core.extensions.gradle.isBuildSrc -import de.fayard.refreshVersions.core.extensions.gradle.isRootProject import de.fayard.refreshVersions.core.extensions.text.indexOfPrevious import de.fayard.refreshVersions.core.internal.codeparsing.gradle.extractGradleScriptSections import de.fayard.refreshVersions.core.internal.codeparsing.gradle.findPluginBlocksRanges import de.fayard.refreshVersions.core.internal.versions.VersionsPropertiesModel.Companion.availableComment -import org.gradle.api.Project import java.io.File internal object SettingsPluginsUpdater { fun updateGradleSettingsWithAvailablePluginsUpdates( - rootProject: Project, + rootProjectSettingsFile: File, + buildSrcSettingsFile: File?, settingsPluginsUpdates: List, buildSrcSettingsPluginsUpdates: List ) { - require(rootProject.isRootProject) - require(rootProject.isBuildSrc.not()) - - val rootProjectSettingsFile = rootProject.file("settings.gradle.kts").let { kotlinDslSettings -> - if (kotlinDslSettings.exists()) kotlinDslSettings else { - rootProject.file("settings.gradle").also { - check(it.exists()) - } - } - } - val buildSrcSettingsFile = rootProject.file("buildSrc/settings.gradle.kts").let { kotlinDslSettings -> - if (kotlinDslSettings.exists()) kotlinDslSettings else { - rootProject.file("buildSrc/settings.gradle").takeIf { - it.exists() - } - } - } updateGradleSettingsWithAvailablePluginsUpdates( settingsFile = rootProjectSettingsFile, settingsPluginsUpdates = settingsPluginsUpdates @@ -49,6 +30,7 @@ internal object SettingsPluginsUpdater { settingsFile: File, settingsPluginsUpdates: List ) { + require(settingsFile.name == "settings.gradle" || settingsFile.name == "settings.gradle.kts") val oldContent = settingsFile.readText() val isKotlinDsl = settingsFile.name.endsWith(".kts") val newContent = updatedGradleSettingsFileContentWithAvailablePluginsUpdates( diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedPluginsTracker.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedPluginsTracker.kt index bd0be341e..47f6a3135 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedPluginsTracker.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedPluginsTracker.kt @@ -10,6 +10,8 @@ import org.gradle.api.initialization.Settings @InternalRefreshVersionsApi object UsedPluginsTracker { + //TODO: Remove this as it's redundant with buildscript dependencies. + fun clearFor(settings: Settings) { if (settings.isBuildSrc) buildSrcHolder = null else projectHolder = null } diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedVersionForTracker.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedVersionForTracker.kt index 7a5cb8495..d252f2cad 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedVersionForTracker.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/UsedVersionForTracker.kt @@ -9,6 +9,8 @@ import org.gradle.api.artifacts.Dependency import org.gradle.api.initialization.Settings internal object UsedVersionForTracker { + //TODO: Attempt to move into DependenciesTracker and remove references to Project, + // and to ArtifactRepositoryContainer, using our own Repo class instead. fun clearFor(settings: Settings) { if (settings.isBuildSrc) buildSrcHolder = null else projectHolder = null diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/VersionManagementKind.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/VersionManagementKind.kt index f4d99fc90..3aa2f572d 100644 --- a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/VersionManagementKind.kt +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/VersionManagementKind.kt @@ -89,7 +89,8 @@ private fun Dependency.hasVersionInVersionCatalog( versionsCatalogLibraries: Collection, versionsCatalogPlugins: Set = emptySet() ): Boolean { - if (this !is ExternalDependency) return false + if (this !is ConfigurationLessDependency) return false + if (versionConstraint == null) return false val matchingLib = versionsCatalogLibraries.any { it.module.group == group && it.module.name == name && it.versionConstraint == versionConstraint diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/DependenciesTracker.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/DependenciesTracker.kt new file mode 100644 index 000000000..fb58dfb0a --- /dev/null +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/DependenciesTracker.kt @@ -0,0 +1,104 @@ +package de.fayard.refreshVersions.core.internal.dependencies + +import de.fayard.refreshVersions.core.DependencyVersionsFetcher +import de.fayard.refreshVersions.core.extensions.gradle.isBuildSrc +import de.fayard.refreshVersions.core.extensions.gradle.mavenModuleId +import de.fayard.refreshVersions.core.extensions.gradle.npmModuleId +import de.fayard.refreshVersions.core.internal.ConfigurationLessDependency +import de.fayard.refreshVersions.core.internal.forMaven +import de.fayard.refreshVersions.core.internal.forNpm +import de.fayard.refreshVersions.core.internal.withGlobalRepos +import de.fayard.refreshVersions.core.internal.withPluginsRepos +import okhttp3.OkHttpClient +import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.repositories.ArtifactRepository +import org.gradle.api.artifacts.repositories.MavenArtifactRepository +import org.gradle.api.initialization.Settings + +internal class DependenciesTracker { + + fun recordBuildscriptAndRegularDependencies(buildSrc: Settings) { + val npmRegistries = listOf("https://registry.npmjs.org/") + buildSrc.gradle.afterProject { + recordBuildscriptAndRegularDependencies(npmRegistries) + } + } + + fun recordBuildscriptAndRegularDependencies(rootProject: Project) { + require(rootProject.isBuildSrc.not()) + val npmRegistries = listOf("https://registry.npmjs.org/") + //TODO: Support custom npm registries. + // See https://kotlinlang.org/docs/js-project-setup.html#additional-yarn-features-yarnrc + // and https://yarnpkg.com/getting-started/migration#update-your-configuration-to-the-new-settings + // and also https://yarnpkg.com/configuration/yarnrc#npmRegistryServer + rootProject.allprojects.forEach { it.recordBuildscriptAndRegularDependencies(npmRegistries) } + } + + private fun Project.recordBuildscriptAndRegularDependencies(npmRegistries: List) { + //TODO: For buildSrc, filter out: + // - org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin + // - org.jetbrains.kotlin:kotlin-reflect + // - org.jetbrains.kotlin:kotlin-stdlib-jdk8 + //TODO: Filter out some configurations like: + // - migration-env (SqlDelight) + // - compiler-env (SqlDelight) + // - sqlite-3-18-dialect and alike (SqlDelight) + val extracted = extractDependencies( + configurations = buildscript.configurations, + repositories = buildscript.repositories.withPluginsRepos(), + npmRegistries = npmRegistries + ) + extractDependencies( + configurations = configurations, + repositories = repositories, + npmRegistries = npmRegistries + ) + extracted.forEach { (dependency, repo) -> + dependenciesWithRepos.getOrPut(dependency) { mutableSetOf() }.addAll(repo) + } + } + + fun dependencyVersionsFetchers( + httpClient: OkHttpClient, + dependencyFilter: (Dependency) -> Boolean + ): Set = dependenciesWithRepos.flatMapTo(mutableSetOf()) { (dependency, repos) -> + if (dependencyFilter(dependency).not()) return@flatMapTo emptySet() + repos.mapNotNullTo(mutableSetOf()) { repo -> + when (repo) { + is Repo.Maven -> DependencyVersionsFetcher.forMaven( + httpClient = httpClient, + moduleId = dependency.mavenModuleId(), + repository = repo + ) + + is Repo.Npm -> DependencyVersionsFetcher.forNpm( + httpClient = httpClient, + moduleId = dependency.npmModuleId(), + npmRegistry = repo.npmRegistry + ) + } + } + } + + private val dependenciesWithRepos = mutableMapOf>() + + private fun extractDependencies( + configurations: ConfigurationContainer, + repositories: List, + npmRegistries: List, + ): Sequence>> { + val mavenRepositories = repositories.withGlobalRepos().filterIsInstance() + return configurations.asSequence().flatMap { + it.dependencies.asSequence() + }.mapNotNull { rawDependency -> + if (rawDependency is ProjectDependency) return@mapNotNull null + val dependency = ConfigurationLessDependency(rawDependency) + dependency to when { + dependency.isNpm -> npmRegistries.map { Repo.Npm(it) } + else -> mavenRepositories.map { Repo.Maven(it) } + } + } + } +} diff --git a/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/Repo.kt b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/Repo.kt new file mode 100644 index 000000000..384b343ed --- /dev/null +++ b/plugins/core/src/main/kotlin/de/fayard/refreshVersions/core/internal/dependencies/Repo.kt @@ -0,0 +1,31 @@ +package de.fayard.refreshVersions.core.internal.dependencies + +import de.fayard.refreshVersions.core.extensions.gradle.passwordCredentials +import org.gradle.api.artifacts.repositories.MavenArtifactRepository +import java.net.URI + +internal sealed class Repo { + data class Maven( + val url: URI, + val passwordCredentials: PasswordCredentials? + ) : Repo() { + + constructor(mavenArtifactRepository: MavenArtifactRepository) : this( + url = mavenArtifactRepository.url, + passwordCredentials = mavenArtifactRepository.passwordCredentials?.let { + PasswordCredentials( + username = it.username ?: return@let null, + password = it.password ?: return@let null + ) + } + ) + + data class PasswordCredentials( + val username: String, + val password: String + ) + } + data class Npm( + val npmRegistry: String + ) : Repo() +}