Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
040cfdc
#1676: auto-import configured extra SDKs
vivu001 Jun 23, 2026
cb6288f
#1676: Added tests for SDKs import
vivu001 Jun 24, 2026
becc527
#1676: Added tests for SDKs import
vivu001 Jun 24, 2026
545d71d
Merge remote-tracking branch 'origin/feature/1676-Import-extra-SDKs-a…
vivu001 Jun 24, 2026
8f7769d
#1676: Fixed IntelliJ not auto-recognizing newly installed extra SDKs
vivu001 Jun 25, 2026
98a77be
#1676: Fixed errored test
vivu001 Jun 25, 2026
ddfd368
Merge branch 'devonfw:main' into feature/1676-Import-extra-SDKs-autom…
vivu001 Jun 25, 2026
df62d45
#1676: Fixed error "Arguments already present"
vivu001 Jun 25, 2026
0a5b9fa
#1676: Fixed error "Arguments already present"
vivu001 Jun 25, 2026
4e996ff
#1676: Updated the extra Java template parsing to use the renamed att…
vivu001 Jul 1, 2026
f18a437
#1676: Updated CHANGELOG.adoc
vivu001 Jul 1, 2026
a06dfa4
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 1, 2026
1162c0b
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 1, 2026
96afb63
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 3, 2026
9a7eb4b
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 7, 2026
c64b534
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 7, 2026
e4ad045
#1676: Fixed misunderstandings and refactored code
vivu001 Jul 7, 2026
39dbb99
#1676: Fixed tests
vivu001 Jul 8, 2026
caaa822
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 8, 2026
9361305
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 9, 2026
88be370
Merge branch 'devonfw:main' into feature/1676-Import-extra-SDKs-autom…
vivu001 Jul 9, 2026
1b4d02b
Merge branch 'devonfw:main' into feature/1676-Import-extra-SDKs-autom…
vivu001 Jul 9, 2026
a108c07
#1676: Fixed extra JDK sync timing and missing-install handling
vivu001 Jul 13, 2026
b7ec91b
#1676: Call configureWorkspace() after installing tools so IntelliJ s…
vivu001 Jul 13, 2026
0cf86fb
#1676: Added GraalVM reflection metadata for dynamic commandlet lookup
vivu001 Jul 13, 2026
507c37e
#1676: Updated CHANGELOG.adoc
vivu001 Jul 13, 2026
64a14df
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 16, 2026
2c59544
#1676: removed synchronizeExtraTools()
vivu001 Jul 20, 2026
eff2e29
#1676: replaced Exception with Log warning
vivu001 Jul 20, 2026
a1ecaba
#1676: replaced hardcoded repository folder literal with constant
vivu001 Jul 20, 2026
b41deaf
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
hohwille Jul 20, 2026
d602c63
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 21, 2026
b5c3608
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 22, 2026
3a145e2
#1676: reorder IdeContext imports for spotless
vivu001 Jul 22, 2026
fce92ca
#1676: fixed Checkstyle violation in UvRepositoryMock import
vivu001 Jul 22, 2026
836c602
#1676: fix Checkstyle violation in Intellij warning text
vivu001 Jul 22, 2026
27f1c50
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Jul 27, 2026
fc56853
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
hohwille Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/1676[#1676]: Import extra sdks automatically into ide
* https://github.com/devonfw/IDEasy/issues/2140[#2140]: just install fails since already installed
* https://github.com/devonfw/IDEasy/issues/2137[#2137]: fix-vpn-tls-problem does not detect TLS certificate issues behind HTTP redirects
* https://github.com/devonfw/IDEasy/issues/1041[#1041]: native image has globbing active
Expand Down Expand Up @@ -104,8 +105,6 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/1904[#1904]: Add Inso CLI to IDEasy commandlets
* https://github.com/devonfw/IDEasy/issues/1952[#1952]: Ability for platform specific dependencies
* https://github.com/devonfw/IDEasy/issues/1950[#1950]: Fix exit autocompletion
* https://github.com/devonfw/IDEasy/issues/1936[#1936]: Improve localization of GUI

* https://github.com/devonfw/IDEasy/issues/1958[#1958]: Fix git pull on settings with local branch without remote

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.devonfw.tools.ide.tool.extra.ExtraToolInstallation;
import com.devonfw.tools.ide.tool.extra.ExtraTools;
import com.devonfw.tools.ide.tool.extra.ExtraToolsMapper;
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
import com.devonfw.tools.ide.variable.IdeVariables;
import com.devonfw.tools.ide.version.VersionIdentifier;

Expand Down Expand Up @@ -349,6 +350,18 @@ private void doUpdateSoftwareStep(Step step) {
List<ExtraToolInstallation> installations = extraTools.getExtraInstallations(tool);
this.context.newStep("Install extra version(s) of " + tool).run(() -> installExtraToolInstallations(tool, installations));
}

// After installing extra tools, synchronize them into all IDE workspaces
List<String> ides = IdeVariables.CREATE_START_SCRIPTS.get(this.context);
if (ides != null) {
for (String ideName : ides) {
ToolCommandlet ideCommandlet = commandletManager.getToolCommandlet(ideName);
if (ideCommandlet instanceof IdeToolCommandlet) {
LOG.info("Synchronizing extra tools into {} workspace", ideName);
((IdeToolCommandlet) ideCommandlet).synchronizeExtraTools();
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public List<ExtraToolInstallation> getExtraInstallations(String tool) {
*/
public void addExtraInstallations(String tool, ExtraToolInstallation extraInstallation) {

if ((tool != null) && (extraInstallation != null) && tool.equalsIgnoreCase(extraInstallation.name())) {
throw new IllegalArgumentException(
"Invalid extra installation name '" + extraInstallation.name() + "' for tool '" + tool
+ "': the extra installation name must not be the same as the tool name.");
}

Comment thread
hohwille marked this conversation as resolved.
List<ExtraToolInstallation> list = this.tool2installationsMap.computeIfAbsent(tool, k -> new ArrayList<>());
list.add(extraInstallation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import com.devonfw.tools.ide.common.Tag;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables;
import com.devonfw.tools.ide.environment.ExtensibleEnvironmentVariables;
import com.devonfw.tools.ide.io.FileAccess;
import com.devonfw.tools.ide.log.IdeLogLevel;
import com.devonfw.tools.ide.merge.xml.XmlMergeDocument;
import com.devonfw.tools.ide.merge.xml.XmlMerger;
import com.devonfw.tools.ide.process.ProcessMode;
import com.devonfw.tools.ide.process.ProcessResult;
import com.devonfw.tools.ide.step.Step;
import com.devonfw.tools.ide.tool.ToolCommandlet;
import com.devonfw.tools.ide.tool.ToolInstallRequest;
import com.devonfw.tools.ide.tool.ToolInstallation;
import com.devonfw.tools.ide.tool.eclipse.Eclipse;
import com.devonfw.tools.ide.tool.extra.ExtraToolInstallation;
import com.devonfw.tools.ide.tool.extra.ExtraTools;
import com.devonfw.tools.ide.tool.extra.ExtraToolsMapper;
import com.devonfw.tools.ide.tool.intellij.Intellij;
import com.devonfw.tools.ide.tool.plugin.PluginBasedCommandlet;
import com.devonfw.tools.ide.tool.vscode.Vscode;
Expand All @@ -29,6 +42,8 @@ public abstract class IdeToolCommandlet extends PluginBasedCommandlet {

private static final Logger LOG = LoggerFactory.getLogger(IdeToolCommandlet.class);

Comment thread
hohwille marked this conversation as resolved.
private final Map<String, Set<Path>> extraSdkMap;

/**
* The constructor.
*
Expand All @@ -40,6 +55,7 @@ public IdeToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {

super(context, tool, tags);
assert (hasIde(tags));
Comment thread
vivu001 marked this conversation as resolved.
this.extraSdkMap = new HashMap<>();
}

private boolean hasIde(Set<Tag> tags) {
Expand All @@ -66,10 +82,17 @@ public ProcessResult runTool(List<String> args) {
@Override
public ToolInstallation install(ToolInstallRequest request) {

configureWorkspace();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah great finding. So the big picture gets clearer.
But as I already wrote maybe this was done on purpose.
Can you test that without this still the plugins get installed into the correct plugins folder inside the IDEasy project and are found as installed when I launch ide intellij?
If that is the case, then this should solve the problem and we simply do not need to configure the workspace at all during installation and you can also remove the extra loop in AbstractUpdateCommandlet.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it: without configureWorkspace() in IdeToolCommandlet.install() the extra SDK installation is still unaffected. The extra SDKs are still installed by the create/update flow and the workspace synchronization is only needed later when ide intellij imports them.

In the AbstractUpdateCommandlet, removing the extra loop shows that extra SDKs still work when launching ide intellij, but the plugins are NOT installed into $IDE_HOME/plugins/intellij. So this workspace sync is still needed for plugin provisioning, even though the SDK part can already be handled at IDE launch.

@hohwille hohwille Jul 30, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the current state and how can we finally resolve this.
IMHO this change is logically correct and also if noting is left in this method you can even remove the method override since it is pointless now.

However, as you also found out and confirmed the plugin installation requires somehow the workspace configuration to be triggered.
Reading your PR diff again, I now found that you are triggering this in IdeaBasedToolCommandlet when a plugin is to be installed.
However, then we are still not done:

  1. How about other IDEs like VSCode or Eclipse - can we install plugins there without configureWorkspace() being invoked. Or is this only an IDEA/JetBrains specific thing not needed for other IDEs (because during configureWorkspace() we create the idea.properties in the workspace what makes this different from non-IDEA based IDEs)? IMHO for Eclipse and VSCode we will now never configure the workspace any more when we change your PR as is so it would be broken.
  2. Performance impact: If we install 20 plugins, we will now configure the workspace 20 times. This seems to be a waste and in that case the old approach to trigger it once during the installation still seems more reasonable unless we somehow can remember that we already configured the workspace and do not rerun it for every plugin. Besides if there is an error or warning for a workspace template the user will be annoyed if he gets the same message 20 times so it is not only about performance.

I really like to merge this PR finally but IMHO this is still not 100% cleared to the end.
Maybe we have to clarify this at the end of the next daily since review ping-pong seems to take way too long...

return super.install(request);
}

/**
* Synchronizes extra tool installations into the IDE workspace.
* This is called after extra tools are installed during ide create/update and before launching the IDE.
*/
public void synchronizeExtraTools() {
configureWorkspace();
}

Comment thread
hohwille marked this conversation as resolved.
Outdated
/**
* Configure (initialize or update) the workspace for this IDE using the templates from the settings.
*/
Expand All @@ -91,6 +114,9 @@ private void doMergeWorkspaceStep(Step step, Path workspaceFolder) {
errors = mergeWorkspace(this.context.getUserHomeIde(), workspaceFolder, errors);
errors = mergeWorkspace(this.context.getSettingsPath(), workspaceFolder, errors);
errors = mergeWorkspace(this.context.getConfPath(), workspaceFolder, errors);

synchronizeExtraToolInstallations();

if (errors == 0) {
Comment thread
hohwille marked this conversation as resolved.
step.success();
} else {
Expand Down Expand Up @@ -131,4 +157,86 @@ public void importRepository(Path repositoryPath) {

throw new UnsupportedOperationException("Repository import is not yet implemented for IDE " + this.tool);
}

/**
* Registers support for synchronizing an extra SDK/template for this IDE.
*
* <p>
* The registered template path must be relative to the IDE workspace root. During workspace synchronization, the generic extra-SDK handling in
* {@link #synchronizeExtraToolInstallations()} uses this mapping to locate the corresponding template file in the settings repository and merge it into the
* current workspace.
* </p>
*
* @param sdk the name of the extra SDK/tool as configured in {@code ide-extra-tools.json}.
* @param relativeTemplatePath the workspace-relative path of the IDE-specific template file to merge.
*/
protected void registerExtraSdkTemplate(String sdk, Path relativeTemplatePath) {
Set<Path> templatePaths = this.extraSdkMap.computeIfAbsent(sdk, _ -> new HashSet<>());
templatePaths.add(relativeTemplatePath);
}

/**
* Synchronizes extra IDEasy tool installations into the current IDE workspace configuration if supported.
*
* <p>
* By default, nothing will happen. Your IDE commandlet has to register one or more according templates in its constructor.
* </p>
*/
protected void synchronizeExtraToolInstallations() {
ExtraTools extraTools = ExtraToolsMapper.get().loadJsonFromFolder(this.context.getSettingsPath());
if (extraTools == null) {
return;
}
for (String sdk : extraTools.getSortedToolNames()) {
Set<Path> templatePaths = this.extraSdkMap.get(sdk);
if ((templatePaths == null) || templatePaths.isEmpty()) {
LOG.debug("Skipping import of extra tool {} into {} because not configured or supported.", sdk, this.tool);
continue;
}
List<ExtraToolInstallation> extraInstallations = extraTools.getExtraInstallations(sdk);
synchronizeExtraToolInstallation(sdk, templatePaths, extraInstallations);
}
}

private void synchronizeExtraToolInstallation(String sdk, Set<Path> templatePaths, List<ExtraToolInstallation> extraInstallations) {
for (Path templatePath : templatePaths) {
Path workspaceFile = this.context.getWorkspacePath().resolve(templatePath);
Path templateFile = this.context.getSettingsPath().resolve(this.tool).resolve(IdeContext.FOLDER_WORKSPACE)
.resolve("repository") // TODO: create and use constant IdeContext.FOLDER_REPOSITORY - this string literal exists at least 16 times in our code base
Comment thread
hohwille marked this conversation as resolved.
Outdated
.resolve(templatePath);
if (Files.exists(templateFile)) {
for (ExtraToolInstallation extraInstallation : extraInstallations) {
synchronizeExtraToolInstallation(sdk, templateFile, workspaceFile, extraInstallation);
}
} else {
LOG.warn("You are missing a template file at {}.", templatePath);
IdeLogLevel.INTERACTION.log(LOG, "Please ask the IDEasy admin in your project to merge your settings with upstream.");
}
}
}

private void synchronizeExtraToolInstallation(String sdk, Path templateFile, Path workspaceFile, ExtraToolInstallation installation) {
String name = installation.name();
Path extraToolHome = this.context.getSoftwareExtraPath().resolve(sdk).resolve(name);
if (!Files.isDirectory(extraToolHome)) {
LOG.warn("Skipping extra tool installation import to {} because it is missing at {}", this.tool, extraToolHome);
IdeLogLevel.INTERACTION.log(LOG, "Please run the following command to fix:\nide update");
return;
}
ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
(AbstractEnvironmentVariables) this.context.getVariables().getParent(), this.context);
String variablePrefix = "EXTRA_" + sdk.toUpperCase(Locale.ROOT);
environmentVariables.setValue(variablePrefix + "_NAME", name);
environmentVariables.setValue(variablePrefix + "_HOME", extraToolHome.toString().replace('\\', '/'));
environmentVariables.setValue(variablePrefix + "_VERSION", installation.version().toString());
if (installation.edition() != null) {
environmentVariables.setValue(variablePrefix + "_EDITION", installation.edition());
}

XmlMerger xmlMerger = new XmlMerger(this.context);
XmlMergeDocument workspaceDocument = xmlMerger.load(workspaceFile);
XmlMergeDocument templateDocument = xmlMerger.loadAndResolve(templateFile, environmentVariables);
Document mergedDocument = xmlMerger.merge(templateDocument, workspaceDocument, false);
xmlMerger.save(mergedDocument, workspaceFile);
}
Comment thread
hohwille marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ protected String getIdeProductPrefix() {
public ProcessResult runTool(ProcessContext pc, ProcessMode processMode, List<String> args) {
if (!args.contains("installPlugins")) {
args.add(this.context.getWorkspacePath().toString());
configureWorkspace();
}

String variableName = getName().toUpperCase(Locale.ROOT).replace("-", "_") + VM_ARGS_ENV_SUFFIX;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class Intellij extends IdeaBasedIdeToolCommandlet {
public Intellij(IdeContext context) {

super(context, "intellij", Set.of(Tag.INTELLIJ));
Comment thread
hohwille marked this conversation as resolved.
registerExtraSdkTemplate("java", Path.of(".intellij/config/options/jdk.table.xml"));
}

@Override
Expand Down Expand Up @@ -97,23 +98,24 @@ protected ToolEditionAndVersion adjustRequestedEdition(ToolEditionAndVersion req
ToolEdition edition = requested.getEdition();
// Check if edition is set as "ultimate"
if ("ultimate".equals(edition.edition())) {

VersionIdentifier version;
if (requested.getVersion() != null) {
version = VersionIdentifier.of(requested.getVersion().toString());
} else {
version = getConfiguredVersion();
}
// Check whether set version warrants switching editions
if ((version.isGreater(INTELLIJ_LAST_SEPARATE_VERSION)) || // Specified version is > 2025.2.6.1 **OR** no specified version but configured version is > 2025.2.6.1
(VersionIdentifier.LATEST.equals(version)) || // No version specified and no configured version
(VersionIdentifier.LATEST_UNSTABLE.equals(version))) { // No version specified and no configured version
if ((version.isGreater(INTELLIJ_LAST_SEPARATE_VERSION)) ||
// Specified version is > 2025.2.6.1 **OR** no specified version but configured version is > 2025.2.6.1
(VersionIdentifier.LATEST.equals(version)) || // No version specified and no configured version
(VersionIdentifier.LATEST_UNSTABLE.equals(version))) { // No version specified and no configured version
// Switching to IntelliJ Standard edition
LOG.warn("""
Notice: You have configured IDEasy to use the IntelliJ Ultimate Edition. Since version 2025.3, the Ultimate and Community editions of IntelliJ have been unified into a single edition.
Since you are attempting to install a version of IntelliJ that is 2025.3 or newer, we are automatically switching your edition to the unified edition to ensure compatibility.
To specifically install the last true ultimate version of IntelliJ, please run "ide install intellij 2025.2.6.1".
Otherwise, we recommend permanently switching to the unified edition by running "ide set-edition intellij intellij".""");
Notice: You have configured IDEasy to use the IntelliJ Ultimate Edition. Since version 2025.3, the Ultimate and Community editions of IntelliJ have been unified into a single edition.
Since you are attempting to install a version of IntelliJ that is 2025.3 or newer, we are automatically switching your edition to the unified edition to ensure compatibility.
To specifically install the last true ultimate version of IntelliJ, please run "ide install intellij 2025.2.6.1".
Otherwise, we recommend permanently switching to the unified edition by running "ide set-edition intellij intellij".""");
edition = new ToolEdition(this.tool, "intellij");
requested.replaceEdition(edition);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,19 @@
"allPublicConstructors": true,
"allDeclaredFields": false,
"allDeclaredMethods": false
},
{
"name": "com.devonfw.tools.ide.tool.ide.IdeToolCommandlet",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredFields": true,
"allDeclaredMethods": true
},
{
"name": "com.devonfw.tools.ide.tool.intellij.Intellij",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredFields": true,
"allDeclaredMethods": true
}
]
Loading
Loading