diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index d3f46e900..a9636c2e3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -257,18 +257,29 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac boolean isEmptyDir(Path dir); /** - * Makes a file executable. Equivalent of using 'chmod a+x'. Adds execute permissions to current file permissions. + * Makes a file executable (analog to 'chmod a+x'). * - * @param filePath {@link Path} to the file. + * @param file {@link Path} to the file. */ - void makeExecutable(Path filePath); + default void makeExecutable(Path file) { + + makeExecutable(file, false); + } + + /** + * Makes a file executable (analog to 'chmod a+x'). + * + * @param file {@link Path} to the file. + * @param confirm - {@code true} to get user confirmation before adding missing executable flags, {@code false} otherwise (always set missing flags). + */ + void makeExecutable(Path file, boolean confirm); /** * Like the linux touch command this method will update the modification time of the given {@link Path} to the current * {@link System#currentTimeMillis() system time}. In case the file does not exist, it will be created as empty file. If already the * {@link Path#getParent() parent folder} does not exist, the operation will fail. * - * @param filePath the {@link Path} to the file or folder. + * @param file the {@link Path} to the file or folder. */ - void touch(Path filePath); + void touch(Path file); } 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 87a3896f3..ae2f0b605 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 @@ -27,6 +27,7 @@ import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -903,52 +904,67 @@ public Path findExistingFile(String fileName, List searchDirs) { } @Override - public void makeExecutable(Path filePath) { + public void makeExecutable(Path file, boolean confirm) { - if (Files.exists(filePath)) { + if (Files.exists(file)) { if (SystemInfoImpl.INSTANCE.isWindows()) { - this.context.trace("Windows does not have executable flags hence omitting for file {}", filePath); + this.context.trace("Windows does not have executable flags hence omitting for file {}", file); return; } try { // Read the current file permissions - Set perms = Files.getPosixFilePermissions(filePath); + Set existingPermissions = Files.getPosixFilePermissions(file); // Add execute permission for all users + Set executablePermissions = new HashSet<>(existingPermissions); boolean update = false; - update |= perms.add(PosixFilePermission.OWNER_EXECUTE); - update |= perms.add(PosixFilePermission.GROUP_EXECUTE); - update |= perms.add(PosixFilePermission.OTHERS_EXECUTE); + update |= executablePermissions.add(PosixFilePermission.OWNER_EXECUTE); + update |= executablePermissions.add(PosixFilePermission.GROUP_EXECUTE); + update |= executablePermissions.add(PosixFilePermission.OTHERS_EXECUTE); if (update) { - this.context.debug("Setting executable flags for file {}", filePath); + if (confirm) { + boolean yesContinue = this.context.question( + "We want to execute " + file.getFileName() + " but this command seems to lack executable permissions!\n" + + "Most probably the tool vendor did forgot to add x-flags in the binary release package.\n" + + "Before running the command, we suggest to set executable permissions to the file:\n" + + file + "\n" + + "For security reasons we ask for your confirmation so please check this request.\n" + + "Changing permissions from " + PosixFilePermissions.toString(existingPermissions) + " to " + PosixFilePermissions.toString( + executablePermissions) + ".\n" + + "Do you confirm to make the command executable before running it?"); + if (!yesContinue) { + return; + } + } + this.context.debug("Setting executable flags for file {}", file); // Set the new permissions - Files.setPosixFilePermissions(filePath, perms); + Files.setPosixFilePermissions(file, executablePermissions); } else { - this.context.trace("Executable flags already present so no need to set them for file {}", filePath); + this.context.trace("Executable flags already present so no need to set them for file {}", file); } } catch (IOException e) { throw new RuntimeException(e); } } else { - this.context.warning("Cannot set executable flag on file that does not exist: {}", filePath); + this.context.warning("Cannot set executable flag on file that does not exist: {}", file); } } @Override - public void touch(Path filePath) { + public void touch(Path file) { - if (Files.exists(filePath)) { + if (Files.exists(file)) { try { - Files.setLastModifiedTime(filePath, FileTime.fromMillis(System.currentTimeMillis())); + Files.setLastModifiedTime(file, FileTime.fromMillis(System.currentTimeMillis())); } catch (IOException e) { - throw new IllegalStateException("Could not update modification-time of " + filePath, e); + throw new IllegalStateException("Could not update modification-time of " + file, e); } } else { try { - Files.createFile(filePath); + Files.createFile(file); } catch (IOException e) { - throw new IllegalStateException("Could not create empty file " + filePath, e); + throw new IllegalStateException("Could not create empty file " + file, e); } } } 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 3290b8cf0..e9d1a8c71 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 @@ -150,7 +150,7 @@ public ProcessResult run(ProcessMode processMode) { this.executable = systemPath.findBinary(this.executable); this.processBuilder.environment().put(IdeVariables.PATH.getName(), path); List args = new ArrayList<>(this.arguments.size() + 4); - String interpreter = addExecutable(this.executable.toString(), args); + String interpreter = addExecutable(args); args.addAll(this.arguments); String command = createCommand(); if (this.context.debug().isEnabled()) { @@ -297,11 +297,12 @@ private String getSheBang(Path file) { return null; } - private String addExecutable(String exec, List args) { + private String addExecutable(List args) { String interpreter = null; - String fileExtension = FilenameUtil.getExtension(exec); + String fileExtension = FilenameUtil.getExtension(this.executable.getFileName().toString()); boolean isBashScript = "sh".equals(fileExtension); + this.context.getFileAccess().makeExecutable(this.executable, true); if (!isBashScript) { String sheBang = getSheBang(this.executable); if (sheBang != null) { @@ -325,7 +326,7 @@ private String addExecutable(String exec, List args) { args.add(0, "/i"); args.add(0, "msiexec"); } - args.add(exec); + args.add(this.executable.toString()); return interpreter; } diff --git a/cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn b/cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/gradle/gradle/default/bin/gradle b/cli/src/test/resources/ide-projects/build/repository/gradle/gradle/default/bin/gradle old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/java/java/default/bin/java b/cli/src/test/resources/ide-projects/build/repository/java/java/default/bin/java old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/mvn/mvn/default/bin/mvn b/cli/src/test/resources/ide-projects/build/repository/mvn/mvn/default/bin/mvn old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npm b/cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npx b/cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npx old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/build/repository/npm/npm/default/bin/npm b/cli/src/test/resources/ide-projects/build/repository/npm/npm/default/bin/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/linux/dotnet b/cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/linux/dotnet old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/mac/dotnet b/cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/mac/dotnet old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/linux/eclipse b/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/linux/eclipse old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/mac/Eclipse.app/Contents/MacOS/eclipse b/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/mac/Eclipse.app/Contents/MacOS/eclipse old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipse b/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipse old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipsec b/cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipsec old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/eclipse/repository/java/java/default/bin/java b/cli/src/test/resources/ide-projects/eclipse/repository/java/java/default/bin/java old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/intellij/repository/java/java/default/bin/java b/cli/src/test/resources/ide-projects/intellij/repository/java/java/default/bin/java old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/jasypt/repository/java/java/default/bin/java b/cli/src/test/resources/ide-projects/jasypt/repository/java/java/default/bin/java old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/jmc/repository/jmc/jmc/default/windows/JDK Mission Control/jmc b/cli/src/test/resources/ide-projects/jmc/repository/jmc/jmc/default/windows/JDK Mission Control/jmc old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/mvn/repository/mvn/mvn/default/bin/mvn b/cli/src/test/resources/ide-projects/mvn/repository/mvn/mvn/default/bin/mvn old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/npm/repository/node/node/default/npm b/cli/src/test/resources/ide-projects/npm/repository/node/node/default/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/npm/repository/node/node/default/npx b/cli/src/test/resources/ide-projects/npm/repository/node/node/default/npx old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/linux/bin/npm b/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/linux/bin/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/mac/bin/npm b/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/mac/bin/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/windows/bin/npm b/cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/windows/bin/npm old mode 100644 new mode 100755 diff --git a/cli/src/test/resources/ide-projects/vscode/repository/vscode/vscode/default/bin/code b/cli/src/test/resources/ide-projects/vscode/repository/vscode/vscode/default/bin/code old mode 100644 new mode 100755