PY-91190 Fix legacy Python SDK migration at startup

Wait for workspace model synchronization before migrating global Python SDKs
so delayed JPS synchronization cannot overwrite the migrated entities.

Use ASSOCIATED_PROJECT_PATH as the effective non-canonical working directory
until migration writes WORKING_DIRECTORY. This keeps legacy interpreters
visible while preserving the original XML before migration.


(cherry picked from commit ae7a7953e379f76ac777d653109cc1a3984c4386)

IJ-MR-215819

GitOrigin-RevId: 027bb719540e88f5907d266e96ef4dc2ed1f2425
This commit is contained in:
Alexey Katsman
2026-07-29 10:24:10 +00:00
committed by intellij-monorepo-bot
parent 54b4838ac2
commit 2a9e37cd6f
2 changed files with 17 additions and 1 deletions
@@ -11,6 +11,7 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.util.JDOMExternalizer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.NioPathUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
@@ -274,9 +275,10 @@ public class PythonSdkAdditionalData implements SdkAdditionalData {
}
}
if (!hasValidWorkingDirectory()) {
if (!hasValidWorkingDirectory() || !myWorkingDirectoryIsCanonical) {
Path workingDirectory = flavorMigration.workingDirectory();
if (workingDirectory == null) workingDirectory = requirementsWorkingDirectory;
if (workingDirectory == null && hasValidWorkingDirectory()) workingDirectory = myWorkingDirectory;
if (workingDirectory == null) workingDirectory = fallbackWorkingDirectory;
if (workingDirectory != null && !workingDirectory.toString().isBlank()) {
@@ -321,6 +323,14 @@ public class PythonSdkAdditionalData implements SdkAdditionalData {
myWorkingDirectoryIsCanonical = false;
myMigrationRequired = true;
}
else if (myAssociatedModulePath != null) {
Path associatedModulePath = NioPathUtil.toNioPathOrNull(myAssociatedModulePath);
if (associatedModulePath != null && !Objects.equals(associatedModulePath, EMPTY_WORKING_DIRECTORY)) {
myWorkingDirectory = associatedModulePath;
myWorkingDirectoryIsCanonical = false;
myMigrationRequired = true;
}
}
myRequirementsFile = element.getAttributeValue(REQUIREMENTS_FILE);
String legacyRequiredTxtPath = element.getAttributeValue(ASSOCIATED_REQUIRED_TXT_PATH);
myLegacyRequiredTxtPath = legacyRequiredTxtPath == null ? null : Path.of(legacyRequiredTxtPath);
@@ -10,6 +10,8 @@ import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.platform.backend.workspace.WorkspaceModel
import com.intellij.platform.backend.workspace.impl.WorkspaceModelInternal
import com.intellij.util.concurrency.annotations.RequiresWriteLock
import org.jetbrains.annotations.ApiStatus
import java.nio.file.Path
@@ -19,6 +21,10 @@ class PythonSdkAdditionalDataMigrationActivity : ProjectActivity, DumbAware {
override suspend fun execute(project: Project) {
if (project.isDisposed) return
@Suppress("UnsafeOpenServiceCast")
(WorkspaceModel.getInstance(project) as WorkspaceModelInternal).awaitSynchronizationWithJpsModel()
if (project.isDisposed) return
val pythonSdks = ProjectJdkTable.getInstance().allJdks.filter { PythonSdkUtil.isPythonSdk(it) }
writeAction {
migratePythonSdkAdditionalData(project, pythonSdks)