diff --git a/app.yml b/app.yml index ff4e30d..e2d0160 100644 --- a/app.yml +++ b/app.yml @@ -10,9 +10,9 @@ dependencies: org.slf4j:slf4j-simple: "2.0.17" actions: - clean: ".{/}mvnw clean" - build: ".{/}mvnw spotless:apply package -DskipTests" - run: "{./target/binary/bin/jpm}" - test: ".{/}mvnw test" + clean: "./mvnw clean" + build: "./mvnw spotless:apply package -DskipTests" + run: "./target/binary/bin/jpm" + test: "./mvnw test" buildj: "javac -cp {{deps}} -d classes --source-path src/main/java src/main/java/org/codejive/jpm/Main.java" runj: "java -cp classes:{{deps}} org.codejive.jpm.Main" diff --git a/src/main/java/org/codejive/jpm/util/ScriptUtils.java b/src/main/java/org/codejive/jpm/util/ScriptUtils.java index 49b9f28..3e2b47f 100644 --- a/src/main/java/org/codejive/jpm/util/ScriptUtils.java +++ b/src/main/java/org/codejive/jpm/util/ScriptUtils.java @@ -37,14 +37,7 @@ public static int executeScript(String command, List classpath, boolean ve (isWindows() && tmpCommand.length() > 8000) || (!isWindows() && tmpCommand.length() > 32000); - if (!usingSubstitutions(command)) { - // First try to parse the command - CommandsParser parser = new CommandsParser(command); - CommandsParser.Commands commands = parser.parse(); - if (commands != null) { - command = suggestSubstitutions(commands); - } - } + command = suggestSubstitutions(command); try (ArgsFiles argsFiles = new ArgsFiles()) { // Process the command for variable substitution and path conversion @@ -73,6 +66,18 @@ public static String quoteArgument(String arg) { return arg; } + static String suggestSubstitutions(String command) { + if (!usingSubstitutions(command)) { + // First try to parse the command + CommandsParser parser = new CommandsParser(command); + CommandsParser.Commands commands = parser.parse(); + if (commands != null) { + command = suggestCommandsSubstitutions(commands); + } + } + return command; + } + // Is the command using any substitutions? (we ignore {{deps}} here) private static boolean usingSubstitutions(String command) { command = command.replace("{]}", "\u0007"); @@ -80,14 +85,14 @@ private static boolean usingSubstitutions(String command) { return pattern.matcher(command).find(); } - static String suggestSubstitutions(CommandsParser.Commands commands) { + private static String suggestCommandsSubstitutions(CommandsParser.Commands commands) { StringBuilder cmd = new StringBuilder(); for (CommandsParser.Node node : commands.elements) { if (node instanceof CommandsParser.Command) { - cmd.append(suggestSubstitutions((CommandsParser.Command) node)); + cmd.append(suggestCommandSubstitutions((CommandsParser.Command) node)); } else if (node instanceof CommandsParser.Group) { CommandsParser.Group group = (CommandsParser.Group) node; - String groupStr = suggestSubstitutions(group.commands); + String groupStr = suggestCommandsSubstitutions(group.commands); cmd.append("(").append(groupStr).append(")"); } else if (node instanceof CommandsParser.Separator) { CommandsParser.Separator sep = (CommandsParser.Separator) node; @@ -101,7 +106,7 @@ static String suggestSubstitutions(CommandsParser.Commands commands) { return cmd.toString(); } - static String suggestSubstitutions(CommandsParser.Command command) { + static String suggestCommandSubstitutions(CommandsParser.Command command) { StringBuilder cmd = new StringBuilder(); if (command.words.isEmpty()) { return ""; @@ -179,12 +184,30 @@ private static String suggestClassPathSubstitution(String classpath) { if (parts.length <= 1) { return null; } + // First see if this plausibly looks like a classpath + // This requires at least one part to be a relative path with + // at least two parts (i.e. have at least one / in it) + // This does mean that it won't suggest substitutions for + // classpaths that are all single-name parts (e.g. "lib:ext"). + if (!classpath.contains("{{deps}}")) { + boolean hasAtLeastOneComplexPart = false; + for (String path : parts) { + Path p = FileUtils.safePath(path); + if (p == null || p.isAbsolute()) { + return null; + } + if (p.getNameCount() >= 2) { + hasAtLeastOneComplexPart = true; + } + } + if (!hasAtLeastOneComplexPart) { + return null; + } + } + // Now do the actual conversion StringBuilder sb = new StringBuilder(); for (String path : parts) { Path p = FileUtils.safePath(path); - if (p == null || p.isAbsolute()) { - return null; - } if (sb.length() == 0) { // Looks like a relative path, let's suggest {./...} or {~/...} if (p.startsWith("~")) { diff --git a/src/test/java/org/codejive/jpm/util/ScriptUtilsTest.java b/src/test/java/org/codejive/jpm/util/ScriptUtilsTest.java index 55365ee..eaba7d0 100644 --- a/src/test/java/org/codejive/jpm/util/ScriptUtilsTest.java +++ b/src/test/java/org/codejive/jpm/util/ScriptUtilsTest.java @@ -257,4 +257,12 @@ void testProcessMultiCommandArgsFilesUse() throws Exception { assertThat(argsFiles.files.get(1)).hasContent(expectedContents2); } } + + @Test + void testProcessCommandWithInvalidClasspath() { + String command = "./mvnw spotless:apply package -DskipTests"; + String result = ScriptUtils.suggestSubstitutions(command); + // Substitutions should not have treated spottless:apply as a classpath + assertThat(result).isEqualTo("{./mvnw} spotless:apply package -DskipTests"); + } }