diff --git a/README.md b/README.md index 2ff3a93..6464118 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,38 @@ $ gitwerk semver major v1.0.0 ``` +### sci + +gitwerk supports to run user-defined scripts written in clojure (powered by [borkdude/sci](https://github.com/borkdude/sci)). + +``` +## can read stdin +$ echo '(semver ctx ["patch"])' | gitwerk sci +v0.0.2 + +## also read a file as a script +$ cat examples/example1.clj +(semver ctx ["patch"]) +$ gitwerk sci examples/example1.clj +v0.0.3 + +## fetch executed command result and modify returned message +$ cat examples/example2.clj +(let [res (semver-auto ctx nil) + status (get-in res [:console-out :status]) + oldv (get-in res [:console-out :old-version]) + newv (get-in res [:console-out :new-version])] + (if (= status :updated) + (str "Version updated: " oldv " -> " newv) + "Version not updated")) +$ gitwerk sci examples/example2.clj +Version not updated + +$ git commit --allow-empty -m "[patch] version updated" +$ gitwerk sci examples/example2.clj +Version updated: v0.0.3 -> v0.0.4 +``` + ## License Copyright © 2019 rinx diff --git a/examples/example1.clj b/examples/example1.clj new file mode 100644 index 0000000..9fa2323 --- /dev/null +++ b/examples/example1.clj @@ -0,0 +1 @@ +(semver ctx ["patch"]) diff --git a/examples/example2.clj b/examples/example2.clj new file mode 100644 index 0000000..23a0152 --- /dev/null +++ b/examples/example2.clj @@ -0,0 +1,7 @@ +(let [res (semver-auto ctx nil) + status (get-in res [:console-out :status]) + oldv (get-in res [:console-out :old-version]) + newv (get-in res [:console-out :new-version])] + (if (= status :updated) + (str "Version updated: " oldv " -> " newv) + "Version not updated")) diff --git a/project.clj b/project.clj index c9a2891..2fe512f 100644 --- a/project.clj +++ b/project.clj @@ -9,6 +9,7 @@ [metosin/jsonista "0.2.2"] [camel-snake-kebab "0.4.0"] [io.quarkus/quarkus-jgit "1.1.0.CR1"] + [borkdude/sci "0.0.12-alpha.7"] [org.martinklepsch/clj-http-lite "0.4.3"]] :profiles {:dev {:dependencies [[org.clojure/tools.namespace "0.2.11"] [orchestra "2019.02.06-1"]] diff --git a/src/gitwerk/command/sci.clj b/src/gitwerk/command/sci.clj new file mode 100644 index 0000000..1252791 --- /dev/null +++ b/src/gitwerk/command/sci.clj @@ -0,0 +1,34 @@ +(ns gitwerk.command.sci + (:require + [clojure.spec.alpha :as spec] + [clojure.pprint :as pprint] + [sci.core :as sci] + [gitwerk.internal.io :as internal.io])) + +(def clj-primitives + {'println println + 'pprint pprint/pprint}) + +(defn run-fn [sci-opts] + (fn [ctx args] + (try + (let [file (first args) + body (if (nil? file) + (internal.io/read-from-stdin) + (internal.io/safe-read file)) + ctx (dissoc ctx :command :summary) + opts (-> sci-opts + (update + :bindings + #(-> % + (merge clj-primitives) + (merge {'ctx ctx})))) + res (sci/eval-string body opts)] + (if (and (map? res) (:status res)) + res + {:status 0 + :console-out res})) + (catch Exception e + {:status 1 + :console-out + {:error (.getMessage e)}})))) diff --git a/src/gitwerk/core.clj b/src/gitwerk/core.clj index cd528de..61bf955 100644 --- a/src/gitwerk/core.clj +++ b/src/gitwerk/core.clj @@ -3,6 +3,7 @@ [clojure.spec.alpha :as spec] [clojure.tools.cli :as cli] [clojure.string :as string] + [clojure.pprint :as pprint] [clojure.edn :as edn] [clojure.java.io :as io] [gitwerk.service.runner :as runner]) @@ -10,19 +11,19 @@ (def cli-header (string/join - "\n" + "\n" + (concat ["gitwerk is a CLI tool for supporting Git(Hub) operations on CI." "" "Usage: gitwerk [command] [options]" "" - "Available commands:" - " clone [url] clone git repository" - " log show git logs of current directory" - " semver [type] print incremented version" - " semver-auto increment version by latest git log message contexts" - " tag show git tags of current directory" - "" - "Options:"])) + "Available commands:"] + (mapv (fn [[k v]] + (let [name (format " %-12s " (name k)) + desc (:description v)] + (str name desc))) runner/definitions) + ["" + "Options:"]))) (def cli-options [["-f" "--file PATH" "config" :id :config-filename @@ -33,19 +34,21 @@ (defn edn-output [ctx res] - (println res)) + (pprint/pprint res)) (defn std-output [{:keys [summary] :as ctx} {:keys [status invalid-arg? console-out]}] - (when console-out - (println console-out)) - (when invalid-arg? - (println cli-header) - (println summary)) - (if status - (System/exit status) - (System/exit 1))) + (when console-out + (if (map? console-out) + (pprint/pprint console-out) + (println console-out))) + (when invalid-arg? + (println cli-header) + (println summary)) + (if status + (System/exit status) + (System/exit 1))) (defn run [{:keys [options] :as ctx}] diff --git a/src/gitwerk/internal/io.clj b/src/gitwerk/internal/io.clj new file mode 100644 index 0000000..c511c83 --- /dev/null +++ b/src/gitwerk/internal/io.clj @@ -0,0 +1,20 @@ +(ns gitwerk.internal.io + (:require + [clojure.spec.alpha :as spec] + [clojure.java.io :as io] + [clojure.string :as string]) + (:import + [java.io BufferedReader])) + +(defn read-from-stdin [] + (->> (BufferedReader. *in*) + (line-seq) + (string/join "\n"))) + +(defn safe-read + [file] + (if (.exists (io/file file)) + (slurp file) + (throw + (Exception. + (str "File not found: " file))))) diff --git a/src/gitwerk/service/runner.clj b/src/gitwerk/service/runner.clj index 46b2519..6f53e78 100644 --- a/src/gitwerk/service/runner.clj +++ b/src/gitwerk/service/runner.clj @@ -4,22 +4,47 @@ [camel-snake-kebab.core :as csk] [gitwerk.command.clone :as command.clone] [gitwerk.command.log :as command.log] + [gitwerk.command.sci :as command.sci] [gitwerk.command.semver :as command.semver] [gitwerk.command.semver-auto :as command.semver-auto] [gitwerk.command.tag :as command.tag])) +(def primitives + {:clone + {:command command.clone/run + :description "clone git repository"} + :log + {:command command.log/run + :description "show git logs of current directory"} + :semver + {:command command.semver/run + :description "print incremented version"} + :semver-auto + {:command command.semver-auto/run + :description "increment version by latest git log message contexts"} + :tag + {:command command.tag/run + :description "show git tags of current directory"}}) + +(def definitions + (let [->binding (fn [[k v]] + (let [sym (symbol (name k)) + com (:command v)] + [sym com])) + bindings (->> primitives + (map ->binding) + (into {})) + sci-opts {:bindings bindings}] + (into primitives + {:sci {:command (command.sci/run-fn sci-opts) + :description "run user-defined script (written in clojure)"}}))) + (defn dispatch [{:keys [args] :as ctx} cmd] (let [default (fn [_ _] {:status 1 :invalid-arg? true}) - cmd (case cmd - :clone command.clone/run - :log command.log/run - :semver command.semver/run - :semver-auto command.semver-auto/run - :tag command.tag/run - default)] + cmd (or (:command (cmd definitions)) default)] (cmd ctx args))) (defn run