Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
917fd17
Initial Feature Commit. Needs further validation
areinicke Apr 15, 2026
2eb7031
Formatting fix
areinicke Apr 15, 2026
64fea0f
Remove unnecessary commemnts
areinicke Apr 15, 2026
b7a58a3
Remove debug print
areinicke Apr 15, 2026
60170d9
Update info message
areinicke Apr 15, 2026
f63d458
Formatting changes
areinicke Apr 15, 2026
0a06938
Formatting changes
areinicke Apr 16, 2026
04451f4
Formatting changes
areinicke Apr 16, 2026
8705bc8
Fix typo
areinicke Apr 20, 2026
7b431be
Update cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij…
areinicke Apr 20, 2026
38580b1
Add Test descriptions
areinicke Apr 20, 2026
a155727
Merge branch 'main' into feature/1800-use-intellij-unified
areinicke Apr 20, 2026
7a4e372
Changelog Formatting Fix
areinicke Apr 20, 2026
95d42db
Changelog Formatting Fix
areinicke Apr 20, 2026
9d55525
Fix missing comment start
areinicke Apr 20, 2026
fb72f62
Merge branch 'main' into feature/1800-use-intellij-unified
hohwille Apr 21, 2026
4f87ca8
Refactor code
areinicke Apr 23, 2026
662ee34
Adjust IntellijUrlUpdater to move newer releases to the standard edition
areinicke Apr 23, 2026
a76cb31
Merge branch 'main' into feature/1800-use-intellij-unified
areinicke Apr 23, 2026
3aea4bd
Add protections against NullPointerException when no version is set
areinicke Apr 23, 2026
5a9727d
Merge branch 'main' into feature/1800-use-intellij-unified
hohwille May 4, 2026
c1d1171
Merge branch 'main' into feature/1800-use-intellij-unified
hohwille May 4, 2026
663952f
Remove unnecessary comment
areinicke May 5, 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/1800[#1800]: IDEasy will automatically switch to IntelliJ standard edition when installing newer ultimate edition versions
* https://github.com/devonfw/IDEasy/issues/1552[#1552]: User-defined MAVEN_ARGS appends and no longer overwrites IDEasy's defaults
* https://github.com/devonfw/IDEasy/issues/1833[#1833]: No settings repo update with missing `.commit.id`
* https://github.com/devonfw/IDEasy/issues/1693[#1693]: Fix behavior when there's no settings repo (.../settings/.git)
Expand Down
20 changes: 20 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,20 @@ private void completeRequestRequested(ToolInstallRequest request) {
edition = new ToolEdition(this.tool, getConfiguredEdition());
requested = new ToolEditionAndVersion(edition);
request.setRequested(requested);

} else {
edition = requested.getEdition();
if (edition == null) {
// If no edition was specified, set it to the configured one
edition = new ToolEdition(this.tool, getConfiguredEdition());
requested.setEdition(edition);
}
}

// Adjust edition if necessary based on requested version. This is needed for tools like IntelliJ where we may need to automatically switch editions
requested = adjustRequestedEdition(requested);
edition = requested.getEdition();
Comment thread
areinicke marked this conversation as resolved.

GenericVersionRange version = requested.getVersion();
if (version == null) {
version = getConfiguredVersion();
Expand All @@ -398,6 +405,19 @@ private void completeRequestRequested(ToolInstallRequest request) {
}
}

/**
* Hook for subclasses to adjust the requested tool edition before the version is finalized.
*
* @param requested the requested {@link ToolEditionAndVersion}
* @return the given or trgansformed {@link ToolEditionAndVersion}
*/
protected ToolEditionAndVersion adjustRequestedEdition(ToolEditionAndVersion requested) {

// default no-op
return requested;
}

Comment thread
areinicke marked this conversation as resolved.

private void completeRequestToolPath(ToolInstallRequest request) {

Path toolPath = request.getToolPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public void setEdition(ToolEdition edition) {
this.edition = edition;
}

/**
* @param edition new value of {@link #getEdition()}.
*/
public void replaceEdition(ToolEdition edition) {

this.edition = edition;
}

Comment thread
areinicke marked this conversation as resolved.
/**
* @return the {@link GenericVersionRange} that is installed or configured.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
import com.devonfw.tools.ide.merge.xml.XmlMerger;
import com.devonfw.tools.ide.process.EnvironmentContext;
import com.devonfw.tools.ide.tool.LocalToolCommandlet;
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.gradle.Gradle;
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
import com.devonfw.tools.ide.tool.ide.IdeaBasedIdeToolCommandlet;
import com.devonfw.tools.ide.tool.mvn.Mvn;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
* {@link IdeToolCommandlet} for <a href="https://www.jetbrains.com/idea/">IntelliJ</a>.
Expand All @@ -34,6 +37,8 @@ public class Intellij extends IdeaBasedIdeToolCommandlet {

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

private static final VersionIdentifier INTELLIJ_LAST_SEPARATE_VERSION = VersionIdentifier.of("2025.2.6.1");

private static final String IDEA = "idea";

private static final String IDEA64_EXE = IDEA + "64.exe";
Expand Down Expand Up @@ -86,6 +91,36 @@ public void setEnvironment(EnvironmentContext environmentContext, ToolInstallati
environmentContext.withEnvVar("IDEA_PROPERTIES", this.context.getWorkspacePath().resolve(IDEA_PROPERTIES).toString());
}

@Override
protected ToolEditionAndVersion adjustRequestedEdition(ToolEditionAndVersion requested) {

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
// 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".""");
edition = new ToolEdition(this.tool, "intellij");
requested.replaceEdition(edition);
}
}
return requested;
}

private EnvironmentVariables getIntellijEnvironmentVariables(Path projectPath) {
ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
(AbstractEnvironmentVariables) this.context.getVariables().getParent(), this.context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import com.devonfw.tools.ide.log.IdeLogLevel;
import com.devonfw.tools.ide.os.SystemInfo;
import com.devonfw.tools.ide.os.SystemInfoMock;
import com.devonfw.tools.ide.tool.ToolEdition;
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
import com.devonfw.tools.ide.version.VersionIdentifier;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;

/**
Expand Down Expand Up @@ -195,6 +198,63 @@ void testIntellijMvnAndGradleRepositoryImport() {
""");
}

/**
* Tests whether IDEasy correctly switches editions when no version is specified or configured
*/
@Test
void testAdjustRequestedEditionSwitchesForUltimateWithoutConfiguredVersion() {

// arrange
IdeTestContext context = newContext("intellij");
Intellij commandlet = context.getCommandletManager().getCommandlet(Intellij.class);
ToolEditionAndVersion requested = new ToolEditionAndVersion(new ToolEdition("intellij", "ultimate"));
requested.setVersion(VersionIdentifier.LATEST);

// act
ToolEditionAndVersion adjusted = commandlet.adjustRequestedEdition(requested);

// assert
assertThat(adjusted.getEdition().edition()).isEqualTo("intellij");
}

/**
* Tests whether IDEasy correctly switches editions when the specified version is after 2025.2.6.1
*/
@Test
void testAdjustRequestedEditionSwitchesForUltimateWithVersionAboveCutoff() {

// arrange
IdeTestContext context = newContext("intellij");
Intellij commandlet = context.getCommandletManager().getCommandlet(Intellij.class);
ToolEditionAndVersion requested = new ToolEditionAndVersion(new ToolEdition("intellij", "ultimate"));
requested.setVersion(VersionIdentifier.of("2025.3"));

// act
ToolEditionAndVersion adjusted = commandlet.adjustRequestedEdition(requested);

// assert
assertThat(adjusted.getEdition().edition()).isEqualTo("intellij");
}

/**
* Tests whether IDEasy correctly remains on the ultimate edition when the specified version is 2025.2.6.1
*/
@Test
void testAdjustRequestedEditionDoesNotSwitchForUltimateAtCutoffVersion() {

// arrange
IdeTestContext context = newContext("intellij");
Intellij commandlet = context.getCommandletManager().getCommandlet(Intellij.class);
ToolEditionAndVersion requested = new ToolEditionAndVersion(new ToolEdition("intellij", "ultimate"));
requested.setVersion(VersionIdentifier.of("2025.2.6.1"));

// act
ToolEditionAndVersion adjusted = commandlet.adjustRequestedEdition(requested);

// assert
assertThat(adjusted.getEdition().edition()).isEqualTo("ultimate");
}

/**
* Tests if the custom jvm options of the ide variable INTELLI_VM_ARGS have been set.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package com.devonfw.tools.ide.url.tool.intellij;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.devonfw.tools.ide.json.JsonMapping;
import com.devonfw.tools.ide.url.updater.IdeaBasedUrlUpdater;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
* {@link IdeaBasedUrlUpdater} base class for IntelliJ.
*/
public class IntellijUrlUpdater extends IdeaBasedUrlUpdater {

private static final String JSON_URL = "products?code=IIU%2CIIC&release.type=release";
private static final VersionIdentifier LAST_SEPARATE_VERSION = VersionIdentifier.of("2025.2.6.1");
protected static final List<String> EDITIONS = List.of("ultimate", "intellij");
protected static final ObjectMapper MAPPER = JsonMapping.createWithReflectionSupportForUrlUpdaters();

Expand All @@ -36,9 +40,43 @@ protected String doGetVersionUrl() {
@Override
protected IntellijJsonObject getJsonObjectFromResponse(String response, String edition) throws JsonProcessingException {
IntellijJsonObject[] jsonObjects = MAPPER.readValue(response, IntellijJsonObject[].class);
moveUnifiedReleases(jsonObjects);
return jsonObjects[EDITIONS.indexOf(edition)];
}

/**
* This function moves releases later than 2025.2.6.1, which are unified releases of IntelliJ, but are still distributed internall as ultimate editions to the community edition releases in the JSON objects,
* so that they are correctly recognized as community edition releases by the rest of the code.
* @param jsonObjects the array of JSON objects parsed from the response, which contains one object for the ultimate edition and one for the community edition.
* The function modifies this array in-place, so that after execution, all unified releases are moved to the community edition JSON object.
*/
private void moveUnifiedReleases(IntellijJsonObject[] jsonObjects) {

IntellijJsonObject ultimate = jsonObjects[EDITIONS.indexOf("ultimate")];
IntellijJsonObject community = jsonObjects[EDITIONS.indexOf("intellij")];
List<IntellijJsonRelease> movedReleases = new ArrayList<>();
Iterator<IntellijJsonRelease> iterator = ultimate.releases().iterator();
while (iterator.hasNext()) {
IntellijJsonRelease release = iterator.next();
if (isUnifiedRelease(release)) {
movedReleases.add(release);
iterator.remove();
}
}
community.releases().addAll(0, movedReleases);
Comment thread
hohwille marked this conversation as resolved.
}

/**
* This function determines, whether a given IntelliJ release is a unified release, meaning that there is no separate community edition available.
* This is the case for all releases greater than 2025.2.6.1.
* @param release the IntelliJ JSON release to check.
* @return {@code true} if the release is a unified release, {@code false} otherwise.
*/
private boolean isUnifiedRelease(IntellijJsonRelease release) {

return VersionIdentifier.of(release.version()).isGreater(LAST_SEPARATE_VERSION);
}


@Override
public String getCpeVendor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,55 @@ void testIntellijJsonUrlUpdaterWithMissingChecksumGeneratesChecksum(@TempDir Pat
Path intellijVersionsPath = tempDir.resolve("intellij").resolve("intellij").resolve("2023.1.2");
assertUrlVersion(intellijVersionsPath, List.of("linux_x64"));
}

/**
* Test if the {@link IntellijUrlUpdater} correctly moves unified releases to the community edition JSON object, so that they are correctly recognized as community edition releases by the rest of the code.
* This is done by providing a mocked JSON response with a unified release and checking whether this release is moved to the community edition JSON object by the {@link IntellijUrlUpdater}.
*/
@Test
void testIntellijJsonResponseMovesUnifiedReleasesToCommunity() throws Exception {

String response = """
[
{
"releases": [
{
"version": "2025.2.6.2",
"downloads": {}
},
{
"version": "2025.2.6.1",
"downloads": {}
},
{
"version": "2024.3.0",
"downloads": {}
}
]
},
{
"releases": [
{
"version": "2025.2.6.1",
"downloads": {}
},
{
"version": "2024.3.0",
"downloads": {}
}
]
}
]
""";

IntellijUrlUpdater updater = new IntellijUrlUpdater();

IntellijJsonObject communityEdition = updater.getJsonObjectFromResponse(response, "intellij");
IntellijJsonObject ultimateEdition = updater.getJsonObjectFromResponse(response, "ultimate");

assertThat(communityEdition.releases()).extracting(IntellijJsonRelease::version)
.containsExactly("2025.2.6.2", "2025.2.6.1", "2024.3.0");
assertThat(ultimateEdition.releases()).extracting(IntellijJsonRelease::version)
.containsExactly("2025.2.6.1", "2024.3.0");
}
}
Loading