Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
40 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
3c42029
Merge branch 'main' into feature/1676-Import-extra-SDKs-automatically…
vivu001 Aug 4, 2026
3b254b8
#1676: fixed configure workspace for all IDEs at launch and during id…
vivu001 Aug 4, 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
1 change: 1 addition & 0 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/1056[#1056]: Improve MSI license display by generating formatted RTF from AsciiDoc
* https://github.com/devonfw/IDEasy/issues/2052[#2052]: Split issue statistics in 2 pie-charts
* https://github.com/devonfw/IDEasy/issues/822[#822]: Added Cwd path limit in shell
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.intellij.Intellij;
import com.devonfw.tools.ide.variable.IdeVariables;
import com.devonfw.tools.ide.version.VersionIdentifier;

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

synchronizeIdeExtraToolInstallations(extraTools);
}

private void installExtraToolInstallations(String tool, List<ExtraToolInstallation> extraInstallations) {
Expand All @@ -373,6 +376,28 @@ private void installExtraToolInstallations(String tool, List<ExtraToolInstallati
}
}

private void synchronizeIdeExtraToolInstallations(ExtraTools extraTools) {

if (extraTools == null) {
return;
}

Path workspacePath = this.context.getWorkspacePath();
if (!Files.isDirectory(workspacePath)) {
LOG.debug("Skipping IDE extra SDK synchronization because workspace path does not exist: {}", workspacePath);
return;
}

Intellij intellij = this.context.getCommandletManager().getCommandlet(Intellij.class);
if (intellij != null) {
this.context.newStep("Synchronize IntelliJ extra SDKs").run(() -> intellij.synchronizeExtraToolInstallations(workspacePath));
}

// later:
// Eclipse eclipse = ...
// VsCode vscode = ...
}

private void updateRepositories() {

if (this.skipRepositories.isTrue()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,18 @@ public void importRepository(Path repositoryPath) {

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

/**
* Synchronizes extra IDEasy tool installations into the IDE workspace configuration if supported.
*
* <p>
* The default implementation does nothing. IDEs such as IntelliJ may override this hook to import extra SDK/tool installations configured via
* {@code settings/ide-extra-tools.json}.
* </p>
*
* @param workspacePath the workspace root whose IDE configuration should be synchronized.
*/
public void synchronizeExtraToolInstallations(Path workspacePath) {
// default no-op
}
Comment thread
hohwille marked this conversation as resolved.
}
205 changes: 188 additions & 17 deletions cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
Expand All @@ -24,6 +25,9 @@
import com.devonfw.tools.ide.tool.ToolEdition;
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
import com.devonfw.tools.ide.tool.ToolInstallation;
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.gradle.Gradle;
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
import com.devonfw.tools.ide.tool.ide.IdeaBasedIdeToolCommandlet;
Expand Down Expand Up @@ -53,6 +57,30 @@ public class Intellij extends IdeaBasedIdeToolCommandlet {

private static final Map<Class<? extends LocalToolCommandlet>, String> BUILD_TOOL_TO_IJ_TEMPLATE = Map.of(Mvn.class, MISC_XML, Gradle.class, GRADLE_XML);

/**
* IntelliJ-specific configuration describing how an extra IDEasy tool installation can be imported as an SDK/tool definition into IntelliJ configuration.
*
* @param templateFile the IntelliJ XML template file used as merge source.
* @param targetFile the IntelliJ configuration file relative to the workspace root that should be updated.
* @param reservedName an optional reserved logical name that must not be reused by an extra installation because it would collide with the main SDK/tool
* definition.
*/
private record IntellijExtraSdkConfig(String templateFile, String targetFile, String reservedName) {

}

/**
* Mapping of IDEasy tool names to IntelliJ-specific SDK import configuration.
*
* <p>
* Only tools contained in this map are imported automatically into IntelliJ. This keeps the import logic generic for IntelliJ while allowing support to be
* added incrementally tool by tool.
* </p>
*/
private static final Map<String, IntellijExtraSdkConfig> EXTRA_TOOL_CONFIGS = Map.of(
"java", new IntellijExtraSdkConfig("jdk-extra-java.xml", ".intellij/config/options/jdk.table.xml", "java")
);

Comment thread
hohwille marked this conversation as resolved.
Outdated
/**
* The constructor.
*
Expand Down Expand Up @@ -88,7 +116,13 @@ protected String getIdeProductPrefix() {
@Override
public void setEnvironment(EnvironmentContext environmentContext, ToolInstallation toolInstallation, boolean additionalInstallation) {
super.setEnvironment(environmentContext, toolInstallation, additionalInstallation);
environmentContext.withEnvVar("IDEA_PROPERTIES", this.context.getWorkspacePath().resolve(IDEA_PROPERTIES).toString());

Path workspacePath = this.context.getWorkspacePath();
// Synchronize extra SDK/tool definitions before IntelliJ starts so newly installed extra tool versions
// become visible in the IDE without requiring a separate repository import step.
synchronizeExtraToolInstallations(workspacePath);

environmentContext.withEnvVar("IDEA_PROPERTIES", workspacePath.resolve(IDEA_PROPERTIES).toString());
}

@Override
Expand All @@ -97,23 +131,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 All @@ -129,6 +164,16 @@ private EnvironmentVariables getIntellijEnvironmentVariables(Path projectPath) {
return environmentVariables.resolved();
}

private Path getWorkspacePath(Path repositoryPath) {
Path workspacesPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
Path workspacePath = this.context.getFileAccess().findAncestor(repositoryPath, workspacesPath, 1);
if (workspacePath == null) {
throw new CliException(
"Cannot import project into workspace: could not find workspace from " + repositoryPath);
}
return workspacePath;
}

private void mergeConfig(Path repositoryPath, String configFilePath) {
Path templatePath = this.context.getSettingsPath().resolve(TEMPLATE_LOCATION);
Path templateFile = templatePath.resolve(configFilePath);
Expand All @@ -137,12 +182,7 @@ private void mergeConfig(Path repositoryPath, String configFilePath) {
"Cannot import project into workspace: template file not found at " + templateFile + "\n"
+ "Please do an upstream merge of your settings git repository.");
}
Path workspacesPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
Path workspacePath = this.context.getFileAccess().findAncestor(repositoryPath, workspacesPath, 1);
if (workspacePath == null) {
throw new CliException(
"Cannot import project into workspace: could not find workspace from " + repositoryPath);
}
Path workspacePath = getWorkspacePath(repositoryPath);
XmlMerger xmlMerger = new XmlMerger(this.context);
EnvironmentVariables environmentVariables = getIntellijEnvironmentVariables(workspacePath.relativize(repositoryPath));
Path workspaceFile = workspacePath.resolve(FOLDER_IDEA_CONFIG).resolve(configFilePath);
Expand All @@ -158,16 +198,147 @@ private void mergeConfig(Path repositoryPath, String configFilePath) {
@Override
public void importRepository(Path repositoryPath) {
CommandletManager commandletManager = this.context.getCommandletManager();
boolean buildDescriptorFound = false;
for (Entry<Class<? extends LocalToolCommandlet>, String> entry : BUILD_TOOL_TO_IJ_TEMPLATE.entrySet()) {
LocalToolCommandlet buildTool = commandletManager.getCommandlet(entry.getKey());
Path buildDescriptor = buildTool.findBuildDescriptor(repositoryPath);
if (buildDescriptor != null) {
String templateFilename = entry.getValue();
LOG.debug("Found build descriptor {} so merging template {}", buildDescriptor, templateFilename);
mergeConfig(repositoryPath, templateFilename);
return;
buildDescriptorFound = true;
break;
}
}
LOG.warn("No supported build descriptor was found for project import in {}", repositoryPath);
if (!buildDescriptorFound) {
LOG.warn("No supported build descriptor was found for project import in {}", repositoryPath);
}

importExtraToolInstallations(repositoryPath);
Comment thread
hohwille marked this conversation as resolved.
Outdated
}

@Override
public void synchronizeExtraToolInstallations(Path workspacePath) {
importExtraToolInstallationsToWorkspace(workspacePath);
}

/**
* Imports configured extra tool installations into IntelliJ for the repository contained in the given workspace.
*
* <p>
* This method is used from repository import flow where only a repository path is known and the workspace must first be resolved from that repository
* location.
* </p>
*
* @param repositoryPath the repository path inside the workspace.
*/
private void importExtraToolInstallations(Path repositoryPath) {
Path workspacePath = getWorkspacePath(repositoryPath);
importExtraToolInstallationsToWorkspace(workspacePath);
}

/**
* Imports all configured and supported extra tool installations into IntelliJ workspace configuration.
*
* <p>
* Extra tool installations are read from {@code settings/ide-extra-tools.json}. For each configured tool, IntelliJ import is only attempted if this class
* provides IntelliJ-specific support via {@link #EXTRA_TOOL_CONFIGS}. This keeps the discovery of extra tools generic while the actual IDE import remains
* explicit and controlled.
* </p>
*
* @param workspacePath the IntelliJ workspace root whose configuration should be updated.
*/
private void importExtraToolInstallationsToWorkspace(Path workspacePath) {
ExtraTools extraTools = ExtraToolsMapper.get().loadJsonFromFolder(this.context.getSettingsPath());
if (extraTools == null) {
return;
}

for (String tool : extraTools.getSortedToolNames()) {
IntellijExtraSdkConfig config = EXTRA_TOOL_CONFIGS.get(tool);
if (config == null) {
LOG.debug("Skipping IntelliJ import of extra tool '{}': no IntelliJ template support configured.", tool);
continue;
}
List<ExtraToolInstallation> extraInstallations = extraTools.getExtraInstallations(tool);
for (ExtraToolInstallation extraInstallation : extraInstallations) {
mergeExtraToolInstallation(workspacePath, tool, extraInstallation, config);
}
}
}

/**
* Merges a single extra IDEasy tool installation into IntelliJ workspace configuration.
*
* <p>
* This method performs validation and variable preparation before delegating to the actual XML merge:
* </p>
* <ul>
* <li>rejects reserved names that would collide with the main SDK/tool definition,</li>
* <li>verifies that the extra tool installation really exists under {@code software/extra},</li>
* <li>verifies that the required IntelliJ merge template is available.</li>
* </ul>
*
* @param workspacePath the IntelliJ workspace root.
* @param tool the IDEasy tool name such as {@code java}.
* @param extraInstallation the configured extra installation from {@code ide-extra-tools.json}.
* @param config the IntelliJ-specific import configuration for the tool.
*/
private void mergeExtraToolInstallation(Path workspacePath, String tool, ExtraToolInstallation extraInstallation,
IntellijExtraSdkConfig config) {

String name = extraInstallation.name();
if ((config.reservedName() != null) && config.reservedName().equalsIgnoreCase(name)) {
LOG.warn("Skipping IntelliJ import for extra {} installation '{}': name conflicts with main {} installation.", tool, name, tool);
return;
}
Comment thread
hohwille marked this conversation as resolved.
Outdated

Path extraToolHome = this.context.getSoftwareExtraPath().resolve(tool).resolve(name);
if (!Files.isDirectory(extraToolHome)) {
LOG.warn("Skipping IntelliJ import for extra {} installation '{}': directory does not exist: {}", tool, name, extraToolHome);
return;
}

Path templateFile = this.context.getSettingsPath()
.resolve(TEMPLATE_LOCATION)
.resolve(config.templateFile());
if (!Files.exists(templateFile)) {
throw new CliException(
"Cannot import extra " + tool + " installation into IntelliJ: template file not found at " + templateFile + "\n"
+ "Please do an upstream merge of your settings git repository.");
}
Comment thread
hohwille marked this conversation as resolved.
Outdated

ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
(AbstractEnvironmentVariables) this.context.getVariables().getParent(), this.context);
environmentVariables.setValue("EXTRA_JAVA_NAME", name);
environmentVariables.setValue("EXTRA_JAVA_HOME", extraToolHome.toString().replace('\\', '/'));
environmentVariables.setValue("EXTRA_JAVA_VERSION", extraInstallation.version().toString());
if (extraInstallation.edition() != null) {
environmentVariables.setValue("EXTRA_JAVA_EDITION", extraInstallation.edition());
}

mergeExtraToolInstallationToWorkspace(workspacePath, config, templateFile, environmentVariables.resolved());
}

/**
* Performs the actual XML merge of an IntelliJ extra SDK/tool definition into the configured target file.
*
* @param workspacePath the IntelliJ workspace root.
* @param config the IntelliJ-specific import configuration.
* @param templateFile the resolved template file to merge from.
* @param environmentVariables the resolved variables used to parameterize the template.
*/
private void mergeExtraToolInstallationToWorkspace(Path workspacePath, IntellijExtraSdkConfig config, Path templateFile,
EnvironmentVariables environmentVariables) {

Path targetFile = workspacePath.resolve(config.targetFile());

XmlMerger xmlMerger = new XmlMerger(this.context);
XmlMergeDocument workspaceDocument = xmlMerger.load(targetFile);
XmlMergeDocument templateDocument = xmlMerger.loadAndResolve(templateFile, environmentVariables);

Document mergedDocument = xmlMerger.merge(templateDocument, workspaceDocument, false);

xmlMerger.save(mergedDocument, targetFile);
}
}
Loading
Loading