Skip to content

Commit 7fe0b65

Browse files
authored
Merge branch 'main' into feature/1992-improve-gitcontextmock
2 parents 149dffb + 423b614 commit 7fe0b65

27 files changed

Lines changed: 1087 additions & 3201 deletions

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
dotnet tool install --global wix --version 5.0.2
5959
wix extension add WixToolset.UI.wixext/5.0.2
6060
wix extension add WixToolset.Util.wixext/5.0.2
61-
wix build Package.wxs WixUI_IDEasySetup.wxs -loc Package.en-us.wxl -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext -o ideasy.msi
61+
wix build Package.wxs WixUI_IDEasySetup.wxs WixUI_FirstStepsDlg.wxs -loc Package.en-us.wxl -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext -o ideasy.msi
6262
- name: Upload unsigned MSI
6363
if: runner.os == 'Windows'
6464
id: upload-msi

CHANGELOG.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE
66

77
Release with new features and bugfixes:
88

9+
* https://github.com/devonfw/IDEasy/issues/1937[#1937]: Link content from settings into workspaces
10+
* https://github.com/devonfw/IDEasy/issues/1056[#1056]: Improve MSI license display by generating formatted RTF from AsciiDoc
911
* https://github.com/devonfw/IDEasy/issues/2052[#2052]: Split issue statistics in 2 pie-charts
1012
* https://github.com/devonfw/IDEasy/issues/822[#822] : Added Cwd path limit in shell
13+
* https://github.com/devonfw/IDEasy/issues/1517[#1517]: Fix IDEasy MSI does not configure Windows Terminal for git-bash
14+
* https://github.com/devonfw/IDEasy/issues/1227[#1227]: Added Setup instrcutions to the Windows installer
1115

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

1418
== 2026.06.001
1519

1620
Release with new features and bugfixes:
1721

18-
* https://github.com/devonfw/IDEasy/issues/797[#797]: Fix VSCode install on macOS
22+
* https://github.com/devonfw/IDEasy/issues/797[#797]: Fix VSCode install on macOS
1923
* https://github.com/devonfw/IDEasy/issues/1956[#1956]: Merging MAVEN_ARGS buggy
2024
* https://github.com/devonfw/IDEasy/issues/1978[#1978]: Enhanced the quality status documentation
2125
* https://github.com/devonfw/IDEasy/issues/1946[#1946]: Add Spyder Python IDE

build-local-dev.sh

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [ -z "${IDE_ROOT:-}" ]; then
5+
echo "Error: IDE_ROOT is not set."
6+
exit 1
7+
fi
8+
9+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10+
PROJECT_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)"
11+
12+
CLI_DIR="$SCRIPT_DIR/cli"
13+
TARGET_DIR="$CLI_DIR/target"
14+
PACKAGE_DIR="$CLI_DIR/src/main/package"
15+
16+
PROJECT_SOFTWARE_DIR="$PROJECT_DIR/software"
17+
GRAALVM_DIR="$PROJECT_SOFTWARE_DIR/extra/graalvm"
18+
19+
LOCAL_DEV="$IDE_ROOT/_ide/software/maven/ideasy/ideasy/local-dev"
20+
INSTALLATION_LINK="$IDE_ROOT/_ide/installation"
21+
22+
echo "Building IDEasy native image..."
23+
24+
if [ ! -d "$GRAALVM_DIR" ]; then
25+
echo "Error: GraalVM is not installed for this IDEasy project:"
26+
echo "$GRAALVM_DIR"
27+
echo
28+
echo "Please install GraalVM first:"
29+
echo "ide install graalvm"
30+
exit 1
31+
fi
32+
33+
export PATH="$GRAALVM_DIR/bin:$PATH"
34+
35+
cd "$CLI_DIR"
36+
mvn -B -ntp -Pnative -DskipTests=true package
37+
38+
echo "Preparing local-dev installation..."
39+
rm -rf "$LOCAL_DEV"
40+
mkdir -p "$LOCAL_DEV"
41+
42+
echo "Copying package contents..."
43+
44+
if [ ! -d "$PACKAGE_DIR" ]; then
45+
echo "Error: Package directory not found: $PACKAGE_DIR"
46+
exit 1
47+
fi
48+
49+
cp -R "$PACKAGE_DIR"/. "$LOCAL_DEV"/
50+
51+
OS_NAME="$(uname -s)"
52+
53+
echo "Creating local-dev software version marker..."
54+
echo "local-dev-version" > "$LOCAL_DEV/.ide.software.version"
55+
56+
mkdir -p "$LOCAL_DEV/bin"
57+
58+
echo "Copying IDEasy executable and native libraries..."
59+
60+
if [ -f "$TARGET_DIR/ideasy.exe" ]; then
61+
cp "$TARGET_DIR/ideasy.exe" "$LOCAL_DEV/bin/ideasy.exe"
62+
fi
63+
64+
if [ -f "$TARGET_DIR/ideasy" ]; then
65+
cp "$TARGET_DIR/ideasy" "$LOCAL_DEV/bin/ideasy"
66+
chmod +x "$LOCAL_DEV/bin/ideasy"
67+
fi
68+
69+
if [ ! -f "$LOCAL_DEV/bin/ideasy.exe" ] && [ ! -f "$LOCAL_DEV/bin/ideasy" ]; then
70+
echo "Error: No ideasy executable found in $TARGET_DIR"
71+
exit 1
72+
fi
73+
74+
if [ -f "$LOCAL_DEV/functions" ]; then
75+
chmod +x "$LOCAL_DEV/functions"
76+
fi
77+
78+
if [ -f "$LOCAL_DEV/setup" ]; then
79+
chmod +x "$LOCAL_DEV/setup"
80+
fi
81+
82+
echo "Updating IDEasy installation link..."
83+
84+
if ! command -v ideasy > /dev/null 2>&1; then
85+
echo "Error: ideasy command not found."
86+
exit 1
87+
fi
88+
89+
IDEASY_CMD="$(readlink -f "$(command -v ideasy)")"
90+
91+
if [ -L "$INSTALLATION_LINK" ]; then
92+
unlink "$INSTALLATION_LINK"
93+
elif [ -e "$INSTALLATION_LINK" ]; then
94+
echo "Error: $INSTALLATION_LINK exists but is not a symbolic link."
95+
echo "Aborting to avoid deleting a real folder."
96+
exit 1
97+
fi
98+
99+
"$IDEASY_CMD" ln -s "$LOCAL_DEV" "$INSTALLATION_LINK"
100+
101+
echo "Done."
102+
echo "You can test it with:"
103+
echo "ide ..."
104+
echo
105+
echo "To switch back to the latest stable IDEasy version, run:"
106+
echo "ideasy upgrade --mode=stable"
107+
echo
108+
echo "To switch to the latest snapshot IDEasy version, run:"
109+
echo "ideasy upgrade --mode=snapshot"

cli/src/main/java/com/devonfw/tools/ide/cli/CliArguments.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class CliArguments implements Iterator<CliArgument> {
1515

1616
private boolean splitShortOpts;
1717

18+
/** If true, do not discard the special end-of-options token "--" but keep it as current argument. */
19+
private boolean preserveEndOptionsToken;
20+
1821
/**
1922
* The constructor.
2023
*
@@ -41,6 +44,17 @@ public CliArguments(CliArgument arg) {
4144
this.initialArgument = arg;
4245
this.endOptions = endOpts;
4346
this.splitShortOpts = splitShortOpts;
47+
this.preserveEndOptionsToken = false;
48+
setCurrent(arg);
49+
}
50+
51+
CliArguments(CliArgument arg, boolean endOpts, boolean splitShortOpts, boolean preserveEndOptionsToken) {
52+
53+
super();
54+
this.initialArgument = arg;
55+
this.endOptions = endOpts;
56+
this.splitShortOpts = splitShortOpts;
57+
this.preserveEndOptionsToken = preserveEndOptionsToken;
4458
setCurrent(arg);
4559
}
4660

@@ -89,7 +103,11 @@ private void setCurrent(CliArgument arg) {
89103

90104
if (arg.isEndOptions()) {
91105
endOptions();
92-
this.currentArg = arg.getNext();
106+
if (this.preserveEndOptionsToken) {
107+
this.currentArg = arg;
108+
} else {
109+
this.currentArg = arg.getNext();
110+
}
93111
} else {
94112
this.currentArg = arg;
95113
}
@@ -155,8 +173,7 @@ public CliArgument next() {
155173
* @return a copy of this {@link CliArguments} to fork a CLI matching of auto-completion.
156174
*/
157175
public CliArguments copy() {
158-
159-
return new CliArguments(this.currentArg, this.endOptions, this.splitShortOpts);
176+
return new CliArguments(this.currentArg, this.endOptions, this.splitShortOpts, this.preserveEndOptionsToken);
160177
}
161178

162179
@Override
@@ -171,7 +188,17 @@ public String toString() {
171188
*/
172189
public static CliArguments ofCompletion(String... args) {
173190

174-
return new CliArguments(CliArgument.ofCompletion(args));
191+
CliArguments ca = new CliArguments(CliArgument.ofCompletion(args));
192+
ca.preserveEndOptionsToken = true;
193+
return ca;
194+
}
195+
196+
/**
197+
* Enable preserving the literal end-of-options token "--" so it is not consumed by the parser. This is used for completion mode where the user input should
198+
* be preserved verbatim.
199+
*/
200+
public void preserveEndOptionsToken() {
201+
this.preserveEndOptionsToken = true;
175202
}
176203

177204
}

cli/src/main/java/com/devonfw/tools/ide/cli/Ideasy.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
88

9+
import com.devonfw.tools.ide.commandlet.CompleteCommandlet;
910
import com.devonfw.tools.ide.commandlet.ContextCommandlet;
1011
import com.devonfw.tools.ide.context.AbstractIdeContext;
1112
import com.devonfw.tools.ide.context.IdeContext;
@@ -103,6 +104,9 @@ private void initContext(CliArguments arguments) {
103104
String key = current.getKey();
104105
Property<?> property = contextCommandlet.getOption(key);
105106
if (property == null) {
107+
if (CompleteCommandlet.NAME.equals(current.get())) {
108+
arguments.preserveEndOptionsToken();
109+
}
106110
break;
107111
}
108112
String value = current.getValue();

cli/src/main/java/com/devonfw/tools/ide/commandlet/CompleteCommandlet.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
*/
1414
public final class CompleteCommandlet extends Commandlet {
1515

16+
/** The name of the complete commandlet. */
17+
public static final String NAME = "complete";
18+
1619
/** {@link StringProperty} with the current CLI arguments to complete. */
1720
public final StringProperty args;
1821

@@ -31,7 +34,7 @@ public CompleteCommandlet(IdeContext context) {
3134
@Override
3235
public String getName() {
3336

34-
return "complete";
37+
return NAME;
3538
}
3639

3740
@Override

cli/src/main/java/com/devonfw/tools/ide/git/repository/RepositoryCommandlet.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,19 @@ private void doImportRepository(RepositoryConfig config) {
141141
workspaces.remove(IdeContext.WORKSPACE_MAIN);
142142
workspaces.addFirst(IdeContext.WORKSPACE_MAIN);
143143
}
144+
144145
Path firstRepository = null;
146+
147+
if (config.isVirtualSettingsRepository()) {
148+
firstRepository = this.context.getSettingsPath();
149+
}
150+
145151
for (String workspaceName : workspaces) {
146152
Path workspacePath = this.context.getWorkspacePath(workspaceName);
147153
Path repositoryPath = workspacePath.resolve(repositoryRelativePath);
148154
Path repositoryCreatedStatusFile = ideStatusDir.resolve("repository." + config.id() + "." + workspaceName);
149155
boolean createRepository = true;
150-
if (Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
156+
if (!config.isVirtualSettingsRepository() && Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
151157
if (firstRepository == null) {
152158
firstRepository = repositoryPath;
153159
}
@@ -173,27 +179,36 @@ private void doImportRepository(RepositoryConfig config) {
173179
buildRepository(config, repositoryPath);
174180
importRepository(config, repositoryPath, config.id());
175181
}
176-
} else {
182+
} else if (!config.isVirtualSettingsRepository()) {
177183
fileAccess.mkdirs(repositoryPath.getParent());
178184
fileAccess.symlink(firstRepository, repositoryPath);
179185
}
180186
}
181-
if (Files.exists(repositoryPath)) {
187+
Path linkRepositoryPath = config.isVirtualSettingsRepository() ? firstRepository : repositoryPath;
188+
if (Files.exists(linkRepositoryPath)) {
182189
for (RepositoryLink link : config.links()) {
183-
createRepositoryLink(link, repositoryPath, workspacePath);
190+
createRepositoryLink(link, linkRepositoryPath, workspacePath);
184191
}
185192
}
186193
}
187194
}
188195

196+
private boolean linkTargetExists(RepositoryLink link, Path linkTargetPath) {
197+
198+
if (!Files.exists(linkTargetPath)) {
199+
LOG.error("Skipping link from '{}' to '{}' because target does not exist: {}", link.link(), link.target(), linkTargetPath);
200+
return false;
201+
}
202+
return true;
203+
}
204+
189205
private void createRepositoryLink(RepositoryLink link, Path repositoryPath, Path workspacePath) {
190206
Path linkPath = workspacePath.resolve(link.link());
191207
String target = link.target();
192208
Path linkTargetPath;
193209
if ((target != null) && !target.isBlank()) {
194210
linkTargetPath = repositoryPath.resolve(target);
195-
if (!Files.exists(linkTargetPath)) {
196-
LOG.error("Skipping link from '{}' to '{}' because target does not exist: {}", link.link(), target, linkTargetPath);
211+
if (!linkTargetExists(link, linkTargetPath)) {
197212
return;
198213
}
199214
} else {

cli/src/main/java/com/devonfw/tools/ide/git/repository/RepositoryConfig.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,7 @@ public GitUrl asGitUrl() {
6363
public static RepositoryConfig loadProperties(Path filePath, IdeContext context) {
6464

6565
RepositoryProperties properties = new RepositoryProperties(filePath, context);
66-
String filename = filePath.getFileName().toString();
67-
final String id;
68-
if (filename.endsWith(IdeContext.EXT_PROPERTIES)) {
69-
id = filename.substring(0, filename.length() - IdeContext.EXT_PROPERTIES.length());
70-
} else {
71-
id = filename;
72-
}
66+
String id = properties.getId();
7367
RepositoryConfig config = new RepositoryConfig(id, properties.getPath(), properties.getWorkingSets(), properties.getWorkspaces(), properties.getGitUrl(),
7468
properties.getGitBranch(), properties.getBuildPath(), properties.getBuildCmd(), properties.getImports(), properties.getLinks(), properties.isActive());
7569
if (properties.isInvalid()) {
@@ -78,4 +72,8 @@ public static RepositoryConfig loadProperties(Path filePath, IdeContext context)
7872
return config;
7973
}
8074

75+
public boolean isVirtualSettingsRepository() {
76+
return IdeContext.SETTINGS_REPOSITORY_KEYWORD.equals(this.id)
77+
&& (this.gitUrl == null || this.gitUrl.isBlank());
78+
}
8179
}

cli/src/main/java/com/devonfw/tools/ide/git/repository/RepositoryProperties.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ public RepositoryProperties(Path file, IdeContext context) {
6666
this.properties = properties;
6767
}
6868

69+
/**
70+
* @return the ID derived from the filename without {@link IdeContext#EXT_PROPERTIES}.
71+
*/
72+
public String getId() {
73+
74+
String filename = this.file.getFileName().toString();
75+
if (filename.endsWith(IdeContext.EXT_PROPERTIES)) {
76+
return filename.substring(0, filename.length() - IdeContext.EXT_PROPERTIES.length());
77+
}
78+
return filename;
79+
}
80+
6981
/**
7082
* @param name the name of the requested property.
7183
* @return the value of the requested property or {@code null} if undefined.
@@ -171,7 +183,12 @@ public String getWorkingSets() {
171183
*/
172184
public String getGitUrl() {
173185

174-
return getProperty(PROPERTY_GIT_URL, true);
186+
return getProperty(PROPERTY_GIT_URL, !isSettingsProperties());
187+
}
188+
189+
private boolean isSettingsProperties() {
190+
191+
return IdeContext.FOLDER_SETTINGS.equals(getId());
175192
}
176193

177194
/**

cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Iterator;
99
import java.util.List;
1010
import java.util.Map;
11+
import java.util.Objects;
1112
import java.util.Set;
1213
import java.util.regex.Matcher;
1314
import java.util.regex.Pattern;
@@ -490,13 +491,13 @@ private void configureGitBashProfile(Path settingsPath, String bashPath) throws
490491
ObjectNode gitBashProfile = mapper.createObjectNode();
491492
String newGuid = "{2ece5bfe-50ed-5f3a-ab87-5cd4baafed2b}";
492493
String iconPath = getGitBashIconPath(bashPath);
493-
String startingDirectory = this.context.getIdeRoot().toString();
494+
Path startingDirectory = Objects.requireNonNullElse(this.context.getIdeRoot(), this.context.getUserHome());
494495

495496
gitBashProfile.put("guid", newGuid);
496497
gitBashProfile.put("name", "Git Bash");
497498
gitBashProfile.put("commandline", bashPath);
498499
gitBashProfile.put("icon", iconPath);
499-
gitBashProfile.put("startingDirectory", startingDirectory);
500+
gitBashProfile.put("startingDirectory", startingDirectory.toString());
500501

501502
((ArrayNode) profilesList).add(gitBashProfile);
502503

0 commit comments

Comments
 (0)