From 0cc8ebae2ca39014caa4b33494c7013ffe4f496f Mon Sep 17 00:00:00 2001 From: Kirill Bochkarev Date: Wed, 5 Aug 2026 02:56:26 +0000 Subject: [PATCH] IJPL-248066 Workspace Model: Codegen-api versioning IJPL-248066 Workspace Model: improve notifications on invalid codegen api version (cherry picked from commit 0ac7a8eba01c711afa5c7a7c54f879cdb05ff490) IJPL-248066 Workspace Model: log error on invalid codegen api version (cherry picked from commit 089be2790b26e6ec8b9b317a938ce820a6cb5716) IJPL-248066 Workspace Model: message to upgrade IDE in case codegen versions are different (cherry picked from commit 7ace5b424ae5153686093b951ff34efb293822ab) IJPL-248066 Workspace Model: fix codegen-api version check Message in case versions were same was falsely saying they were incompatible (cherry picked from commit 1344526a96b109bb1d6ee0ee6778619fa4bceb54) IJPL-248066 Workspace Model: change codegen-api version check Merge-request: IJ-MR-212689 Merged-by: Kirill Bochkarev (cherry picked from commit 559c716fa09eb8621935c404790c3cc5d26a7f01) Merge-request: IJ-MR-217063 Merged-by: Kirill Bochkarev GitOrigin-RevId: b45269b9bd1dfcd07ce4c17ff6b999a9e2ab6de6 --- .../DevKitWorkspaceModelBundle.properties | 3 + .../src/codegen/writer/CodeWriter.kt | 71 +++++++++++++++---- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/plugins/devkit/intellij.devkit.workspaceModel/resources/messages/DevKitWorkspaceModelBundle.properties b/plugins/devkit/intellij.devkit.workspaceModel/resources/messages/DevKitWorkspaceModelBundle.properties index 2bda6c7fcd14..64588af24cda 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/resources/messages/DevKitWorkspaceModelBundle.properties +++ b/plugins/devkit/intellij.devkit.workspaceModel/resources/messages/DevKitWorkspaceModelBundle.properties @@ -28,6 +28,9 @@ command.name.generate.code.for.workspace.entities.in=Generate Code for Workspace notification.workspace.incompatible.codegen.api.versions=Incompatible codegen api versions notification.workspace.incompatible.codegen.api.versions.content.older=Codegen-api version in devkit plugin ({0}) is older than in codegen-impl ({1}). Please, update your IDE notification.workspace.incompatible.codegen.api.versions.content.newer=Codegen-api version in devkit plugin ({0}) is newer than in codegen-impl ({1}). Please, downgrade your IDE +notification.workspace.incompatible.codegen.api.versions.content.none=Could not read codegen api version from {0}. Check logs for a warning with details +notification.workspace.compatible.but.different.codegen.api.versions=Different codegen api versions +notification.workspace.compatible.but.different.codegen.api.versions.content=Codegen-api version in devkit plugin ({0}) and in codegen-impl ({1}) differ. Generated code might be missing some latest features. Please, update your IDE notification.workspace.code.generation.not.available=Workspace code generation not available notification.workspace.code.generation.not.available.message=Entity code generation is not supported for plugins yet action.WorkspaceModelGenerateAllModulesAction.text=Generate Workspace Model Implementation for All Modules diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/CodeWriter.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/CodeWriter.kt index 9c8a8ce7018b..f5338aad4224 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/CodeWriter.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/CodeWriter.kt @@ -16,6 +16,7 @@ import com.intellij.openapi.application.readAction import com.intellij.openapi.application.writeIntentReadAction import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.fatalErrorWithWarnDetails import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.module.Module import com.intellij.openapi.project.DumbService @@ -52,7 +53,6 @@ import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.isPublic import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType import org.jetbrains.kotlin.resolve.ImportPath -import java.io.IOException import java.net.URI import java.util.ServiceLoader import java.util.jar.Manifest @@ -216,16 +216,48 @@ object CodeWriter { copyHeaderComment(sourceFile, file) } + private data class ApiVersion(val major: Int, val minor: Int, val patch: Int) : Comparable { + + fun compatible(other: ApiVersion): Boolean { + return major == other.major && minor == other.minor + } + + override fun toString(): String = "$major.$minor.$patch" + + override fun compareTo(other: ApiVersion): Int { + return when { + major != other.major -> major - other.major + minor != other.minor -> minor - other.minor + else -> patch - other.patch + } + } + } + private fun codegenApiVersionsAreCompatible(project: Project, codeGeneratorFromDownloadedJar: CodeGenerator): Boolean { val apiVersionInDevkit = getApiVersionFromJSON(CodeGenerator::class.java) val apiVersionFromDownloadedJar = getApiVersionFromManifest(codeGeneratorFromDownloadedJar::class.java) + if (apiVersionInDevkit == null || apiVersionFromDownloadedJar == null) { + val where = if (apiVersionInDevkit == null) "devkit plugin" else "codegen-impl" + DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.none", where) + return false + } - if (apiVersionInDevkit == apiVersionFromDownloadedJar) { + if (apiVersionInDevkit.compatible(apiVersionFromDownloadedJar)) { + if (apiVersionInDevkit.patch == apiVersionFromDownloadedJar.patch) + return true + val groupId = DevKitWorkspaceModelBundle.message("notification.workspace.compatible.but.different.codegen.api.versions") + val message = DevKitWorkspaceModelBundle.message("notification.workspace.compatible.but.different.codegen.api.versions.content", + apiVersionInDevkit, + apiVersionFromDownloadedJar) + NotificationGroupManager.getInstance() + .getNotificationGroup(groupId) + .createNotification(title = groupId, message, NotificationType.WARNING) + .notify(project) return true } val message = - if (apiVersionFromDownloadedJar == CodegenApiVersion.UNKNOWN_VERSION || apiVersionInDevkit > apiVersionFromDownloadedJar) { + if (apiVersionInDevkit > apiVersionFromDownloadedJar) { DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.newer", apiVersionInDevkit, apiVersionFromDownloadedJar) @@ -245,7 +277,7 @@ object CodeWriter { return false } - private fun getApiVersionFromJSON(clazz: Class<*>): String { + private fun getApiVersionFromJSON(clazz: Class<*>): ApiVersion? { return getApiVersionFromJarFile(clazz, CodegenApiVersion.JSON_RELATIVE_PATH) { jsonPath -> URI(jsonPath).toURL().openStream().reader().use { reader -> val jsonReader = JsonReaderEx(reader.readText()) @@ -255,7 +287,7 @@ object CodeWriter { } } - private fun getApiVersionFromManifest(clazz: Class<*>): String { + private fun getApiVersionFromManifest(clazz: Class<*>): ApiVersion? { return getApiVersionFromJarFile(clazz, CodegenApiVersion.MANIFEST_RELATIVE_PATH) { manifestPath -> URI(manifestPath).toURL().openStream().use { val manifest = Manifest(it) @@ -265,23 +297,34 @@ object CodeWriter { } } - private fun getApiVersionFromJarFile(clazz: Class<*>, relativePathToFile: String, readApiVersionFromFile: (String) -> String?): String { + private fun getApiVersionFromJarFile( + clazz: Class<*>, + relativePathToFile: String, + readApiVersionFromFile: (String) -> String?, + ): ApiVersion? { val classPath = "${clazz.name.replace(".", "/")}.class" val classAbsolutePath = clazz.getResource("${clazz.simpleName}.class")?.toString() // Absolute path is jar path + class path - ?: error("Absolute path for the class $clazz was not found") + if (classAbsolutePath == null) { + LOG.warn("Failed to read codegen-api version: Absolute path for the class $clazz was not found") + return null + } val fileAbsolutePath = classAbsolutePath.replace(classPath, relativePathToFile) - val apiVersion: String? + var apiVersion: ApiVersion? = null try { - apiVersion = readApiVersionFromFile(fileAbsolutePath) + val stringVersion = readApiVersionFromFile(fileAbsolutePath) + if (stringVersion != null) { + val (major, minor, patch) = stringVersion.split(".", "-").take(3).map { it.toInt() } + apiVersion = ApiVersion(major, minor, patch) + } } - catch (e: IOException) { - LOG.info("Failed to read codegen-api version from file \"$fileAbsolutePath\": " + e.message) - return CodegenApiVersion.UNKNOWN_VERSION + catch (e: Exception) { + LOG.warn("Failed to read codegen-api version from file \"$fileAbsolutePath\": " + e.message) + return null } - return apiVersion ?: CodegenApiVersion.UNKNOWN_VERSION + return apiVersion } private fun generate( @@ -480,8 +523,6 @@ object CodeWriter { const val MANIFEST_RELATIVE_PATH = "META-INF/MANIFEST.MF" const val ATTRIBUTE_NAME = "Codegen-Api-Version" - - const val UNKNOWN_VERSION = "unknown version" } }