diff --git a/src/main/java/dev/jbang/util/Util.java b/src/main/java/dev/jbang/util/Util.java index c0e789e81..8e03ef352 100644 --- a/src/main/java/dev/jbang/util/Util.java +++ b/src/main/java/dev/jbang/util/Util.java @@ -67,7 +67,9 @@ import dev.jbang.catalog.Catalog; import dev.jbang.cli.BaseCommand; import dev.jbang.cli.ExitException; +import dev.jbang.dependencies.DependencyResolver; import dev.jbang.dependencies.DependencyUtil; +import dev.jbang.dependencies.ModularClassPath; import dev.jbang.source.Source; public class Util { @@ -93,7 +95,7 @@ public class Util { "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)*$"); private static final Pattern subUrlPattern = Pattern.compile( - "^(%?%https?://.+$)|(%?%\\{https?://[^}]+})"); + "^(%?%https?://.+$)|(%?%\\{[a-z]+:[^}]+})"); private static boolean verbose; private static boolean quiet; @@ -1894,10 +1896,22 @@ public static String substituteRemote(String arg) { if (txt.startsWith("{") && txt.endsWith("}")) { txt = txt.substring(1, txt.length() - 1); } - try { - return Matcher.quoteReplacement(Util.downloadAndCacheFile(txt).toString()); - } catch (IOException e) { - throw new ExitException(BaseCommand.EXIT_INVALID_INPUT, "Error substituting remote file: " + txt, e); + if (txt.startsWith("http://") || txt.startsWith("https://")) { + try { + return Matcher.quoteReplacement(Util.downloadAndCacheFile(txt).toString()); + } catch (IOException e) { + throw new ExitException(BaseCommand.EXIT_INVALID_INPUT, "Error substituting remote file: " + txt, + e); + } + } else if (txt.startsWith("deps:")) { + List deps = Arrays.asList(txt.substring(5).split(",")); + DependencyResolver resolver = new DependencyResolver(); + resolver.addDependencies(deps); + ModularClassPath mcp = resolver.resolve(); + return mcp.getClassPath(); + } else { + String type = txt.substring(0, txt.indexOf(":")); + throw new ExitException(BaseCommand.EXIT_INVALID_INPUT, "Unknown substitution type: " + type); } }); } diff --git a/src/test/java/dev/jbang/cli/TestRun.java b/src/test/java/dev/jbang/cli/TestRun.java index 6529921c8..a37fdf933 100644 --- a/src/test/java/dev/jbang/cli/TestRun.java +++ b/src/test/java/dev/jbang/cli/TestRun.java @@ -2427,6 +2427,32 @@ void testRemoteFileArgComplexEscaped() throws Exception { assertThat(result.err, not(containsString("foo%%{" + arg + "}bar"))); } + @Test + @SuppressWarnings("unchecked") + void testDepsSubstituteArg() throws Exception { + if (Util.isWindows()) { + environmentVariables.set(Util.JBANG_RUNTIME_SHELL, "powershell"); + } + String script = examplesTestFolder.resolve("helloworld.java").toString(); + CaptureResult result = checkedRun(null, "run", "--verbose", script, + "%{deps:info.picocli:picocli:4.6.3,log4j:log4j:1.2.17}"); + assertThat(result.err, containsString("Resolving dependencies...")); + assertThat(result.err, + containsString("info/picocli/picocli/4.6.3/picocli-4.6.3.jar".replace("/", File.separator))); + assertThat(result.err, containsString("log4j/log4j/1.2.17/log4j-1.2.17.jar".replace("/", File.separator))); + } + + @Test + @SuppressWarnings("unchecked") + void testUnknownSubstituteArg() throws Exception { + if (Util.isWindows()) { + environmentVariables.set(Util.JBANG_RUNTIME_SHELL, "powershell"); + } + String script = examplesTestFolder.resolve("helloworld.java").toString(); + ExitException e = Assertions.assertThrows(ExitException.class, + () -> checkedRun(null, "run", "--verbose", script, "%{foo:bar}")); + } + @Test void testHelloWorldGAVWithModulesButNoManifest() throws IOException { environmentVariables.clear("JAVA_HOME");