From 17ac61dc787989c99ec0dc6fb5d087e959689c98 Mon Sep 17 00:00:00 2001 From: Peter Verhas Date: Fri, 25 Oct 2024 00:28:34 +0200 Subject: [PATCH] starting the git macro package --- FAQ.adoc | 9 +- FAQ.adoc.jam | 5 +- README.jrf | 2 +- documentation/MODULES.adoc | 24 +- documentation/MODULES.adoc.jam | 3 + jamal-all/README.adoc | 2 + jamal-all/pom.xml | 4 + jamal-asciidoc/README.adoc | 2 + jamal-asciidoc/pom.xml | 4 + jamal-asciidoc258/pom.xml | 4 + jamal-cmd/pom.xml | 4 + .../src/test/resources/jamal.sh.template.jam | 2 +- jamal-git/README.adoc | 120 ++++++++++ jamal-git/README.adoc.jam | 70 ++++++ jamal-git/pom.jam | 21 ++ jamal-git/pom.xml | 71 ++++++ .../main/java/javax0/jamal/git/Connect.java | 41 ++++ .../src/main/java/javax0/jamal/git/Repo.java | 25 +++ .../src/main/java/javax0/jamal/git/Tag.java | 212 ++++++++++++++++++ jamal-git/src/main/java/module-info.java | 11 + .../META-INF/services/javax0.jamal.api.Macro | 2 + .../test/java/javax0/jamal/git/TestGit.java | 197 ++++++++++++++++ jamal-maven-extension/README.adoc | 11 +- jamal-maven-extension/README.adoc.jam | 10 +- jamal-maven-extension/pom.xml | 5 + jamal-maven-input/README.adoc | 4 +- jamal-maven-plugin/pom.xml | 4 + jamal-snippet/README.adoc | 2 +- jamal-sql/demodb.mv.db | Bin 24576 -> 24576 bytes jamal-test/DEMO.md | 4 +- jamal-test/DEMO.md.jam | 4 +- .../src/test/resources/demoConverted.docx | Bin 12792 -> 12792 bytes .../test/resources/includetestConverted.docx | Bin 34572 -> 34572 bytes .../src/test/resources/pictureConverted.docx | Bin 21869 -> 21869 bytes .../src/test/resources/sampleConverted.docx | Bin 40810 -> 40817 bytes jamal.sh | 8 +- modules.jim | 2 +- pom.xml | 6 + 38 files changed, 858 insertions(+), 37 deletions(-) create mode 100644 jamal-git/README.adoc create mode 100644 jamal-git/README.adoc.jam create mode 100644 jamal-git/pom.jam create mode 100644 jamal-git/pom.xml create mode 100644 jamal-git/src/main/java/javax0/jamal/git/Connect.java create mode 100644 jamal-git/src/main/java/javax0/jamal/git/Repo.java create mode 100644 jamal-git/src/main/java/javax0/jamal/git/Tag.java create mode 100644 jamal-git/src/main/java/module-info.java create mode 100644 jamal-git/src/main/resources/META-INF/services/javax0.jamal.api.Macro create mode 100644 jamal-git/src/test/java/javax0/jamal/git/TestGit.java diff --git a/FAQ.adoc b/FAQ.adoc index 42a6eae00..5c3be9167 100644 --- a/FAQ.adoc +++ b/FAQ.adoc @@ -57,7 +57,10 @@ unless the file starts with the characters `{@`. === I installed the ASCIIDOC plugin, and it gives error for the Jamal documentation -If you try to look at the git master HEAD version there is a possibility that the documentation has errors. + +It can only happen if you use a non-release, development version. + +If you try to look at the git master HEAD version, there is a possibility that the documentation has errors. Check on GitHub if the build is broken. It usually is not. After that check that you have the latest version of the plugin is installed. @@ -65,6 +68,4 @@ When you edit the git master HEAD version, you are editing a not released versio The current documentation of Jamal may use the newest features, which were not released yet. To edit these, you need to install the non-released version. To do that run `mvn install` in the Jamal project directory first. -After that `cd jamal-asciidoc` and `sh ./install-asciidoc.sh`. - - +After that `cd jamal-asciidoc` and `sh ./install-asciidoc.sh`. \ No newline at end of file diff --git a/FAQ.adoc.jam b/FAQ.adoc.jam index 256dd876c..ec1b3c27f 100644 --- a/FAQ.adoc.jam +++ b/FAQ.adoc.jam @@ -51,7 +51,10 @@ unless the file starts with the characters `{@escape*``{@``}`. } {Q|I installed the ASCIIDOC plugin, and it gives error for the Jamal documentation| -If you try to look at the git master HEAD version there is a possibility that the documentation has errors. + +It can only happen if you use a non-release, development version. + +If you try to look at the git master HEAD version, there is a possibility that the documentation has errors. Check on GitHub if the build is broken. It usually is not. After that check that you have the latest version of the plugin is installed. diff --git a/README.jrf b/README.jrf index ad7d3d501..a51cbaec2 100644 --- a/README.jrf +++ b/README.jrf @@ -1,5 +1,5 @@ # This is a Jamal reference file containing serialized base64 encoded macros -# Created: 2024-10-08 20:08:24 +0200 +# Created: 2024-10-25 00:14:29 +0200 # id|openStr|closeStr|verbatim|tailParameter|pure|content|parameters # TOC VE9D|eyU=|JX0=|0|0|0|Ci4gPDxJbnN0YWxsYXRpb24+PgouIDw8R1M+PgouIDw8Q29uZmlndXJhdGlvbj4+Ci4gPDxGZWF0dXJlcz4+Ci4gPDxDb250cmlidXRpbmc+PgouIDw8RG9jdW1lbnRhdGlvbj4+Ci4gPDxMaWNlbnNlPj4KLiA8PENoYW5nZWxvZz4+Ci4gPDxSb2FkbWFwPj4KLiA8PFN1cHBvcnQ+PgouIDw8RkFRPj4KLiA8PE1haW50ZW5hbmNlPj4=| diff --git a/documentation/MODULES.adoc b/documentation/MODULES.adoc index 8b0453c9e..e2cf35cc4 100644 --- a/documentation/MODULES.adoc +++ b/documentation/MODULES.adoc @@ -250,43 +250,45 @@ Using these macros, you can split up a Yaml file into smaller pieces and use mac Macros implementing interface to OpenAI. -=== 35. link:https://github.com/verhas/jamal/blob/master/jamal-sql/README.adoc[SQL] ^_macro_^ +=== 35. link:https://github.com/verhas/jamal/blob/master/jamal-git/README.adoc[GIT] ^_macro_^ + + +Macros implementing interface to local git repository. + +=== 36. link:https://github.com/verhas/jamal/blob/master/jamal-sql/README.adoc[SQL] ^_macro_^ Macros implementing SQL interface. -=== 36. link:https://github.com/verhas/jamal/blob/master/jamal-xls/README.adoc[XLS] ^_macro_^ +=== 37. link:https://github.com/verhas/jamal/blob/master/jamal-xls/README.adoc[XLS] ^_macro_^ Macros implementing Excel interface. -=== 37. link:https://github.com/verhas/jamal/blob/master/jamal-rest/README.adoc[REST] ^_macro_^ +=== 38. link:https://github.com/verhas/jamal/blob/master/jamal-rest/README.adoc[REST] ^_macro_^ Macros implementing REST interface. -=== 38. link:https://github.com/verhas/jamal/blob/master/jamal-java/README.adoc[JAVA] ^_debugger_^ +=== 39. link:https://github.com/verhas/jamal/blob/master/jamal-java/README.adoc[JAVA] ^_debugger_^ Experimental module integrating the Java compiler into Jamal as macros. -=== 39. link:https://github.com/verhas/jamal/blob/master/jamal-debug-ui/README.adoc[DEBUG-UI] ^_debugger_^ +=== 40. link:https://github.com/verhas/jamal/blob/master/jamal-debug-ui/README.adoc[DEBUG-UI] ^_debugger_^ This is not a module. The code in the directory `jamal-debug-ui` contains the REACT.js based ui for the debugger. -=== 40. link:https://github.com/verhas/jamal/blob/master/jamal-docker/README.adoc[DOCKER] ^_embed_^ +=== 41. link:https://github.com/verhas/jamal/blob/master/jamal-docker/README.adoc[DOCKER] ^_embed_^ This is not a module. The code in the directory `jamal-docker` contains a Dockerfile to build a Docker image with Jamal. -=== 41. link:https://github.com/verhas/jamal/blob/master/jamal-packaging/README.adoc[PACKAGING] ^_packaging_^ - - - -Contains the different packaging code in the subdirectories. +=== 42. link:https://github.com/verhas/jamal/blob/master/jamal-packaging/README.adoc[PACKAGING] ^_packaging_^ +Contains the different packaging code in the subdirectories. \ No newline at end of file diff --git a/documentation/MODULES.adoc.jam b/documentation/MODULES.adoc.jam index 54d86df84..b3e336965 100644 --- a/documentation/MODULES.adoc.jam +++ b/documentation/MODULES.adoc.jam @@ -186,6 +186,9 @@ Using these macros, you can split up a Yaml file into smaller pieces and use mac {chapter :openai:macro} Macros implementing interface to OpenAI. +{chapter :git:macro} +Macros implementing interface to local git repository. + {chapter :sql:macro} Macros implementing SQL interface. diff --git a/jamal-all/README.adoc b/jamal-all/README.adoc index b1e7f632b..8c8719f26 100644 --- a/jamal-all/README.adoc +++ b/jamal-all/README.adoc @@ -54,6 +54,8 @@ This will add the following dependencies to your project: * `jamal-rest` +* `jamal-git` + * `jamal-groovy` * `jamal-io` diff --git a/jamal-all/pom.xml b/jamal-all/pom.xml index f11f8988f..a149ef3a5 100644 --- a/jamal-all/pom.xml +++ b/jamal-all/pom.xml @@ -95,6 +95,10 @@ com.javax0.jamal jamal-rest + + com.javax0.jamal + jamal-git + com.javax0.jamal jamal-groovy diff --git a/jamal-asciidoc/README.adoc b/jamal-asciidoc/README.adoc index 848356447..ef692cdca 100644 --- a/jamal-asciidoc/README.adoc +++ b/jamal-asciidoc/README.adoc @@ -680,6 +680,8 @@ The ZIP file you extracted to the `.Asciidoctor/lib` folder contains the followi * `jamal-rest` +* `jamal-git` + * `jamal-test` * `jamal-debug` diff --git a/jamal-asciidoc/pom.xml b/jamal-asciidoc/pom.xml index 217785206..6117836e8 100644 --- a/jamal-asciidoc/pom.xml +++ b/jamal-asciidoc/pom.xml @@ -120,6 +120,10 @@ com.javax0.jamal jamal-rest + + com.javax0.jamal + jamal-git + com.javax0.jamal jamal-test diff --git a/jamal-asciidoc258/pom.xml b/jamal-asciidoc258/pom.xml index ba9670da2..dbc8d6acd 100644 --- a/jamal-asciidoc258/pom.xml +++ b/jamal-asciidoc258/pom.xml @@ -101,6 +101,10 @@ com.javax0.jamal jamal-rest + + com.javax0.jamal + jamal-git + com.javax0.jamal jamal-test diff --git a/jamal-cmd/pom.xml b/jamal-cmd/pom.xml index c3be5116a..fbeff5e39 100644 --- a/jamal-cmd/pom.xml +++ b/jamal-cmd/pom.xml @@ -124,6 +124,10 @@ com.javax0.jamal jamal-rest + + com.javax0.jamal + jamal-git + com.javax0.jamal jamal-maven-input diff --git a/jamal-cmd/src/test/resources/jamal.sh.template.jam b/jamal-cmd/src/test/resources/jamal.sh.template.jam index 5d0f5e549..1f33ba09c 100644 --- a/jamal-cmd/src/test/resources/jamal.sh.template.jam +++ b/jamal-cmd/src/test/resources/jamal.sh.template.jam @@ -6,7 +6,7 @@ {@define CENTRAL=https://repo1.maven.org/maven2}\ CLASSPATH="" -function download() {} +download() {} DIR=${}1//\.//} if ! test -f {REPO}/$DIR/$2/$3/$2-$3.jar; then echo "downloading {CENTRAL}/$DIR/$2/$3/jamal-$2-$3.jar" diff --git a/jamal-git/README.adoc b/jamal-git/README.adoc new file mode 100644 index 000000000..0f97f593a --- /dev/null +++ b/jamal-git/README.adoc @@ -0,0 +1,120 @@ += Jamal Git Macros + + +This macro package can fetch information from a local git repository. + +To use this module, you have to add the dependency to your Maven project, as: + +[source,xml] +---- + + com.javax0.jamal + jamal-git + 2.8.2-SNAPSHOT + +---- + +This macro package is included in the prepackaged versions of Jamal. + +== Macros Implemented + + +=== `git` + +This macro should be used to specify the directory of the git repository. +For example + +.Jamal source +[source] +---- +{@git location=../.git} +---- + +will locate the repository and then subsequent macros can fetch information from the repository. +The parameter option (parop) `location` is mandatory and it should point to the `.git` directory of the repository. + +The parameters of the opened repository are stored in the user defined macro `$git`. +You can reference this macro directly as `{$git}` and the value will always be an empty string. + +If you want to handle multiple git repositories, you can use the parop `id` to give an identifier to the repository. +If used, the name specified will be used as a user-defined macro name to store the preference to the git repository. +In this case the parop `id` should be used in all later calls to the git macros using the specific git repository. + +=== `git:tag(s)`, `got:branch(es)` + +This macro can be called as + +* `git:tag`, +* `git:tags`, +* `git:branch`, or +* `git:branches` + +The macro can collect tags or branches from the local git repository. +The singular and plural forms behave the same, and they exist to help the readability. +The macro names containing `tag` return a tag or tags, and the ones containing `branch` return a branch or branches. + +The behavior of the macro can be controlled using parops. +These are the following: + +* `id` +is the identifier of the opened git repository. +The default value is `$git` which is also the default value of the `git:connect` macro. +If you are not dealing with more than one git repositories you can omit this parameter. +Use it only if you also used it in the `git:connect` macro. +* `match` +is a regular expression that the tag name should match. +If this parameter is present, then only those tags are listed that match the regular expression. +* `index` +is the index of the tag to list. +If this parameter is present, then only the tag at the index is listed. +The index is 0-based. +Negative index is also allowed. +In this case, the index is counted from the end of the list. +For example, -1 means the last tag. +If the index is too large or too small, then an error is thrown. +* `last` +If this parameter is present, then only the last tag is listed. +It is the same as `index=-1`. +* `first` +If this parameter is present, then only the first tag is listed. +It is the same as `index=0`. +* `single` +If this parameter is present, then the result is a single tag. +If this parameter is present and the result is more than one tag then an error is thrown. + +* `orderByName` orders the tags by name. +* `orderByDate` orders the tags by the commit date. ++ +The default value is `orderByName`. +`orderByName` and `orderByDate` are exclusive; you can use only one. + +* `name` will return the name(s) of the tag(s) or branch(es). +* `time` will return the time of the commit of the tag(s) or branch(es). +* `hash` will return the hash of the commit of the tag(s) or branch(es). ++ +The default value is `name`. +`name`, `time`, and `hash` are exclusive; you can use only one. +* `sep` +is the separator between the tags. +The default value is `,` (a comma). +This string (not only a single character is possible) is used to separate the tags in the result. +The list can be used as the value list for the `for` macro. +In the very special case when some of the tag or branch names contains a comma, then you can use this parameter. + + +Examples: + +.Jamal source +[source] +---- +{@git location=../.git} +The latest release of Jamal is {#string:substring (begin=1) {@git:tag last orderByName match=v.*}}. +---- + +result in: + +.output +[source] +---- +The latest release of Jamal is 2.8.1. +---- \ No newline at end of file diff --git a/jamal-git/README.adoc.jam b/jamal-git/README.adoc.jam new file mode 100644 index 000000000..cff0d2763 --- /dev/null +++ b/jamal-git/README.adoc.jam @@ -0,0 +1,70 @@ += Jamal Git Macros +{%@import res:jamal.jim%} +{%@snip:xml pom=pom.xml%}\ +{%#define VERSION={%pom /project/version/text()%}%}\ + +This macro package can fetch information from a local git repository. + +To use this module, you have to add the dependency to your Maven project, as: + +[source,xml] +---- + + com.javax0.jamal + {%pom /project/artifactId/text()%} + {%VERSION%} + +---- + +This macro package is included in the prepackaged versions of Jamal. + +== Macros Implemented +{%@snip:collect from=src/main/%} + +=== `git` + +This macro should be used to specify the directory of the git repository. +For example + +{%sample/{@git location=../.git}%} + +will locate the repository and then subsequent macros can fetch information from the repository. +The parameter option (parop) `location` is mandatory and it should point to the `.git` directory of the repository. + +The parameters of the opened repository are stored in the user defined macro `$git`. +You can reference this macro directly as `{$git}` and the value will always be an empty string. + +If you want to handle multiple git repositories, you can use the parop `id` to give an identifier to the repository. +If used, the name specified will be used as a user-defined macro name to store the preference to the git repository. +In this case the parop `id` should be used in all later calls to the git macros using the specific git repository. + +=== `git:tag(s)`, `got:branch(es)` + +This macro can be called as + +* `git:tag`, +* `git:tags`, +* `git:branch`, or +* `git:branches` + +The macro can collect tags or branches from the local git repository. +The singular and plural forms behave the same, and they exist to help the readability. +The macro names containing `tag` return a tag or tags, and the ones containing `branch` return a branch or branches. + +The behavior of the macro can be controlled using parops. +These are the following: + +{%#replaceLines replace="/^.*?\"(.*?)\".*/* `$1`/" replace=~^\s*//\s*~~ replace="/.*enumeration.*/" +{%@snip tag_parameters%}%} + +Examples: + +{%sample/{@git location=../.git} +The latest release of Jamal is {#string:substring (begin=1) {@git:tag last orderByName match=v.*}}. +%} + +result in: + +{%output%} + + diff --git a/jamal-git/pom.jam b/jamal-git/pom.jam new file mode 100644 index 000000000..d5613ce8f --- /dev/null +++ b/jamal-git/pom.jam @@ -0,0 +1,21 @@ +{@import https://raw.githubusercontent.com/central7/pom/1/pom.jim} +{@import ../version.jim} +{project jamal maven input} + {GAV ::jamal-git:{VERSION}} + {parent :{GROUPID}:jamal-parent} + {description :Jamal Git repo reading plugin} + + {@define openForTests={opens/git/git}} + {build|{plugins| {@include ../plugins.jim}}} + + {dependencies# + {#for MODULE in (engine,extensions,tools)= + {dependency|{GROUPID}|jamal-MODULE}} + {#for MODULE in (testsupport)= + {dependency|{GROUPID}|jamal-MODULE||test}} + {#for MODULE in (api,engine)= + {dependency|org.junit.jupiter|junit-jupiter-MODULE}} + {dependency|org.eclipse.jgit|org.eclipse.jgit|6.6.1.202309021850-r} + } +{end project} +{@xmlFormat} \ No newline at end of file diff --git a/jamal-git/pom.xml b/jamal-git/pom.xml new file mode 100644 index 000000000..44b843266 --- /dev/null +++ b/jamal-git/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + jamal maven input + jamal-git + 2.8.2-SNAPSHOT + + com.javax0.jamal + jamal-parent + 2.8.2-SNAPSHOT + + Jamal Git repo reading plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens jamal.git/javax0.jamal.git=ALL-UNNAMED + -XX:+EnableDynamicAgentLoading + + + + + + + + com.javax0.jamal + jamal-engine + + + com.javax0.jamal + jamal-extensions + + + com.javax0.jamal + jamal-tools + + + com.javax0.jamal + jamal-testsupport + test + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-engine + + + org.eclipse.jgit + org.eclipse.jgit + 6.6.1.202309021850-r + + + \ No newline at end of file diff --git a/jamal-git/src/main/java/javax0/jamal/git/Connect.java b/jamal-git/src/main/java/javax0/jamal/git/Connect.java new file mode 100644 index 000000000..dbcfd9f88 --- /dev/null +++ b/jamal-git/src/main/java/javax0/jamal/git/Connect.java @@ -0,0 +1,41 @@ +package javax0.jamal.git; + +import javax0.jamal.api.BadSyntax; +import javax0.jamal.api.Input; +import javax0.jamal.api.Macro; +import javax0.jamal.api.Processor; +import javax0.jamal.tools.Scanner; +import javax0.jamal.tools.param.StringParameter; +import org.eclipse.jgit.api.Git; + +@Macro.Name("git") +public class Connect implements Macro, Scanner.WholeInput { + @Override + public String evaluate(Input in, Processor processor) throws BadSyntax { + final var scanner = newScanner(in, processor); + final var id = scanner.str(null, "id").defaultValue("$git"); + final var location = scanner.file(null, "location"); + scanner.done(); + + try { + final var repo = Git.open(location.file()); + final var holder = new Repo(repo, id.get()); + processor.define(holder); + processor.deferredClose(holder); + } catch (Exception e) { + throw new BadSyntax("Cannot open git repository at " + location.file().getAbsolutePath(), e); + } + return ""; + } + + static Git git(Processor processor, StringParameter id) throws BadSyntax { + final var repoId = id.get() != null ? id.get() : "$git"; + + return processor.getRegister().getUserDefined(repoId) + .filter(Repo.class::isInstance) + .map(Repo.class::cast) + .orElseThrow(() -> new BadSyntax("The git repository '" + repoId + "' does not exist or not a repo")) + .getObject(); + } + +} diff --git a/jamal-git/src/main/java/javax0/jamal/git/Repo.java b/jamal-git/src/main/java/javax0/jamal/git/Repo.java new file mode 100644 index 000000000..a04540997 --- /dev/null +++ b/jamal-git/src/main/java/javax0/jamal/git/Repo.java @@ -0,0 +1,25 @@ +package javax0.jamal.git; + +import javax0.jamal.api.BadSyntax; +import javax0.jamal.api.UserDefinedMacro; +import javax0.jamal.tools.IdentifiedObjectHolder; +import org.eclipse.jgit.api.Git; + +import java.io.Closeable; + +public class Repo extends IdentifiedObjectHolder implements UserDefinedMacro , Closeable { + + public Repo(Git git, String id) { + super(git, id); + } + + @Override + public String evaluate(String... parameters) throws BadSyntax { + return ""; + } + + @Override + public void close() { + getObject().close(); + } +} diff --git a/jamal-git/src/main/java/javax0/jamal/git/Tag.java b/jamal-git/src/main/java/javax0/jamal/git/Tag.java new file mode 100644 index 000000000..b09225ed2 --- /dev/null +++ b/jamal-git/src/main/java/javax0/jamal/git/Tag.java @@ -0,0 +1,212 @@ +package javax0.jamal.git; + +import javax0.jamal.api.BadSyntax; +import javax0.jamal.api.Input; +import javax0.jamal.api.Macro; +import javax0.jamal.tools.Scanner; +import javax0.jamal.tools.param.EnumerationParameter; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTag; +import org.eclipse.jgit.revwalk.RevWalk; + +import java.util.Comparator; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * The {@code Tag} class implements a macro to interact with Git tags and branches in a Jamal environment. + * It allows retrieving and listing Git tags or branches, optionally filtering or sorting them by name, date, + * or matching against a regular expression. + * + *

Supported macro names: "git:tag", "git:tags", "git:branch", "git:branches". + * + *

Usage examples can include listing tags, filtering them based on regex, or retrieving information such + * as the commit time, hash, or tag name. + */ +@Macro.Name({"git:tag", "git:tags", "git:branch", "git:branches"}) +public class Tag implements Macro, Scanner.WholeInput { + + enum OrderBy { + orderByName, orderByDate + } + + enum TagWhat { + name, time, hash + } + + /** + * Evaluates the macro by retrieving and processing Git tags or branches. + * + *

The macro supports several options such as filtering tags using a regular expression, + * retrieving tags in a specific order (by name or by date), or extracting specific information + * such as the tag name, commit time, or hash. It also supports options like retrieving a specific + * tag by index, or getting the first or last tag. + * + * @param in the input provided to the macro, which can contain additional parameters like the regular expression to match + * @param processor the Jamal processor instance + * @return the result of evaluating the macro, which is a list of tags or branches formatted as a string, + * potentially filtered and sorted based on the input options + * @throws BadSyntax if there is a conflict in the options provided or if the Git repository cannot be accessed + */ + @Override + public String evaluate(Input in, javax0.jamal.api.Processor processor) throws BadSyntax { + final var me = processor.getId(); + final var scanner = newScanner(in, processor); + // snippet tag_parameters + final var id = scanner.str(null, "id").optional(); + // is the identifier of the opened git repository. + // The default value is `$git` which is also the default value of the `git:connect` macro. + // If you are not dealing with more than one git repositories you can omit this parameter. + // Use it only if you also used it in the `git:connect` macro. + final var match = scanner.str(null, "match").optional(); + // is a regular expression that the tag name should match. + // If this parameter is present, then only those tags are listed that match the regular expression. + final var index = scanner.number(null, "index").optional(); + // is the index of the tag to list. + // If this parameter is present, then only the tag at the index is listed. + // The index is 0-based. + // Negative index is also allowed. + // In this case, the index is counted from the end of the list. + // For example, -1 means the last tag. + // If the index is too large or too small, then an error is thrown. + final var last = scanner.bool(null, "last"); + // If this parameter is present, then only the last tag is listed. + // It is the same as `index=-1`. + final var first = scanner.bool(null, "first"); + // If this parameter is present, then only the first tag is listed. + // It is the same as `index=0`. + final var single = scanner.bool(null, "single"); + // If this parameter is present, then the result is a single tag. + // If this parameter is present and the result is more than one tag then an error is thrown. + final var order = scanner.enumeration(OrderBy.class).defaultValue(OrderBy.orderByName); + // * `orderByName` orders the tags by name. + // * `orderByDate` orders the tags by the commit date. + //+ + // The default value is `orderByName`. + // `orderByName` and `orderByDate` are exclusive; you can use only one. + final var what = scanner.enumeration(TagWhat.class).defaultValue(TagWhat.name); + // * `name` will return the name(s) of the tag(s) or branch(es). + // * `time` will return the time of the commit of the tag(s) or branch(es). + // * `hash` will return the hash of the commit of the tag(s) or branch(es). + //+ + // The default value is `name`. + // `name`, `time`, and `hash` are exclusive; you can use only one. + final var sep = scanner.str(null, "sep").defaultValue(","); + // is the separator between the tags. + // The default value is `,` (a comma). + // This string (not only a single character is possible) is used to separate the tags in the result. + // The list can be used as the value list for the `for` macro. + // In the very special case when some of the tag or branch names contains a comma, then you can use this parameter. + // end snippet + scanner.done(); + BadSyntax.when(last.isPresent() && index.isPresent(), "You cannot specify both 'last' and 'index'"); + BadSyntax.when(first.isPresent() && index.isPresent(), "You cannot specify both 'first' and 'index'"); + BadSyntax.when(last.isPresent() && first.isPresent(), "You cannot specify both 'last' and 'first'"); + try { + final var git = Connect.git(processor, id); + final var tags = me.startsWith("git:tag") ? git.tagList().call() : git.branchList().call(); + switch (order.get(OrderBy.class)) { + case orderByName: + tags.sort(Comparator.comparing(Ref::getName)); + break; + case orderByDate: + tags.sort((tag1, tag2) -> getCommitTimeForTag(tag1, git) - getCommitTimeForTag(tag2, git)); + break; + } + final Function mapper = getMapper(what, git); + var tagStream = tags.stream() + .map(mapper) + .map(s -> s.substring(s.lastIndexOf('/') + 1)); + if (match.isPresent()) { + final var regex = match.get(); + tagStream = tagStream.filter(s -> s.matches(regex)); + } + final var tagList = tagStream.collect(Collectors.toList()); + BadSyntax.when(tagList.isEmpty(), "No tags were found"); + if (index.isPresent() || last.isPresent() || first.isPresent()) { + var i = last.isPresent() ? tagList.size() - 1 : first.isPresent() ? 0 : index.get() < 0 ? tagList.size() + index.get() : index.get(); + BadSyntax.when(i >= tagList.size(), "The index is too large"); + BadSyntax.when(i < 0, "The index is too small"); + return tagList.get(i); + } else { + BadSyntax.when(single.is() && tagList.size() > 1, "There are more than one tags"); + return String.join(sep.get(), tagList); + } + } catch (BadSyntax bs) { + throw bs; + } catch (Exception e) { + throw new BadSyntax("Cannot list tags from git repository '" + id + "'", e); + } + } + + /** + * Retrieves a mapper function that converts a {@link Ref} object (Git tag or branch) into a string representation. + * + *

The mapper function depends on the {@code what} parameter and returns either the tag/branch name, + * the commit hash, or the commit time. + * + * @param what the type of information to retrieve (name, hash, or time) + * @param git the Git object representing the current repository + * @return a function that maps a Git {@link Ref} object to a string based on the selected type + * @throws BadSyntax if the {@code what} parameter is invalid + */ + private Function getMapper(EnumerationParameter what, Git git) throws BadSyntax { + final Function mapper; + switch (what.get(TagWhat.class)) { + case hash: + mapper = s -> s.getObjectId().getName(); + break; + case time: + mapper = s -> String.valueOf(getCommitTimeForTag(s, git)); + break; + case name: + mapper = s -> { + final var name = s.getName(); + return name.substring(name.lastIndexOf('/') + 1); + }; + break; + default: + throw new BadSyntax("Internal error: unknown tag what"); + } + return mapper; + } + + /** + * Retrieves the commit time for a given Git tag. + * + *

This method resolves the commit associated with the provided Git tag reference. + * If the tag is an annotated tag, the method uses {@link RevWalk} to resolve the tag + * and get the commit it points to. It then retrieves the commit time of the associated commit. + * + * @param tag the {@link Ref} object representing the Git tag + * @param git the {@link Git} object representing the current repository + * @return the commit time as an integer, which is the number of seconds since the epoch (Unix timestamp) + * @throws RuntimeException if the commit time cannot be retrieved due to an error + */ + private int getCommitTimeForTag(Ref tag, Git git) { + try (RevWalk revWalk = new RevWalk(git.getRepository())) { + ObjectId objectId = tag.getObjectId(); + RevCommit commit; + + // Check if the tag is an annotated tag + RevObject revObject = revWalk.parseAny(objectId); + if (revObject instanceof RevTag) { + // If it's an annotated tag, get the commit the tag points to + RevTag revTag = (RevTag) revObject; + return (int) revTag.getTaggerIdent().getWhen().getTime() / 1000; + } else { + // If it's a lightweight tag, directly parse the commit + commit = revWalk.parseCommit(objectId); + } + + return commit.getCommitTime(); + } catch (Exception e) { + throw new RuntimeException("Failed to retrieve commit time for tag: " + tag.getName(), e); + } + } + +} diff --git a/jamal-git/src/main/java/module-info.java b/jamal-git/src/main/java/module-info.java new file mode 100644 index 000000000..e586ebe93 --- /dev/null +++ b/jamal-git/src/main/java/module-info.java @@ -0,0 +1,11 @@ +import javax0.jamal.api.Macro; +import javax0.jamal.git.Connect; +import javax0.jamal.git.Tag; + +module jamal.git { + requires jamal.api; + requires jamal.tools; + requires org.eclipse.jgit; + exports javax0.jamal.git; + provides Macro with Connect, Tag; +} \ No newline at end of file diff --git a/jamal-git/src/main/resources/META-INF/services/javax0.jamal.api.Macro b/jamal-git/src/main/resources/META-INF/services/javax0.jamal.api.Macro new file mode 100644 index 000000000..ff1380e12 --- /dev/null +++ b/jamal-git/src/main/resources/META-INF/services/javax0.jamal.api.Macro @@ -0,0 +1,2 @@ +javax0.jamal.git.Connect +javax0.jamal.git.Tag \ No newline at end of file diff --git a/jamal-git/src/test/java/javax0/jamal/git/TestGit.java b/jamal-git/src/test/java/javax0/jamal/git/TestGit.java new file mode 100644 index 000000000..204f11e41 --- /dev/null +++ b/jamal-git/src/test/java/javax0/jamal/git/TestGit.java @@ -0,0 +1,197 @@ +package javax0.jamal.git; + +import javax0.jamal.testsupport.TestThat; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.junit.jupiter.api.*; + +import java.io.File; +import java.nio.file.Files; +import java.util.Date; +import java.util.TimeZone; + +public class TestGit { + + private Repository repository; + private Git git; + private File repoDir; + + /** + * Sets up a local Git repository in a temporary directory for testing purposes. + * + *

This method is executed before each test, initializing a new local Git repository + * and performing the following operations: + *

    + *
  • Creates a temporary directory to act as the Git repository.
  • + *
  • Initializes the Git repository within the temporary directory.
  • + *
  • Makes an initial commit with a test file named {@code testFile.txt}.
  • + *
  • Retrieves the default branch after the first commit, + * creates a new branch named "main", switches to the "main" branch, and + * deletes the default branch.
  • + *
  • Creates several additional branches, and tags.
  • + *
+ * The repository is local, and no remote repository is set up. After the setup, + * the test environment will have a Git repository with the specified branches, + * tags, and an initial commit on the "main" branch. + * + * @throws Exception if any I/O or Git operation fails during the setup. + */ + @BeforeEach + public void setup() throws Exception { + // Create a temporary directory for the test repo + repoDir = Files.createTempDirectory("testRepo").toFile(); + // Initialize the repository + repository = FileRepositoryBuilder.create(new File(repoDir, ".git")); + repository.create(); + git = new Git(repository); + + // Make an initial commit + File testFile = new File(repoDir, "testFile.txt"); + Files.write(testFile.toPath(), "Initial content".getBytes()); + git.add().addFilepattern("testFile.txt").call(); + git.commit().setMessage("Initial commit").call(); + final var currentBranch = git.getRepository().getBranch(); + git.branchCreate().setName("main").call(); + git.checkout().setName("main").call(); + git.branchDelete().setBranchNames(currentBranch).setForce(true).call(); + + // Create some branches + git.branchCreate().setName("branch1").call(); + git.branchCreate().setName("branch2").call(); + git.branchCreate().setName("branch3").call(); + git.branchCreate().setName("branch4").call(); + git.branchCreate().setName("branch5").call(); + git.branchCreate().setName("release").call(); + + final var datev1_0 = new Date(); + final var taggerV1_0 = new PersonIdent("John Doe", "john@email.com", new Date(10000), TimeZone.getTimeZone("GMT")); + final var taggerV2_0 = new PersonIdent("John Doe", "john@email.com", new Date(20000), TimeZone.getTimeZone("GMT")); + final var taggerV3_0 = new PersonIdent("John Doe", "john@email.com", new Date(30000), TimeZone.getTimeZone("GMT")); + + // Create some tags + git.tag().setName("v1.0").setMessage("This is the tag v1.0").setTagger(taggerV1_0).call(); + git.tag().setName("v2.0").setMessage("This is the tag v1.0").setTagger(taggerV2_0).call(); + git.tag().setName("3.0").setMessage("This is the tag v1.0").setTagger(taggerV3_0).call(); + } + + /** + * Cleans up the resources and deletes the temporary Git repository after each test. + * + *

This method is executed after each test, performing the following operations: + *

    + *
  • Closes the Git repository to release any resources associated with it.
  • + *
  • Closes the Git instance to free up resources used during Git operations.
  • + *
  • Recursively deletes the temporary directory and all its contents + * that were created during the setup phase.
  • + *
+ * The cleanup ensures that the test environment remains isolated, + * with no leftover data or repository after each test execution. + * + * @throws Exception if an error occurs while closing the repository or deleting files. + */ + @AfterEach + public void tearDown() throws Exception { + // Clean up the temporary repository + git.getRepository().close(); + git.close(); + deleteDirectory(repoDir); + } + + private void deleteDirectory(File directory) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file); + } else { + Assumptions.assumeTrue(file.delete()); + } + } + } + Assumptions.assumeTrue(directory.delete()); + } + + @Test + @DisplayName("Get all the tags") + public void allTags() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags orderByDate}") + .results("v1.0,v2.0,3.0"); + } + + @Test + @DisplayName("Get all the tags starting with V") + public void allTagsV() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags orderByDate match=\"v.*\"}") + .results("v1.0,v2.0"); + } + + @Test + @DisplayName("Get all the tags starting with V special separator") + public void allTagsVSep() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags orderByDate match=\"v.*\" sep=:}") + .results("v1.0:v2.0"); + } + + @Test + @DisplayName("Get the latest tag") + public void latestTag() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags last orderByDate}") + .results("3.0"); + } + + @Test + @DisplayName("Get the last tag by name") + public void lastTag() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags last orderByName}") + .results("v2.0"); + } + + @Test + @DisplayName("Get the last tag by name time") + public void lastTagtime() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags last orderByName time}") + .results("20"); + } + + @Test + @DisplayName("Get the last tag by name hashCode") + public void lastTagHash() throws Exception { + final var result = TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:tags last orderByName hash}") + .results(); + Assertions.assertTrue(result.matches("[0-9a-f]{40}")); + } + + + @Test + @DisplayName("Get all the branchs") + public void allBranches() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:branches orderByName}") + .results("branch1,branch2,branch3,branch4,branch5,main,release"); + } + + @Test + @DisplayName("Get the last branch by name") + public void lastBranch() throws Exception { + TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:branches last orderByName}") + .results("release"); + } + + @Test + @DisplayName("Get the last branch by name time") + public void lastBranchtime() throws Exception { + final var time = TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:branches last orderByName time}") + .results(); + Assertions.assertTrue(time.matches("\\d+")); + } + + @Test + @DisplayName("Get the last branch by name hashCode") + public void lastBranchHash() throws Exception { + final var result = TestThat.theInput("{@git location=\"" + repoDir.getAbsolutePath() + "\"}{@git:branches last orderByName hash}") + .results(); + Assertions.assertTrue(result.matches("[0-9a-f]{40}")); + } + +} diff --git a/jamal-maven-extension/README.adoc b/jamal-maven-extension/README.adoc index ecf9b691c..cee6866a2 100644 --- a/jamal-maven-extension/README.adoc +++ b/jamal-maven-extension/README.adoc @@ -19,7 +19,7 @@ Create a `.mvn` in your project root to use this module. This directory will contain the file `extensions.xml` with the content: [source,xml] ---- +---- @@ -28,7 +28,7 @@ This directory will contain the file `extensions.xml` with the content: 2.8.2-SNAPSHOT ---- +---- When you start Maven, it will read the `pom.xml.jam` or `pom.jam` file, convert it using Jamal and create the `pom.xml` file. @@ -50,7 +50,7 @@ When the Maven Platform Reader returns with the object model, the extension pass NOTE: This way you can have the `pom.xml` file and the implementation of the extension was simpler. -image::call-sequence.SVG[] +image::call-sequence.svg[] ==== == Using the Generated `pom.xml` @@ -281,7 +281,6 @@ For further information read the link:../jamal-xls/README.adoc[documentation] of For further information read the link:../jamal-rest/README.adoc[documentation] of the module. +=== `git` - - - +For further information read the link:../jamal-git/README.adoc[documentation] of the module. \ No newline at end of file diff --git a/jamal-maven-extension/README.adoc.jam b/jamal-maven-extension/README.adoc.jam index 427e68fd1..5268621ae 100644 --- a/jamal-maven-extension/README.adoc.jam +++ b/jamal-maven-extension/README.adoc.jam @@ -21,7 +21,7 @@ Create a `.mvn` in your project root to use this module. This directory will contain the file `extensions.xml` with the content: [source,xml] ---- +---- @@ -30,7 +30,7 @@ This directory will contain the file `extensions.xml` with the content: {%VERSION%} ---- +---- When you start Maven, it will read the `pom.xml.jam` or `pom.jam` file, convert it using Jamal and create the `pom.xml` file. @@ -52,7 +52,7 @@ When the Maven Platform Reader returns with the object model, the extension pass NOTE: This way you can have the `pom.xml` file and the implementation of the extension was simpler. {%@import res:kroki.jim%} -{%kroki/call-sequence/plantuml/SVG/ +{%kroki/call-sequence/plantuml/svg/ @startuml actor User skinparam style strictuml @@ -307,6 +307,10 @@ The library provides macros that can load Java Built-in Macros from Maven artifa {%further rest%} +=== {%#block {%depCounter%}%} `git` + +{%further git%} + {%#assert:equals /{%dependencies%}/{%depCounter last%}/Not all dependencies are documented or some documented dependency is not there any more%} diff --git a/jamal-maven-extension/pom.xml b/jamal-maven-extension/pom.xml index 3b25826a3..ab228daa4 100644 --- a/jamal-maven-extension/pom.xml +++ b/jamal-maven-extension/pom.xml @@ -123,6 +123,11 @@ jamal-rest 2.8.2-SNAPSHOT
+ + com.javax0.jamal + jamal-git + 2.8.2-SNAPSHOT + com.javax0.jamal jamal-maven-input diff --git a/jamal-maven-input/README.adoc b/jamal-maven-input/README.adoc index dcee05fa6..01472f577 100644 --- a/jamal-maven-input/README.adoc +++ b/jamal-maven-input/README.adoc @@ -56,7 +56,7 @@ you will get [source] ---- -Cannot get the content of the file 'maven:com.javax0.jamal:jamal-groovy:1.12.5::version.properties' +Cannot read file 'maven:com.javax0.jamal:jamal-groovy:1.12.5::version.properties' from any of the directories: ---- but @@ -106,4 +106,4 @@ They can be used by their name instead of the full URL. * `google-maven-central`, `https://maven-central-eu.storage-download.googleapis.com/repos/central/data/` * `google-maven-central`, `https://maven-central-asia.storage-download.googleapis.com/repos/central/data/` **** -==== +==== \ No newline at end of file diff --git a/jamal-maven-plugin/pom.xml b/jamal-maven-plugin/pom.xml index 013c10cec..93bf85f67 100644 --- a/jamal-maven-plugin/pom.xml +++ b/jamal-maven-plugin/pom.xml @@ -98,6 +98,10 @@ com.javax0.jamal jamal-rest + + com.javax0.jamal + jamal-git + com.javax0.jamal jamal-maven-input diff --git a/jamal-snippet/README.adoc b/jamal-snippet/README.adoc index ad71d3be9..e1fc2eca7 100644 --- a/jamal-snippet/README.adoc +++ b/jamal-snippet/README.adoc @@ -3816,7 +3816,7 @@ will result in the output .output [source] ---- -2024-10-08 09:13:51 +2024-10-25 00:14:29 ---- diff --git a/jamal-sql/demodb.mv.db b/jamal-sql/demodb.mv.db index 93740a587741d9ef923819bb9a4b75f8f1049778..a6144b812b13524dd27ea90710b2e6235419c34e 100644 GIT binary patch literal 24576 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]`. -So I wrote, `This is a gopass:[<<]o>>d thing,...` +I enclosed the variable part of the text in `<<` and `>>`. +So I wrote, `This is a go<>d thing,...` As simple as that. ## Avoid copy-paste totally diff --git a/jamal-test/DEMO.md.jam b/jamal-test/DEMO.md.jam index cbd3d8c20..5a7a3f75c 100644 --- a/jamal-test/DEMO.md.jam +++ b/jamal-test/DEMO.md.jam @@ -54,8 +54,8 @@ Like here: {%@variation This is a go<>d thing, because the documentation is always up-to-date.%} How did this happen? -I enclosed the variable part of the text in `pass:[<<]` and `pass:[>>]`. -So I wrote, `This is a gopass:[<<]o>>d thing,...` +I enclosed the variable part of the text in `<<` and `>>`. +So I wrote, `This is a go<>d thing,...` As simple as that. ## Avoid copy-paste totally diff --git a/jamal-word/src/test/resources/demoConverted.docx b/jamal-word/src/test/resources/demoConverted.docx index 4389a1fd863bede09a74be6196a0f6d52bea6d90..dfcbb93092057967db698c9b904a9503b45f9960 100644 GIT binary patch delta 216 zcmey7{3Dq+z?+#xgn@&DgW)w}EXZ~OtmF=x z2{)J_EMy8%;w?N4!gwOGlo>45BLOwfTk1G3SVUCgCL4$``JEXZ~OtmF=x z2{)J_EMy8%;w?N4!gwOGlo>45BLOwfTk1G3SVUCgCL4$``Jt<8 diff --git a/jamal-word/src/test/resources/includetestConverted.docx b/jamal-word/src/test/resources/includetestConverted.docx index c2fdd90639ab90bea2659998194595f17f807572..936a46cd7a9da8f87e03edfefc3bf0e398ff0732 100644 GIT binary patch delta 221 zcmeC_W9sQ+;tlX-W)WfF;NW0*%^10ncLpOfklwt4Q4+#9!t@Qo*vnD~VHmPSae~$K z@q0i-ZVIR{fkZY5Uk(A$lMUI#Hor-K&jwa@FK-tQm@&KhIz*(jsROKWQi~l}xnqkN wn67HE2GcuQY{2w)C~em24(2zs+Jfl=t&U)tr46Faxy=e9-{t_O54U*&0NtlhbN~PV delta 221 zcmeC_W9sQ+;tlX-W)WfF;NW1m=IODKcLpOfklwt4Q4+#9!t@Qo*vnD~VHmPSae~$K z@q0i-ZVIR{fkZY5Uk(A$lMUI#Hor-K&jwa@FK-tQm@&KhIz*(jsROKWQi~l}xnqkN wn67HE2GcuQY{2w)C~em24(2zs+Jfl=t&U)tr46Faxy=e9-{t_O54U*&03rQTqyPW_ diff --git a/jamal-word/src/test/resources/pictureConverted.docx b/jamal-word/src/test/resources/pictureConverted.docx index 1a047b2202f0abbfa6ec4acbc4bb0f3b9eb4f208..e33155fcf422438dc8fe0a217c864a97c06d82e0 100644 GIT binary patch delta 304 zcmaF6it+6#M&1B#W)=|!4h{~6*Nl-HdCxO41L@888T}y)L*~Cw25Sj~v4SlT!rXfkIe7!fEkBfu0urTdK*HtU-aRE zFz)$QLl`ms9bm@f=K*#gO_MbO&A@bGpf#ADA7}%n?*&5S<%1ynxFC11_=+G~F#RIP o5lrg@L-;wt5Ou4AtswHj4q#e4#2ZXkhJ=CXOCkPXS|!vI08f5v(EtDd delta 304 zcmaF6it+6#M&1B#W)=|!4h{~6Yn~n(dCxO41L@888T}y)L*~Cw25Sj~v4SlT!rXfkIe7!fEkBfu0urTdK*HtU-aRE zFz)$QLl`ms9bm@f=K*#gO_MbO&A@bGpf#ADA7}%n?*&5S<%1ynxFC11_=+G~F#RIP o5lrg@L-;wt5Ou4AtswHj4q#e4#2ZXkhJ=CXOCkPXS|!vI0Mcx5RR910 diff --git a/jamal-word/src/test/resources/sampleConverted.docx b/jamal-word/src/test/resources/sampleConverted.docx index db11d76683c13e438d9ab8307ad681bd27942e5d..955a1115064bdb1547ec482f6676e56eff2b08cd 100644 GIT binary patch delta 2329 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 diff --git a/jamal.sh b/jamal.sh index 8de87c9da..1fe957629 100755 --- a/jamal.sh +++ b/jamal.sh @@ -1,7 +1,7 @@ #!/bin/sh CLASSPATH="" -function download() { +download() { DIR=${1//\.//} if ! test -f $HOME/.m2/repository/$DIR/$2/$3/$2-$3.jar; then echo "downloading https://repo1.maven.org/maven2/$DIR/$2/$3/jamal-$2-$3.jar" @@ -125,8 +125,12 @@ download "com.github.virtuald" "curvesapi" "1.08" download "org.apache.commons" "commons-compress" "1.26.2" download "org.apache.commons" "commons-lang3" "3.14.0" download "com.javax0.jamal" "jamal-rest" "2.8.2-SNAPSHOT" -download "com.javax0.jamal" "jamal-maven-input" "2.8.2-SNAPSHOT" +download "com.javax0.jamal" "jamal-git" "2.8.2-SNAPSHOT" download "com.javax0.jamal" "jamal-extensions" "2.8.2-SNAPSHOT" +download "org.eclipse.jgit" "org.eclipse.jgit" "6.6.1.202309021850-r" +download "com.googlecode.javaewah" "JavaEWAH" "1.2.3" +download "org.slf4j" "slf4j-api" "1.7.36" +download "com.javax0.jamal" "jamal-maven-input" "2.8.2-SNAPSHOT" download "com.javax0.jamal" "jamal-jar-input" "2.8.2-SNAPSHOT" download "com.javax0.jamal" "jamal-word" "2.8.2-SNAPSHOT" download "com.javax0.jamal" "jamal-testsupport" "2.8.2-SNAPSHOT" diff --git a/modules.jim b/modules.jim index dc5364cc0..ba7147528 100644 --- a/modules.jim +++ b/modules.jim @@ -74,7 +74,7 @@ A JAR file that implements the handling of re,ote resources for the 'MAVEN:' pre ------------------------------ Macro modules, which can be included into any installation being safe. -{#define modules:SAFE_MACROS=assertions,jamal,markdown,mock,snippet,yaml,json,prog,maven-load,sql,xls,rest} +{#define modules:SAFE_MACROS=assertions,jamal,markdown,mock,snippet,yaml,json,prog,maven-load,sql,xls,rest,git} ------------------------------ diff --git a/pom.xml b/pom.xml index 1e6d3d931..edd2768db 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ jamal-sql jamal-xls jamal-rest + jamal-git jamal-groovy jamal-io jamal-openai @@ -267,6 +268,11 @@ jamal-rest 2.8.2-SNAPSHOT + + com.javax0.jamal + jamal-git + 2.8.2-SNAPSHOT + com.javax0.jamal jamal-groovy