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
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new UninstallPluginCommandlet(context));
add(new UpgradeCommandlet(context));
add(new TruststoreCommandlet(context));
add(new ContainerCommandlet(context));
add(new Gh(context));
add(new Helm(context));
add(new Java(context));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package com.devonfw.tools.ide.commandlet;

import java.nio.file.Path;
import java.nio.file.Paths;

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.property.BooleanProperty;

/**
* {@link Commandlet} to print the environment variables.
*/
public class ContainerCommandlet extends Commandlet {

private static final String IMAGE_NAME = "ideasy-linux";

private final BooleanProperty rebuild;

/**
* The constructor.
*
* @param context the {@link IdeContext}.
*/
public ContainerCommandlet(IdeContext context) {

super(context);

addKeyword("container");

this.rebuild = add(new BooleanProperty("--rebuild", false, "-r"));
}

@Override
public String getName() {

return "container";
}

@Override
protected void doRun() {

try {

verifyDocker();
verifySshAgent();

Path dockerfile = Paths.get("IDEasy", "docker", "Dockerfile").toAbsolutePath();
Path buildContext = Paths.get("IDEasy").toAbsolutePath();

if (Boolean.TRUE.equals(this.rebuild.getValue())) {
removeImage();
}

if (!imageExists()) {
buildImage(
toWslPath(dockerfile),
toWslPath(buildContext));
}
startContainer();

} catch (Exception e) {

throw new RuntimeException(e);
}
}

private void verifyDocker() throws Exception {

int exitCode = runAndWaitSilent(
"wsl",
"docker",
"--version");

if (exitCode != 0) {
throw new IllegalStateException("Docker is not available.");
}
}

private void verifySshAgent() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSH agent checked but not forwarded: verifySshAgent() only checks the Windows OpenSSH agent; the container gets neither SSH_AUTH_SOCK nor a socket mount.


int exitCode = runAndWaitSilent(
"C:\\Windows\\System32\\OpenSSH\\ssh-add.exe",
"-l");

if (exitCode != 0) {

throw new IllegalStateException(
"No SSH identity loaded in Windows OpenSSH agent.");
}
}

private boolean imageExists() throws Exception {

return runAndWaitSilent(
"wsl",
"docker",
"image",
"inspect",
IMAGE_NAME) == 0;
}

private void removeImage() throws Exception {

runAndWaitSilent(
"wsl",
"docker",
"rmi",
"-f",
IMAGE_NAME);
}

private void buildImage(String dockerfile, String context) throws Exception {

int exitCode = runAndWait(
"wsl",
"docker",
"build",
"-t",
IMAGE_NAME,
"-f",
dockerfile,
context);

if (exitCode != 0) {

throw new RuntimeException(
"Docker build failed with exit code " + exitCode);
}
}

private String getWslHome() throws Exception {

Process process = new ProcessBuilder(
"wsl",
"sh",
"-c",
"printf $HOME")
.start();

return new String(process.getInputStream().readAllBytes()).trim();
}

private void startContainer() throws Exception {

String wslHome = getWslHome();

new ProcessBuilder(
"cmd",
"/c",
"start",
"wsl",
"docker",
"run",
"--rm",
"-it",

"-e", "DISPLAY",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WSLg/display env is non-functional (156-162 )l: -e DISPLAY/WAYLAND_DISPLAY/XDG_RUNTIME_DIR read Windows values that don't work inside WSL.

"-e", "WAYLAND_DISPLAY",
"-e", "XDG_RUNTIME_DIR",
"-e", "PULSE_SERVER",

"-v", "/tmp/.X11-unix:/tmp/.X11-unix",
"-v", "/mnt/wslg:/mnt/wslg",

"-v", wslHome + "/.ssh:/root/.ssh",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~/.ssh mounted but permissions not normalized, no chmod 700/600; Linux OpenSSH refuses world-readable/foreign-uid keys.


"-v", "ideasy-data:/projects",
"-v", "ideasy-home:/root",

IMAGE_NAME)
.start();
}

private int runAndWait(String... command) throws Exception {

ProcessBuilder pb = new ProcessBuilder(command);

pb.inheritIO();

return pb.start().waitFor();
}

private int runAndWaitSilent(String... command) throws Exception {

ProcessBuilder pb = new ProcessBuilder(command);

pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);
pb.redirectError(ProcessBuilder.Redirect.DISCARD);

return pb.start().waitFor();
}

private String toWslPath(Path path) {

return path.toString()
.replace("\\", "/")
.replace("C:", "/mnt/c");
}
}
63 changes: 63 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
FROM ubuntu:24.04
ARG IDEASY_VERSION=2026.08.001
ENV IDE_ROOT=/projects
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
gzip \
tar \
bash \
libfreetype6 \
fontconfig \
libx11-6 \
libxext6 \
libxrender1 \
libxtst6 \
libxi6 \
libxrandr2 \
libxinerama1 \
libxcursor1 \
libxcomposite1 \
libxdamage1 \
openssh-client \
libgtk-3-0 \
libglib2.0-0 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcairo2 \
libpango-1.0-0 \
libgdk-pixbuf-2.0-0 \
libnss3 \
libgbm1 \
libasound2t64 \
&& rm -rf /var/lib/apt/lists/*

# GitHub Copilot CLI

RUN apt-get update \
&& apt-get install -y nodejs npm \
&& npm install -g @github/copilot \
&& rm -rf /var/lib/apt/lists/*

# IDEasy

RUN mkdir -p /opt/ideasy "${IDE_ROOT}" \
&& curl -fsSL \
"https://repo1.maven.org/maven2/com/devonfw/tools/IDEasy/ide-cli/${IDEASY_VERSION}/ide-cli-${IDEASY_VERSION}-linux-x64.tar.gz" \
-o /tmp/ideasy.tar.gz \
&& tar --extract --gzip --no-same-owner \
--file /tmp/ideasy.tar.gz \
--directory /opt/ideasy \
&& cd /opt/ideasy \
&& ./bin/ideasy --batch --force install \
&& rm /tmp/ideasy.tar.gz

ENV PATH="${IDE_ROOT}/_ide/installation/bin:${PATH}"

RUN ideasy --batch create basic-project -

WORKDIR /projects/basic-project

CMD ["bash"]