Skip to content

Commit 66befb0

Browse files
satorushohwille
andauthored
#1844: Fix VSCode installation hanging in WSL (#1932)
Co-authored-by: Jörg Hohwiller <hohwille@users.noreply.github.com>
1 parent 5dae551 commit 66befb0

7 files changed

Lines changed: 118 additions & 2 deletions

File tree

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Release with new features and bugfixes:
2929
* https://github.com/devonfw/IDEasy/issues/1685[#1685]: Add Nest CLI to IDEasy commandlets
3030
* https://github.com/devonfw/IDEasy/issues/1882[#1882]: Add AWS CDK to IDEasy commandlets
3131
* https://github.com/devonfw/IDEasy/issues/800[#800]: Fix infinite recursion in Sonar start/stop on macOS
32+
* https://github.com/devonfw/IDEasy/issues/1844[#1844]: Fix vscode installation hanging indefinitely in WSL Linux environments
3233

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

cli/src/main/java/com/devonfw/tools/ide/os/SystemInfo.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,12 @@ default boolean isLinux() {
5656
return getOs() == OperatingSystem.LINUX;
5757
}
5858

59+
/**
60+
* @return {@code true} if we are running inside WSL (Windows Subsystem for Linux).
61+
*/
62+
default boolean isWsl() {
63+
64+
return false;
65+
}
66+
5967
}

cli/src/main/java/com/devonfw/tools/ide/os/SystemInfoImpl.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ public class SystemInfoImpl implements SystemInfo {
3232

3333
private final SystemArchitecture architecture;
3434

35+
private final boolean wsl;
36+
3537
/**
3638
* The constructor.
3739
*/
3840
private SystemInfoImpl() {
3941

4042
this(System.getProperty(PROPERTY_OS_NAME).trim(), System.getProperty(PROPERTY_OS_VERSION).trim(),
41-
resolveArchitecture());
43+
resolveArchitecture(), System.getenv("WSL_DISTRO_NAME") != null || System.getenv("WSL_INTEROP") != null);
4244
}
4345

4446
private static String resolveArchitecture() {
@@ -70,12 +72,26 @@ private static String resolveArchitecture() {
7072
*/
7173
public SystemInfoImpl(String osName, String osVersion, String architectureName) {
7274

75+
this(osName, osVersion, architectureName, false);
76+
}
77+
78+
/**
79+
* The constructor.
80+
*
81+
* @param osName the {@link #getOsName() OS name}
82+
* @param osVersion the {@link #getOsVersion() OS version}.
83+
* @param architectureName the {@link #getArchitectureName() architecture name}.
84+
* @param wsl {@code true} if running inside WSL (Windows Subsystem for Linux).
85+
*/
86+
public SystemInfoImpl(String osName, String osVersion, String architectureName, boolean wsl) {
87+
7388
super();
7489
this.osName = osName;
7590
this.osVersion = VersionIdentifier.of(osVersion);
7691
this.architectureName = architectureName;
7792
this.os = OperatingSystem.ofName(this.osName);
7893
this.architecture = detectArchitecture(this.architectureName);
94+
this.wsl = wsl;
7995
}
8096

8197
private static SystemArchitecture detectArchitecture(String architectureName) {
@@ -117,6 +133,12 @@ public SystemArchitecture getArchitecture() {
117133
return this.architecture;
118134
}
119135

136+
@Override
137+
public boolean isWsl() {
138+
139+
return this.wsl;
140+
}
141+
120142
@Override
121143
public String toString() {
122144

cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ public boolean installPlugin(ToolPluginDescriptor plugin, Step step, ProcessCont
9393
@Override
9494
protected void configureToolArgs(ProcessContext pc, ProcessMode processMode, List<String> args) {
9595

96+
if (this.context.getSystemInfo().isWsl()) {
97+
pc.withEnvVar("DONT_PROMPT_WSL_INSTALL", "1");
98+
}
9699
Path vsCodeConf = this.context.getWorkspacePath().resolve(".vscode/.userdata");
97100
pc.addArg("--new-window");
98101
pc.addArg("--user-data-dir=" + vsCodeConf);

cli/src/test/java/com/devonfw/tools/ide/os/SystemInfoImplTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,26 @@ void testLinuxDetection() {
8484
assertThat(systemInfo.toString()).isEqualTo("linux@x64(Linux[3.13.0-74-generic]@x64)");
8585
}
8686

87+
/** Test {@link SystemInfoMock#LINUX_WSL_X64} is detected as WSL and Linux. */
88+
@Test
89+
void testWslDetection() {
90+
91+
// act
92+
SystemInfo systemInfo = SystemInfoMock.LINUX_WSL_X64;
93+
// assert
94+
assertThat(systemInfo.isWsl()).isTrue();
95+
assertThat(systemInfo.isLinux()).isTrue();
96+
assertThat(systemInfo.isWindows()).isFalse();
97+
}
98+
99+
/** Test that plain Linux is not detected as WSL. */
100+
@Test
101+
void testNonWslLinuxIsNotWsl() {
102+
103+
// act
104+
SystemInfo systemInfo = SystemInfoMock.LINUX_X64;
105+
// assert
106+
assertThat(systemInfo.isWsl()).isFalse();
107+
}
108+
87109
}

cli/src/test/java/com/devonfw/tools/ide/os/SystemInfoMock.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ public class SystemInfoMock {
2020
/** {@link OperatingSystem#LINUX} with {@link SystemArchitecture#X64}. */
2121
public static final SystemInfo LINUX_X64 = new SystemInfoImpl("Linux", "3.13.0-74-generic", "x64");
2222

23-
private static final List<SystemInfo> MOCKS = List.of(WINDOWS_X64, MAC_X64, MAC_ARM64, LINUX_X64);
23+
/** {@link OperatingSystem#LINUX} with {@link SystemArchitecture#X64} running inside WSL. */
24+
public static final SystemInfo LINUX_WSL_X64 = new SystemInfoImpl("Linux", "5.15.153.1-microsoft-standard-WSL2", "x86_64", true);
25+
26+
private static final List<SystemInfo> MOCKS = List.of(WINDOWS_X64, MAC_X64, MAC_ARM64, LINUX_X64, LINUX_WSL_X64);
2427

2528
public static SystemInfo of(String osString) {
2629

cli/src/test/java/com/devonfw/tools/ide/tool/vscode/VscodeTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.devonfw.tools.ide.tool.vscode;
22

33
import java.util.ArrayList;
4+
import java.util.HashMap;
45
import java.util.List;
6+
import java.util.Map;
57

68
import org.junit.jupiter.api.Test;
79

810
import com.devonfw.tools.ide.context.AbstractIdeContextTest;
11+
import com.devonfw.tools.ide.context.AbstractIdeTestContext;
912
import com.devonfw.tools.ide.context.IdeTestContext;
1013
import com.devonfw.tools.ide.context.ProcessContextTestImpl;
14+
import com.devonfw.tools.ide.os.SystemInfoMock;
1115
import com.devonfw.tools.ide.process.ProcessContext;
1216
import com.devonfw.tools.ide.process.ProcessMode;
1317
import com.devonfw.tools.ide.process.ProcessResult;
@@ -114,6 +118,34 @@ private void checkInstallation(IdeTestContext context) {
114118
assertThat(context).logAtSuccess().hasMessageContaining("Successfully installed vscode in version 1.92.1");
115119
}
116120

121+
@Test
122+
void testConfigureToolArgsSetsWslEnvVarOnWsl() {
123+
124+
// arrange
125+
IdeTestContext context = newContext(PROJECT_VSCODE);
126+
context.setSystemInfo(SystemInfoMock.LINUX_WSL_X64);
127+
Vscode commandlet = new Vscode(context);
128+
EnvCapturingProcessContext pc = new EnvCapturingProcessContext(context);
129+
// act
130+
commandlet.configureToolArgs(pc, ProcessMode.DEFAULT, List.of());
131+
// assert
132+
assertThat(pc.getEnvVar("DONT_PROMPT_WSL_INSTALL")).isEqualTo("1");
133+
}
134+
135+
@Test
136+
void testConfigureToolArgsDoesNotSetWslEnvVarOnNonWsl() {
137+
138+
// arrange
139+
IdeTestContext context = newContext(PROJECT_VSCODE);
140+
context.setSystemInfo(SystemInfoMock.LINUX_X64);
141+
Vscode commandlet = new Vscode(context);
142+
EnvCapturingProcessContext pc = new EnvCapturingProcessContext(context);
143+
// act
144+
commandlet.configureToolArgs(pc, ProcessMode.DEFAULT, List.of());
145+
// assert
146+
assertThat(pc.getEnvVar("DONT_PROMPT_WSL_INSTALL")).isNull();
147+
}
148+
117149
/**
118150
* Test double for {@link Vscode} that captures CLI arguments passed to {@link #runTool(ProcessContext, ProcessMode, List)}
119151
* so tests can assert command construction without spawning an external process.
@@ -137,4 +169,29 @@ public ProcessResult runTool(ProcessContext pc, ProcessMode processMode, List<St
137169
return new ProcessResultImpl("code", "code", 0, List.of());
138170
}
139171
}
172+
173+
/**
174+
* {@link ProcessContextTestImpl} subclass that captures calls to {@link #withEnvVar(String, String)} for test assertions.
175+
*/
176+
private static class EnvCapturingProcessContext extends ProcessContextTestImpl {
177+
178+
private final Map<String, String> capturedEnvVars = new HashMap<>();
179+
180+
private EnvCapturingProcessContext(IdeTestContext context) {
181+
182+
super(context);
183+
}
184+
185+
@Override
186+
public ProcessContext withEnvVar(String key, String value) {
187+
188+
this.capturedEnvVars.put(key, value);
189+
return super.withEnvVar(key, value);
190+
}
191+
192+
String getEnvVar(String key) {
193+
194+
return this.capturedEnvVars.get(key);
195+
}
196+
}
140197
}

0 commit comments

Comments
 (0)