diff --git a/build.gradle b/build.gradle index 73fd1e86c..c776c0757 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ dependencies { //implementation 'com.google.guava:guava:28.2-jre' - testCompile "com.github.tomakehurst:wiremock-jre8:2.27.2" + testCompile "com.github.tomakehurst:wiremock-jre8:2.32.0" testImplementation "org.junit.jupiter:junit-jupiter:5.7.1" testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.1') diff --git a/docs/modules/ROOT/pages/alias_catalogs.adoc b/docs/modules/ROOT/pages/alias_catalogs.adoc index 9e994b1b0..645a489c6 100644 --- a/docs/modules/ROOT/pages/alias_catalogs.adoc +++ b/docs/modules/ROOT/pages/alias_catalogs.adoc @@ -29,18 +29,29 @@ The aliases you create are stored locally (see <>), but JB You can access those catalogs explicitly (see <>) but it is much easier to use what we call "implicit catalogs", which are aliases that have a special format and JBang is smart enough to know where to find their definition. +There are two kinds, one that resolves to a Git backed service (github,gitlab and bitbucket) and one that resolves to +https sites. + Examples: +`jbang tree@jbang.dev` will (because of the dot in the name) will lookup a catalog first at https://jbang.dev/jbang-catalog.json. + `jbang hello@jbangdev` will run the alias `hello` as defined in `jbang-catalog.json` found in https://github.com/jbangdev/jbang-catalog. -This allows anyone to provide a set of jbang scripts defined in their github, gitlab or bitbucket repositories. +This allows anyone to provide a set of jbang scripts defined in their website or in a github, gitlab or bitbucket repositories. -The full format is `@(/repository)(/branch)(~path)` allowing you to do things like: +The full format is `@(/repository)(/branch)(~path)` or `@(/path/to/catalog)` allowing you to do things like: .Implicit Catalog Examples: |==== |Command | Description +|`jbang hello@acme.corp` +|`hello` alias found in `https://acme.corp/jbang-catalog.json` if available or the default branch searched on github, gitlab and bitbucket in that order. + +|`jbang hello@acme.corp/a/path/to/jbang-catalog.json` +|`hello` alias found in `https://acme.corp/a/path/to/jbang-catalog.json` if available. + |`jbang hello@acme` |`hello` alias found in `acme/jbang-catalog/jbang-catalog.json` of the default branch searched on github, gitlab and bitbucket in that order. diff --git a/src/main/java/dev/jbang/catalog/ImplicitCatalogRef.java b/src/main/java/dev/jbang/catalog/ImplicitCatalogRef.java index ac0791059..b2aa1f7cd 100644 --- a/src/main/java/dev/jbang/catalog/ImplicitCatalogRef.java +++ b/src/main/java/dev/jbang/catalog/ImplicitCatalogRef.java @@ -1,13 +1,10 @@ package dev.jbang.catalog; -import static dev.jbang.cli.BaseCommand.EXIT_INVALID_INPUT; - import java.util.Arrays; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; -import dev.jbang.cli.ExitException; import dev.jbang.util.Util; public class ImplicitCatalogRef { @@ -34,6 +31,12 @@ public String url(String host, String infix) { return host + org + "/" + repo + infix + ref + "/" + path + Catalog.JBANG_CATALOG_JSON; } + /** + * Return ImplicitCatalogRef if can be parsed into a git accessible structure. + * + * @param name + * @return null if cannot parse it + */ public static ImplicitCatalogRef parse(String name) { String[] parts = name.split("~", 2); String path; @@ -44,7 +47,8 @@ public static ImplicitCatalogRef parse(String name) { } String[] names = parts[0].split("/"); if (names.length > 3) { - throw new ExitException(EXIT_INVALID_INPUT, "Invalid catalog name '" + name + "'"); + + return null; } String org = names[0]; String repo; @@ -62,16 +66,25 @@ public static ImplicitCatalogRef parse(String name) { return new ImplicitCatalogRef(org, repo, ref, path); } - static Optional getImplicitCatalogUrl(String catalogName) { - ImplicitCatalogRef icr = parse(catalogName); + public static Optional getImplicitCatalogUrl(String catalogName) { + + Optional icr = Optional.ofNullable(parse(catalogName)); Optional url = chain( - () -> tryDownload(icr.url(GITHUB_URL, "/blob/")), - () -> icr.isPossibleCommit() ? tryDownload(icr.url(GITHUB_URL, "/blob/")) : Optional.empty(), - () -> tryDownload(icr.url(GITLAB_URL, "/-/blob/")), - () -> icr.isPossibleCommit() ? tryDownload(icr.url(GITLAB_URL, "/-/blob/")) : Optional.empty(), - () -> tryDownload(icr.url(BITBUCKET_URL, "/src/")), - () -> icr.isPossibleCommit() ? tryDownload(icr.url(BITBUCKET_URL, "/src/")) : Optional.empty()) - .findFirst(); + () -> catalogName.startsWith("https://") || catalogName.startsWith("http://") + || catalogName.startsWith("file://") ? tryDownload(catalogName) : Optional.empty(), + () -> catalogName.contains(".") ? tryDownload("https://" + catalogName) : Optional.empty(), + () -> icr.isPresent() ? tryDownload(icr.get().url(GITHUB_URL, "/blob/")) : Optional.empty(), + () -> icr.isPresent() && icr.get().isPossibleCommit() ? tryDownload(icr.get().url(GITHUB_URL, "/blob/")) + : Optional.empty(), + () -> icr.isPresent() ? tryDownload(icr.get().url(GITLAB_URL, "/-/blob/")) : Optional.empty(), + () -> icr.isPresent() && icr.get().isPossibleCommit() + ? tryDownload(icr.get().url(GITLAB_URL, "/-/blob/")) + : Optional.empty(), + () -> icr.isPresent() ? tryDownload(icr.get().url(BITBUCKET_URL, "/src/")) : Optional.empty(), + () -> icr.isPresent() && icr.get().isPossibleCommit() + ? tryDownload(icr.get().url(BITBUCKET_URL, "/src/")) + : Optional.empty()) + .findFirst(); return url; } diff --git a/src/test/java/dev/jbang/TestImplicitAlias.java b/src/test/java/dev/jbang/TestImplicitAlias.java new file mode 100644 index 000000000..dfe925f1d --- /dev/null +++ b/src/test/java/dev/jbang/TestImplicitAlias.java @@ -0,0 +1,63 @@ +package dev.jbang; + +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import dev.jbang.catalog.Alias; +import dev.jbang.catalog.ImplicitCatalogRef; + +/** + * TODO: use mock to handle https lookups instead of jbang.dev + */ +public class TestImplicitAlias extends BaseTest { + + @Test + public void testGitImplicitCatalog() { + assertThat(ImplicitCatalogRef.getImplicitCatalogUrl("jbangdev").get(), + Matchers.equalTo("https://github.com/jbangdev/jbang-catalog/blob/HEAD/jbang-catalog.json")); + assertThat(ImplicitCatalogRef.getImplicitCatalogUrl("jbangdev/jbang-examples").get(), + Matchers.equalTo("https://github.com/jbangdev/jbang-examples/blob/HEAD/jbang-catalog.json")); + } + + @Test + public void testImplictURLAlias() { + + Alias url = Alias.get("tree@xam.dk"); + assertThat(url.scriptRef, Matchers.equalTo("tree/main.java")); + + } + + @Test + public void testImplictExplicitURLAlias() { + + Alias url = Alias.get("tree@https://xam.dk"); + assertThat(url.scriptRef, Matchers.equalTo("tree/main.java")); + + } + + // @Test needs fixing to not generate absolute paths but instead relative paths. + public void testFileURLAlias() throws IOException { + + assertThat(jbangTempDir.resolve("inner").toFile().mkdirs(), Matchers.is(true)); + + Files.copy(examplesTestFolder.resolve("helloworld.java"), jbangTempDir.resolve("inner/helloworld.java")); + String src = jbangTempDir.resolve("inner/helloworld.java").toString(); + Path path = jbangTempDir.resolve("jbang-catalog.json"); + + checkedRun(null, "alias", "add", "-f", path.toString(), "--name=apptest", src); + + String url = "apptest@" + path.toUri(); + assertThat(url, Matchers.stringContainsInOrder("file://")); + + Alias alias = Alias.get(url); + + assertThat(alias.scriptRef, Matchers.equalTo("helloworld.java")); + + } +}