[plugins] IJPL-247121 Incorrect suggestion to update JetBrains OS Integration/Web Browser (JCEF) plugins

(cherry picked from commit 838352db684782a99e8fb4a33e2a82705a180df6)

IJ-CR-211621

GitOrigin-RevId: 166b8991db1b3b67ff030eff6a5a2727c1df1a88
This commit is contained in:
Yuriy Artamonov
2026-07-08 20:09:33 +00:00
committed by intellij-monorepo-bot
parent b4169f6b61
commit e3db588246
5 changed files with 93 additions and 0 deletions
@@ -55,6 +55,7 @@ com.intellij.ide.plugins.DynamicPluginListener
- s:values():com.intellij.ide.plugins.IdeaPluginOsRequirement[]
*f:com.intellij.ide.plugins.IdeaPluginOsRequirement$Companion
- f:fromModuleId(com.intellij.openapi.extensions.PluginId):com.intellij.ide.plugins.IdeaPluginOsRequirement
- f:fromOs(com.intellij.util.system.OS):com.intellij.ide.plugins.IdeaPluginOsRequirement
- f:getHostOsModuleIds():java.util.List
*a:com.intellij.lang.LanguageMatcher
- s:match(com.intellij.lang.Language):com.intellij.lang.LanguageMatcher
@@ -48,6 +48,14 @@ enum class IdeaPluginOsRequirement {
fun fromModuleId(moduleId: PluginId): IdeaPluginOsRequirement? =
directory[moduleId] ?: Unknown.takeIf { looksLikeOsModuleId(moduleId.idString) }
fun fromOs(os: OS): IdeaPluginOsRequirement? = when (os) {
OS.Windows -> Windows
OS.macOS -> Mac
OS.Linux -> Linux
OS.FreeBSD -> FreeBSD
OS.Other -> null
}
private fun looksLikeOsModuleId(idString: String): Boolean = idString.startsWith(osModuleIdPrefix)
}
}
@@ -44,6 +44,10 @@ enum class PluginCpuArchRequirement {
return directory[pluginId] ?: Unknown.takeIf { looksLikeCpuArchModuleId(pluginId.idString) }
}
fun fromArch(arch: CpuArch): PluginCpuArchRequirement? {
return fromPluginId(PluginId.getId(archPluginAliasIdPrefix + arch.name.lowercase()))
}
private fun looksLikeCpuArchModuleId(idString: String): Boolean = idString.startsWith(archPluginAliasIdPrefix)
}
}
@@ -48,6 +48,8 @@ private const val PLATFORM_ALIAS_DEPENDENCY_PREFIX = "com.intellij.module"
internal val QODANA_PLUGINS_THIRD_PARTY_ACCEPT = System.getProperty("idea.qodana.thirdpartyplugins.accept").toBoolean()
internal val FLEET_BACKEND_PLUGINS_THIRD_PARTY_ACCEPT = System.getProperty("fleet.backend.third-party.plugins.accept").toBoolean()
private val LOG = Logger.getInstance(PluginManagerCore::class.java)
/**
* See [Plugin Model](https://youtrack.jetbrains.com/articles/IJPL-A-31/Plugin-Model) documentation.
*
@@ -454,8 +456,23 @@ object PluginManagerCore {
fun isIncompatible(descriptor: IdeaPluginDescriptor, buildNumber: BuildNumber?): Boolean =
checkBuildNumberCompatibility(descriptor, buildNumber ?: PluginManagerCore.buildNumber) != null
private val OS_ARCH_DEPENDENCY_VERSION: Regex = Regex("([\\w.]+)-(\\w+)-(\\w+)")
@ApiStatus.Internal
fun getUnfulfilledOsRequirement(descriptor: IdeaPluginDescriptor): IdeaPluginOsRequirement? {
if (descriptor.dependencies.isEmpty()) {
// try to infer Arch requirement from version, some plugin repositories do not provide dependencies
val matchedVersion = descriptor.version?.let { OS_ARCH_DEPENDENCY_VERSION.matchEntire(it) }
val osTag = matchedVersion?.groupValues[2] ?: return null
LOG.debug("Inferred OS for version: ${descriptor.version} of ${descriptor.pluginId} is $osTag")
return OS.fromString(osTag)
.takeIf { it != OS.Other }
?.let { IdeaPluginOsRequirement.fromOs(it) }
?.takeIf { osReq -> !osReq.isHostOs() }
}
return descriptor.getDependencies().asSequence()
.mapNotNull { dep -> IdeaPluginOsRequirement.fromModuleId(dep.pluginId).takeIf { !dep.isOptional } }
.firstOrNull { osReq -> !osReq.isHostOs() }
@@ -463,6 +480,19 @@ object PluginManagerCore {
@ApiStatus.Internal
fun getUnfulfilledCpuArchRequirement(descriptor: IdeaPluginDescriptor): PluginCpuArchRequirement? {
if (descriptor.dependencies.isEmpty()) {
// try to infer Arch requirement from version, some plugin repositories do not provide dependencies
val matchedVersion = descriptor.version?.let { OS_ARCH_DEPENDENCY_VERSION.matchEntire(it) }
val archTag = matchedVersion?.groupValues[3] ?: return null
LOG.debug("Inferred arch for version: ${descriptor.version} of ${descriptor.pluginId} is $archTag")
return CpuArch.fromString(archTag)
.takeIf { it != CpuArch.OTHER && it != CpuArch.UNKNOWN }
?.let { PluginCpuArchRequirement.fromArch(it) }
?.takeIf { osReq -> !osReq.isHostArch() }
}
return descriptor.getDependencies().asSequence()
.mapNotNull { dep -> PluginCpuArchRequirement.fromPluginId(dep.pluginId).takeIf { !dep.isOptional } }
.firstOrNull { osReq -> !osReq.isHostArch() }
@@ -221,6 +221,56 @@ class PluginManagerTest {
}
}
@Test
fun `unfulfilled os requirement is inferred from version when dependencies are empty`() {
fun descriptor(version: String?) = object : TestIdeaPluginDescriptor() {
override fun getDependencies(): List<IdeaPluginDependency> = emptyList()
override fun getVersion(): String? = version
override fun getPluginId(): PluginId = PluginId.getId("test.plugin")
}
fun assertInferred(version: String?, expected: IdeaPluginOsRequirement?) {
assertThat(PluginManagerCore.getUnfulfilledOsRequirement(descriptor(version)))
.isEqualTo(expected?.takeIf { !it.isHostOs() })
}
assertInferred("1.0.0-windows-amd64", IdeaPluginOsRequirement.Windows)
assertInferred("1.0.0-mac-arm64", IdeaPluginOsRequirement.Mac)
assertInferred("1.0.0-linux-amd64", IdeaPluginOsRequirement.Linux)
assertInferred("1.0.0-freebsd-amd64", IdeaPluginOsRequirement.FreeBSD)
// unrecognized OS tag maps to OS.Other and is filtered out
assertInferred("1.0.0-solaris-amd64", null)
// versions that do not match the <version>-<os>-<arch> pattern infer nothing
assertInferred("1.0.0", null)
assertInferred("241.SNAPSHOT", null)
// a missing version must not throw and infers nothing
assertInferred(null, null)
}
@Test
fun `unfulfilled cpu arch requirement is inferred from version when dependencies are empty`() {
fun descriptor(version: String?) = object : TestIdeaPluginDescriptor() {
override fun getDependencies(): List<IdeaPluginDependency> = emptyList()
override fun getVersion(): String? = version
override fun getPluginId(): PluginId = PluginId.getId("test.plugin")
}
fun assertInferred(version: String?, expected: PluginCpuArchRequirement?) {
assertThat(PluginManagerCore.getUnfulfilledCpuArchRequirement(descriptor(version)))
.isEqualTo(expected?.takeIf { !it.isHostArch() })
}
assertInferred("1.0.0-windows-amd64", PluginCpuArchRequirement.X86_64)
assertInferred("1.0.0-windows-x86_64", PluginCpuArchRequirement.X86_64)
assertInferred("1.0.0-windows-x86", PluginCpuArchRequirement.X86)
assertInferred("1.0.0-windows-arm64", PluginCpuArchRequirement.ARM64)
assertInferred("1.0.0-windows-aarch64", PluginCpuArchRequirement.ARM64)
// unrecognized arch tag maps to CpuArch.OTHER/UNKNOWN and is filtered out
assertInferred("1.0.0-windows-sparc", null)
// versions that do not match the <version>-<os>-<arch> pattern infer nothing
assertInferred("1.0.0", null)
// a missing version must not throw and infers nothing
assertInferred(null, null)
}
companion object {
private val testDataPath: String
get() = PlatformTestUtil.getPlatformTestDataPath() + "plugins/sort"