diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da7684b4f..1600fd196 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,10 +18,5 @@ jobs: java-version: '17' - name: Build project with Maven run: mvn -B -ntp -Dstyle.color=always install - - name: Deploy to OSSRH nexus - env: - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - run: mvn --settings .mvn/settings.xml -DskipTests=true -Darchetype.test.skip=true -Dmaven.install.skip=true -Dstyle.color=always -B -ntp deploy - name: Coveralls GitHub Action - uses: coverallsapp/github-action@v2.2.3 \ No newline at end of file + uses: coverallsapp/github-action@v2.2.3 diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index d696fe129..c4aa75ceb 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -1,5 +1,6 @@ name: Nightly CI Build and Snapshot Release -on: +on: + workflow_dispatch: schedule: - cron: '0 2 * * *' @@ -65,7 +66,7 @@ jobs: - name: Make natives available and build project run: | mvn -B -ntp -Dstyle.color=always install - mv ./natives/* ./cli/target/ + mv ./natives/* ./cli/target/package/bin/ - name: Deploy to OSSRH nexus env: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97aea20eb..6fdf43f06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,7 +76,7 @@ jobs: git tag -a "release/${next_version}" -m "tagged version ${next_version}" export GPG_TTY=$TTY mvn --settings .mvn/settings.xml -B -ntp deploy -Pdeploy -Dgpg.pin.entry.mode=loopback -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} - mv ./natives/* ./cli/target/ + mv ./natives/* ./cli/target/package/bin/ env: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/cli/pom.xml b/cli/pom.xml index c8c8a168e..c2624a8fe 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.devonfw.tools.IDEasy.dev @@ -107,6 +107,29 @@ + + + maven-resources-plugin + 3.3.1 + + + copy-resources + validate + + copy-resources + + + ${project.build.directory}/package + + + src/main/package + true + + + + + + @@ -114,41 +137,41 @@ deploy - - - org.codehaus.mojo - build-helper-maven-plugin - 3.4.0 - - - attach-artifacts - package - - attach-artifact - - - - - target/${project.artifactId}-linux-x64.tar.gz - tar.gz - linux - - - target/${project.artifactId}-windows-x64.tar.gz - tar.gz - windows - - - target/${project.artifactId}-mac-x64.tar.gz - tar.gz - mac - - - - - - - + + + org.codehaus.mojo + build-helper-maven-plugin + 3.4.0 + + + attach-artifacts + package + + attach-artifact + + + + + target/${project.artifactId}-linux-x64.tar.gz + tar.gz + linux + + + target/${project.artifactId}-windows-x64.tar.gz + tar.gz + windows + + + target/${project.artifactId}-mac-x64.tar.gz + tar.gz + mac + + + + + + + diff --git a/cli/src/main/assembly/exec.xml b/cli/src/main/assembly/exec.xml index 7e8727689..4360773ce 100644 --- a/cli/src/main/assembly/exec.xml +++ b/cli/src/main/assembly/exec.xml @@ -8,20 +8,8 @@ false - ${project.build.directory} - ./ - - * - */** - - - - ${project.build.directory} - ./bin - - ${imageName}.exe - ${imageName} - + ${project.build.directory}/package + . 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 15c5cdbe0..d79afdf33 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 @@ -25,6 +25,7 @@ import com.devonfw.tools.ide.tool.node.Node; import com.devonfw.tools.ide.tool.oc.Oc; import com.devonfw.tools.ide.tool.quarkus.Quarkus; +import com.devonfw.tools.ide.tool.sonar.Sonar; import com.devonfw.tools.ide.tool.terraform.Terraform; import com.devonfw.tools.ide.tool.vscode.Vscode; @@ -83,6 +84,7 @@ public CommandletManagerImpl(IdeContext context) { add(new Aws(context)); add(new Cobigen(context)); add(new Jmc(context)); + add(new Sonar(context)); } private void add(Commandlet commandlet) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java index c997c7a05..e3f3a6224 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java @@ -37,7 +37,7 @@ public String getName() { @Override public boolean isIdeHomeRequired() { - return false; + return true; } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index de17c0f98..25d4b71bc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -2,11 +2,13 @@ import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.os.SystemInfoImpl; import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.url.model.file.UrlChecksum; import com.devonfw.tools.ide.util.DateTimeUtil; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.util.HexUtil; + import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; @@ -223,22 +225,41 @@ public String checksum(Path file) { } } + private boolean isJunction(Path path) { + + if (!SystemInfoImpl.INSTANCE.isWindows()) { + return false; + } + + try { + BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + return attr.isOther() && attr.isDirectory(); + } catch (NoSuchFileException e) { + return false; // file doesn't exist + } catch (IOException e) { + // errors in reading the attributes of the file + throw new IllegalStateException( + "An unexpected error occurred whilst checking if the file: " + path + " is a junction", e); + } + } + @Override public void backup(Path fileOrFolder) { - if (Files.isSymbolicLink(fileOrFolder)) { + if (Files.isSymbolicLink(fileOrFolder) || isJunction(fileOrFolder)) { delete(fileOrFolder); - return; + } else { + // fileOrFolder is a directory + Path backupPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_UPDATES).resolve(IdeContext.FOLDER_BACKUPS); + LocalDateTime now = LocalDateTime.now(); + String date = DateTimeUtil.formatDate(now); + String time = DateTimeUtil.formatTime(now); + Path backupDatePath = backupPath.resolve(date); + mkdirs(backupDatePath); + Path target = backupDatePath.resolve(fileOrFolder.getFileName().toString() + "_" + time); + this.context.info("Creating backup by moving {} to {}", fileOrFolder, target); + move(fileOrFolder, target); } - Path backupPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_UPDATES).resolve(IdeContext.FOLDER_BACKUPS); - LocalDateTime now = LocalDateTime.now(); - String date = DateTimeUtil.formatDate(now); - String time = DateTimeUtil.formatTime(now); - Path backupDatePath = backupPath.resolve(date); - mkdirs(backupDatePath); - Path target = backupDatePath.resolve(fileOrFolder.getFileName().toString() + "_" + time); - this.context.info("Creating backup by moving {} to {}", fileOrFolder, target); - move(fileOrFolder, target); } @Override @@ -317,31 +338,14 @@ private void copyRecursive(Path source, Path target, FileCopyMode mode) throws I */ private void deleteLinkIfExists(Path path) throws IOException { - boolean exists = false; - boolean isJunction = false; - if (this.context.getSystemInfo().isWindows()) { - try { // since broken junctions are not detected by Files.exists(brokenJunction) - BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); - exists = true; - isJunction = attr.isOther() && attr.isDirectory(); - } catch (NoSuchFileException e) { - // ignore, since there is no previous file at the location, so nothing to delete - return; - } - } - exists = exists || Files.exists(path); - boolean isSymlink = exists && Files.isSymbolicLink(path); + boolean isJunction = isJunction(path); // since broken junctions are not detected by Files.exists() + boolean isSymlink = Files.exists(path) && Files.isSymbolicLink(path); assert !(isSymlink && isJunction); - if (exists) { - if (isJunction || isSymlink) { - this.context.info("Deleting previous " + (isJunction ? "junction" : "symlink") + " at " + path); - Files.delete(path); - } else { - throw new IllegalStateException( - "The file at " + path + " was not deleted since it is not a symlink or a Windows junction"); - } + if (isJunction || isSymlink) { + this.context.info("Deleting previous " + (isJunction ? "junction" : "symlink") + " at " + path); + Files.delete(path); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLoggerOut.java b/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLoggerOut.java index 1d8d320d6..734cbea7e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLoggerOut.java +++ b/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLoggerOut.java @@ -48,7 +48,9 @@ public void log(String message) { String startColor = null; if (this.colored) { startColor = this.level.getStartColor(); - this.out.append(startColor); + if (startColor != null) { + this.out.append(startColor); + } } this.out.append(message); if (startColor != null) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java index a4997d4cf..dea607c2c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java @@ -182,9 +182,9 @@ private String createCommandMessage(String interpreter, String suffix) { sb.append(interpreter); } int size = this.arguments.size(); - if (size > 1) { + if (size > 0) { sb.append(" with arguments"); - for (int i = 1; i < size; i++) { + for (int i = 0; i < size; i++) { String arg = this.arguments.get(i); sb.append(" '"); sb.append(arg); diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java new file mode 100644 index 000000000..d1db549e0 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java @@ -0,0 +1,59 @@ +package com.devonfw.tools.ide.property; + +import java.util.Locale; + +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.context.IdeContext; + +/** + * {@link Property} with {@link #getValueType() value type} {@link Boolean}. + */ +public class EnumProperty> extends Property { + + private final Class valueType; + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param alias the {@link #getAlias() property alias}. + */ + public EnumProperty(String name, boolean required, String alias, Class valueType) { + + super(name, required, alias, null); + this.valueType = valueType; + } + + @Override + public Class getValueType() { + + return this.valueType; + } + + @Override + public V parse(String valueAsString, IdeContext context) { + + for (V enumConstant : this.valueType.getEnumConstants()) { + String name = enumConstant.name().toLowerCase(Locale.ROOT); + if (name.equals(valueAsString)) { + return enumConstant; + } + } + + throw new IllegalArgumentException(String.format("Invalid Enum option: %s", valueAsString)); + } + + @Override + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, + CompletionCandidateCollector collector) { + + for (V enumConstant : this.valueType.getEnumConstants()) { + String name = enumConstant.name().toLowerCase(Locale.ROOT); + if (name.startsWith(arg)) { + collector.add(name, null, this, commandlet); + } + } + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index b5d8d4df6..ea9f7656b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -48,7 +48,16 @@ public ToolCommandlet(IdeContext context, String tool, Set tags) { this.tool = tool; this.tags = tags; addKeyword(tool); - this.arguments = add(new StringListProperty("", false, "args")); + this.arguments = new StringListProperty("", false, "args"); + initProperties(); + } + + /** + * Add initial Properties to the tool + */ + protected void initProperties() { + + add(this.arguments); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/Sonar.java b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/Sonar.java new file mode 100644 index 000000000..e3f77051a --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/Sonar.java @@ -0,0 +1,105 @@ +package com.devonfw.tools.ide.tool.sonar; + +import java.nio.file.Path; +import java.util.Properties; +import java.util.Set; + +import com.devonfw.tools.ide.common.Tag; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.EnumProperty; +import com.devonfw.tools.ide.tool.LocalToolCommandlet; +import com.devonfw.tools.ide.tool.java.Java; +import com.devonfw.tools.ide.tool.mvn.Mvn; +import com.devonfw.tools.ide.util.PropertiesFileUtil; + +public class Sonar extends LocalToolCommandlet { + + public final EnumProperty command; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. method. + */ + public Sonar(IdeContext context) { + + super(context, "sonar", Set.of(Tag.CODE_QA)); + + this.command = add(new EnumProperty<>("", true, "command", SonarCommand.class)); + add(this.arguments); + } + + @Override + protected void initProperties() { + + // Empty on purpose + } + + @Override + public boolean install(boolean silent) { + + getCommandlet(Java.class).install(); + return super.install(silent); + } + + @Override + public void run() { + + SonarCommand command = this.command.getValue(); + + Path toolPath = getToolPath(); + if (!toolPath.toFile().exists()) { + super.install(true); + } + + switch (command) { + case ANALYZE: + getCommandlet(Mvn.class).runTool(null, "sonar:sonar"); + break; + case START: + printSonarWebPort(); + arguments.setValueAsString("start", context); + super.run(); + break; + case STOP: + arguments.setValueAsString("stop", context); + super.run(); + break; + default: + } + } + + @Override + protected String getBinaryName() { + + SonarCommand command = this.command.getValue(); + + Path toolBinPath = getToolBinPath(); + String sonarLocation = null; + + if (this.context.getSystemInfo().isWindows()) { + if (command.equals(SonarCommand.START)) { + sonarLocation = "windows-x86-64/StartSonar.bat"; + } else if (command.equals(SonarCommand.STOP)) { + sonarLocation = "windows-x86-64/SonarService.bat"; + } + } else if (this.context.getSystemInfo().isMac()) { + sonarLocation = "macosx-universal-64/sonar.sh"; + } else { + sonarLocation = "linux-x86-64/sonar.sh"; + } + return toolBinPath.resolve(sonarLocation).toString(); + } + + private void printSonarWebPort() { + + this.context.info("SonarQube is running at localhost on the following port (default 9000):"); + Path sonarPropertiesPath = getToolPath().resolve("conf/sonar.properties"); + + Properties sonarProperties = PropertiesFileUtil.loadProperties(sonarPropertiesPath); + String sonarWebPort = sonarProperties.getProperty("sonar.web.port"); + if (sonarWebPort != null) { + this.context.info(sonarWebPort); + } + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarCommand.java b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarCommand.java new file mode 100644 index 000000000..1ec15148e --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarCommand.java @@ -0,0 +1,8 @@ +package com.devonfw.tools.ide.tool.sonar; + +/** + * Represents commands for controlling a sonar operation in The{@link Sonar} Tool. + */ +public enum SonarCommand { + START, STOP, ANALYZE +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/PropertiesFileUtil.java b/cli/src/main/java/com/devonfw/tools/ide/util/PropertiesFileUtil.java new file mode 100644 index 000000000..b0525639b --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/util/PropertiesFileUtil.java @@ -0,0 +1,27 @@ +package com.devonfw.tools.ide.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +public class PropertiesFileUtil { + + /** + * Loads properties from a file at the given path. + * + * @param path the path to the properties file + * @return a Properties object loaded with properties from the file + */ + public static Properties loadProperties(Path path) { + + Properties properties = new Properties(); + + try (var inputStream = Files.newInputStream(path)) { + properties.load(inputStream); + } catch (IOException e) { + throw new IllegalArgumentException(String.format("Cannot read Properties File at %s", path.toString()), e); + } + return properties; + } +} diff --git a/cli/src/main/package/bin/ide b/cli/src/main/package/bin/ide new file mode 100644 index 000000000..7b12b7d00 --- /dev/null +++ b/cli/src/main/package/bin/ide @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# immediately exit on errors +set -e + +# Check if arguments are given +if [ -n "${1}" ]; then + # Call native ideasy with user-provided arguments + ideasy "$@" +fi + +ide_env= +if [ "${OSTYPE}" = "cygwin" ] || [ "${OSTYPE}" = "msys" ]; then + ide_env="$(ideasy env --bash)" +else + ide_env="$(ideasy env)" +fi +eval "$ide_env" +unset ide_env diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index ef6b3eda7..297ae29de 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -32,8 +32,10 @@ cmd-shell=Commandlet to start built-in shell with advanced auto-completion. cmd-terraform=Tool commandlet for Terraform cmd-vscode=Tool commandlet for Visual Studio Code (IDE) cmd-cobigen=Tool commandlet for Cobigen +cmd-sonar=Tool commandlet for SonarQube val-args=The commandline arguments to pass to the tool. val-edition=The tool edition. +val-sonar-command=START|STOP|ANALYZE val-tool=The tool commandlet to select. val-version=The tool version val-set-version-version=The tool version to set. diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index 688bd923a..d7bb5ebb5 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -29,6 +29,7 @@ cmd-set-version=Setzt die Version des selektierten Werkzeugs. cmd-terraform=Werkzeug Kommando für Terraform. cmd-vscode=Werkzeug Kommando für Visual Studio Code (IDE) cmd-cobigen=Werkzeug Kommando für Cobigen. +cmd-sonar=Werkzeug Kommando für SonarQube. val-args=Die Kommandozeilen-Argumente zur Übergabe an das Werkzeug. val-edition=Die Werkzeug Edition. val-tool=Das zu selektierende Werkzeug Kommando. diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java index 3620e6f3e..b66687777 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java @@ -1,11 +1,12 @@ package com.devonfw.tools.ide.commandlet; +import org.junit.jupiter.api.Test; + import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.context.IdeTestContextMock; import com.devonfw.tools.ide.environment.VariableLine; import com.devonfw.tools.ide.log.IdeLogLevel; -import org.junit.jupiter.api.Test; /** * Test of {@link EnvironmentCommandlet}. @@ -22,12 +23,12 @@ public void testNormalizeWindowsValue2Windows() { assertThat(env.normalizeWindowsValue("")).isEqualTo(""); assertThat(env.normalizeWindowsValue("*")).isEqualTo("*"); assertThat(env.normalizeWindowsValue("$:\\\\{garbage}§")).isEqualTo("$:\\\\{garbage}§"); - assertThat(env.normalizeWindowsValue("/c/Windows/system32/drivers/etc/hosts")).isEqualTo( - "C:\\Windows\\system32\\drivers\\etc\\hosts"); - assertThat(env.normalizeWindowsValue("C:\\Windows\\system32\\drivers\\etc\\hosts")).isEqualTo( - "C:\\Windows\\system32\\drivers\\etc\\hosts"); - assertThat(env.normalizeWindowsValue("C:\\Users\\login/.ide/scripts/ide")).isEqualTo( - "C:\\Users\\login\\.ide\\scripts\\ide"); + assertThat(env.normalizeWindowsValue("/c/Windows/system32/drivers/etc/hosts")) + .isEqualTo("C:\\Windows\\system32\\drivers\\etc\\hosts"); + assertThat(env.normalizeWindowsValue("C:\\Windows\\system32\\drivers\\etc\\hosts")) + .isEqualTo("C:\\Windows\\system32\\drivers\\etc\\hosts"); + assertThat(env.normalizeWindowsValue("C:\\Users\\login/.ide/scripts/ide")) + .isEqualTo("C:\\Users\\login\\.ide\\scripts\\ide"); assertThat(env.normalizeWindowsValue("\\login/.ide/scripts/ide")).isEqualTo("\\login/.ide/scripts/ide"); } @@ -42,10 +43,10 @@ public void testNormalizeWindowsValue2Bash() { assertThat(env.normalizeWindowsValue("")).isEqualTo(""); assertThat(env.normalizeWindowsValue("*")).isEqualTo("*"); assertThat(env.normalizeWindowsValue("$:\\\\{garbage}§")).isEqualTo("$:\\\\{garbage}§"); - assertThat(env.normalizeWindowsValue("C:\\Windows\\system32\\drivers\\etc\\hosts")).isEqualTo( - "/c/Windows/system32/drivers/etc/hosts"); - assertThat(env.normalizeWindowsValue("/c/Windows/system32/drivers/etc/hosts")).isEqualTo( - "/c/Windows/system32/drivers/etc/hosts"); + assertThat(env.normalizeWindowsValue("C:\\Windows\\system32\\drivers\\etc\\hosts")) + .isEqualTo("/c/Windows/system32/drivers/etc/hosts"); + assertThat(env.normalizeWindowsValue("/c/Windows/system32/drivers/etc/hosts")) + .isEqualTo("/c/Windows/system32/drivers/etc/hosts"); } /** @@ -90,14 +91,14 @@ public void testRun() { } /** - * Test of {@link EnvironmentCommandlet} does not require home. + * Test that {@link EnvironmentCommandlet} requires home. */ @Test - public void testThatHomeIsNotReqired() { + public void testThatHomeIsRequired() { // arrange EnvironmentCommandlet env = new EnvironmentCommandlet(IdeTestContextMock.get()); // act & assert - assertThat(env.isIdeHomeRequired()).isFalse(); + assertThat(env.isIdeHomeRequired()).isTrue(); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/repo/ToolRepositoryMock.java b/cli/src/test/java/com/devonfw/tools/ide/repo/ToolRepositoryMock.java index bb187d6c7..b12f203c8 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/repo/ToolRepositoryMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/repo/ToolRepositoryMock.java @@ -57,6 +57,7 @@ public Path download(String tool, String edition, VersionIdentifier version) { Path editionFolder = this.repositoryFolder.resolve(tool).resolve(edition); Path versionFolder = editionFolder.resolve(version.toString()); if (!Files.isDirectory(versionFolder)) { + this.context.debug("Could not find version {} so using 'default' for {}/{}", version, tool, edition); versionFolder = editionFolder.resolve("default"); } if (!Files.isDirectory(versionFolder)) { @@ -74,6 +75,8 @@ public Path download(String tool, String edition, VersionIdentifier version) { Path child = iterator.next(); if (Files.isRegularFile(child) && child.getFileName().startsWith("content.")) { contentArchive = child; + this.context.debug("Using compressed archive {} for mock download of {}/{}", child.getFileName(), tool, + edition); } else { break; } diff --git a/documentation/IDEasy-usage.asciidoc b/documentation/IDEasy-usage.asciidoc index 7917902c1..95b182c73 100644 --- a/documentation/IDEasy-usage.asciidoc +++ b/documentation/IDEasy-usage.asciidoc @@ -1,39 +1,39 @@ -= Usage - -include::usage.asciidoc[leveloffset=2] - -<<<< - -include::configuration.asciidoc[leveloffset=2] - -<<<< - -include::variables.asciidoc[leveloffset=2] - -<<<< - -include::cli.asciidoc[leveloffset=2] - -include::docker-desktop-alternative.asciidoc[leveloffset=3] - -<<<< - -include::structure.asciidoc[leveloffset=2] - -include::conf.asciidoc[leveloffset=3] - -include::log.asciidoc[leveloffset=3] - -include::scripts.asciidoc[leveloffset=3] - -include::settings.asciidoc[leveloffset=3] - -include::software.asciidoc[leveloffset=3] - -include::system.asciidoc[leveloffset=3] - -include::updates.asciidoc[leveloffset=3] - -include::workspaces.asciidoc[leveloffset=3] - -include::projects.asciidoc[leveloffset=3] += Usage + +include::usage.asciidoc[leveloffset=2] + +<<<< + +include::configuration.asciidoc[leveloffset=2] + +<<<< + +include::variables.asciidoc[leveloffset=2] + +<<<< + +include::cli.asciidoc[leveloffset=2] + +include::docker-desktop-alternative.asciidoc[leveloffset=3] + +<<<< + +include::structure.asciidoc[leveloffset=2] + +include::conf.asciidoc[leveloffset=3] + +include::log.asciidoc[leveloffset=3] + +include::scripts.asciidoc[leveloffset=3] + +include::settings.asciidoc[leveloffset=3] + +include::software.asciidoc[leveloffset=3] + +include::system.asciidoc[leveloffset=3] + +include::updates.asciidoc[leveloffset=3] + +include::workspaces.asciidoc[leveloffset=3] + +include::projects.asciidoc[leveloffset=3] diff --git a/documentation/cli.asciidoc b/documentation/cli.asciidoc index d73866633..cc79c8001 100644 --- a/documentation/cli.asciidoc +++ b/documentation/cli.asciidoc @@ -45,49 +45,3 @@ The benefit when using `ide` as wrapper is that it will even work when the comma We see the main benefit in this for writing portable scripts that you may commit to your git repository and that will then run everywhere and will lazily install the required tools on the fly. In your daily usage you can and surely should avoid to always type `ide` as prefix to every command. However, when you automate and want to avoid "command not found" errors, you can simply prefix the command with `ide`. - -=== Commandlet overview - -The following commandlets are currently available: - -* link:android-studio.asciidoc[android-studio] -* link:aws.asciidoc[aws] -* link:az.asciidoc[az] -* link:build.asciidoc[build] -* link:cobigen.asciidoc[cobigen] -* link:docker.asciidoc[docker] -* link:dotnet.asciidoc[dotnet] -* link:eclipse.asciidoc[eclipse] -* link:gcloud.asciidoc[gcloud] -* link:gcviewer.asciidoc[gcviewer] -* link:gh.asciidoc[gh] -* link:graalvm.asciidoc[graalvm] -* link:gradle.asciidoc[gradle] -* link:helm.asciidoc[helm] -* link:help.asciidoc[help] -* link:ide.asciidoc[ide] -* link:intellij.asciidoc[intellij] -* link:ionic.asciidoc[ionic] -* link:jasypt.asciidoc[jasypt] -* link:java.asciidoc[java] -* link:jenkins.asciidoc[jenkins] -* link:jmc.asciidoc[jmc] -* link:kotlinc.asciidoc[kotlinc] -* link:kotlinc-native.asciidoc[kotlinc-native] -* link:kubectl.asciidoc[kubectl] -* link:lazydocker.asciidoc[lazydocker] -* link:mvn.asciidoc[mvn] -* link:ng.asciidoc[ng] -* link:node.asciidoc[node] -* link:npm.asciidoc[npm] -* link:oc.asciidoc[oc] -* link:python.asciidoc[python] -* link:pip.asciidoc[pip] -* link:quarkus.asciidoc[quarkus] -* link:release.asciidoc[release] -* link:rewrite.asciidoc[rewrite] -* link:sonar.asciidoc[sonar] -* link:terraform.asciidoc[terraform] -* link:tomcat.asciidoc[tomcat] -* link:vscode.asciidoc[vscode] -* link:yarn.asciidoc[yarn] diff --git a/documentation/coding-conventions.asciidoc b/documentation/coding-conventions.asciidoc index 6feadb37a..703976731 100644 --- a/documentation/coding-conventions.asciidoc +++ b/documentation/coding-conventions.asciidoc @@ -21,6 +21,16 @@ We follow these additional naming rules: * Names of Generics should be easy to understand. Where suitable follow the common rule `E=Element`, `T=Type`, `K=Key`, `V=Value` but feel free to use longer names for more specific cases such as `ID`, `DTO` or `ENTITY`. The capitalized naming helps to distinguish a generic type from a regular class. * For `boolean` getter methods use `is` prefix instead of `get` but for `boolean` variable names avoid the `is` prefix (`boolean force = ...` instead of `boolean isForce = ...`) unless the name is a reserved keyword (e.g. `boolean abstract = true` will result in a compile error so consider using `boolean isAbstract = true` instead). +== Proper IDE configuration +Ensure your IDE (IntelliJ, Eclipse, VSCode, ...) is properly configured. +This is what https://github.com/devonfw/IDEasy[IDEasy] is all about. +A reasonable configuration of formatter, save actions, etc. will ensure: + +* no start imports are used (`import java.util.*`) +* no diff-wars in git (if every developer in the team uses the same formatter settings the diffs in git will only show what really changed) +* import statements are properly grouped and sorted +* code has properly indentation and formatting (e.g. newlines after opening curly braces) + == Obsolete APIs Please avoid using the following APIs: @@ -169,7 +179,7 @@ public class MavenDownloader { public void download(String url) { ... } } ---- -Here `url` is used as a constant however it is not declared as such. +Here `url` is used as a constant however it is not declared as such. Other classes could modify the value (`MavenDownloader.url = "you have been hacked";`). Instead we should better do this: [source,java] ----