diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index b95c6b8a9f..ce433d61a2 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -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/1909[#1909]: add commandlet for Maven Daemon 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]. diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 19c53c7975..7648df2e88 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -8,8 +8,6 @@ import java.util.Map; import java.util.NoSuchElementException; -import com.devonfw.tools.ide.tool.inso.Inso; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +35,7 @@ import com.devonfw.tools.ide.tool.gradle.Gradle; import com.devonfw.tools.ide.tool.gui.Gui; import com.devonfw.tools.ide.tool.helm.Helm; +import com.devonfw.tools.ide.tool.inso.Inso; import com.devonfw.tools.ide.tool.intellij.Intellij; import com.devonfw.tools.ide.tool.jasypt.Jasypt; import com.devonfw.tools.ide.tool.java.Java; @@ -45,8 +44,9 @@ import com.devonfw.tools.ide.tool.kotlinc.KotlincNative; import com.devonfw.tools.ide.tool.kubectl.KubeCtl; import com.devonfw.tools.ide.tool.lazydocker.LazyDocker; -import com.devonfw.tools.ide.tool.mvn.Mvn; import com.devonfw.tools.ide.tool.msvc.Msvc; +import com.devonfw.tools.ide.tool.mvn.Mvn; +import com.devonfw.tools.ide.tool.mvnd.Mvnd; import com.devonfw.tools.ide.tool.nest.Nest; import com.devonfw.tools.ide.tool.ng.Ng; import com.devonfw.tools.ide.tool.node.Node; @@ -172,6 +172,7 @@ public CommandletManagerImpl(IdeContext context) { add(new Nest(context)); add(new Cdk(context)); add(new Claude(context)); + add(new Mvnd(context)); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MavenCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MavenCommandlet.java new file mode 100644 index 0000000000..b11da4369f --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MavenCommandlet.java @@ -0,0 +1,75 @@ +package com.devonfw.tools.ide.tool.mvn; + + +import java.util.Set; + + +import com.devonfw.tools.ide.common.Tag; +import com.devonfw.tools.ide.completion.AutoCompletionRegistry; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.tool.LocalToolCommandlet; + + +/** + * Abstract base class for Maven-compatible tool commandlets. + */ +public abstract class MavenCommandlet extends LocalToolCommandlet { + + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + * @param tool the name of the tool. + */ + protected MavenCommandlet(IdeContext context, String tool) { + + super(context, tool, Set.of(Tag.JAVA, Tag.BUILD)); + } + + /** + * Initializes common Maven auto-completion candidates. + * + * @param registry the {@link AutoCompletionRegistry} to initialize. + */ + @Override + protected void initAutoCompletionRegistry(AutoCompletionRegistry registry) { + + registry.add("clean"); + registry.add("package"); + registry.add("install"); + registry.add("deploy"); + registry.add("test"); + registry.add("verify"); + registry.add("validate"); + registry.add("compile"); + registry.add("dependency:tree"); + registry.add("dependency:list"); + registry.add("help:effective-settings"); + registry.add("-DskipTests"); + registry.add("-Dmaven.test.skip=true"); + registry.add("exec:java"); + registry.add("-Dexec.mainClass="); + registry.add("-Dexec.args="); + registry.add("-P"); + registry.add("-pl"); + registry.add("-am"); + registry.add("-amd"); + registry.add("--also-make"); + registry.add("--also-make-dependents"); + registry.add("--fail-at-end"); + registry.add("--fail-fast"); + registry.add("-T1C"); + registry.add("-q"); + registry.add("-X"); + registry.add("-e"); + registry.add("-U"); + registry.add("-o"); + registry.add("-f"); + registry.add("-s"); + registry.add("-rf"); + registry.add("-DdeployAtEnd=true"); + } +} + + diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java index d91b8ccd99..d3675707a8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java @@ -13,8 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.devonfw.tools.ide.common.Tag; -import com.devonfw.tools.ide.completion.AutoCompletionRegistry; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.git.GitContext; import com.devonfw.tools.ide.io.FileAccess; @@ -22,16 +20,16 @@ import com.devonfw.tools.ide.process.ProcessMode; import com.devonfw.tools.ide.process.ProcessResult; import com.devonfw.tools.ide.step.Step; -import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; import com.devonfw.tools.ide.tool.ToolInstallRequest; import com.devonfw.tools.ide.variable.IdeVariables; import com.devonfw.tools.ide.variable.VariableSyntax; + /** * {@link ToolCommandlet} for maven. */ -public class Mvn extends LocalToolCommandlet { +public class Mvn extends MavenCommandlet { private static final Logger LOG = LoggerFactory.getLogger(Mvn.class); @@ -70,39 +68,9 @@ public class Mvn extends LocalToolCommandlet { */ public Mvn(IdeContext context) { - super(context, "mvn", Set.of(Tag.JAVA, Tag.BUILD)); + super(context, "mvn"); } - /** - * Initializes Maven-specific auto-completion candidates. - * - * @param registry the {@link AutoCompletionRegistry} to initialize. - */ - @Override - protected void initAutoCompletionRegistry(AutoCompletionRegistry registry) { - - registry.add("clean"); - registry.add("package"); - registry.add("install"); - registry.add("deploy"); - registry.add("test"); - registry.add("verify"); - registry.add("validate"); - registry.add("compile"); - registry.add("dependency:tree"); - registry.add("dependency:list"); - registry.add("help:effective-settings"); - registry.add("-DskipTests"); - registry.add("exec:java"); - registry.add("-Dexec.mainClass="); - registry.add("-Dexec.args="); - registry.add("--also-make"); - registry.add("--also-make-dependents"); - registry.add("--fail-at-end"); - registry.add("--fail-fast"); - registry.add("-T1C"); - registry.add("-DdeployAtEnd=true"); - } @Override protected void configureToolBinary(ProcessContext pc, ProcessMode processMode) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvnd/Mvnd.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvnd/Mvnd.java new file mode 100644 index 0000000000..9031b45b55 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvnd/Mvnd.java @@ -0,0 +1,86 @@ +package com.devonfw.tools.ide.tool.mvnd; + + +import com.devonfw.tools.ide.completion.AutoCompletionRegistry; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.tool.mvn.MavenCommandlet; + +/** + * {@link ToolCommandlet} for maven daemon. + */ +public class Mvnd extends MavenCommandlet { + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public Mvnd(IdeContext context) { + + super(context, "mvnd"); + } + + /** + * Initializes Maven Daemon-specific auto-completion candidates. + * + * @param registry the {@link AutoCompletionRegistry} to initialize. + */ + @Override + protected void initAutoCompletionRegistry(AutoCompletionRegistry registry) { + + super.initAutoCompletionRegistry(registry); + registry.add("--status"); + registry.add("--stop"); + registry.add("--purge"); + registry.add("--completion"); + registry.add("--diag"); + registry.add("--file"); + registry.add("-Djava.home="); + registry.add("-Djdk.java.options="); + registry.add("-Dmaven.multiModuleProjectDirectory="); + registry.add("-Dmaven.repo.local="); + registry.add("--settings"); + registry.add("-Dmvnd.buildTime="); + registry.add("--builder"); + registry.add("-Dmvnd.cancelConnectTimeout="); + registry.add("-Dmvnd.connectTimeout="); + registry.add("-Dmvnd.coreExtensionsExclude="); + registry.add("-Dmvnd.daemonStorage="); + registry.add("-Dmvnd.debug="); + registry.add("-Dmvnd.debug.address="); + registry.add("-Dmvnd.duplicateDaemonGracePeriod="); + registry.add("-Dmvnd.enableAssertions="); + registry.add("-Dmvnd.expirationCheckDelay="); + registry.add("-Dmvnd.home="); + registry.add("-Dmvnd.idleTimeout="); + registry.add("-Dmvnd.jvmArgs="); + registry.add("-Dmvnd.keepAlive="); + registry.add("-Dmvnd.logPurgePeriod="); + registry.add("-Dmvnd.maxHeapSize="); + registry.add("-Dmvnd.maxLostKeepAlive="); + registry.add("-Dmvnd.minHeapSize="); + registry.add("-Dmvnd.minThreads="); + registry.add("-Dmvnd.noBuffering="); + registry.add("-Dmvnd.noDaemon="); + registry.add("-Dmvnd.noModelCache="); + registry.add("-Dmvnd.pluginRealmEvictPattern="); + registry.add("-Dmvnd.propertiesPath="); + registry.add("--raw-streams"); + registry.add("-Dmvnd.registry="); + registry.add("-Dmvnd.rollingWindowSize="); + registry.add("--serial"); + registry.add("-Dmvnd.socketConnectTimeout="); + registry.add("-Dmvnd.socketFamily="); + registry.add("-Dmvnd.threadStackSize="); + registry.add("--threads"); + registry.add("-Dstyle.color="); + registry.add("-Duser.dir="); + registry.add("-Duser.home="); + } + + @Override + public String getToolHelpArguments() { + return "--help"; + } +} diff --git a/cli/src/main/resources/nls/Help.properties b/cli/src/main/resources/nls/Help.properties index c197f8cb0b..a362bbf35d 100644 --- a/cli/src/main/resources/nls/Help.properties +++ b/cli/src/main/resources/nls/Help.properties @@ -91,6 +91,8 @@ cmd.msvc=Tool commandlet for MSVC (Microsoft Visual C++ Build Tools). cmd.msvc.detail=MSVC provides the C++ compiler and build tools required by Rust on Windows. Detailed documentation can be found at https://visualstudio.microsoft.com/visual-cpp-build-tools/ cmd.mvn=Tool commandlet for Maven (Build-Tool). cmd.mvn.detail=Apache Maven is a build automation and dependency management tool for Java projects. Detailed documentation can be found at https://maven.apache.org/guides/index.html +cmd.mvnd=Tool commandlet for Maven Daemon (Performance Wrapper for Maven). +cmd.mvnd.detail=The Maven Daemon (mvnd) is a daemon infrastructure for Maven that helps to reduce the build time. Detailed documentation can be found at https://maven.apache.org/tools/mvnd.html cmd.nest=Tool commandlet for Nest CLI. cmd.nest.detail=The Nest CLI is a command-line interface tool that helps you to initialize, develop, and maintain your Nest applications. Detailed documentation can be found at https://docs.nestjs.com/cli/overview cmd.ng=Tool commandlet for Angular CLI. diff --git a/cli/src/main/resources/nls/Help_de.properties b/cli/src/main/resources/nls/Help_de.properties index ffe1bd8459..e6d6cf6b3e 100644 --- a/cli/src/main/resources/nls/Help_de.properties +++ b/cli/src/main/resources/nls/Help_de.properties @@ -91,6 +91,8 @@ cmd.msvc=Werkzeug Kommando für MSVC (Microsoft Visual C++ Build-Werkzeuge). cmd.msvc.detail=MSVC stellt den C++ Compiler und Build-Werkzeuge bereit, die von Rust unter Windows benötigt werden. Detaillierte Dokumentation ist zu finden unter https://visualstudio.microsoft.com/visual-cpp-build-tools/ cmd.mvn=Werkzeug Kommando für Maven (Build-Werkzeug). cmd.mvn.detail=Apache Maven ist ein Build-Automatisierungs- und Abhängigkeitsverwaltungstool für Java-Projekte. Detaillierte Dokumentation ist zu finden unter https://maven.apache.org/guides/index.html +cmd.mvnd=Werkzeug Kommando für Maven Daemon (Performance-Wrapper für Maven). +cmd.mvnd.detail=Maven Daemon (mvnd) ist ein Performance-Wrapper für Apache Maven, der die Build-Zeit durch die Verwendung eines Daemon-Prozesses erheblich reduziert. Detaillierte Dokumentation ist zu finden unter https://maven.apache.org/tools/mvnd.html cmd.nest=Werkzeug Kommando für Nest CLI. cmd.nest.detail=Die Nest CLI ist ein Command‑Line‑Interface‑Tool zur Initialisierung, Entwicklung und Wartung von Nest‑Anwendungen. Detaillierte Dokumentation ist zu finden unter https://docs.nestjs.com/cli/overview cmd.ng=Werkzeug Kommando für Angular CLI. diff --git a/cli/src/test/java/com/devonfw/tools/ide/completion/CompleteTest.java b/cli/src/test/java/com/devonfw/tools/ide/completion/CompleteTest.java index a4db4f7a21..2830abe197 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/completion/CompleteTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/completion/CompleteTest.java @@ -302,4 +302,40 @@ void testCompleteMavenToolArgumentsForEmptyInput() { assertThat(candidates.stream().map(CompletionCandidate::text)) .contains("clean", "package", "install", "dependency:list", "dependency:tree", "-DskipTests"); } + + /** + * Test of tool argument auto-completion for Maven Daemon. + */ + @Test + void testCompleteMavenDaemonToolArguments() { + + // arrange + AbstractIdeContext context = newContext(PROJECT_BASIC, null, false); + CliArguments args = CliArguments.ofCompletion("mvnd", "-Dmvnd.c"); + + // act + List candidates = context.complete(args, true); + + // assert + assertThat(candidates.stream().map(CompletionCandidate::text)) + .contains("-Dmvnd.cancelConnectTimeout=", "-Dmvnd.connectTimeout=", "-Dmvnd.coreExtensionsExclude="); + } + + /** + * Test of Maven Daemon tool argument auto-completion for empty input. + */ + @Test + void testCompleteMavenDaemonToolArgumentsForEmptyInput() { + + // arrange + AbstractIdeContext context = newContext(PROJECT_BASIC, null, false); + CliArguments args = CliArguments.ofCompletion("mvnd", ""); + + // act + List candidates = context.complete(args, true); + + // assert + assertThat(candidates.stream().map(CompletionCandidate::text)) + .contains("--purge", "--settings", "--completion", "dependency:list", "dependency:tree", "-DskipTests"); + } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/completion/IdeCompleterTest.java b/cli/src/test/java/com/devonfw/tools/ide/completion/IdeCompleterTest.java index efe209d0aa..edf4b201b4 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/completion/IdeCompleterTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/completion/IdeCompleterTest.java @@ -49,7 +49,7 @@ void testIdeCompleterBatch() { void testIdeCompleterInstall() { this.reader.setCompleter(newCompleter()); - assertBuffer("install mvn ", new TestBuffer("install mv").tab()); + assertBuffer("install mvn", new TestBuffer("install mv").tab()); } /** @@ -59,7 +59,7 @@ void testIdeCompleterInstall() { void testIdeCompleterHelpWithToolCompletion() { this.reader.setCompleter(newCompleter()); - assertBuffer("help mvn ", new TestBuffer("help mv").tab().tab()); + assertBuffer("help mvn", new TestBuffer("help mv").tab().tab()); } /** @@ -126,7 +126,7 @@ void testIdeCompleterWithInvalidInputDoesNothing() { void testIdeCompleterHandlesOptionsBeforeCommand() { this.reader.setCompleter(newCompleter()); - assertBuffer("get-version mvn ", new TestBuffer("get-version mv").tab().tab()); + assertBuffer("get-version mvn", new TestBuffer("get-version mv").tab().tab()); } /** diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/mvnd/MvndTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/mvnd/MvndTest.java new file mode 100644 index 0000000000..18f45b6eee --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/mvnd/MvndTest.java @@ -0,0 +1,56 @@ +package com.devonfw.tools.ide.tool.mvnd; + +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.ide.common.Tag; +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; + +/** + * Test of {@link Mvnd}. + */ +@WireMockTest +class MvndTest extends AbstractIdeContextTest { + + private static final String MVND_PROJECT = "mvnd"; + + @Test + void testMvndBasicProperties(WireMockRuntimeInfo wireMockRuntimeInfo) { + + // arrange + IdeTestContext context = newContext(MVND_PROJECT, wireMockRuntimeInfo); + Mvnd mvnd = new Mvnd(context); + + // assert + assertThat(mvnd.getName()).isEqualTo("mvnd"); + assertThat(mvnd.getTags()).contains(Tag.JAVA, Tag.BUILD); + assertThat(mvnd.getToolHelpArguments()).isEqualTo("--help"); + } + + @Test + void testMvndInstallAndRun(WireMockRuntimeInfo wireMockRuntimeInfo) { + + // arrange + IdeTestContext context = newContext(MVND_PROJECT, wireMockRuntimeInfo); + Mvnd mvnd = new Mvnd(context); + + // act + mvnd.install(); + + // assert + assertThat(context.getSoftwarePath().resolve("java/bin/java")).exists(); + assertThat(context.getSoftwarePath().resolve("mvnd/bin/mvnd")).exists(); + assertThat(context).logAtSuccess().hasMessageContaining("Successfully installed mvnd"); + + // act + mvnd.arguments.addValue("foo"); + mvnd.arguments.addValue("bar"); + mvnd.run(); + + // assert + assertThat(context).logAtInfo().hasMessageContaining("mvnd foo bar"); + } + +} diff --git a/cli/src/test/resources/ide-projects/mvnd/_ide/urls/java/java/17.0.10_7/urls b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/java/java/17.0.10_7/urls new file mode 100755 index 0000000000..62e6853f29 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/java/java/17.0.10_7/urls @@ -0,0 +1 @@ +${testbaseurl}/download/java/java/17.0.10_7/java-17.0.10_7.tgz diff --git a/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/1.0.6/urls b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/1.0.6/urls new file mode 100755 index 0000000000..e7b5d974f0 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/1.0.6/urls @@ -0,0 +1 @@ +${testbaseurl}/download/mvnd/mvnd/1.0.6/mvnd-1.0.6.tgz diff --git a/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/dependencies.json b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/dependencies.json new file mode 100644 index 0000000000..f90d36a2c5 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvnd/_ide/urls/mvnd/mvnd/dependencies.json @@ -0,0 +1,8 @@ +{ + "[1.0,)": [ + { + "tool": "java", + "versionRange": "[8,)" + } + ] +} diff --git a/cli/src/test/resources/ide-projects/mvnd/project/home/.ide/ide.properties b/cli/src/test/resources/ide-projects/mvnd/project/home/.ide/ide.properties new file mode 100755 index 0000000000..e69de29bb2 diff --git a/cli/src/test/resources/ide-projects/mvnd/project/settings/ide.properties b/cli/src/test/resources/ide-projects/mvnd/project/settings/ide.properties new file mode 100755 index 0000000000..e69de29bb2 diff --git a/cli/src/test/resources/ide-projects/mvnd/project/workspaces/main/.gitkeep b/cli/src/test/resources/ide-projects/mvnd/project/workspaces/main/.gitkeep new file mode 100755 index 0000000000..e69de29bb2 diff --git a/cli/src/test/resources/ide-projects/mvnd/repository/java/java/default/bin/java b/cli/src/test/resources/ide-projects/mvnd/repository/java/java/default/bin/java new file mode 100755 index 0000000000..bb96ab46f2 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvnd/repository/java/java/default/bin/java @@ -0,0 +1,2 @@ +#!/bin/bash +echo java $* diff --git a/cli/src/test/resources/ide-projects/mvnd/repository/mvnd/mvnd/default/bin/mvnd b/cli/src/test/resources/ide-projects/mvnd/repository/mvnd/mvnd/default/bin/mvnd new file mode 100755 index 0000000000..365d501406 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvnd/repository/mvnd/mvnd/default/bin/mvnd @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ "$1" == "--version" ]; then + echo "1.0.0" +else + echo "mvnd $*" +fi + diff --git a/documentation/LICENSE.adoc b/documentation/LICENSE.adoc index a517f68f7f..bc2dd4ece3 100644 --- a/documentation/LICENSE.adoc +++ b/documentation/LICENSE.adoc @@ -51,6 +51,7 @@ The column `inclusion` indicates the way the component is included: |https://github.com/github/copilot-cli[GitHub Copilot CLI]|Optional|https://github.com/github/copilot-cli/blob/main/LICENSE.md[Proprietary] |https://github.com/anthropics/claude-code/tree/main[Claude Code CLI]|Optional|https://github.com/anthropics/claude-code/blob/main/LICENSE.md[Proprietary] |https://maven.apache.org/[Maven]|Default Setup|https://www.apache.org/licenses/LICENSE-2.0[Apache 2.0] +|https://maven.apache.org/[Maven Daemon]|Default Setup|https://www.apache.org/licenses/LICENSE-2.0[Apache 2.0] |https://code.visualstudio.com/[VS Code]|Optional|https://github.com/Microsoft/vscode/blob/master/LICENSE.txt[MIT] (https://code.visualstudio.com/License/[Terms]) |https://vscodium.com/[VSCodium]|Optional|https://github.com/VSCodium/vscodium/blob/master/LICENSE[MIT] |https://github.com/devonfw/extension-pack-vscode[extension-pack-vscode] |Optional|https://github.com/devonfw/extension-pack-vscode/blob/master/LICENSE[Apache 2.0]