#1548: avoid Windows plugin locks during IDE updates - #2363
Conversation
e9f3936 to
169e2f7
Compare
Coverage Report for CI Build 33384691425Coverage decreased (-0.03%) to 73.597%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions63 previously-covered lines in 5 files lost coverage.
Coverage Stats💛 - Coveralls |
169e2f7 to
74f0388
Compare
laert-ll
left a comment
There was a problem hiding this comment.
Nice work! The approach matches what was suggested in the issue, and I like that you updated all the places that use the plugin folder (Eclipse config, VS Code extensions dir, JetBrains VM options). The -Didea.plugins.path bit fits well into the existing VM args merging, and the testing steps in the description are really clear, thank you for that! I left some comments for some concerns I had, but otherwise LGTM :)
| public boolean installPlugin(ToolPluginDescriptor plugin, Step step, ProcessContext pc) { | ||
|
|
||
| if (plugin.url() == null) { | ||
| IdeaPluginDownloader pluginDownloader = new IdeaPluginDownloader(this.context, this); | ||
| return pluginDownloader.installPlugin(plugin, step, pc); | ||
| } |
There was a problem hiding this comment.
Marketplace plugins no longer go through the IDE's installPlugins, so I'm concerned that problems might come up if there's plugins with dependencies, this might need to be tested further imo.
There was a problem hiding this comment.
Don't forget to rebase and bump this up to the latest release.
| if (fileName.endsWith(".zip")) { | ||
| // Marketplace ZIP already contains the actual plugin root folder, | ||
| // e.g. plantuml4idea/lib/... | ||
| fileAccess.extractZip(downloadedFile, installationPath); |
There was a problem hiding this comment.
Unzipping directly means the folder gets its name from the archive instead of the plugin ID. uninstallPlugin searches by plugin ID, so there could potentially be a problem with finding the plugins when trying to uninstall them. A test for uninstall might be useful in this case to make sure.
| return ""; | ||
| } | ||
|
|
||
| return switch (contentType.get()) { |
There was a problem hiding this comment.
This only accepts the exact strings application/zip and application/java-archive and anything else returns "" and the install fails with "Unknown file type" at line 153. Something like application/zip;charset=utf-8 or application/octet-stream would break it. This was for Android Studio only before, now it affects JetBrains plugins, thus could we instead match on the prefix and fall back to the filename?
| LOG.debug("Omitting installation of inactive plugin {} ({}).", plugin.name(), plugin.id()); | ||
| } | ||
|
|
||
| private void cleanupOldPluginVersions(VersionIdentifier currentVersion) { |
There was a problem hiding this comment.
We delete the old version folder here, but not its marker files in .ide/, so those pile up with every update. Could we delete the matching markers here too?
Related: --force-plugin-reinstall now only resets the current version's markers, so the "Resetting all installed plugins..." message at line 152 isn't quite accurate anymore.
74f0388 to
ab69d8e
Compare
ab69d8e to
1cc23e9
Compare
This PR fixes #1548
Implemented changes:
This PR changes plugin handling so that IDE updates on Windows no longer fail when the currently running IDE keeps files from an old plugin installation locked.
Previously, plugins were installed into one shared directory per IDE, for example:
plugins/intellij/During an IntelliJ update, IDEasy reset this directory before installing plugins for the new IDE version. On Windows, this could fail when the currently running IntelliJ instance still had plugin files open, for example native files belonging to plugins such as GitHub Copilot.
Plugins are now stored per IDE version:
This allows the new IDE version and its plugins to be installed independently from the plugin directory still used by the running old IDE.
Plugin directory handling
PluginBasedCommandletnow resolves the plugin installation path using the installed IDE version:plugins/<tool>/<version>For example:
Plugin marker files also contain the IDE version so plugin state is tracked independently for each installed IDE version. After plugins for the new IDE version have been installed, IDEasy tries to remove obsolete version directories (this cleanup is best-effort only).
If Windows prevents deletion because the old IDE still holds files open, IDEasy logs a warning such as:
The IDE update itself continues successfully. Once the old IDE is closed, a later
install/updatecan remove the obsolete plugin directory.JetBrains IDE plugin loading:
IntelliJ-based IDEs are configured to use the version-specific plugin path through:
-Didea.plugins.path=<IDE_HOME>/plugins/<tool>/<version>The generated VM options therefore point each IDE version to its corresponding plugin directory. Example:
-Didea.plugins.path=C:\Users\...\IDEasy\plugins\intellij\2026.2This applies to IntelliJ IDEA, PyCharm and Android Studio.
JetBrains Marketplace plugin installation:
Marketplace plugins are now downloaded directly instead of invoking the IDE's installPlugins command.
This is important because invoking IntelliJ while another IntelliJ instance is already running can fail with the single-instance restriction and therefore still prevent plugin installation during an IDE update.
Plugin archives are installed according to their archive type:
ZIP plugins are extracted directly into the version-specific plugin directory so their own directory structure is preserved. For example a Marketplace archive containing:
becomes:
JAR plugins are extracted into a directory named after the plugin ID. This distinction is necessary because standalone plugin JARs do not necessarily contain their own surrounding plugin directory. Custom plugin repository URLs continue to use the existing IDE command-based installation mechanism.
Other IDEs:
VS Code now also uses the version-specific plugin directory through its
--extensions-dirargument.Eclipse configuration paths are based on the version-specific plugin installation directory, for example:
plugins/eclipse/2024-09/configurationTests were updated accordingly.
Testing instructions
Please add concise, understandable instructions on how a reviewer can test/verify the functionality of your contribution here:
The issue was reproduced using IntelliJ IDEA on Windows.
Configure an older IntelliJ version, for example:
INTELLIJ_VERSION=2026.1.4Install IntelliJ:
ide install intellijVerify that the configured plugin is installed under the version-specific directory:
ls -la "$IDE_HOME/plugins/intellij"Expected:
2026.1.4Start IntelliJ:
ide intellijKeep the old IntelliJ instance running.
Change the configured IntelliJ version to a newer supported version, for example:
INTELLIJ_VERSION=2026.2While the old IntelliJ instance is still running, execute:
ide updateVerify that:
Example warning:
Close the old IntelliJ instance and start the new one:
ide intellijVerify:
Example:
-Didea.plugins.path=...\plugins\intellij\2026.2Close IntelliJ and run another update:
ide updateVerify that the obsolete plugin directory can now be removed and only the current plugin directory remains:
ls -la "$IDE_HOME/plugins/intellij"Expected:
2026.2Checklist for this PR
Make sure everything is checked before merging this PR. For further info please also see
our DoD.
mvn clean testlocally all tests pass and build is successful#«issue-id»: «brief summary»(e.g.#921: fixed setup.batand notfeature/921 fixed setup.bat). If no issue ID exists, title only.In Progressand assigned to you or there is no issue (might happen for very small PRs)with
internalpom.xmlfiles or otherwise if runtime dependencies changed, you have updated our LICENSE.asciidocChecklist for tool commandlets
Have you added a new
«tool»as commandlet? There are the following additional checks:«tool»«TOOL»_VERSIONand«TOOL»_EDITIONare honored by your commandlet