Skip to content

Commit

Permalink
Remap plugin problems when resolving plugin dependencies (#1124)
Browse files Browse the repository at this point in the history
* Use problem level remapper when creating modules
* Extract plugin creation to separate overridable method
* Provide PluginInfo when constructing plugins
* Make IDE Plugin Manager accessible in subclasses to customize creation
* Introduce plugin dependency repository and a model class
* Resolve dependencies while ignoring creation problems
* Use original repository when tracing failed dependencies
* Introduce interface that provides an IdePlugin
* Simplify IdePlugin-based details
  • Loading branch information
novotnyr authored Aug 5, 2024
1 parent 1ad50e3 commit e47fb0f
Show file tree
Hide file tree
Showing 16 changed files with 419 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class ProductInfoBasedIdeManager : IdeManager() {
): PluginWithArtifactPathResult {
return IdePluginManager
.createManager(pathResolver)
.createBundledModule(pluginArtifactPath, ideVersion, descriptorName)
.createBundledModule(pluginArtifactPath, ideVersion, descriptorName, bundledPluginCreationResultResolver)
.withPath(pluginArtifactPath)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -794,9 +794,11 @@ internal class PluginCreator private constructor(
return false
}
val invalidPlugin = newInvalidPlugin(bean, document)
return problemResolver.classify(invalidPlugin, problems).any {
it.level == ERROR
}
return problemResolver
.classify(invalidPlugin, problems)
.filter {
it.level == ERROR
}.notEmpty(context = bean)
}

private fun validateId(plugin: PluginBean) {
Expand Down Expand Up @@ -950,6 +952,19 @@ internal class PluginCreator private constructor(

private val PluginCreationSuccess<IdePlugin>.problems: List<PluginProblem>
get() = warnings + unacceptableWarnings

private fun List<PluginProblem>.notEmpty(context: PluginBean): Boolean {
return if (isEmpty()) {
false
} else {
if (LOG.isDebugEnabled) {
val errorMsg = joinToString()
LOG.debug("Plugin '${context.id}' has $size error(s): $errorMsg")
}
true
}
}

}

private fun PluginCreationResult<IdePlugin>.add(telemetry: PluginTelemetry): PluginCreationResult<IdePlugin> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ package com.jetbrains.pluginverifier.dependencies.resolution
import com.jetbrains.pluginverifier.misc.retry
import com.jetbrains.pluginverifier.plugin.PluginDetailsCache
import com.jetbrains.pluginverifier.repository.PluginRepository
import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginRepository

/**
* [DependencyFinder] that searches for the dependency in the [PluginRepository].
* The [pluginVersionSelector] is used to select a specific version of the plugin
* if multiple versions are available.
*/
class RepositoryDependencyFinder(
private val pluginRepository: PluginRepository,
pluginRepository: PluginRepository,
private val pluginVersionSelector: PluginVersionSelector,
private val pluginDetailsCache: PluginDetailsCache
) : DependencyFinder {

private val pluginRepository = DependencyPluginRepository(pluginRepository)

override val presentableName
get() = pluginRepository.toString()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import java.nio.file.Path
* uses the [extractDirectory] for extracting `.zip`-ped plugins.
*/
abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path) : PluginDetailsProvider {
private val idePluginManager = IdePluginManager.createManager(extractDirectory)
protected val idePluginManager = IdePluginManager.createManager(extractDirectory)

private val IdePlugin.problems: List<PluginProblem>
get() = if (this is StructurallyValidated) this.problems else emptyList()
Expand All @@ -33,7 +33,7 @@ abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path)

override fun providePluginDetails(pluginInfo: PluginInfo, pluginFileLock: FileLock) =
pluginFileLock.closeOnException {
with(idePluginManager.createPlugin(pluginFileLock.file)) {
with(createPlugin(pluginInfo, pluginFileLock)) {
when (this) {
is PluginCreationSuccess<IdePlugin> -> {
readPluginClasses(
Expand Down Expand Up @@ -84,4 +84,7 @@ abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path)
PluginDetailsProvider.Result.InvalidPlugin(pluginInfo, listOf(UnableToReadPluginFile(message)))
}
}

protected open fun createPlugin(pluginInfo: PluginInfo, pluginFileLock: FileLock) =
idePluginManager.createPlugin(pluginFileLock.file)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.jetbrains.pluginverifier.plugin

import com.jetbrains.plugin.structure.base.plugin.PluginCreationResult
import com.jetbrains.plugin.structure.intellij.classes.locator.CompileServerExtensionKey
import com.jetbrains.plugin.structure.intellij.classes.plugin.BundledPluginClassesFinder
import com.jetbrains.plugin.structure.intellij.classes.plugin.IdePluginClassesLocations
import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin
import com.jetbrains.plugin.structure.intellij.problems.IntelliJPluginCreationResultResolver
import com.jetbrains.plugin.structure.intellij.problems.JetBrainsPluginCreationResultResolver
import com.jetbrains.plugin.structure.intellij.problems.PluginCreationResultResolver
import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.files.FileLock
import com.jetbrains.pluginverifier.repository.repositories.bundled.BundledPluginInfo
import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginInfo
import java.nio.file.Path

/**
Expand All @@ -16,11 +22,22 @@ import java.nio.file.Path
class DefaultPluginDetailsProvider(extractDirectory: Path) : AbstractPluginDetailsProvider(extractDirectory) {
private val nonBundledPluginDetailsProvider: PluginDetailsProviderImpl = PluginDetailsProviderImpl(extractDirectory)

private val dependencyProblemResolver: PluginCreationResultResolver =
JetBrainsPluginCreationResultResolver.fromClassPathJson(IntelliJPluginCreationResultResolver())

override fun readPluginClasses(pluginInfo: PluginInfo, idePlugin: IdePlugin): IdePluginClassesLocations {
return if (pluginInfo is BundledPluginInfo) {
BundledPluginClassesFinder.findPluginClasses(idePlugin, additionalKeys = listOf(CompileServerExtensionKey))
} else {
nonBundledPluginDetailsProvider.readPluginClasses(pluginInfo, idePlugin)
}
}

override fun createPlugin(pluginInfo: PluginInfo, pluginFileLock: FileLock): PluginCreationResult<IdePlugin> {
return if (pluginInfo is DependencyPluginInfo) {
idePluginManager.createPlugin(pluginFileLock.file, validateDescriptor = false, problemResolver = dependencyProblemResolver)
} else {
super.createPlugin(pluginInfo, pluginFileLock)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
package com.jetbrains.pluginverifier.plugin

import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.WithIdePlugin
import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntry
import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntryResult
import com.jetbrains.pluginverifier.repository.cache.createSizeLimitedResourceCache
import com.jetbrains.pluginverifier.repository.cleanup.SizeWeight
import com.jetbrains.pluginverifier.repository.provider.ProvideResult
import com.jetbrains.pluginverifier.repository.provider.ResourceProvider
import com.jetbrains.pluginverifier.repository.repositories.bundled.BundledPluginInfo
import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginInfo
import com.jetbrains.pluginverifier.repository.repositories.local.LocalPluginInfo

/**
Expand All @@ -23,8 +25,8 @@ import com.jetbrains.pluginverifier.repository.repositories.local.LocalPluginInf
*/
class SizeLimitedPluginDetailsCache(
cacheSize: Int,
val pluginFileProvider: PluginFileProvider,
val pluginDetailsProvider: PluginDetailsProvider
pluginFileProvider: PluginFileProvider,
pluginDetailsProvider: PluginDetailsProvider
) : PluginDetailsCache {

private val internalCache = createSizeLimitedResourceCache(
Expand Down Expand Up @@ -75,19 +77,42 @@ private class PluginDetailsResourceProvider(
override fun provide(key: PluginInfo) = when (key) {
is LocalPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin))
is BundledPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin))
is DependencyPluginInfo -> provideDependencyDetails(key)
else -> provideFileAndDetails(key)
}

private fun provideFileAndDetails(pluginInfo: PluginInfo): ProvideResult<PluginDetailsProvider.Result> {
return with(pluginFileProvider.getPluginFile(pluginInfo)) {
when (this) {
is PluginFileProvider.Result.Found -> {
val pluginDetailsResult = pluginDetailsProvider.providePluginDetails(pluginInfo, pluginFileLock)
ProvideResult.Provided(pluginDetailsResult)
}
is PluginFileProvider.Result.NotFound -> ProvideResult.NotFound(reason)
is PluginFileProvider.Result.Failed -> ProvideResult.Failed(reason, error)
return pluginFileProvider
.getPluginFile(pluginInfo)
.provideDetails(pluginInfo)
}

private fun provideDependencyDetails(dependency: DependencyPluginInfo): ProvideResult<PluginDetailsProvider.Result> {
val unwrappedPluginInfo = dependency.pluginInfo
val unwrappedPlugin = (unwrappedPluginInfo as? WithIdePlugin)?.idePlugin
return if (unwrappedPlugin != null) {
pluginDetailsProvider.providePluginDetails(unwrappedPluginInfo, unwrappedPlugin).provided
} else {
pluginFileProvider
.getPluginFile(unwrappedPluginInfo)
.provideDetails(dependency)
}
}

private fun PluginFileProvider.Result.provideDetails(pluginInfo: PluginInfo): ProvideResult<PluginDetailsProvider.Result> {
return when (this) {
is PluginFileProvider.Result.Found -> {
pluginDetailsProvider
.providePluginDetails(pluginInfo, pluginFileLock)
.provided
}

is PluginFileProvider.Result.NotFound -> ProvideResult.NotFound(reason)
is PluginFileProvider.Result.Failed -> ProvideResult.Failed(reason, error)
}
}
}

private val PluginDetailsProvider.Result.provided
get() = ProvideResult.Provided(this)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/

package com.jetbrains.pluginverifier.repository

import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin

/**
* Indicates a [plugin info][PluginInfo] that contains a resolved [IDE Plugin][IdePlugin].
*/
interface WithIdePlugin {
val idePlugin: IdePlugin
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package com.jetbrains.pluginverifier.repository.repositories.bundled
import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin
import com.jetbrains.plugin.structure.intellij.version.IdeVersion
import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.WithIdePlugin
import java.io.ObjectInputStream
import java.util.*

Expand All @@ -15,15 +16,15 @@ import java.util.*
*/
class BundledPluginInfo(
val ideVersion: IdeVersion,
val idePlugin: IdePlugin
override val idePlugin: IdePlugin
) : PluginInfo(
idePlugin.pluginId!!,
idePlugin.pluginName ?: idePlugin.pluginId!!,
idePlugin.pluginVersion ?: ideVersion.asString(),
idePlugin.sinceBuild,
idePlugin.untilBuild,
idePlugin.vendor
) {
), WithIdePlugin {

private fun writeReplace(): Any = throw UnsupportedOperationException("Bundled plugins cannot be serialized")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.jetbrains.pluginverifier.repository.repositories.dependency

import com.jetbrains.pluginverifier.repository.PluginInfo

/**
* Plugin information that is resolved as a plugin dependency.
*/
class DependencyPluginInfo(val pluginInfo: PluginInfo) : PluginInfo(
pluginInfo.pluginId,
pluginInfo.pluginName,
pluginInfo.version,
pluginInfo.sinceBuild,
pluginInfo.untilBuild,
pluginInfo.vendor
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/

package com.jetbrains.pluginverifier.repository.repositories.dependency

import com.jetbrains.plugin.structure.intellij.version.IdeVersion
import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.PluginRepository

/**
* Repository wrapping another repository while mapping plugins into [dependencies][DependencyPluginInfo].
*/
class DependencyPluginRepository(private val delegateRepository: PluginRepository) : PluginRepository {
override val presentableName: String = "${delegateRepository.presentableName} (used for plugin dependencies)"

override fun getLastCompatiblePlugins(ideVersion: IdeVersion): List<PluginInfo> =
delegateRepository.getLastCompatiblePlugins(ideVersion).asDependencies()

override fun getLastCompatibleVersionOfPlugin(ideVersion: IdeVersion, pluginId: String): PluginInfo? =
delegateRepository.getLastCompatibleVersionOfPlugin(ideVersion, pluginId).asDependency()

override fun getAllVersionsOfPlugin(pluginId: String): List<PluginInfo> =
delegateRepository.getAllVersionsOfPlugin(pluginId).asDependencies()

override fun getPluginsDeclaringModule(moduleId: String, ideVersion: IdeVersion?): List<PluginInfo> =
delegateRepository.getPluginsDeclaringModule(moduleId, ideVersion).asDependencies()

private fun List<PluginInfo>.asDependencies(): List<DependencyPluginInfo> = map { DependencyPluginInfo(it) }

private fun PluginInfo?.asDependency(): PluginInfo? = this?.let { DependencyPluginInfo(it) }

}


Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package com.jetbrains.pluginverifier.repository.repositories.local

import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin
import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.WithIdePlugin
import java.io.ObjectInputStream

/**
Expand All @@ -14,15 +15,15 @@ import java.io.ObjectInputStream
* @see [LocalPluginRepository]
*/
class LocalPluginInfo(
val idePlugin: IdePlugin
override val idePlugin: IdePlugin
) : PluginInfo(
idePlugin.pluginId!!,
idePlugin.pluginName ?: idePlugin.pluginId!!,
idePlugin.pluginVersion!!,
idePlugin.sinceBuild,
idePlugin.untilBuild,
idePlugin.vendor
) {
), WithIdePlugin {

val definedModules: Set<String>
get() = idePlugin.definedModules
Expand Down
Loading

0 comments on commit e47fb0f

Please sign in to comment.