Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e4d996f
feat: add scripts for gui launcher windows, linux, macos
quando632 Jun 16, 2026
e7c5854
feat: update scripts and using correct icon
quando632 Jun 17, 2026
4fdac6f
Merge branch 'main' into feature/1917-create-desktop-shortcut
quando632 Jun 17, 2026
8ebccaf
Merge branch 'main' into feature/1917-create-desktop-shortcut
quando632 Jun 18, 2026
2a7d872
fix: minor directory bug for linux
quando632 Jun 18, 2026
88bbd81
Merge branch 'feature/1917-create-desktop-shortcut' of https://github…
quando632 Jun 18, 2026
470006e
feat: update linux script
quando632 Jun 19, 2026
86ac392
Merge branch 'main' into feature/1917-create-desktop-shortcut
quando632 Jun 22, 2026
1c7a1cc
fix: not updated root environment
quando632 Jun 23, 2026
87c0e24
fix: reset gitignore and attributes
quando632 Jun 23, 2026
79e973c
doc: add issue to changelog
quando632 Jun 24, 2026
690be00
feat: update window script to not open a new terminal
quando632 Jun 24, 2026
824a9a5
feat: add fix for macOs and Linux script
quando632 Jun 24, 2026
bef16c6
Merge branch 'main' into feature/1917-create-desktop-shortcut
quando632 Jun 24, 2026
193569b
fix: set executable bit on linux/macos gui shortcut scripts
quando632 Jun 25, 2026
033a2bf
Merge branch 'main' into feature/1917-create-desktop-shortcut
quando632 Jun 29, 2026
0928f15
Merge remote-tracking branch 'origin/main' into feature/1917-create-d…
maybeec Jul 4, 2026
7276e4c
Merge remote-tracking branch 'upstream/main' into feature/1917-create…
maybeec Jul 4, 2026
1c43f0e
feat: create shortcut on ide installation
quando632 Jul 7, 2026
1724881
#1917: create desktop shortcut on ide installation
quando632 Jul 7, 2026
1644850
#1917: add tests and fix shortcut creation robustness
quando632 Jul 7, 2026
11b1878
Merge branch 'feature/1917-create-desktop-shortcut' of https://github…
quando632 Jul 7, 2026
e809173
#1917: fix duplicate import and improve comment
quando632 Jul 7, 2026
a216be2
fix: minor code quality issues
quando632 Jul 8, 2026
accfa9c
Merge branch 'main' into feature/1917-create-desktop-shortcut
hohwille Jul 10, 2026
a837418
Update CHANGELOG for release 2026.07.002
hohwille Jul 10, 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/1917[#1917]: Allow the creation of a desktop shortcut for the GUI

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/47?closed=1[milestone 2026.07.002].

Expand Down
109 changes: 109 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.tool;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
Expand Down Expand Up @@ -295,6 +296,7 @@ public void installIdeasy(Path cwd) {
addToShellRc(BASHRC, ideRoot, null);
addToShellRc(ZSHRC, ideRoot, "autoload -U +X bashcompinit && bashcompinit");
installIdeasyWindowsEnv(ideRoot, installationPath);
installDesktopShortcut(installationPath);
IdeLogLevel.SUCCESS.log(LOG, "IDEasy has been installed successfully on your system.");
LOG.warn("IDEasy has been setup for new shells but it cannot work in your current shell(s).\n"
+ "To use it here, run 'source ~/.bashrc' (or your shell config). Otherwise, open a new terminal or reboot.");
Expand Down Expand Up @@ -330,6 +332,113 @@ private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) {
setGitLongpaths();
}
}
private void installDesktopShortcut(Path installationPath) {

try {
if (this.context.getSystemInfo().isLinux()) {
installLinuxDesktopShortcut(installationPath);
} else if (this.context.getSystemInfo().isMac()) {
installMacDesktopShortcut(installationPath);
} else if (this.context.getSystemInfo().isWindows()) {
installWindowsDesktopShortcut(installationPath);
}
} catch (Exception e) {
LOG.warn("Failed to create desktop shortcut.", e);
}
}

private void installLinuxDesktopShortcut(Path installationPath) throws IOException {

Path templateFile = installationPath.resolve("gui/linux/ideasy-gui.desktop");
if (!Files.exists(templateFile)) {
LOG.warn("Desktop file template not found at {}. Skipping desktop shortcut creation.", templateFile);
return;
}
Path ideasyBin = installationPath.resolve("bin/ideasy");
Path logoPath = installationPath.resolve("gui/logo.png");
String content = Files.readString(templateFile)
.replace("@IDEASY_BIN@", ideasyBin.toString())
.replace("@IDEASY_ICON@", logoPath.toString());

Path applicationsDir = this.context.getUserHome().resolve(".local/share/applications");
this.context.getFileAccess().mkdirs(applicationsDir);
Path desktopFile = applicationsDir.resolve("ideasy-gui.desktop");
this.context.getFileAccess().writeFileContent(content, desktopFile);
this.context.getFileAccess().makeExecutable(desktopFile);

// without this, the new entry is only visible in application menus after the next login
try {
this.context.newProcess().executable("update-desktop-database").addArg(applicationsDir.toString())
.run(ProcessMode.DEFAULT_CAPTURE);
} catch (Exception e) {
LOG.debug("update-desktop-database failed (optional): {}", e.getMessage());
}

// Mark as trusted for GNOME 3.28+ so the shortcut is executable without prompting
try {
this.context.newProcess().executable("gio")
.addArgs("set", desktopFile.toString(), "metadata::trusted", "true")
.run(ProcessMode.DEFAULT_CAPTURE);
} catch (Exception e) {
LOG.debug("gio set trusted failed (optional): {}", e.getMessage());
}

IdeLogLevel.SUCCESS.log(LOG, "Created desktop shortcut at {}", desktopFile);
}

private void installMacDesktopShortcut(Path installationPath) {

Path ideasyBin = installationPath.resolve("bin/ideasy");
String escapedBin = ideasyBin.toString().replace("\"", "\\\"");
String content = "#!/bin/bash\n\"" + escapedBin + "\" gui\n";
Path applicationsDir = this.context.getUserHome().resolve("Applications");
this.context.getFileAccess().mkdirs(applicationsDir);
Path commandFile = applicationsDir.resolve("IDEasy GUI.command");
this.context.getFileAccess().writeFileContent(content, commandFile);
this.context.getFileAccess().makeExecutable(commandFile);
IdeLogLevel.SUCCESS.log(LOG, "Created macOS launcher at {}", commandFile);
}

private void installWindowsDesktopShortcut(Path installationPath) {

Path ideasyExe = installationPath.resolve("bin\\ideasy.exe");
Path icoPath = installationPath.resolve("gui\\logo.ico");
// Shell Folders contains the already-expanded Desktop path, including OneDrive-redirected locations
WindowsHelper helper = WindowsHelper.get(this.context);
String desktopStr = helper.getRegistryValue(
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Desktop");
Path desktopPath = (desktopStr != null && !desktopStr.isBlank()) ? Path.of(desktopStr) : this.context.getUserHome().resolve("Desktop");
this.context.getFileAccess().mkdirs(desktopPath);
createWindowsShortcut(desktopPath.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath);
String startMenuStr = helper.getRegistryValue(
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Programs");
Path startMenu = (startMenuStr != null && !startMenuStr.isBlank()) ? Path.of(startMenuStr)
: this.context.getUserHome().resolve("AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs");
if (Files.isDirectory(startMenu)) {
createWindowsShortcut(startMenu.resolve("IDEasy GUI.lnk"), ideasyExe, icoPath);
}
}

private static String psEscapePath(Path path) {
return path.toString().replace("'", "''");
}

private void createWindowsShortcut(Path lnkPath, Path targetExe, Path icoPath) {

String ps = "$ws = New-Object -ComObject WScript.Shell; "
+ "$s = $ws.CreateShortcut('" + psEscapePath(lnkPath) + "'); "
+ "$s.TargetPath = '" + psEscapePath(targetExe) + "'; "
+ "$s.Arguments = 'gui'; "
+ "$s.IconLocation = '" + psEscapePath(icoPath) + ",0'; "
+ "$s.Save()";
try {
this.context.newProcess().executable("powershell").addArgs("-Command", ps)
.run(ProcessMode.DEFAULT_CAPTURE);
IdeLogLevel.SUCCESS.log(LOG, "Created shortcut at {}", lnkPath);
} catch (Exception e) {
LOG.warn("Failed to create shortcut at {}.", lnkPath, e);
}
}

private void setGitLongpaths() {
this.context.getGitContext().findGitRequired();
Expand Down
10 changes: 10 additions & 0 deletions cli/src/main/package/gui/linux/ideasy-gui.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Desktop Entry]
Version=$[project.version]
Type=Application
Name=IDEasy GUI
Comment=Launch IDEasy GUI
Exec=@IDEASY_BIN@ gui
Icon=@IDEASY_ICON@
Terminal=false
Categories=Development;
StartupNotify=true
Binary file added cli/src/main/package/gui/logo.ico
Binary file not shown.
Binary file added cli/src/main/package/gui/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.nio.file.Path;

import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
Expand Down Expand Up @@ -112,6 +113,30 @@ void testInstallIdeasy(String os) {
+ "devon\n"
+ "source ~/.devon/autocomplete\n"
+ addedRcLines);
verifyDesktopShortcut(context, systemInfo, installationPath);
}

private void verifyDesktopShortcut(IdeTestContext context, SystemInfo systemInfo, Path installationPath) {

if (systemInfo.isLinux()) {
Path desktopFile = context.getUserHome().resolve(".local/share/applications/ideasy-gui.desktop");
assertThat(desktopFile).exists();
assertThat(desktopFile).content()
.contains("Exec=" + installationPath.resolve("bin/ideasy") + " gui")
.contains("Icon=" + installationPath.resolve("gui/logo.png"));
} else if (systemInfo.isMac()) {
Path commandFile = context.getUserHome().resolve("Applications/IDEasy GUI.command");
assertThat(commandFile).exists();
assertThat(commandFile).content()
.contains(installationPath.resolve("bin/ideasy").toString())
.contains("gui");
} else if (systemInfo.isWindows()) {
Assumptions.assumeTrue(System.getProperty("os.name", "").toLowerCase().startsWith("win"),
"Skipped: .lnk creation requires PowerShell, which is only available on Windows");
assertThat(context.getUserHome().resolve("Desktop/IDEasy GUI.lnk")).exists();
assertThat(context.getUserHome().resolve(
"AppData/Roaming/Microsoft/Windows/Start Menu/Programs/IDEasy GUI.lnk")).exists();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Desktop Entry]
Version=test-SNAPSHOT

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.

Suggested change
Version=test-SNAPSHOT
Version=$[project.version]

Type=Application
Name=IDEasy GUI
Comment=Launch IDEasy GUI
Exec=@IDEASY_BIN@ gui

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.

Can't we come up with a simple command here that needs no additional replacement?
Something like

bash -c "$IDE_ROOT/_ide/installation/bin/ideasy gui"

Icon=@IDEASY_ICON@
Comment thread
hohwille marked this conversation as resolved.
Terminal=false
Categories=Development;
StartupNotify=true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
logo.ico mock

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.

Can't you just remove this Downloads folder and instead in your test simply copy ../cli/src/main/package/ to the faked HOME directory and use that as "installationPath"?
Then you do not need to mock resources.
And in case you do a *.png or *.ico file it should be valid (it may be an empty image with 0x0 pixels but it should be a valid png/ico file then).
Still the easiest and most maintainable solution should be to delete this Downloads folder and copy the real files.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading