Skip to content

Commit 8e971e1

Browse files
authored
#881: add self healing feature to add x-flags before running command (#904)
1 parent 3a24512 commit 8e971e1

File tree

27 files changed

+54
-26
lines changed
  • cli/src
    • main/java/com/devonfw/tools/ide
    • test/resources/ide-projects
      • basic/project/software/mvn/bin
      • build/repository
        • gradle/gradle/default/bin
        • java/java/default/bin
        • mvn/mvn/default/bin
        • node/node/default/node_modules/npm/bin
        • npm/npm/default/bin
      • dotnet/repository/dotnet/dotnet/default
      • eclipse/repository
      • intellij/repository/java/java/default/bin
      • jasypt/repository/java/java/default/bin
      • jmc/repository/jmc/jmc/default/windows/JDK Mission Control
      • mvn/repository/mvn/mvn/default/bin
      • npm/repository
      • vscode/repository/vscode/vscode/default/bin

27 files changed

+54
-26
lines changed

cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java

+16-5
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,29 @@ default void extract(Path archiveFile, Path targetDir, Consumer<Path> postExtrac
257257
boolean isEmptyDir(Path dir);
258258

259259
/**
260-
* Makes a file executable. Equivalent of using 'chmod a+x'. Adds execute permissions to current file permissions.
260+
* Makes a file executable (analog to 'chmod a+x').
261261
*
262-
* @param filePath {@link Path} to the file.
262+
* @param file {@link Path} to the file.
263263
*/
264-
void makeExecutable(Path filePath);
264+
default void makeExecutable(Path file) {
265+
266+
makeExecutable(file, false);
267+
}
268+
269+
/**
270+
* Makes a file executable (analog to 'chmod a+x').
271+
*
272+
* @param file {@link Path} to the file.
273+
* @param confirm - {@code true} to get user confirmation before adding missing executable flags, {@code false} otherwise (always set missing flags).
274+
*/
275+
void makeExecutable(Path file, boolean confirm);
265276

266277
/**
267278
* Like the linux touch command this method will update the modification time of the given {@link Path} to the current
268279
* {@link System#currentTimeMillis() system time}. In case the file does not exist, it will be created as empty file. If already the
269280
* {@link Path#getParent() parent folder} does not exist, the operation will fail.
270281
*
271-
* @param filePath the {@link Path} to the file or folder.
282+
* @param file the {@link Path} to the file or folder.
272283
*/
273-
void touch(Path filePath);
284+
void touch(Path file);
274285
}

cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java

+33-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.security.NoSuchAlgorithmException;
2828
import java.time.LocalDateTime;
2929
import java.util.ArrayList;
30+
import java.util.HashSet;
3031
import java.util.Iterator;
3132
import java.util.List;
3233
import java.util.Map;
@@ -903,52 +904,67 @@ public Path findExistingFile(String fileName, List<Path> searchDirs) {
903904
}
904905

905906
@Override
906-
public void makeExecutable(Path filePath) {
907+
public void makeExecutable(Path file, boolean confirm) {
907908

908-
if (Files.exists(filePath)) {
909+
if (Files.exists(file)) {
909910
if (SystemInfoImpl.INSTANCE.isWindows()) {
910-
this.context.trace("Windows does not have executable flags hence omitting for file {}", filePath);
911+
this.context.trace("Windows does not have executable flags hence omitting for file {}", file);
911912
return;
912913
}
913914
try {
914915
// Read the current file permissions
915-
Set<PosixFilePermission> perms = Files.getPosixFilePermissions(filePath);
916+
Set<PosixFilePermission> existingPermissions = Files.getPosixFilePermissions(file);
916917

917918
// Add execute permission for all users
919+
Set<PosixFilePermission> executablePermissions = new HashSet<>(existingPermissions);
918920
boolean update = false;
919-
update |= perms.add(PosixFilePermission.OWNER_EXECUTE);
920-
update |= perms.add(PosixFilePermission.GROUP_EXECUTE);
921-
update |= perms.add(PosixFilePermission.OTHERS_EXECUTE);
921+
update |= executablePermissions.add(PosixFilePermission.OWNER_EXECUTE);
922+
update |= executablePermissions.add(PosixFilePermission.GROUP_EXECUTE);
923+
update |= executablePermissions.add(PosixFilePermission.OTHERS_EXECUTE);
922924

923925
if (update) {
924-
this.context.debug("Setting executable flags for file {}", filePath);
926+
if (confirm) {
927+
boolean yesContinue = this.context.question(
928+
"We want to execute " + file.getFileName() + " but this command seems to lack executable permissions!\n"
929+
+ "Most probably the tool vendor did forgot to add x-flags in the binary release package.\n"
930+
+ "Before running the command, we suggest to set executable permissions to the file:\n"
931+
+ file + "\n"
932+
+ "For security reasons we ask for your confirmation so please check this request.\n"
933+
+ "Changing permissions from " + PosixFilePermissions.toString(existingPermissions) + " to " + PosixFilePermissions.toString(
934+
executablePermissions) + ".\n"
935+
+ "Do you confirm to make the command executable before running it?");
936+
if (!yesContinue) {
937+
return;
938+
}
939+
}
940+
this.context.debug("Setting executable flags for file {}", file);
925941
// Set the new permissions
926-
Files.setPosixFilePermissions(filePath, perms);
942+
Files.setPosixFilePermissions(file, executablePermissions);
927943
} else {
928-
this.context.trace("Executable flags already present so no need to set them for file {}", filePath);
944+
this.context.trace("Executable flags already present so no need to set them for file {}", file);
929945
}
930946
} catch (IOException e) {
931947
throw new RuntimeException(e);
932948
}
933949
} else {
934-
this.context.warning("Cannot set executable flag on file that does not exist: {}", filePath);
950+
this.context.warning("Cannot set executable flag on file that does not exist: {}", file);
935951
}
936952
}
937953

938954
@Override
939-
public void touch(Path filePath) {
955+
public void touch(Path file) {
940956

941-
if (Files.exists(filePath)) {
957+
if (Files.exists(file)) {
942958
try {
943-
Files.setLastModifiedTime(filePath, FileTime.fromMillis(System.currentTimeMillis()));
959+
Files.setLastModifiedTime(file, FileTime.fromMillis(System.currentTimeMillis()));
944960
} catch (IOException e) {
945-
throw new IllegalStateException("Could not update modification-time of " + filePath, e);
961+
throw new IllegalStateException("Could not update modification-time of " + file, e);
946962
}
947963
} else {
948964
try {
949-
Files.createFile(filePath);
965+
Files.createFile(file);
950966
} catch (IOException e) {
951-
throw new IllegalStateException("Could not create empty file " + filePath, e);
967+
throw new IllegalStateException("Could not create empty file " + file, e);
952968
}
953969
}
954970
}

cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public ProcessResult run(ProcessMode processMode) {
150150
this.executable = systemPath.findBinary(this.executable);
151151
this.processBuilder.environment().put(IdeVariables.PATH.getName(), path);
152152
List<String> args = new ArrayList<>(this.arguments.size() + 4);
153-
String interpreter = addExecutable(this.executable.toString(), args);
153+
String interpreter = addExecutable(args);
154154
args.addAll(this.arguments);
155155
String command = createCommand();
156156
if (this.context.debug().isEnabled()) {
@@ -297,11 +297,12 @@ private String getSheBang(Path file) {
297297
return null;
298298
}
299299

300-
private String addExecutable(String exec, List<String> args) {
300+
private String addExecutable(List<String> args) {
301301

302302
String interpreter = null;
303-
String fileExtension = FilenameUtil.getExtension(exec);
303+
String fileExtension = FilenameUtil.getExtension(this.executable.getFileName().toString());
304304
boolean isBashScript = "sh".equals(fileExtension);
305+
this.context.getFileAccess().makeExecutable(this.executable, true);
305306
if (!isBashScript) {
306307
String sheBang = getSheBang(this.executable);
307308
if (sheBang != null) {
@@ -325,7 +326,7 @@ private String addExecutable(String exec, List<String> args) {
325326
args.add(0, "/i");
326327
args.add(0, "msiexec");
327328
}
328-
args.add(exec);
329+
args.add(this.executable.toString());
329330
return interpreter;
330331
}
331332

cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/gradle/gradle/default/bin/gradle

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/java/java/default/bin/java

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/mvn/mvn/default/bin/mvn

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/node/node/default/node_modules/npm/bin/npx

100644100755
File mode changed.

cli/src/test/resources/ide-projects/build/repository/npm/npm/default/bin/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/linux/dotnet

100644100755
File mode changed.

cli/src/test/resources/ide-projects/dotnet/repository/dotnet/dotnet/default/mac/dotnet

100644100755
File mode changed.

cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/linux/eclipse

100644100755
File mode changed.

cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/mac/Eclipse.app/Contents/MacOS/eclipse

100644100755
File mode changed.

cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipse

100644100755
File mode changed.

cli/src/test/resources/ide-projects/eclipse/repository/eclipse/eclipse/default/windows/eclipsec

100644100755
File mode changed.

cli/src/test/resources/ide-projects/eclipse/repository/java/java/default/bin/java

100644100755
File mode changed.

cli/src/test/resources/ide-projects/intellij/repository/java/java/default/bin/java

100644100755
File mode changed.

cli/src/test/resources/ide-projects/jasypt/repository/java/java/default/bin/java

100644100755
File mode changed.

cli/src/test/resources/ide-projects/jmc/repository/jmc/jmc/default/windows/JDK Mission Control/jmc

100644100755
File mode changed.

cli/src/test/resources/ide-projects/mvn/repository/mvn/mvn/default/bin/mvn

100644100755
File mode changed.

cli/src/test/resources/ide-projects/npm/repository/node/node/default/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/npm/repository/node/node/default/npx

100644100755
File mode changed.

cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/linux/bin/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/mac/bin/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/npm/repository/npm/npm/default/windows/bin/npm

100644100755
File mode changed.

cli/src/test/resources/ide-projects/vscode/repository/vscode/vscode/default/bin/code

100644100755
File mode changed.

0 commit comments

Comments
 (0)