Prototype verify extension and apply it for Kotlin updater in AS 3.2

This commit is contained in:
Nikolay Krasko
2018-06-07 15:43:16 +03:00
parent 871dacbf6c
commit 34111e8944
13 changed files with 273 additions and 1 deletions
+3
View File
@@ -2946,6 +2946,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+3
View File
@@ -2946,6 +2946,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+3
View File
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+3
View File
@@ -2945,6 +2945,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+3
View File
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+5
View File
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
@@ -2971,6 +2974,8 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<binaryExtension implementation="org.jetbrains.kotlin.idea.util.KotlinJsMetaBinary"/>
<highlighterExtension implementation="org.jetbrains.kotlin.idea.highlighter.dsl.DslHighlighterExtension"/>
<pluginUpdateVerifier implementation="org.jetbrains.kotlin.idea.update.GooglePluginUpdateVerifier"/>
</extensions>
</idea-plugin>
@@ -27,6 +27,8 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.Alarm
import com.intellij.util.io.HttpRequests
import com.intellij.util.text.VersionComparatorUtil
import org.jetbrains.kotlin.idea.update.PluginUpdateVerifier
import org.jetbrains.kotlin.idea.update.verify
import java.io.File
import java.io.IOException
import java.io.PrintWriter
@@ -46,6 +48,8 @@ sealed class PluginUpdateStatus {
class CheckFailed(val message: String, val detail: String? = null) : PluginUpdateStatus()
class Unverified(val message: String, val updateStatus: Update) : PluginUpdateStatus()
fun mergeWith(other: PluginUpdateStatus): PluginUpdateStatus {
if (other is Update) {
when (this) {
@@ -57,7 +61,7 @@ sealed class PluginUpdateStatus {
return other
}
}
is PluginUpdateStatus.CheckFailed -> {
is CheckFailed, is Unverified -> {
// proceed to return this
}
}
@@ -150,9 +154,14 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos
lastUpdateStatus = updateStatus
checkQueued = false
if (updateStatus is PluginUpdateStatus.Update) {
updateStatus = verify(updateStatus)
}
if (updateStatus !is PluginUpdateStatus.CheckFailed) {
recordSuccessfulUpdateCheck()
}
ApplicationManager.getApplication().invokeLater({
callback(updateStatus)
}, ModalityState.any())
@@ -156,6 +156,11 @@ class KotlinUpdatesSettingsConfigurable : SearchableConfigurable, Configurable.N
is PluginUpdateStatus.CheckFailed ->
form.setUpdateStatus("Update check failed: ${pluginUpdateStatus.message}", false)
is PluginUpdateStatus.Unverified -> {
val version = pluginUpdateStatus.updateStatus.pluginDescriptor.version
form.setUpdateStatus("A new version $version is found. ${pluginUpdateStatus.message}", false)
}
}
false // do not auto-retry update check
@@ -0,0 +1,147 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.update
import com.intellij.ide.plugins.IdeaPluginDescriptor
import org.intellij.lang.annotations.Language
import java.util.*
import javax.xml.bind.JAXBContext
import javax.xml.bind.annotation.*
import kotlin.collections.HashSet
class GooglePluginUpdateVerifier : PluginUpdateVerifier() {
override fun verify(pluginDescriptor: IdeaPluginDescriptor): PluginVerifyResult? {
if (pluginDescriptor.pluginId.idString != "org.jetbrains.kotlin") {
return null
}
// val approvedKotlinVersions = fetchApprovedKotlinVersions()
// if (pluginDescriptor.version !in approvedKotlinVersions) {
// // TODO: Modify message
// return PluginVerifyResult.decline("Plugin is waiting for Google approve.")
// }
return PluginVerifyResult.accept()
}
private fun fetchApprovedKotlinVersions(): Set<String> {
// TODO: Choose the host and do an actual request
// TODO: Fetching the full list each time might be time-consuming
// TODO: Process parsing and fetching errors
// TODO: Emulate long operation to check progress indicators. Remove after actual implementation is ready.
Thread.sleep(200)
@Language("XML")
val answer = """
<plugin-compatibility>
<studio-release name="3.2 Canary" channel="" since-build="181.1" until-build="181.*">
<idea-plugin id="org.jetbrains.kotlin" version="1.2.41-release-Studio3.2c12-3" channel="stable" sha256="">
<idea-version since-build="181.3007.1" until-build="181.*"/>
</idea-plugin>
<idea-plugin id="org.jetbrains.kotlin" version="1.2.50-eap-17-Studio3.2-1" channel="eap-1.2" sha256="">
<idea-version since-build="181.3007.1" until-build="181.*"/>
</idea-plugin>
</studio-release>
<studio-release name="3.1.2" channel="" since-build="173.4" until-build="173.*">
<idea-plugin id="org.jetbrains.kotlin" version="1.2.41-release-Studio3.1-1" channel="stable" sha256="">
<idea-version since-build="173.1" until-build="173.4301.25"/>
</idea-plugin>
</studio-release>
</plugin-compatibility>
""".trimIndent()
val context = JAXBContext.newInstance(PluginCompatibility::class.java)
val unmarshaller = context.createUnmarshaller()
val pluginCompatibility = unmarshaller.unmarshal(answer.byteInputStream()) as PluginCompatibility
val approvedKotlinVersions = HashSet<String>()
for (studioRelease in pluginCompatibility.releases()) {
for (ideaPlugin in studioRelease.plugins()) {
if (ideaPlugin.id == KOTLIN_PLUGIN_ID) {
val version = ideaPlugin.version
if (version != null) {
approvedKotlinVersions.add(version)
}
}
}
}
return approvedKotlinVersions
}
companion object {
private const val KOTLIN_PLUGIN_ID = "org.jetbrains.kotlin"
private fun PluginCompatibility.releases() = studioRelease ?: emptyArray()
private fun StudioRelease.plugins() = ideaPlugin ?: emptyArray()
@XmlRootElement(name = "plugin-compatibility")
@XmlAccessorType(XmlAccessType.FIELD)
class PluginCompatibility {
@XmlElement(name = "studio-release")
var studioRelease: Array<StudioRelease>? = null
override fun toString(): String {
return "PluginCompatibility(studioRelease=${Arrays.toString(studioRelease)})"
}
}
@XmlAccessorType(XmlAccessType.FIELD)
class StudioRelease {
@XmlAttribute(name = "until-build")
var untilBuild: String? = null
@XmlAttribute(name = "since-build")
var sinceBuild: String? = null
@XmlAttribute
var name: String? = null
@XmlAttribute
var channel: String? = null
@XmlElement(name = "idea-plugin")
var ideaPlugin: Array<IdeaPlugin>? = null
override fun toString(): String {
return "StudioRelease(" +
"untilBuild=$untilBuild, name=$name, ideaPlugin=${Arrays.toString(ideaPlugin)}, " +
"sinceBuild=$sinceBuild, channel=$channel" +
")"
}
}
@XmlAccessorType(XmlAccessType.FIELD)
class IdeaPlugin {
@XmlAttribute
var id: String? = null
@XmlAttribute
var sha256: String? = null
@XmlAttribute
var channel: String? = null
@XmlAttribute
var version: String? = null
@XmlElement(name = "idea-version")
var ideaVersion: IdeaVersion? = null
override fun toString(): String {
return "IdeaPlugin(id=$id, sha256=$sha256, ideaVersion=$ideaVersion, channel=$channel, version=$version)"
}
}
@XmlAccessorType(XmlAccessType.FIELD)
class IdeaVersion {
@XmlAttribute(name = "until-build")
var untilBuild: String? = null
@XmlAttribute(name = "since-build")
var sinceBuild: String? = null
override fun toString(): String {
return "IdeaVersion(untilBuild=$untilBuild, sinceBuild=$sinceBuild)"
}
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.update
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.openapi.extensions.ExtensionPointName
abstract class PluginUpdateVerifier {
/**
* @param pluginDescriptor
* @return null means verifier is not responsible for the given plugin descriptor.
*/
abstract fun verify(pluginDescriptor: IdeaPluginDescriptor): PluginVerifyResult?
companion object {
internal var EP_NAME = ExtensionPointName<PluginUpdateVerifier>("org.jetbrains.kotlin.pluginUpdateVerifier")
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.update
class PluginVerifyResult {
var verified: Boolean = true
/**
* Short sentence explaining why plugin is available in JetBrains repository but can't be installed.
* Unapproved result should have a not-null declined message.
*/
var declineMessage: String? = null
companion object {
fun decline(reason: String): PluginVerifyResult = PluginVerifyResult().apply {
declineMessage = reason
verified = false
}
fun accept() = PluginVerifyResult()
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.update
import org.jetbrains.kotlin.idea.PluginUpdateStatus
// Do an additional verification with PluginUpdateVerifier. Enabled only in AS 32.
fun verify(updateStatus: PluginUpdateStatus.Update): PluginUpdateStatus {
return updateStatus
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.update
import com.intellij.ide.plugins.IdeaPluginDescriptor
import org.jetbrains.kotlin.idea.PluginUpdateStatus
// Do an additional verification with PluginUpdateVerifier. Enabled only in AS 32.
fun verify(updateStatus: PluginUpdateStatus.Update): PluginUpdateStatus {
val pluginDescriptor: IdeaPluginDescriptor = updateStatus.pluginDescriptor
val pluginVerifiers = PluginUpdateVerifier.EP_NAME.extensions
val declineMessage = pluginVerifiers.asSequence()
.map { verifier ->
val verifyResult = verifier.verify(pluginDescriptor)
if (verifyResult != null && !verifyResult.verified) {
verifyResult.declineMessage
} else {
null
}
}
.firstOrNull { reason -> reason != null }
if (declineMessage != null) {
return PluginUpdateStatus.Unverified(declineMessage, updateStatus)
}
return updateStatus
}