Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/2131[#2131]: Improve `ide upgrade --mode=` auto-completion
* https://github.com/devonfw/IDEasy/issues/1870[#1870]: Add generic get-version implementation for global tools under windows
* https://github.com/devonfw/IDEasy/issues/1558[#1558]: Added installation log information to the "Select Project Folder" and exit dialogs and enhanced `windows-installer/README.adoc`.
* https://github.com/devonfw/IDEasy/issues/1135[#1135]: IDEasy does not set env variables on Windows PowerShell
* https://github.com/devonfw/IDEasy/issues/1135[#1135]: Fix PowerShell env variable initialization on Windows by sourcing functions from the PowerShell profile
Comment on lines +22 to +23

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.

Why was the issue added twice with different titles?
Also this needs to be moved up to the most current release (Sorry, I was too late with reviews).

* https://github.com/devonfw/IDEasy/issues/2187[#2187]: Start SoapUI commandlet in background
* https://github.com/devonfw/IDEasy/issues/2189[#2189]: Integrate Ruff
* https://github.com/devonfw/IDEasy/issues/2126[#2126]: Fix language selection dropdown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ public interface IdeContext extends IdeStartContext {
* configured every time. This is only for settings that have to be the same for every developer in the project. An example would be the number of spaces used
* for indentation and other code-formatting settings. If all developers in a project team use the same formatter settings, this will actively prevent
* diff-wars. However, the entire team needs to agree on these settings.<br> Never configure aspects inside this update folder that may be of personal flavor
* such as the color theme. Otherwise developers will hate you as you actively take away their freedom to customize the IDE to their personal needs and
* wishes. Therefore do all "biased" or "flavored" configurations in {@link #FOLDER_SETUP setup} so these are only pre-configured but can be changed by the
* such as the color theme. Otherwise, developers will hate you as you actively take away their freedom to customize the IDE to their personal needs and
* wishes. Therefore, do all "biased" or "flavored" configurations in {@link #FOLDER_SETUP setup} so these are only pre-configured but can be changed by the
* user as needed.
*/
String FOLDER_UPDATE = "update";
Expand Down
126 changes: 119 additions & 7 deletions cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public class IdeasyCommandlet extends MvnBasedLocalToolCommandlet {

private static final String BASH_CODE_SOURCE_FUNCTIONS = "source \"$IDE_ROOT/_ide/installation/functions\"";

public static final String POWERSHELL_CODE_SOURCE_FUNCTIONS =
". \"$env:IDE_ROOT\\_ide\\installation\\functions.ps1\"";

/** The {@link #getName() tool name}. */
public static final String TOOL_NAME = "ideasy";
public static final String BASHRC = ".bashrc";
Expand All @@ -65,6 +68,7 @@ public class IdeasyCommandlet extends MvnBasedLocalToolCommandlet {
//artifactName: String, required: boolean
"bin", true,
"functions", true,
"functions.ps1", true,
"internal", true,
"gui", true,
"system", true,
Expand Down Expand Up @@ -299,10 +303,12 @@ public void installIdeasy(Path cwd) {
addToShellRc(BASHRC, ideRoot, null);
addToShellRc(ZSHRC, ideRoot, "autoload -U +X bashcompinit && bashcompinit");
installIdeasyWindowsEnv(ideRoot, installationPath);
configurePowerShellProfiles(true);
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.");
+ "To use it here, reload your shell configuration (e.g. 'source ~/.bashrc' in bash "
+ "or '. $PROFILE.CurrentUserAllHosts' in PowerShell). Otherwise, open a new terminal or reboot.");
}

private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) {
Expand Down Expand Up @@ -443,6 +449,114 @@ private void createWindowsShortcut(Path lnkPath, Path targetExe, Path icoPath) {
}
}

private void configurePowerShellProfiles(boolean install) {

if (!this.context.getSystemInfo().isWindows()) {
return;
}

// Windows PowerShell 5.x and PowerShell 7+ have different profile locations.
modifyPowerShellProfile("powershell", install);
modifyPowerShellProfile("pwsh", install);
Comment on lines +459 to +460

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.

This code will also run in JUnits and however tweak the real end-users powershell setup.
We want to avoid such side-effects and ensure IdeTestContext ensures encapsulation.
I would therefore suggest to extend this special feature to WindowsHelper that has its own Mock implementation so we do not manipulate or even "destroy" the end-users environment when he executes JUnit tests.

Or am I missing something and this is already mocked away via ProcessContext so the exeuctions of powershell and pwsh will more or less be void?

}
Comment thread
cap-juan marked this conversation as resolved.

private void modifyPowerShellProfile(String executable, boolean install) {

Path profilePath = getPowerShellProfilePath(executable);
if (profilePath == null) {
return;
}

logIdeasyModification(profilePath.toString(), install);

FileAccess fileAccess = this.context.getFileAccess();
List<String> lines = fileAccess.readFileLines(profilePath);

if (lines == null && !install) {
return;
}

List<String> modifiedLines = modifyPowerShellProfileLines(lines, install);

Path parent = profilePath.getParent();
if (parent != null) {
fileAccess.mkdirs(parent);
}

fileAccess.writeFileLines(modifiedLines, profilePath);
LOG.debug("Successfully updated PowerShell profile {}", profilePath);
}

List<String> modifyPowerShellProfileLines(List<String> lines, boolean install) {

List<String> modifiedLines;

if (lines == null) {
modifiedLines = new ArrayList<>();
} else {
modifiedLines = new ArrayList<>(lines);
}

boolean configured = modifiedLines.stream()
.map(String::trim)
.anyMatch(POWERSHELL_CODE_SOURCE_FUNCTIONS::equals);

if (install) {
if (!configured) {
modifiedLines.add(POWERSHELL_CODE_SOURCE_FUNCTIONS);
}
} else {
modifiedLines.removeIf(
line -> line.trim().equals(POWERSHELL_CODE_SOURCE_FUNCTIONS));
}

return modifiedLines;
}

private void logIdeasyModification(String target, boolean configure) {
String action = configure ? "Configuring" : "Removing";
LOG.info("{} IDEasy in {}", action, target);
}

private Path getPowerShellProfilePath(String executable) {

try {
ProcessResult result = this.context.newProcess()
.executable(executable)
.addArgs("-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts")
.run(ProcessMode.DEFAULT_CAPTURE);

if (!result.isSuccessful()) {
LOG.debug("{} is not available or its profile could not be determined.", executable);
return null;
}

List<String> output = result.getOut();
if (output == null || output.isEmpty()) {
LOG.debug("{} returned no PowerShell profile path.", executable);
return null;
}

String profilePath = output.stream()
.map(String::strip)
.filter(line -> !line.isEmpty())
.findFirst()
.orElse(null);

if (profilePath == null) {
LOG.debug("{} returned no PowerShell profile path.", executable);
return null;
}

return Path.of(profilePath);

} catch (Exception e) {
// pwsh is optional. Windows PowerShell normally exists on supported Windows versions.
LOG.debug("Could not determine profile for {}: {}", executable, e.getMessage());
return null;
}
}

private void setGitLongpaths() {
this.context.getGitContext().findGitRequired();
Path configPath = this.context.getUserHome().resolve(".gitconfig");
Expand Down Expand Up @@ -694,11 +808,8 @@ private void removeFromShellRc(String filename, Path ideRoot) {
*/
private void modifyShellRc(String filename, Path ideRoot, boolean add, String extraLine) {

if (add) {
LOG.info("Configuring IDEasy in {}", filename);
} else {
LOG.info("Removing IDEasy from {}", filename);
}
logIdeasyModification(filename, add);

Path rcFile = this.context.getUserHome().resolve(filename);
FileAccess fileAccess = this.context.getFileAccess();
List<String> lines = fileAccess.readFileLines(rcFile);
Expand Down Expand Up @@ -791,6 +902,7 @@ public void uninstallIdeasy() {
removeFromShellRc(ZSHRC, ideRoot);
Path idePath = this.context.getIdePath();
uninstallIdeasyWindowsEnv(ideRoot);
configurePowerShellProfiles(false);
uninstallIdeasyIdePath(idePath);
deleteDownloadCache();
IdeLogLevel.SUCCESS.log(LOG, "IDEasy has been uninstalled from your system.");
Expand All @@ -816,7 +928,7 @@ private void tryDeleteDownloadCache(Path path) {
try {
this.context.getFileAccess().delete(path);
} catch (IllegalStateException e) {
// best effort - on macOS ~/Downloads can deny access (EPERM), don't fail the whole uninstall over it
// best effort - on macOS ~/Downloads can deny access (EPERM), don't fail the whole uninstallation over it
String cause = (e.getCause() != null) ? e.getCause().getMessage() : e.getMessage();
LOG.warn("Could not delete download cache at {} ({}). The folder will be left in place; you can remove it manually via Finder.", path, cause);
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/main/package/functions
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function icd() {
echo "Change directory and initialize IDEasy environment."
echo "The icd command can be used as an alternative to the regular cd command."
echo "As additional effect, it will automatically update your environment variables."
echo "Futher, it allows shortcuts to quickly navigate to common directories of IDEasy."
echo "Further, it allows shortcuts to quickly navigate to common directories of IDEasy."
echo "Without any arguments icd will navigate to your top-level project directory (IDE_HOME)."
echo
echo "OPTIONS:"
Expand Down
Loading