Compare commits
47 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47e55fe5e7 | ||
|
|
717cef7af5 | ||
|
|
cd968459a7 | ||
|
|
8b717eb001 | ||
|
|
6ad6045b94 | ||
|
|
b00133ca05 | ||
|
|
64ecb94de8 | ||
|
|
d29cf6aa65 | ||
|
|
1635931483 | ||
|
|
75c2216649 | ||
|
|
c2e3d8f8b8 | ||
|
|
16bea5b7db | ||
|
|
85c554e643 | ||
|
|
4fb0da7daf | ||
|
|
76313a7089 | ||
|
|
28cf3de4ef | ||
|
|
81f8845d72 | ||
|
|
decf791000 | ||
|
|
53f79da09d | ||
|
|
8bc0852799 | ||
|
|
6cbbdd118d | ||
|
|
5754d433aa | ||
|
|
6df443fabf | ||
|
|
93081b75e6 | ||
|
|
66867eee7f | ||
|
|
eb0b01c0a6 | ||
|
|
b9f3a28555 | ||
|
|
aee5337cef | ||
|
|
c2d6ba847c | ||
|
|
e075b13bfe | ||
|
|
842ff34739 | ||
|
|
f2cfdff899 | ||
|
|
5fbf1d7b04 | ||
|
|
8f059da7e6 | ||
|
|
538fc6f414 | ||
|
|
f360afa613 | ||
|
|
973c4e5c9e | ||
|
|
401aeecf52 | ||
|
|
de4c3610c9 | ||
|
|
0f31e57977 | ||
|
|
37326045aa | ||
|
|
6214f06146 | ||
|
|
05ecf97127 | ||
|
|
1fdd8231bd | ||
|
|
82aa362710 | ||
|
|
490073f93a | ||
|
|
39241ebc82 |
21 changed files with 870 additions and 310 deletions
28
.circleci/config.yml
Normal file
28
.circleci/config.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
version: 2.1
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
docker:
|
||||||
|
- image: clojure:openjdk-11-tools-deps-1.10.3.1087-slim-bullseye
|
||||||
|
working_directory: ~/repo
|
||||||
|
environment:
|
||||||
|
LEIN_ROOT: "true"
|
||||||
|
BABASHKA_PLATFORM: linux
|
||||||
|
resource_class: large
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- v1-dependencies-{{ checksum "deps.edn" }}
|
||||||
|
# fallback to using latest cache if no exact match is found
|
||||||
|
- v1-dependencies-
|
||||||
|
- run: |
|
||||||
|
script/test
|
||||||
|
- save_cache:
|
||||||
|
paths:
|
||||||
|
- ~/.m2
|
||||||
|
key: v1-dependencies-{{ checksum "deps.edn" }}
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
ci:
|
||||||
|
jobs:
|
||||||
|
- test
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -3,3 +3,7 @@
|
||||||
.lein-failures
|
.lein-failures
|
||||||
/pom.xml
|
/pom.xml
|
||||||
.lein-repl-history
|
.lein-repl-history
|
||||||
|
.cache
|
||||||
|
.clj-kondo/babashka
|
||||||
|
.clj-kondo/rewrite-clj
|
||||||
|
src/scratch.clj
|
||||||
|
|
|
||||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- [#63](https://github.com/babashka/pods/issues/63): create directory before un-tarring
|
||||||
|
- [#59](https://github.com/babashka/pods/issues/59): delete port file on exit
|
||||||
|
- [#65](https://github.com/babashka/pods/issues/65): fix warnings when defining var with core name in JVM
|
||||||
|
- [#66](https://github.com/babashka/pods/issues/66): Allow metadata on fn arguments for transit+json
|
||||||
|
|
||||||
|
## v0.2.0
|
||||||
|
|
||||||
|
- [#61](https://github.com/babashka/pods/issues/61): add transit as explicit JVM dependency
|
||||||
|
- [#60](https://github.com/babashka/pods/issues/60): transform pod reader error into exception of caller
|
||||||
|
- Switch "out" and "err" messages to print and flush instead of `println` ([@justone](https://github.com/justone))
|
||||||
|
- Set TCP_NODELAY on transport socket ([@retrogradeorbit](https://github.com/retrogradeorbit))
|
||||||
|
- Allow env vars OS_NAME & OS_ARCH to override os props ([@cap10morgan](https://github.com/cap10morgan))
|
||||||
|
- [#49](https://github.com/babashka/pods/issues/49): don't log socket closed exception
|
||||||
|
|
||||||
|
## v0.1.0
|
||||||
|
|
||||||
|
Initial version
|
||||||
104
README.md
104
README.md
|
|
@ -1,4 +1,6 @@
|
||||||
# babashka.pods
|
# Babashka pods
|
||||||
|
|
||||||
|
[](https://clojars.org/babashka/babashka.pods)
|
||||||
|
|
||||||
Babashka pods are programs that can be used as Clojure libraries by babashka.
|
Babashka pods are programs that can be used as Clojure libraries by babashka.
|
||||||
|
|
||||||
|
|
@ -72,7 +74,44 @@ On the JVM:
|
||||||
|
|
||||||
### Where does the pod come from?
|
### Where does the pod come from?
|
||||||
|
|
||||||
When calling `load-pod` with a string or vector of strings, the pod is looked up on the local file system (either using the PATH, or using an absolute path). When it is called with a qualified symbol and a version - like `(load-pod 'org.babashka/aws "0.0.5")` then it will be looked up in and downloaded from the [pod-registry](https://github.com/babashka/pod-registry).
|
When calling `load-pod` with a string or vector of strings (or declaring it in your `bb.edn`),
|
||||||
|
the pod is looked up on the local file system (either using the PATH, or using an absolute path).
|
||||||
|
When it is called with a qualified symbol and a version - like `(load-pod 'org.babashka/aws "0.0.5")`
|
||||||
|
then it will be looked up in and downloaded from the [pod-registry](https://github.com/babashka/pod-registry). You can customize the file system location that `load-pod` will use by setting the `BABASHKA_PODS_DIR` environment variable.
|
||||||
|
|
||||||
|
By default babashka will search for a pod binary matching your system's OS and arch. If you want to download
|
||||||
|
pods for a different OS / arch (e.g. for deployment to servers), you can set one or both of the following
|
||||||
|
environment variables:
|
||||||
|
|
||||||
|
- `BABASHKA_PODS_OS_NAME=Linux` (or `Mac OS X` or any other value returned by Java's `os.name` property)
|
||||||
|
- `BABASHKA_PODS_OS_ARCH=aarch64` (or `amd64` or any other value returned by Java's `os.arch` property)
|
||||||
|
|
||||||
|
### In a babashka project
|
||||||
|
|
||||||
|
As of babashka 0.8.0 you can declare the pods your babashka project uses in your `bb.edn` file like so:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
:pods {org.babashka/hsqldb {:version "0.1.0"} ; will be downloaded from the babashka pod registry
|
||||||
|
my.local/pod {:path "../pod-my-local/my-pod-binary"
|
||||||
|
:cache false}} ; optionally disable namespace caching if you're actively working on this pod
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can just require the pods in your code like any other clojure lib:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(ns my.project
|
||||||
|
(:require [pod.babashka.hsqldb :as sql]
|
||||||
|
[my.local.pod :as my-pod]))
|
||||||
|
|
||||||
|
(def db "jdbc:hsqldb:mem:testdb;sql.syntax_mys=true")
|
||||||
|
(sql/execute! db ["create table foo ( foo int );"])
|
||||||
|
;;=> [#:next.jdbc{:update-count 0}]
|
||||||
|
|
||||||
|
(my-pod/do-a-thing "foo")
|
||||||
|
;;=> "something"
|
||||||
|
```
|
||||||
|
|
||||||
|
The pods will then be loaded on demand when you require them. No need to call `load-pod` explicitly.
|
||||||
|
|
||||||
## Sci
|
## Sci
|
||||||
|
|
||||||
|
|
@ -91,7 +130,7 @@ light weight replacement for native interop (JNI, JNA, etc.).
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
Beyond the already available pods mentioned above, eductional examples of pods
|
Beyond the already available pods mentioned above, educational examples of pods
|
||||||
can be found [here](examples):
|
can be found [here](examples):
|
||||||
|
|
||||||
- [pod-lispyclouds-sqlite](examples/pod-lispyclouds-sqlite): a pod that
|
- [pod-lispyclouds-sqlite](examples/pod-lispyclouds-sqlite): a pod that
|
||||||
|
|
@ -189,7 +228,7 @@ JSON. It also declares that the pod exposes one namespace,
|
||||||
|
|
||||||
To encode payloads in EDN use `"edn"` and for Transit JSON use `"transit+json"`.
|
To encode payloads in EDN use `"edn"` and for Transit JSON use `"transit+json"`.
|
||||||
|
|
||||||
The pod encodes the above map to bencode and writes it to stdoud. The pod client
|
The pod encodes the above map to bencode and writes it to stdout. The pod client
|
||||||
reads this message from the pod's stdout.
|
reads this message from the pod's stdout.
|
||||||
|
|
||||||
Upon receiving this message, the pod client creates these namespaces and vars.
|
Upon receiving this message, the pod client creates these namespaces and vars.
|
||||||
|
|
@ -335,6 +374,55 @@ In the pod client:
|
||||||
nil
|
nil
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Metadata
|
||||||
|
|
||||||
|
**From pod to pod client**
|
||||||
|
|
||||||
|
*Fixed Metadata on vars*
|
||||||
|
|
||||||
|
Pods may attach metadata to functions and macros by sending data to the pod client
|
||||||
|
in a `"meta"` field as part of a `"var"` section. The metadata must be an appropriate
|
||||||
|
map, encoded as an EDN string. This is only applicable to vars in the pod and will be
|
||||||
|
ignored if the var refers to Client-side code, since metadata can already be defined
|
||||||
|
in those code blocks (see 'Dynamic Metadata' below to enable the encoding of metadata).
|
||||||
|
|
||||||
|
For example, a pod can define a function called `add`:
|
||||||
|
|
||||||
|
``` clojure
|
||||||
|
{"format" "json"
|
||||||
|
"namespaces"
|
||||||
|
[{"name" "pod.babashka.demo"
|
||||||
|
"vars" [{"name" "add"
|
||||||
|
"meta" "{:doc \"arithmetic addition of 2 arguments\" :arglists ([a b])}"}]}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Dynamic Metadata*
|
||||||
|
|
||||||
|
Pods may send metadata on values returned to the client if metadata encoding is enabled
|
||||||
|
for the particular transport format used by the pod.
|
||||||
|
|
||||||
|
For example, if your pod uses `:transit+json` as its format, you can enable metadata
|
||||||
|
encoding by adding `:transform transit/write-meta` (or whatever transit is aliased to)
|
||||||
|
to the optional map passed to `transit/writer`. e.g.:
|
||||||
|
|
||||||
|
````clojure
|
||||||
|
(transit/writer baos :json {:transform transit/write-meta})
|
||||||
|
````
|
||||||
|
|
||||||
|
##### From pod client to pod
|
||||||
|
|
||||||
|
Currently sending metadata on arguments passed to a pod function is available only for the
|
||||||
|
`transit+json` format and can be enabled on a per var basis.
|
||||||
|
|
||||||
|
A pod can enable metadata to be read on arguments by sending the "arg-meta" field to "true"
|
||||||
|
for the var representing that function. For example:
|
||||||
|
|
||||||
|
````clojure
|
||||||
|
{:format :transit+json
|
||||||
|
:namespaces [{:name "pod.babashka.demo"
|
||||||
|
:vars [{"name" "round-trip" "arg-meta" "true"}]}]}
|
||||||
|
````
|
||||||
|
|
||||||
#### Deferred namespace loading
|
#### Deferred namespace loading
|
||||||
|
|
||||||
When your pod exposes multiple namespaces that can be used independently from
|
When your pod exposes multiple namespaces that can be used independently from
|
||||||
|
|
@ -409,7 +497,7 @@ The arguments to `babashka.pods/invoke` are:
|
||||||
- a pod identifier string derived from the first described namespace.
|
- a pod identifier string derived from the first described namespace.
|
||||||
- the symbol of the var to invoke
|
- the symbol of the var to invoke
|
||||||
- the arguments to the var
|
- the arguments to the var
|
||||||
- an opts map containing `:handler` containing callback functions: `:success`, `:error` and `:done`
|
- an opts map containing `:handlers` containing callback functions: `:success`, `:error` and `:done`
|
||||||
|
|
||||||
The return value of `babashka.pods/invoke` is a map containing `:result`. When
|
The return value of `babashka.pods/invoke` is a map containing `:result`. When
|
||||||
not using callbacks, this is the return value from the pod var invocation. When
|
not using callbacks, this is the return value from the pod var invocation. When
|
||||||
|
|
@ -435,9 +523,9 @@ callback is only called if no errors were sent by the pod.
|
||||||
|
|
||||||
In the above example the wrapper function calls the pod identified by
|
In the above example the wrapper function calls the pod identified by
|
||||||
`"pod.babashka.filewatcher"`. It calls the var
|
`"pod.babashka.filewatcher"`. It calls the var
|
||||||
`pod.babashka.filewatcher/watch*`. In `:on-success` it pulls out received
|
`pod.babashka.filewatcher/watch*`. In `:success` it pulls out received
|
||||||
values, passing them to the user-provided callback. Additionally, it prints any
|
values, passing them to the user-provided callback. Additionally, it prints any
|
||||||
errors received from the pod library in `:on-error` to `*err*`.
|
errors received from the pod library in `:error` to `*err*`.
|
||||||
|
|
||||||
A user will then use `pod.babashka.filewatcher/watch` like this:
|
A user will then use `pod.babashka.filewatcher/watch` like this:
|
||||||
|
|
||||||
|
|
@ -454,7 +542,7 @@ user=> (fw/watch "/tmp" (fn [result] (prn "result" result)))
|
||||||
nil
|
nil
|
||||||
user=> (spit "/tmp/foobar123.txt" "foo")
|
user=> (spit "/tmp/foobar123.txt" "foo")
|
||||||
nil
|
nil
|
||||||
user=> "result" {:path "/private/tmp/foobar123.txt", :type "create"}
|
user=> "result" {:path "/private/tmp/foobar123.txt", :type :create}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run tests
|
## Run tests
|
||||||
|
|
|
||||||
2
bb.edn
Normal file
2
bb.edn
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
{:tasks {test {:doc "Run tests"
|
||||||
|
:task (shell "script/test")}}}
|
||||||
5
deps.edn
5
deps.edn
|
|
@ -1,6 +1,7 @@
|
||||||
{:deps {nrepl/bencode {:mvn/version "1.1.0"}
|
{:deps {nrepl/bencode {:mvn/version "1.1.0"}
|
||||||
cheshire/cheshire {:mvn/version "5.10.0"}
|
cheshire/cheshire {:mvn/version "5.10.0"}
|
||||||
com.cognitect/transit-clj {:mvn/version "1.0.324"}}
|
com.cognitect/transit-clj {:mvn/version "1.0.324"}
|
||||||
|
babashka/fs {:mvn/version "0.1.6"}}
|
||||||
:aliases
|
:aliases
|
||||||
{:sci
|
{:sci
|
||||||
{:extra-deps
|
{:extra-deps
|
||||||
|
|
@ -18,5 +19,5 @@
|
||||||
{lambdaisland/kaocha {:mvn/version "1.0.632"}}
|
{lambdaisland/kaocha {:mvn/version "1.0.632"}}
|
||||||
:main-opts ["-m" "kaocha.runner"]}
|
:main-opts ["-m" "kaocha.runner"]}
|
||||||
:test-pod
|
:test-pod
|
||||||
{:paths ["src" "test-pod"]
|
{:extra-paths ["src" "test-pod"]
|
||||||
:main-opts ["-m" "pod.test-pod"]}}}
|
:main-opts ["-m" "pod.test-pod"]}}}
|
||||||
|
|
|
||||||
14
project.clj
14
project.clj
|
|
@ -1,15 +1,17 @@
|
||||||
(defproject babashka/babashka.pods "0.0.1"
|
(defproject babashka/babashka.pods "0.2.0"
|
||||||
:description "babashka pods"
|
:description "babashka pods"
|
||||||
:url "https://github.com/babashka/babashka.pods"
|
:url "https://github.com/babashka/babashka.pods"
|
||||||
:scm {:name "git"
|
:scm {:name "git"
|
||||||
:url "https://github.com/babashka/babashka.pods"}
|
:url "https://github.com/babashka/babashka.pods"}
|
||||||
:license {:name "EPL-1.0"
|
:license {:name "EPL-1.0"
|
||||||
:url "https://www.eclipse.org/legal/epl-1.0/"}
|
:url "https://www.eclipse.org/legal/epl-1.0/"}
|
||||||
:dependencies [[org.clojure/clojure "1.10.2-alpha1"]
|
:dependencies [[org.clojure/clojure "1.10.3"]
|
||||||
[nrepl/bencode "1.1.0"]
|
[nrepl/bencode "1.1.0"]
|
||||||
[cheshire "5.10.0"]]
|
[cheshire "5.10.0"]
|
||||||
|
[babashka/fs "0.1.6"]
|
||||||
|
[com.cognitect/transit-clj "1.0.329"]]
|
||||||
:deploy-repositories [["clojars" {:url "https://clojars.org/repo"
|
:deploy-repositories [["clojars" {:url "https://clojars.org/repo"
|
||||||
:username :env/babashka_nrepl_clojars_user
|
:username :env/clojars_user
|
||||||
:password :env/babashka_nrepl_clojars_pass
|
:password :env/clojars_pass
|
||||||
:sign-releases false}]]
|
:sign-releases false}]]
|
||||||
:profiles {:test {:dependencies [[borkdude/sci "0.0.13-alpha.27"]]}})
|
:profiles {:test {:dependencies [[borkdude/sci "0.2.4"]]}})
|
||||||
|
|
|
||||||
17
script/changelog.clj
Executable file
17
script/changelog.clj
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bb
|
||||||
|
|
||||||
|
(ns changelog
|
||||||
|
(:require [clojure.string :as str]))
|
||||||
|
|
||||||
|
(let [changelog (slurp "CHANGELOG.md")
|
||||||
|
replaced (str/replace changelog
|
||||||
|
#" #(\d+)"
|
||||||
|
(fn [[_ issue after]]
|
||||||
|
(format " [#%s](https://github.com/babashka/pods/issues/%s)%s"
|
||||||
|
issue issue (str after))))
|
||||||
|
replaced (str/replace replaced
|
||||||
|
#"@([a-zA-Z0-9-_]+)([, \.)])"
|
||||||
|
(fn [[_ name after]]
|
||||||
|
(format "[@%s](https://github.com/%s)%s"
|
||||||
|
name name after)))]
|
||||||
|
(spit "CHANGELOG.md" replaced))
|
||||||
19
script/test
19
script/test
|
|
@ -1,29 +1,32 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eou pipefail
|
||||||
|
|
||||||
export BABASHKA_POD_TEST_FORMAT
|
export BABASHKA_POD_TEST_FORMAT
|
||||||
export BABASHKA_POD_TEST_SOCKET
|
export BABASHKA_POD_TEST_SOCKET
|
||||||
|
|
||||||
# format = edn
|
# format = edn
|
||||||
BABASHKA_POD_TEST_FORMAT=edn
|
BABASHKA_POD_TEST_FORMAT=edn
|
||||||
echo "Testing edn"
|
echo "Testing edn"
|
||||||
clojure -A:test -n babashka.pods.jvm-test
|
clojure -M:test -n babashka.pods.jvm-test
|
||||||
clojure -A:sci:test -n babashka.pods.sci-test
|
clojure -M:sci:test -n babashka.pods.sci-test
|
||||||
|
clojure -M:test -n babashka.pods.impl-test
|
||||||
|
|
||||||
# format = json
|
# format = json
|
||||||
BABASHKA_POD_TEST_FORMAT=json
|
BABASHKA_POD_TEST_FORMAT=json
|
||||||
echo "Testing json"
|
echo "Testing json"
|
||||||
clojure -A:test -n babashka.pods.jvm-test
|
clojure -M:test -n babashka.pods.jvm-test
|
||||||
clojure -A:sci:test -n babashka.pods.sci-test
|
clojure -M:sci:test -n babashka.pods.sci-test
|
||||||
|
|
||||||
# format = json
|
# format = json
|
||||||
BABASHKA_POD_TEST_FORMAT="transit+json"
|
BABASHKA_POD_TEST_FORMAT="transit+json"
|
||||||
echo "Testing transit"
|
echo "Testing transit"
|
||||||
clojure -A:test -n babashka.pods.jvm-test
|
clojure -M:test -n babashka.pods.jvm-test
|
||||||
clojure -A:sci:test -n babashka.pods.sci-test
|
clojure -M:sci:test -n babashka.pods.sci-test
|
||||||
|
|
||||||
# socket = true
|
# socket = true
|
||||||
unset BABASHKA_POD_TEST_FORMAT
|
unset BABASHKA_POD_TEST_FORMAT
|
||||||
BABASHKA_POD_TEST_SOCKET=true
|
BABASHKA_POD_TEST_SOCKET=true
|
||||||
echo "Testing socket"
|
echo "Testing socket"
|
||||||
clojure -A:test -n babashka.pods.jvm-test
|
clojure -M:test -n babashka.pods.jvm-test
|
||||||
clojure -A:sci:test -n babashka.pods.sci-test
|
clojure -M:sci:test -n babashka.pods.sci-test
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,18 @@
|
||||||
(defn invoke
|
(defn invoke
|
||||||
([pod-id-or-pod sym args] (invoke pod-id-or-pod sym args {}))
|
([pod-id-or-pod sym args] (invoke pod-id-or-pod sym args {}))
|
||||||
([pod-id-or-pod sym args opts] (jvm/invoke pod-id-or-pod sym args opts)))
|
([pod-id-or-pod sym args opts] (jvm/invoke pod-id-or-pod sym args opts)))
|
||||||
|
|
||||||
|
(defmacro copy-var [name var]
|
||||||
|
`(do (def ~name ~var)
|
||||||
|
(let [m# (meta (var ~var))
|
||||||
|
doc# (:doc m#)
|
||||||
|
arglists# (:arglists m#)]
|
||||||
|
(alter-meta! (var ~name) assoc
|
||||||
|
:arglists arglists#
|
||||||
|
:doc doc#))))
|
||||||
|
|
||||||
|
#_:clj-kondo/ignore
|
||||||
|
(do
|
||||||
|
(copy-var add-transit-read-handler! jvm/add-transit-read-handler!)
|
||||||
|
(copy-var add-transit-write-handler! jvm/add-transit-write-handler!)
|
||||||
|
(copy-var set-default-transit-write-handler! jvm/set-default-transit-write-handler!))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@
|
||||||
(defn bytes->string [^"[B" bytes]
|
(defn bytes->string [^"[B" bytes]
|
||||||
(String. bytes))
|
(String. bytes))
|
||||||
|
|
||||||
|
(defn bytes->boolean [^"[B" bytes]
|
||||||
|
(= "true" (String. bytes)))
|
||||||
|
|
||||||
(defn get-string [m k]
|
(defn get-string [m k]
|
||||||
(-> (get m k)
|
(-> (get m k)
|
||||||
bytes->string))
|
bytes->string))
|
||||||
|
|
@ -36,17 +39,63 @@
|
||||||
(some-> (get m k)
|
(some-> (get m k)
|
||||||
bytes->string))
|
bytes->string))
|
||||||
|
|
||||||
|
(defn get-maybe-boolean [m k]
|
||||||
|
(some-> (get m k)
|
||||||
|
bytes->boolean))
|
||||||
|
|
||||||
(defn next-id []
|
(defn next-id []
|
||||||
(str (java.util.UUID/randomUUID)))
|
(str (java.util.UUID/randomUUID)))
|
||||||
|
|
||||||
(defn transit-json-read [^String s]
|
(def ^:dynamic *pod-id* nil)
|
||||||
|
|
||||||
|
(defonce transit-read-handlers (atom {}))
|
||||||
|
(defonce transit-read-handler-maps (atom {}))
|
||||||
|
|
||||||
|
(defn update-transit-read-handler-map []
|
||||||
|
(swap! transit-read-handler-maps assoc *pod-id*
|
||||||
|
(transit/read-handler-map (get @transit-read-handlers *pod-id*))))
|
||||||
|
|
||||||
|
(defn transit-json-read [pod-id ^String s]
|
||||||
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
||||||
(let [r (transit/reader bais :json)]
|
(let [r (transit/reader bais :json {:handlers (get @transit-read-handler-maps pod-id)})]
|
||||||
(transit/read r))))
|
(transit/read r))))
|
||||||
|
|
||||||
(defn transit-json-write [^String s]
|
;; https://www.cognitect.com/blog/2015/9/10/extending-transit
|
||||||
|
(defn add-transit-read-handler!
|
||||||
|
([tag fn]
|
||||||
|
(let [rh (transit/read-handler fn)]
|
||||||
|
(swap! transit-read-handlers assoc-in [*pod-id* tag] rh)
|
||||||
|
(update-transit-read-handler-map)
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
(defonce transit-write-handlers (atom {}))
|
||||||
|
(defonce transit-write-handler-maps (atom {}))
|
||||||
|
|
||||||
|
(defn update-transit-write-handler-map []
|
||||||
|
(swap! transit-write-handler-maps assoc *pod-id*
|
||||||
|
(transit/write-handler-map (get @transit-write-handlers *pod-id*))))
|
||||||
|
|
||||||
|
;; https://www.cognitect.com/blog/2015/9/10/extending-transit
|
||||||
|
(defn add-transit-write-handler!
|
||||||
|
[classes tag fn]
|
||||||
|
(let [rh (transit/write-handler tag fn)]
|
||||||
|
(doseq [class classes]
|
||||||
|
(swap! transit-write-handlers assoc-in [*pod-id* class] rh)))
|
||||||
|
(update-transit-write-handler-map)
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(defonce transit-default-write-handlers (atom {}))
|
||||||
|
|
||||||
|
(defn set-default-transit-write-handler! [tag-fn val-fn]
|
||||||
|
(let [wh (transit/write-handler tag-fn val-fn)]
|
||||||
|
(swap! transit-default-write-handlers assoc *pod-id* wh)))
|
||||||
|
|
||||||
|
(defn transit-json-write
|
||||||
|
[pod-id ^String s metadata?]
|
||||||
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||||
(let [w (transit/writer baos :json)]
|
(let [w (transit/writer baos :json (cond-> {:handlers (get @transit-write-handler-maps pod-id)
|
||||||
|
:default-handler (get @transit-default-write-handlers pod-id)}
|
||||||
|
metadata? (assoc :transform transit/write-meta)))]
|
||||||
(transit/write w s)
|
(transit/write w s)
|
||||||
(str baos))))
|
(str baos))))
|
||||||
|
|
||||||
|
|
@ -58,7 +107,7 @@
|
||||||
write-fn (case format
|
write-fn (case format
|
||||||
:edn pr-str
|
:edn pr-str
|
||||||
:json cheshire/generate-string
|
:json cheshire/generate-string
|
||||||
:transit+json transit-json-write)
|
:transit+json #(transit-json-write (:pod-id pod) % (:arg-meta opts)))
|
||||||
id (next-id)
|
id (next-id)
|
||||||
chan (if handlers handlers
|
chan (if handlers handlers
|
||||||
(promise))
|
(promise))
|
||||||
|
|
@ -82,11 +131,18 @@
|
||||||
#(Boolean/parseBoolean %))
|
#(Boolean/parseBoolean %))
|
||||||
name-sym (symbol name)
|
name-sym (symbol name)
|
||||||
sym (symbol ns-name-str name)
|
sym (symbol ns-name-str name)
|
||||||
code (get-maybe-string var "code")]
|
code (get-maybe-string var "code")
|
||||||
|
vmeta (some-> (get var "meta")
|
||||||
|
bytes->string
|
||||||
|
edn/read-string)
|
||||||
|
name-sym (if vmeta
|
||||||
|
(with-meta name-sym vmeta)
|
||||||
|
name-sym)
|
||||||
|
metadata? (get-maybe-boolean var "arg-meta")]
|
||||||
[name-sym
|
[name-sym
|
||||||
(or code
|
(or code
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(let [res (invoke pod sym args {:async async?})]
|
(let [res (invoke pod sym args {:async async? :arg-meta metadata?})]
|
||||||
res)))]))
|
res)))]))
|
||||||
vars))
|
vars))
|
||||||
|
|
||||||
|
|
@ -112,84 +168,96 @@
|
||||||
(throw e)))))
|
(throw e)))))
|
||||||
:transit+json
|
:transit+json
|
||||||
(fn [s]
|
(fn [s]
|
||||||
(try (transit-json-read s)
|
(try (transit-json-read (:pod-id pod) s)
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(binding [*out* *err*]
|
(binding [*out* *err*]
|
||||||
(println "Cannot read Transit JSON: " (pr-str s))
|
(println "Cannot read Transit JSON: " (pr-str s))
|
||||||
(throw e))))))]
|
(throw e))))))]
|
||||||
(try
|
(binding [*pod-id* (:pod-id pod)]
|
||||||
(loop []
|
(try
|
||||||
(let [reply (try (read stdout)
|
(loop []
|
||||||
(catch java.io.EOFException _
|
(let [reply (try (read stdout)
|
||||||
::EOF))]
|
(catch java.io.EOFException _
|
||||||
(when-not (identical? ::EOF reply)
|
::EOF)
|
||||||
(let [id (get reply "id")
|
(catch java.net.SocketException e
|
||||||
id (bytes->string id)
|
(if (= "Socket closed" (ex-message e))
|
||||||
value* (find reply "value")
|
::EOF
|
||||||
value (some-> value*
|
(throw e))))]
|
||||||
second
|
(when-not (identical? ::EOF reply)
|
||||||
bytes->string
|
(let [id (get reply "id")
|
||||||
read-fn)
|
id (bytes->string id)
|
||||||
status (get reply "status")
|
value* (find reply "value")
|
||||||
status (set (map (comp keyword bytes->string) status))
|
[exception value] (try (some->> value*
|
||||||
error? (contains? status :error)
|
second
|
||||||
done? (or error? (contains? status :done))
|
bytes->string
|
||||||
[ex-message ex-data]
|
read-fn
|
||||||
(when error?
|
(vector nil))
|
||||||
[(or (some-> (get reply "ex-message")
|
(catch Exception e
|
||||||
bytes->string)
|
[e nil]))
|
||||||
"")
|
status (get reply "status")
|
||||||
(or (some-> (get reply "ex-data")
|
status (set (map (comp keyword bytes->string) status))
|
||||||
bytes->string
|
error? (or exception (contains? status :error))
|
||||||
read-fn)
|
done? (or error? exception (contains? status :done))
|
||||||
{})])
|
[ex-message ex-data]
|
||||||
namespace (when-let [v (get reply "vars")]
|
(when error?
|
||||||
(let [name-str (-> (get reply "name")
|
[(or (some-> (get reply "ex-message")
|
||||||
bytes->string)
|
bytes->string)
|
||||||
name (symbol name-str)]
|
"")
|
||||||
{:name name
|
(or (some-> (get reply "ex-data")
|
||||||
:vars (bencode->vars pod name-str v)}))
|
bytes->string
|
||||||
chan (get @chans id)
|
read-fn)
|
||||||
promise? (instance? clojure.lang.IPending chan)
|
{})])
|
||||||
exception (when (and promise? error?)
|
namespace (when-let [v (get reply "vars")]
|
||||||
(ex-info ex-message ex-data))
|
(let [name-str (-> (get reply "name")
|
||||||
;; NOTE: if we need more fine-grained handlers, we will add
|
bytes->string)
|
||||||
;; a :raw handler that will just get the bencode message's raw
|
name (symbol name-str)]
|
||||||
;; data
|
{:name name
|
||||||
{error-handler :error
|
:vars (bencode->vars pod name-str v)}))
|
||||||
done-handler :done
|
chan (get @chans id)
|
||||||
success-handler :success} (when (map? chan)
|
promise? (instance? clojure.lang.IPending chan)
|
||||||
chan)
|
exception (or exception
|
||||||
out (some-> (get reply "out")
|
(when (and promise? error?)
|
||||||
bytes->string)
|
(ex-info ex-message ex-data)))
|
||||||
err (some-> (get reply "err")
|
;; NOTE: if we need more fine-grained handlers, we will add
|
||||||
bytes->string)]
|
;; a :raw handler that will just get the bencode message's raw
|
||||||
;; NOTE: write to out and err before delivering promise for making
|
;; data
|
||||||
;; listening to output synchronous.
|
{error-handler :error
|
||||||
(when out
|
done-handler :done
|
||||||
(binding [*out* out-stream]
|
success-handler :success} (when (map? chan)
|
||||||
(println out)))
|
chan)
|
||||||
(when err (binding [*out* err-stream]
|
out (some-> (get reply "out")
|
||||||
(println err)))
|
bytes->string)
|
||||||
(when (or value* error? namespace)
|
err (some-> (get reply "err")
|
||||||
(cond promise?
|
bytes->string)]
|
||||||
(deliver chan (cond error? exception
|
;; NOTE: write to out and err before delivering promise for making
|
||||||
value value
|
;; listening to output synchronous.
|
||||||
namespace namespace))
|
(when out
|
||||||
(and (not error?) success-handler)
|
(binding [*out* out-stream]
|
||||||
(success-handler value)
|
(print out)
|
||||||
(and error? error-handler)
|
(.flush ^java.io.Writer out-stream)))
|
||||||
(error-handler {:ex-message ex-message
|
(when err
|
||||||
:ex-data ex-data})))
|
(binding [*out* err-stream]
|
||||||
(when (and done? (not error?))
|
(print err)
|
||||||
(when promise?
|
(.flush ^java.io.Writer err-stream)))
|
||||||
(deliver chan nil))
|
(when (or value* error? namespace)
|
||||||
(when done-handler
|
(cond promise?
|
||||||
(done-handler))))
|
(deliver chan (cond error? exception
|
||||||
(recur))))
|
value value
|
||||||
(catch Exception e
|
namespace namespace))
|
||||||
(binding [*out* *err* #_err-stream]
|
(and (not error?) success-handler)
|
||||||
(prn e))))))
|
(success-handler value)
|
||||||
|
(and error? error-handler)
|
||||||
|
(error-handler {:ex-message ex-message
|
||||||
|
:ex-data ex-data})))
|
||||||
|
(when (and done? (not error?))
|
||||||
|
(when promise?
|
||||||
|
(deliver chan nil))
|
||||||
|
(when done-handler
|
||||||
|
(done-handler))))
|
||||||
|
(recur))))
|
||||||
|
(catch Exception e
|
||||||
|
(binding [*out* *err* #_err-stream]
|
||||||
|
(prn e)))))))
|
||||||
|
|
||||||
(def pods (atom {}))
|
(def pods (atom {}))
|
||||||
|
|
||||||
|
|
@ -201,27 +269,24 @@
|
||||||
(defn lookup-pod [pod-id]
|
(defn lookup-pod [pod-id]
|
||||||
(get @pods pod-id))
|
(get @pods pod-id))
|
||||||
|
|
||||||
|
(defn destroy* [{:keys [:stdin :process :ops]}]
|
||||||
|
(if (contains? ops :shutdown)
|
||||||
|
(do (write stdin
|
||||||
|
{"op" "shutdown"
|
||||||
|
"id" (next-id)})
|
||||||
|
(.waitFor ^Process process))
|
||||||
|
(.destroy ^Process process)))
|
||||||
|
|
||||||
(defn destroy [pod-id-or-pod]
|
(defn destroy [pod-id-or-pod]
|
||||||
(let [pod-id (get-pod-id pod-id-or-pod)]
|
(let [pod-id (get-pod-id pod-id-or-pod)]
|
||||||
(when-let [pod (lookup-pod pod-id)]
|
(when-let [pod (lookup-pod pod-id)]
|
||||||
(if (contains? (:ops pod) :shutdown)
|
(destroy* pod)
|
||||||
(do (write (:stdin pod)
|
|
||||||
{"op" "shutdown"
|
|
||||||
"id" (next-id)})
|
|
||||||
(.waitFor ^Process (:process pod)))
|
|
||||||
(.destroy ^Process (:process pod)))
|
|
||||||
(when-let [rns (:remove-ns pod)]
|
(when-let [rns (:remove-ns pod)]
|
||||||
(doseq [[ns-name _] (:namespaces pod)]
|
(doseq [[ns-name _] (:namespaces pod)]
|
||||||
(rns ns-name))))
|
(rns ns-name))))
|
||||||
(swap! pods dissoc pod-id)
|
(swap! pods dissoc pod-id)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(def next-pod-id
|
|
||||||
(let [counter (atom 0)]
|
|
||||||
(fn []
|
|
||||||
(let [[o _] (swap-vals! counter inc)]
|
|
||||||
o))))
|
|
||||||
|
|
||||||
(def bytes->symbol
|
(def bytes->symbol
|
||||||
(comp symbol bytes->string))
|
(comp symbol bytes->string))
|
||||||
|
|
||||||
|
|
@ -244,7 +309,8 @@
|
||||||
the socket is connected."
|
the socket is connected."
|
||||||
^Socket
|
^Socket
|
||||||
[^String hostname ^Integer port]
|
[^String hostname ^Integer port]
|
||||||
(Socket. hostname port))
|
(doto (Socket. hostname port)
|
||||||
|
(.setTcpNoDelay true)))
|
||||||
|
|
||||||
(defn close-socket
|
(defn close-socket
|
||||||
"Close the socket, and also closes its input and output streams."
|
"Close the socket, and also closes its input and output streams."
|
||||||
|
|
@ -254,7 +320,8 @@
|
||||||
(catch java.net.SocketException _ nil)))
|
(catch java.net.SocketException _ nil)))
|
||||||
|
|
||||||
(defn port-file [pid]
|
(defn port-file [pid]
|
||||||
(io/file (str ".babashka-pod-" pid ".port")))
|
(doto (io/file (str ".babashka-pod-" pid ".port"))
|
||||||
|
(.deleteOnExit)))
|
||||||
|
|
||||||
(defn read-port [^java.io.File port-file]
|
(defn read-port [^java.io.File port-file]
|
||||||
(loop []
|
(loop []
|
||||||
|
|
@ -263,67 +330,104 @@
|
||||||
(let [s (slurp f)]
|
(let [s (slurp f)]
|
||||||
(when (str/ends-with? s "\n")
|
(when (str/ends-with? s "\n")
|
||||||
(str/trim s))))]
|
(str/trim s))))]
|
||||||
(Integer. s)
|
(Integer/parseInt s)
|
||||||
(recur)))))
|
(recur)))))
|
||||||
|
|
||||||
(defn debug [& strs]
|
(defn debug [& strs]
|
||||||
(binding [*out* *err*]
|
(binding [*out* *err*]
|
||||||
(println (str/join " " (map pr-str strs)))))
|
(println (str/join " " (map pr-str strs)))))
|
||||||
|
|
||||||
;; TODO: symbol -> look up pod in local cache, invoke if present, else
|
(defn resolve-pod [pod-spec {:keys [:version :path :force] :as opts}]
|
||||||
;; download via package.
|
(when (qualified-symbol? pod-spec)
|
||||||
;; What about versions?
|
(when (and (not version) (not path))
|
||||||
;; bb can package definitions of popular pods in its resources
|
(throw (IllegalArgumentException. "Version or path must be provided")))
|
||||||
;; but what if the resources have an error - maybe best to fetch the definitions from github
|
(when (and version path)
|
||||||
;; (load-pod 'org.babashka/postgresql)
|
(throw (IllegalArgumentException. "You must provide either version or path, not both"))))
|
||||||
;; (load-pod 'org.babashka/postgresql_0.0.1)
|
(let [resolved (when (and (qualified-symbol? pod-spec) version)
|
||||||
|
(resolver/resolve pod-spec version force))
|
||||||
|
opts (if resolved
|
||||||
|
(if-let [extra-opts (:options resolved)]
|
||||||
|
(merge opts extra-opts)
|
||||||
|
opts)
|
||||||
|
opts)
|
||||||
|
pod-spec (cond
|
||||||
|
resolved [(:executable resolved)]
|
||||||
|
path [path]
|
||||||
|
(string? pod-spec) [pod-spec]
|
||||||
|
:else pod-spec)]
|
||||||
|
{:pod-spec pod-spec, :opts opts}))
|
||||||
|
|
||||||
|
(defn run-pod [pod-spec {:keys [:transport] :as _opts}]
|
||||||
|
(let [pb (ProcessBuilder. ^java.util.List pod-spec)
|
||||||
|
socket? (identical? :socket transport)
|
||||||
|
_ (if socket?
|
||||||
|
(.inheritIO pb)
|
||||||
|
(.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT))
|
||||||
|
_ (cond-> (doto (.environment pb)
|
||||||
|
(.put "BABASHKA_POD" "true"))
|
||||||
|
socket? (.put "BABASHKA_POD_TRANSPORT" "socket"))
|
||||||
|
p (.start pb)
|
||||||
|
port-file (when socket? (port-file (.pid p)))
|
||||||
|
socket-port (when socket? (read-port port-file))
|
||||||
|
[socket stdin stdout]
|
||||||
|
(if socket?
|
||||||
|
(let [^Socket socket
|
||||||
|
(loop []
|
||||||
|
(if-let [sock (try (create-socket "localhost" socket-port)
|
||||||
|
(catch java.net.ConnectException _
|
||||||
|
nil))]
|
||||||
|
sock
|
||||||
|
(recur)))]
|
||||||
|
[socket
|
||||||
|
(.getOutputStream socket)
|
||||||
|
(PushbackInputStream. (.getInputStream socket))])
|
||||||
|
[nil (.getOutputStream p) (java.io.PushbackInputStream. (.getInputStream p))])]
|
||||||
|
{:process p
|
||||||
|
:socket socket
|
||||||
|
:stdin stdin
|
||||||
|
:stdout stdout}))
|
||||||
|
|
||||||
|
(defn describe-pod [{:keys [:stdin :stdout]}]
|
||||||
|
(write stdin {"op" "describe"
|
||||||
|
"id" (next-id)})
|
||||||
|
(read stdout))
|
||||||
|
|
||||||
|
(defn describe->ops [describe-reply]
|
||||||
|
(some->> (get describe-reply "ops") keys (map keyword) set))
|
||||||
|
|
||||||
|
(defn describe->metadata [describe-reply resolve-fn]
|
||||||
|
(let [format (-> (get describe-reply "format") bytes->string keyword)
|
||||||
|
ops (describe->ops describe-reply)
|
||||||
|
readers (when (identical? :edn format)
|
||||||
|
(read-readers describe-reply resolve-fn))]
|
||||||
|
{:format format, :ops ops, :readers readers}))
|
||||||
|
|
||||||
|
(defn run-pod-for-metadata [pod-spec opts]
|
||||||
|
(let [running-pod (run-pod pod-spec opts)
|
||||||
|
describe-reply (describe-pod running-pod)
|
||||||
|
ops (describe->ops describe-reply)]
|
||||||
|
(destroy* (assoc running-pod :ops ops))
|
||||||
|
describe-reply))
|
||||||
|
|
||||||
|
(defn load-pod-metadata [unresolved-pod-spec {:keys [:download-only] :as opts}]
|
||||||
|
(let [{:keys [:pod-spec :opts]} (resolve-pod unresolved-pod-spec opts)]
|
||||||
|
(if download-only
|
||||||
|
(resolver/warn "Not running pod" unresolved-pod-spec "to pre-cache metadata because OS and/or arch are different than system")
|
||||||
|
(run-pod-for-metadata pod-spec opts))))
|
||||||
|
|
||||||
(defn load-pod
|
(defn load-pod
|
||||||
([pod-spec] (load-pod pod-spec nil))
|
([pod-spec] (load-pod pod-spec nil))
|
||||||
([pod-spec opts]
|
([pod-spec opts]
|
||||||
(let [{:keys [:version :force]} opts
|
(let [{:keys [:pod-spec :opts]} (resolve-pod pod-spec opts)
|
||||||
resolved (when (qualified-symbol? pod-spec)
|
{:keys [:remove-ns :resolve]} opts
|
||||||
(resolver/resolve pod-spec version force))
|
|
||||||
opts (if resolved
|
{p :process, stdin :stdin, stdout :stdout, socket :socket
|
||||||
(if-let [extra-opts (:options resolved)]
|
:as running-pod}
|
||||||
(merge opts extra-opts)
|
(run-pod pod-spec opts)
|
||||||
opts)
|
|
||||||
opts)
|
reply (or (:metadata opts)
|
||||||
{:keys [:remove-ns :resolve :transport]} opts
|
(describe-pod running-pod))
|
||||||
pod-spec (cond resolved [(:executable resolved)]
|
{:keys [:format :ops :readers]} (describe->metadata reply resolve)
|
||||||
(string? pod-spec) [pod-spec]
|
|
||||||
:else pod-spec)
|
|
||||||
pb (ProcessBuilder. ^java.util.List pod-spec)
|
|
||||||
socket? (identical? :socket transport)
|
|
||||||
_ (if socket?
|
|
||||||
(.inheritIO pb)
|
|
||||||
(.redirectError pb java.lang.ProcessBuilder$Redirect/INHERIT))
|
|
||||||
_ (cond-> (doto (.environment pb)
|
|
||||||
(.put "BABASHKA_POD" "true"))
|
|
||||||
socket? (.put "BABASHKA_POD_TRANSPORT" "socket"))
|
|
||||||
p (.start pb)
|
|
||||||
port-file (when socket? (port-file (.pid p)))
|
|
||||||
socket-port (when socket? (read-port port-file))
|
|
||||||
[socket stdin stdout]
|
|
||||||
(if socket?
|
|
||||||
(let [^Socket socket
|
|
||||||
(loop []
|
|
||||||
(if-let [sock (try (create-socket "localhost" socket-port)
|
|
||||||
(catch java.net.ConnectException _
|
|
||||||
nil))]
|
|
||||||
sock
|
|
||||||
(recur)))]
|
|
||||||
[socket
|
|
||||||
(.getOutputStream socket)
|
|
||||||
(PushbackInputStream. (.getInputStream socket))])
|
|
||||||
[nil (.getOutputStream p) (java.io.PushbackInputStream. (.getInputStream p))])
|
|
||||||
_ (write stdin {"op" "describe"
|
|
||||||
"id" (next-id)})
|
|
||||||
reply (read stdout)
|
|
||||||
format (-> (get reply "format") bytes->string keyword)
|
|
||||||
ops (some->> (get reply "ops") keys (map keyword) set)
|
|
||||||
readers (when (identical? :edn format)
|
|
||||||
(read-readers reply resolve))
|
|
||||||
pod {:process p
|
pod {:process p
|
||||||
:pod-spec pod-spec
|
:pod-spec pod-spec
|
||||||
:stdin stdin
|
:stdin stdin
|
||||||
|
|
|
||||||
|
|
@ -15,29 +15,43 @@
|
||||||
"x86_64"
|
"x86_64"
|
||||||
arch))
|
arch))
|
||||||
|
|
||||||
(def os {:os/name (System/getProperty "os.name")
|
(defn normalize-os [os]
|
||||||
:os/arch (let [arch (System/getProperty "os.arch")]
|
(-> os str/lower-case (str/replace #"\s+" "_")))
|
||||||
(normalize-arch arch))})
|
|
||||||
|
(def os
|
||||||
|
(delay
|
||||||
|
{:os/name (or (System/getenv "BABASHKA_PODS_OS_NAME")
|
||||||
|
(System/getProperty "os.name"))
|
||||||
|
:os/arch (let [arch (or (System/getenv "BABASHKA_PODS_OS_ARCH")
|
||||||
|
(System/getProperty "os.arch"))]
|
||||||
|
(normalize-arch arch))}))
|
||||||
|
|
||||||
(defn warn [& strs]
|
(defn warn [& strs]
|
||||||
(binding [*out* *err*]
|
(binding [*out* *err*]
|
||||||
(apply println strs)))
|
(apply println strs)))
|
||||||
|
|
||||||
(defn match-artifacts [package]
|
(defn match-artifacts
|
||||||
(let [artifacts (:pod/artifacts package)
|
([package] (match-artifacts package (:os/arch @os)))
|
||||||
res (filter (fn [{os-name :os/name
|
([package arch]
|
||||||
os-arch :os/arch}]
|
(let [artifacts (:pod/artifacts package)
|
||||||
(let [os-arch (normalize-arch os-arch)]
|
res (filter (fn [{os-name :os/name
|
||||||
(and (re-matches (re-pattern os-name) (:os/name os))
|
os-arch :os/arch}]
|
||||||
(re-matches (re-pattern os-arch)
|
(let [os-arch (normalize-arch os-arch)]
|
||||||
(:os/arch os)))))
|
(and (re-matches (re-pattern os-name) (:os/name @os))
|
||||||
artifacts)]
|
(re-matches (re-pattern os-arch)
|
||||||
(when (empty? res)
|
arch))))
|
||||||
(throw (IllegalArgumentException. (format "No executable found for pod %s (%s) and OS %s/%s"
|
artifacts)]
|
||||||
(:pod/name package)
|
(if (empty? res)
|
||||||
(:pod/version package)
|
(if (and (= "Mac OS X" (:os/name @os))
|
||||||
(:os/name os)
|
(= "aarch64" (:os/arch @os)))
|
||||||
(:os/arch os)))))
|
;; Rosetta2 fallback on Apple M1 machines
|
||||||
res))
|
(match-artifacts package "x86_64")
|
||||||
|
(throw (IllegalArgumentException. (format "No executable found for pod %s (%s) and OS %s/%s"
|
||||||
|
(:pod/name package)
|
||||||
|
(:pod/version package)
|
||||||
|
(:os/name @os)
|
||||||
|
(:os/arch @os)))))
|
||||||
|
res))))
|
||||||
|
|
||||||
(defn unzip [{:keys [^java.io.File zip-file
|
(defn unzip [{:keys [^java.io.File zip-file
|
||||||
^java.io.File destination-dir
|
^java.io.File destination-dir
|
||||||
|
|
@ -77,7 +91,10 @@
|
||||||
^"[Ljava.nio.file.CopyOption;"
|
^"[Ljava.nio.file.CopyOption;"
|
||||||
(into-array
|
(into-array
|
||||||
[java.nio.file.StandardCopyOption/REPLACE_EXISTING])))
|
[java.nio.file.StandardCopyOption/REPLACE_EXISTING])))
|
||||||
(sh "tar" "xf" (.getPath tmp-file) "--directory" (.getPath destination-dir))
|
(.mkdirs destination-dir)
|
||||||
|
(let [res (sh "tar" "xf" (.getPath tmp-file) "--directory" (.getPath destination-dir))]
|
||||||
|
(when-not (zero? (:exit res))
|
||||||
|
(throw (ex-info (:err res) res))))
|
||||||
(.delete tmp-file)))
|
(.delete tmp-file)))
|
||||||
|
|
||||||
(defn make-executable [dest-dir executables verbose?]
|
(defn make-executable [dest-dir executables verbose?]
|
||||||
|
|
@ -97,11 +114,20 @@
|
||||||
(with-open [is (.getInputStream conn)]
|
(with-open [is (.getInputStream conn)]
|
||||||
(io/copy is dest))))
|
(io/copy is dest))))
|
||||||
|
|
||||||
(def pod-manifests-dir
|
(defn repo-dir []
|
||||||
|
(io/file (if-let [pods-dir (System/getenv "BABASHKA_PODS_DIR")]
|
||||||
|
(io/file pods-dir)
|
||||||
|
(io/file (or
|
||||||
|
(System/getenv "XDG_DATA_HOME")
|
||||||
|
(System/getProperty "user.home"))
|
||||||
|
".babashka"
|
||||||
|
"pods"))
|
||||||
|
"repository"))
|
||||||
|
|
||||||
|
(def pods-repo-dir
|
||||||
;; wrapped in delay for GraalVM native-image
|
;; wrapped in delay for GraalVM native-image
|
||||||
(delay (io/file (or (System/getenv "XDG_DATA_HOME")
|
(delay
|
||||||
(System/getProperty "user.home"))
|
(repo-dir)))
|
||||||
".babashka" "pods" "repository")))
|
|
||||||
|
|
||||||
(defn github-url [qsym version]
|
(defn github-url [qsym version]
|
||||||
(format
|
(format
|
||||||
|
|
@ -110,7 +136,7 @@
|
||||||
|
|
||||||
(defn pod-manifest
|
(defn pod-manifest
|
||||||
[qsym version force?]
|
[qsym version force?]
|
||||||
(let [f (io/file @pod-manifests-dir (str qsym) (str version) "manifest.edn")]
|
(let [f (io/file @pods-repo-dir (str qsym) (str version) "manifest.edn")]
|
||||||
(if (and (not force?)
|
(if (and (not force?)
|
||||||
(.exists f))
|
(.exists f))
|
||||||
(edn/read-string (slurp f))
|
(edn/read-string (slurp f))
|
||||||
|
|
@ -121,27 +147,30 @@
|
||||||
^java.io.File
|
^java.io.File
|
||||||
[{pod-name :pod/name
|
[{pod-name :pod/name
|
||||||
pod-version :pod/version}]
|
pod-version :pod/version}]
|
||||||
(io/file (or
|
(let [base-file
|
||||||
(System/getenv "XDG_CACHE_HOME")
|
(if-let [pods-dir (System/getenv "BABASHKA_PODS_DIR")]
|
||||||
(System/getProperty "user.home"))
|
(io/file pods-dir)
|
||||||
".babashka"
|
(io/file (or
|
||||||
"pods"
|
(System/getenv "XDG_CACHE_HOME")
|
||||||
"repository"
|
(System/getProperty "user.home"))
|
||||||
(str pod-name)
|
".babashka"
|
||||||
pod-version))
|
"pods"))]
|
||||||
|
(io/file base-file
|
||||||
|
"repository"
|
||||||
|
(str pod-name)
|
||||||
|
pod-version
|
||||||
|
(normalize-os (:os/name @os))
|
||||||
|
(:os/arch @os))))
|
||||||
|
|
||||||
(defn data-dir
|
(defn data-dir
|
||||||
^java.io.File
|
^java.io.File
|
||||||
[{pod-name :pod/name
|
[{pod-name :pod/name
|
||||||
pod-version :pod/version}]
|
pod-version :pod/version}]
|
||||||
(io/file (or
|
(io/file @pods-repo-dir
|
||||||
(System/getenv "XDG_DATA_HOME")
|
|
||||||
(System/getProperty "user.home"))
|
|
||||||
".babashka"
|
|
||||||
"pods"
|
|
||||||
"repository"
|
|
||||||
(str pod-name)
|
(str pod-name)
|
||||||
pod-version))
|
pod-version
|
||||||
|
(normalize-os (:os/name @os))
|
||||||
|
(:os/arch @os)))
|
||||||
|
|
||||||
(defn sha256 [file]
|
(defn sha256 [file]
|
||||||
(let [buf (byte-array 8192)
|
(let [buf (byte-array 8192)
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,18 @@
|
||||||
(defn- unroot-resource [^String path]
|
(defn- unroot-resource [^String path]
|
||||||
(symbol (.. path
|
(symbol (.. path
|
||||||
(substring 1)
|
(substring 1)
|
||||||
(replace \/ \. )
|
(replace \/ \.)
|
||||||
(replace \_ \-))))
|
(replace \_ \-))))
|
||||||
|
|
||||||
(defn- process-namespace [{:keys [:name :vars]}]
|
(defn- process-namespace [{:keys [:name :vars]}]
|
||||||
(binding [*ns* (load-string (format "(ns %s) *ns*" name))]
|
(binding [*ns* (load-string (format "(ns %s) *ns*" name))]
|
||||||
(doseq [[var-sym v] vars]
|
(doseq [[var-sym v] vars]
|
||||||
|
(when-let [maybe-core (some-> (ns-resolve *ns* var-sym) meta :ns str symbol)]
|
||||||
|
(when (= 'clojure.core maybe-core)
|
||||||
|
(ns-unmap *ns* var-sym)))
|
||||||
(cond
|
(cond
|
||||||
(ifn? v)
|
(ifn? v)
|
||||||
(do
|
(intern name var-sym v)
|
||||||
(ns-unmap *ns* var-sym)
|
|
||||||
(intern name var-sym v))
|
|
||||||
(string? v)
|
(string? v)
|
||||||
(load-string v)))))
|
(load-string v)))))
|
||||||
|
|
||||||
|
|
@ -55,9 +56,10 @@
|
||||||
(when defer?
|
(when defer?
|
||||||
[ns-name pod]))
|
[ns-name pod]))
|
||||||
namespaces)))
|
namespaces)))
|
||||||
(doseq [[ns-sym vars lazy?] namespaces
|
(binding [impl/*pod-id* (:pod-id pod)]
|
||||||
:when (not lazy?)]
|
(doseq [[ns-sym vars lazy?] namespaces
|
||||||
(process-namespace {:name ns-sym :vars vars}))
|
:when (not lazy?)]
|
||||||
|
(process-namespace {:name ns-sym :vars vars})))
|
||||||
(future (impl/processor pod))
|
(future (impl/processor pod))
|
||||||
{:pod/id (:pod-id pod)})))
|
{:pod/id (:pod-id pod)})))
|
||||||
|
|
||||||
|
|
@ -69,3 +71,18 @@
|
||||||
(defn invoke
|
(defn invoke
|
||||||
([pod-id sym args] (invoke pod-id sym args {}))
|
([pod-id sym args] (invoke pod-id sym args {}))
|
||||||
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
||||||
|
|
||||||
|
(defmacro copy-var [name var]
|
||||||
|
`(do (def ~name ~var)
|
||||||
|
(let [m# (meta (var ~var))
|
||||||
|
doc# (:doc m#)
|
||||||
|
arglists# (:arglists m#)]
|
||||||
|
(alter-meta! (var ~name) assoc
|
||||||
|
:arglists arglists#
|
||||||
|
:doc doc#))))
|
||||||
|
|
||||||
|
#_:clj-kondo/ignore
|
||||||
|
(do
|
||||||
|
(copy-var add-transit-read-handler! impl/add-transit-read-handler!)
|
||||||
|
(copy-var add-transit-write-handler! impl/add-transit-write-handler!)
|
||||||
|
(copy-var set-default-transit-write-handler! impl/set-default-transit-write-handler!))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
(ns babashka.pods.sci
|
(ns babashka.pods.sci
|
||||||
(:require [babashka.pods.impl :as impl]
|
(:require [babashka.pods.impl :as impl]
|
||||||
[sci.core :as sci]))
|
[sci.core :as sci]
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[babashka.pods.impl.resolver :as resolver]
|
||||||
|
[babashka.fs :as fs])
|
||||||
|
(:import (java.io PushbackInputStream File)))
|
||||||
|
|
||||||
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
(defn- process-namespace [ctx {:keys [:name :vars]}]
|
(defn- process-namespace [ctx {:keys [:name :vars]}]
|
||||||
(let [env (:env ctx)
|
(let [env (:env ctx)
|
||||||
|
|
@ -11,14 +17,58 @@
|
||||||
(swap! env update-in [:namespaces ns-name]
|
(swap! env update-in [:namespaces ns-name]
|
||||||
(fn [ns-map]
|
(fn [ns-map]
|
||||||
(if ns-map ns-map {:obj sci-ns})))
|
(if ns-map ns-map {:obj sci-ns})))
|
||||||
(doseq [[var-name var-value] vars]
|
(doseq [[var-name var-value :as var] vars]
|
||||||
(cond (ifn? var-value)
|
(cond (ifn? var-value)
|
||||||
(swap! env assoc-in [:namespaces ns-name var-name]
|
(swap! env assoc-in [:namespaces ns-name var-name]
|
||||||
(sci/new-var
|
(sci/new-var
|
||||||
(symbol (str ns-name) (str var-name)) var-value))
|
(symbol (str ns-name) (str var-name)) var-value (meta var-name)))
|
||||||
(string? var-value)
|
(string? var-value)
|
||||||
(sci/eval-string* ctx var-value))))))
|
(sci/eval-string* ctx var-value))))))
|
||||||
|
|
||||||
|
(defn metadata-cache-file ^File [^File bb-edn-file pod-spec {:keys [:version :path]}]
|
||||||
|
(if version
|
||||||
|
(io/file (resolver/cache-dir {:pod/name pod-spec :pod/version version})
|
||||||
|
"metadata.cache")
|
||||||
|
(let [config-dir (.getParentFile bb-edn-file)
|
||||||
|
cache-dir (io/file config-dir ".babashka")
|
||||||
|
pod-file (-> path io/file .getName)
|
||||||
|
cache-file (io/file cache-dir (str pod-file ".metadata.cache"))]
|
||||||
|
cache-file)))
|
||||||
|
|
||||||
|
(defn load-metadata-from-cache [bb-edn-file pod-spec opts]
|
||||||
|
(let [cache-file (metadata-cache-file bb-edn-file pod-spec opts)]
|
||||||
|
(when (.exists cache-file)
|
||||||
|
(with-open [r (PushbackInputStream. (io/input-stream cache-file))]
|
||||||
|
(impl/read r)))))
|
||||||
|
|
||||||
|
(defn load-pod-metadata* [bb-edn-file pod-spec {:keys [:version :cache] :as opts}]
|
||||||
|
(let [metadata (impl/load-pod-metadata pod-spec opts)
|
||||||
|
cache-file (when (and metadata cache)
|
||||||
|
(metadata-cache-file bb-edn-file pod-spec opts))]
|
||||||
|
(when cache-file
|
||||||
|
(io/make-parents cache-file)
|
||||||
|
(when (fs/writable? (fs/parent cache-file))
|
||||||
|
(with-open [w (io/output-stream cache-file)]
|
||||||
|
(impl/write w metadata))))
|
||||||
|
metadata))
|
||||||
|
|
||||||
|
(defn load-pod-metadata
|
||||||
|
([pod-spec opts] (load-pod-metadata nil pod-spec opts))
|
||||||
|
([bb-edn-file pod-spec {:keys [:cache] :as opts}]
|
||||||
|
(let [metadata
|
||||||
|
(if-let [cached-metadata (when cache
|
||||||
|
(load-metadata-from-cache bb-edn-file
|
||||||
|
pod-spec
|
||||||
|
opts))]
|
||||||
|
cached-metadata
|
||||||
|
(load-pod-metadata* bb-edn-file pod-spec opts))]
|
||||||
|
(reduce
|
||||||
|
(fn [pod-namespaces ns]
|
||||||
|
(let [ns-sym (-> ns (get "name") impl/bytes->string symbol)]
|
||||||
|
(assoc pod-namespaces ns-sym {:pod-spec pod-spec
|
||||||
|
:opts (assoc opts :metadata metadata)})))
|
||||||
|
{} (get metadata "namespaces")))))
|
||||||
|
|
||||||
(defn load-pod
|
(defn load-pod
|
||||||
([ctx pod-spec] (load-pod ctx pod-spec nil))
|
([ctx pod-spec] (load-pod ctx pod-spec nil))
|
||||||
([ctx pod-spec version opts] (load-pod ctx pod-spec (assoc opts :version version)))
|
([ctx pod-spec version opts] (load-pod ctx pod-spec (assoc opts :version version)))
|
||||||
|
|
@ -65,9 +115,10 @@
|
||||||
(when prev-load-fn
|
(when prev-load-fn
|
||||||
(prev-load-fn m))))]
|
(prev-load-fn m))))]
|
||||||
(swap! env assoc :load-fn new-load-fn)))
|
(swap! env assoc :load-fn new-load-fn)))
|
||||||
(doseq [[ns-name vars lazy?] namespaces
|
(binding [impl/*pod-id* (:pod-id pod)]
|
||||||
:when (not lazy?)]
|
(doseq [[ns-name vars lazy?] namespaces
|
||||||
(process-namespace ctx {:name ns-name :vars vars}))
|
:when (not lazy?)]
|
||||||
|
(process-namespace ctx {:name ns-name :vars vars})))
|
||||||
(sci/future (impl/processor pod))
|
(sci/future (impl/processor pod))
|
||||||
{:pod/id (:pod-id pod)})))
|
{:pod/id (:pod-id pod)})))
|
||||||
|
|
||||||
|
|
@ -79,3 +130,18 @@
|
||||||
(defn invoke
|
(defn invoke
|
||||||
([pod-id sym args] (invoke pod-id sym args {}))
|
([pod-id sym args] (invoke pod-id sym args {}))
|
||||||
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
([pod-id sym args opts] (impl/invoke-public pod-id sym args opts)))
|
||||||
|
|
||||||
|
(defmacro copy-var [name var]
|
||||||
|
`(do (def ~name ~var)
|
||||||
|
(let [m# (meta (var ~var))
|
||||||
|
doc# (:doc m#)
|
||||||
|
arglists# (:arglists m#)]
|
||||||
|
(alter-meta! (var ~name) assoc
|
||||||
|
:arglists arglists#
|
||||||
|
:doc doc#))))
|
||||||
|
|
||||||
|
#_:clj-kondo/ignore
|
||||||
|
(do
|
||||||
|
(copy-var add-transit-read-handler! impl/add-transit-read-handler!)
|
||||||
|
(copy-var add-transit-write-handler! impl/add-transit-write-handler!)
|
||||||
|
(copy-var set-default-transit-write-handler! impl/set-default-transit-write-handler!))
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,34 @@
|
||||||
|
|
||||||
(defn transit-json-read [^String s]
|
(defn transit-json-read [^String s]
|
||||||
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
(with-open [bais (java.io.ByteArrayInputStream. (.getBytes s "UTF-8"))]
|
||||||
(let [r (transit/reader bais :json)]
|
(let [r (transit/reader bais :json {:handlers
|
||||||
|
{"local-date-time"
|
||||||
|
(transit/read-handler
|
||||||
|
(fn [s]
|
||||||
|
(java.time.LocalDateTime/parse s)))
|
||||||
|
"java.array"
|
||||||
|
(transit/read-handler
|
||||||
|
(fn [v]
|
||||||
|
(into-array v)))}})]
|
||||||
(transit/read r))))
|
(transit/read r))))
|
||||||
|
|
||||||
(defn transit-json-write [^String s]
|
(defn transit-json-write [s]
|
||||||
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||||
(let [w (transit/writer baos :json)]
|
(let [w (transit/writer baos :json {:handlers
|
||||||
|
{java.time.LocalDateTime
|
||||||
|
(transit/write-handler
|
||||||
|
"local-date-time"
|
||||||
|
str)}
|
||||||
|
:default-handler
|
||||||
|
(transit/write-handler
|
||||||
|
(fn [x] (when (.isArray (class x)) "java.array"))
|
||||||
|
vec)})]
|
||||||
|
(transit/write w s)
|
||||||
|
(str baos))))
|
||||||
|
|
||||||
|
(defn transit-json-write-meta [s]
|
||||||
|
(with-open [baos (java.io.ByteArrayOutputStream. 4096)]
|
||||||
|
(let [w (transit/writer baos :json {:transform transit/write-meta})]
|
||||||
(transit/write w s)
|
(transit/write w s)
|
||||||
(str baos))))
|
(str baos))))
|
||||||
|
|
||||||
|
|
@ -94,7 +116,8 @@
|
||||||
"my/other-tag" "pod.test-pod/read-other-tag"}
|
"my/other-tag" "pod.test-pod/read-other-tag"}
|
||||||
"namespaces"
|
"namespaces"
|
||||||
[{"name" "pod.test-pod"
|
[{"name" "pod.test-pod"
|
||||||
"vars" (into [{"name" "add-sync"}
|
"vars" (into [{"name" "add-sync"
|
||||||
|
"meta" "{:doc \"add the arguments\"}"}
|
||||||
{"name" "range-stream"
|
{"name" "range-stream"
|
||||||
"async" "true"}
|
"async" "true"}
|
||||||
{"name" "assoc"}
|
{"name" "assoc"}
|
||||||
|
|
@ -111,7 +134,35 @@
|
||||||
{"name" "other-tag"}
|
{"name" "other-tag"}
|
||||||
;; reads thing with other tag
|
;; reads thing with other tag
|
||||||
{"name" "read-other-tag"
|
{"name" "read-other-tag"
|
||||||
"code" "(defn read-other-tag [x] [x x])"}]
|
"code" "(defn read-other-tag [x] [x x])"
|
||||||
|
"meta" "{:doc \"unread\"}"}
|
||||||
|
{"name" "round-trip-meta"
|
||||||
|
"arg-meta" "true"}
|
||||||
|
{"name" "dont-round-trip-meta"
|
||||||
|
"arg-meta" "false"}
|
||||||
|
{"name" "-local-date-time"}
|
||||||
|
{"name" "transit-stuff"
|
||||||
|
"code" "
|
||||||
|
(babashka.pods/add-transit-read-handler! \"local-date-time\"
|
||||||
|
(fn [s] (java.time.LocalDateTime/parse s)))
|
||||||
|
|
||||||
|
(babashka.pods/add-transit-write-handler! #{java.time.LocalDateTime}
|
||||||
|
\"local-date-time\"
|
||||||
|
str )
|
||||||
|
|
||||||
|
(defn local-date-time [x]
|
||||||
|
(-local-date-time x))
|
||||||
|
|
||||||
|
;; serialize Java arrays as vectors with tag java.array
|
||||||
|
(babashka.pods/set-default-transit-write-handler!
|
||||||
|
(fn [x] (when (.isArray (class x)) \"java.array\"))
|
||||||
|
vec)
|
||||||
|
|
||||||
|
(babashka.pods/add-transit-read-handler! \"java.array\"
|
||||||
|
into-array)
|
||||||
|
|
||||||
|
"}
|
||||||
|
{"name" "incorrect-edn"}]
|
||||||
dependents)}
|
dependents)}
|
||||||
{"name" "pod.test-pod.loaded"
|
{"name" "pod.test-pod.loaded"
|
||||||
"defer" "true"}
|
"defer" "true"}
|
||||||
|
|
@ -135,65 +186,91 @@
|
||||||
pod.test-pod/add-sync
|
pod.test-pod/add-sync
|
||||||
(try (let [ret (apply + args)]
|
(try (let [ret (apply + args)]
|
||||||
(write out
|
(write out
|
||||||
{"value" (write-fn ret)
|
{"value" (write-fn ret)
|
||||||
"id" id
|
"id" id
|
||||||
"status" ["done"]}))
|
"status" ["done"]}))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(write out
|
(write out
|
||||||
{"ex-data" (write-fn {:args args})
|
{"ex-data" (write-fn {:args args})
|
||||||
"ex-message" (.getMessage e)
|
"ex-message" (.getMessage e)
|
||||||
"status" ["done" "error"]
|
"status" ["done" "error"]
|
||||||
"id" id})))
|
"id" id})))
|
||||||
pod.test-pod/range-stream
|
pod.test-pod/range-stream
|
||||||
(let [rng (apply range args)]
|
(let [rng (apply range args)]
|
||||||
(doseq [v rng]
|
(doseq [v rng]
|
||||||
(write out
|
(write out
|
||||||
{"value" (write-fn v)
|
{"value" (write-fn v)
|
||||||
"id" id})
|
"id" id})
|
||||||
(Thread/sleep 100))
|
(Thread/sleep 100))
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id}))
|
"id" id}))
|
||||||
pod.test-pod/assoc
|
pod.test-pod/assoc
|
||||||
(write out
|
(write out
|
||||||
{"value" (write-fn (apply assoc args))
|
{"value" (write-fn (apply assoc args))
|
||||||
"status" ["done"]
|
"status" ["done"]
|
||||||
"id" id})
|
"id" id})
|
||||||
pod.test-pod/error
|
pod.test-pod/error
|
||||||
(write out
|
(write out
|
||||||
{"ex-data" (write-fn {:args args})
|
{"ex-data" (write-fn {:args args})
|
||||||
"ex-message" (str "Illegal arguments")
|
"ex-message" (str "Illegal arguments")
|
||||||
"status" ["done" "error"]
|
"status" ["done" "error"]
|
||||||
"id" id})
|
"id" id})
|
||||||
pod.test-pod/print
|
pod.test-pod/print
|
||||||
(do (write out
|
(do (write out
|
||||||
{"out" (pr-str args)
|
{"out" (with-out-str (prn args))
|
||||||
"id" id})
|
"id" id})
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id}))
|
"id" id}))
|
||||||
pod.test-pod/print-err
|
pod.test-pod/print-err
|
||||||
(do (write out
|
(do (write out
|
||||||
{"err" (pr-str args)
|
{"err" (with-out-str (prn args))
|
||||||
"id" id})
|
"id" id})
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id}))
|
"id" id}))
|
||||||
pod.test-pod/return-nil
|
pod.test-pod/return-nil
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"id" id
|
||||||
"value" (write-fn nil)})
|
"value" (write-fn nil)})
|
||||||
pod.test-pod/reader-tag
|
pod.test-pod/reader-tag
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"id" id
|
||||||
"value" "#my/tag[1 2 3]"})
|
"value" "#my/tag[1 2 3]"})
|
||||||
pod.test-pod/other-tag
|
pod.test-pod/other-tag
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"id" id
|
||||||
"value" "#my/other-tag[1]"}))
|
"value" "#my/other-tag[1]"})
|
||||||
|
pod.test-pod/round-trip-meta
|
||||||
|
(write out
|
||||||
|
{"status" ["done"]
|
||||||
|
"id" id
|
||||||
|
"value"
|
||||||
|
(case format
|
||||||
|
:transit+json (transit-json-write-meta (first args))
|
||||||
|
(write-fn (first args)))})
|
||||||
|
pod.test-pod/dont-round-trip-meta
|
||||||
|
(write out
|
||||||
|
{"status" ["done"]
|
||||||
|
"id" id
|
||||||
|
"value"
|
||||||
|
(case format
|
||||||
|
:transit+json (transit-json-write-meta (first args))
|
||||||
|
(write-fn (first args)))})
|
||||||
|
pod.test-pod/-local-date-time
|
||||||
|
(write out
|
||||||
|
{"status" ["done"]
|
||||||
|
"id" id
|
||||||
|
"value" (write-fn (first args))})
|
||||||
|
pod.test-pod/incorrect-edn
|
||||||
|
(write out
|
||||||
|
{"status" ["done"]
|
||||||
|
"id" id
|
||||||
|
"value" (write-fn {(keyword "foo bar") 1})}))
|
||||||
(recur))
|
(recur))
|
||||||
:shutdown (System/exit 0)
|
:shutdown (System/exit 0)
|
||||||
:load-ns (let [ns (-> (get message "ns")
|
:load-ns (let [ns (-> (get message "ns")
|
||||||
|
|
@ -204,20 +281,20 @@
|
||||||
(case ns
|
(case ns
|
||||||
pod.test-pod.loaded
|
pod.test-pod.loaded
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"id" id
|
||||||
"name" "pod.test-pod.loaded"
|
"name" "pod.test-pod.loaded"
|
||||||
"vars" [{"name" "loaded"
|
"vars" [{"name" "loaded"
|
||||||
"code" "(defn loaded [x] (inc x))"}]})
|
"code" "(defn loaded [x] (inc x))"}]})
|
||||||
pod.test-pod.loaded2
|
pod.test-pod.loaded2
|
||||||
(write out
|
(write out
|
||||||
{"status" ["done"]
|
{"status" ["done"]
|
||||||
"id" id
|
"id" id
|
||||||
"name" "pod.test-pod.loaded2"
|
"name" "pod.test-pod.loaded2"
|
||||||
"vars" [{"name" "x"
|
"vars" [{"name" "x"
|
||||||
"code" "(require '[pod.test-pod.loaded :as loaded])"}
|
"code" "(require '[pod.test-pod.loaded :as loaded])"}
|
||||||
{"name" "loaded"
|
{"name" "loaded"
|
||||||
"code" "(defn loaded [x] (loaded/loaded x))"}]}))
|
"code" "(defn loaded [x] (loaded/loaded x))"}]}))
|
||||||
(recur)))))))
|
(recur)))))))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(binding [*out* *err*]
|
(binding [*out* *err*]
|
||||||
|
|
@ -225,6 +302,6 @@
|
||||||
|
|
||||||
(defn -main [& args]
|
(defn -main [& args]
|
||||||
#_(binding [*out* *err*]
|
#_(binding [*out* *err*]
|
||||||
(prn :args args))
|
(prn :args args))
|
||||||
(when (= "true" (System/getenv "BABASHKA_POD"))
|
(when (= "true" (System/getenv "BABASHKA_POD"))
|
||||||
(run-pod (set args))))
|
(run-pod (set args))))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(require '[babashka.pods :as pods])
|
(require '[babashka.pods :as pods])
|
||||||
|
|
||||||
(pods/load-pod 'org.babashka/buddy "0.0.1")
|
(pods/load-pod 'org.babashka/buddy "0.1.0")
|
||||||
|
|
||||||
(require '[pod.babashka.buddy.codecs :as codecs]
|
(require '[pod.babashka.buddy.codecs :as codecs]
|
||||||
'[pod.babashka.buddy.hash :as hash])
|
'[pod.babashka.buddy.hash :as hash])
|
||||||
|
|
@ -8,4 +8,4 @@
|
||||||
(println (-> (hash/sha256 "foobar")
|
(println (-> (hash/sha256 "foobar")
|
||||||
(codecs/bytes->hex)))
|
(codecs/bytes->hex)))
|
||||||
|
|
||||||
(pods/load-pod 'org.babashka/etaoin) ;; should cause error when version is missing
|
(pods/load-pod 'org.babashka/etaoin) ;; should cause error when version & path are missing
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,13 @@
|
||||||
|
|
||||||
(def socket (System/getenv "BABASHKA_POD_TEST_SOCKET"))
|
(def socket (System/getenv "BABASHKA_POD_TEST_SOCKET"))
|
||||||
|
|
||||||
(def pod-id (:pod/id (pods/load-pod (cond-> ["clojure" "-A:test-pod"]
|
(def cmd (cond-> ["clojure" "-M:test-pod"]
|
||||||
(= "json" fmt) (conj "--json")
|
(= "json" fmt) (conj "--json")
|
||||||
(= "transit+json" fmt) (conj "--transit+json"))
|
(= "transit+json" fmt) (conj "--transit+json")))
|
||||||
|
|
||||||
|
;; (.println System/err cmd)
|
||||||
|
|
||||||
|
(def pod-id (:pod/id (pods/load-pod cmd
|
||||||
{:socket (boolean socket)})))
|
{:socket (boolean socket)})))
|
||||||
|
|
||||||
(require '[pod.test-pod :as pod])
|
(require '[pod.test-pod :as pod])
|
||||||
|
|
@ -51,6 +55,10 @@
|
||||||
(def add-result (pod.test-pod/add-sync 1 2 3))
|
(def add-result (pod.test-pod/add-sync 1 2 3))
|
||||||
(def nil-result (pod.test-pod/return-nil))
|
(def nil-result (pod.test-pod/return-nil))
|
||||||
|
|
||||||
|
(def add-sync-meta (:doc (meta #'pod.test-pod/add-sync)))
|
||||||
|
(def error-meta (:doc (meta #'pod.test-pod/error)))
|
||||||
|
(def read-other-tag-meta (:doc (meta #'pod.test-pod/read-other-tag)))
|
||||||
|
|
||||||
(def x9 pod.test-pod/x9)
|
(def x9 pod.test-pod/x9)
|
||||||
|
|
||||||
(def tagged (if (= "edn" fmt)
|
(def tagged (if (= "edn" fmt)
|
||||||
|
|
@ -64,12 +72,43 @@
|
||||||
|
|
||||||
(def fn-called (pod.test-pod/fn-call inc 2))
|
(def fn-called (pod.test-pod/fn-call inc 2))
|
||||||
|
|
||||||
|
;; (.println System/err (str :fmt " " fmt))
|
||||||
|
(def local-date-time
|
||||||
|
(if (= "transit+json" fmt)
|
||||||
|
(instance? java.time.LocalDateTime (pod.test-pod/local-date-time (java.time.LocalDateTime/now)))
|
||||||
|
true))
|
||||||
|
|
||||||
|
(def assoc-string-array
|
||||||
|
(if (= "transit+json" fmt)
|
||||||
|
(let [v (:a (pod.test-pod/assoc {} :a (into-array String ["foo"])))]
|
||||||
|
(.isArray (class v)))
|
||||||
|
true))
|
||||||
|
|
||||||
|
(def round-trip-meta
|
||||||
|
(if (= "transit+json" fmt)
|
||||||
|
(= {:my-meta 2} (meta (pod.test-pod/round-trip-meta (with-meta [2] {:my-meta 2}))))
|
||||||
|
true))
|
||||||
|
|
||||||
|
(def round-trip-meta-nested
|
||||||
|
(if (= "transit+json" fmt)
|
||||||
|
(= {:my-meta 3} (meta (first (pod.test-pod/round-trip-meta [(with-meta [3] {:my-meta 3})]))))
|
||||||
|
true))
|
||||||
|
|
||||||
|
(def dont-round-trip-meta
|
||||||
|
(if (= "transit+json" fmt)
|
||||||
|
(= nil (meta (pod.test-pod/dont-round-trip-meta (with-meta [2] {:my-meta 2}))))
|
||||||
|
true))
|
||||||
|
|
||||||
(require '[pod.test-pod.only-code :as only-code])
|
(require '[pod.test-pod.only-code :as only-code])
|
||||||
(def should-be-1 (only-code/foo))
|
(def should-be-1 (only-code/foo))
|
||||||
|
|
||||||
(require '[pod.test-pod.loaded2 :as loaded2])
|
(require '[pod.test-pod.loaded2 :as loaded2])
|
||||||
(def loaded (loaded2/loaded 1))
|
(def loaded (loaded2/loaded 1))
|
||||||
|
|
||||||
|
(def incorrect-edn-response
|
||||||
|
(try (pod.test-pod/incorrect-edn)
|
||||||
|
(catch Exception e (ex-message e))))
|
||||||
|
|
||||||
(pods/unload-pod pod-id)
|
(pods/unload-pod pod-id)
|
||||||
(def successfully-removed (nil? (find-ns 'pod.test-pod)))
|
(def successfully-removed (nil? (find-ns 'pod.test-pod)))
|
||||||
|
|
||||||
|
|
@ -90,4 +129,13 @@
|
||||||
other-tagged
|
other-tagged
|
||||||
loaded
|
loaded
|
||||||
fn-called
|
fn-called
|
||||||
should-be-1]
|
local-date-time
|
||||||
|
assoc-string-array
|
||||||
|
round-trip-meta
|
||||||
|
round-trip-meta-nested
|
||||||
|
dont-round-trip-meta
|
||||||
|
should-be-1
|
||||||
|
add-sync-meta
|
||||||
|
error-meta
|
||||||
|
read-other-tag-meta
|
||||||
|
incorrect-edn-response]
|
||||||
|
|
|
||||||
11
test/babashka/pods/impl_test.clj
Normal file
11
test/babashka/pods/impl_test.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
(ns babashka.pods.impl-test
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[babashka.pods.impl :refer :all]))
|
||||||
|
|
||||||
|
(deftest load-pod-test
|
||||||
|
(testing "resolve fn gets called when pod has EDN data readers"
|
||||||
|
(let [resolved? (atom false)
|
||||||
|
test-resolve (fn [_sym]
|
||||||
|
(reset! resolved? true))]
|
||||||
|
(load-pod ["clojure" "-M:test-pod"] {:resolve test-resolve})
|
||||||
|
(is @resolved?))))
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
(let [out (java.io.StringWriter.)
|
(let [out (java.io.StringWriter.)
|
||||||
err (java.io.StringWriter.)
|
err (java.io.StringWriter.)
|
||||||
ex (binding [*out* out
|
ex (binding [*out* out
|
||||||
*err* err]
|
*err* err]
|
||||||
(try (load-string
|
(try (load-string
|
||||||
pod-registry)
|
pod-registry)
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
e)))]
|
e)))]
|
||||||
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
||||||
(is (str/includes? (pr-str ex) "Version must be provided" ))))
|
(is (str/includes? (pr-str ex) "Version or path must be provided"))))
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,19 @@
|
||||||
{'load-pod (fn [& args]
|
{'load-pod (fn [& args]
|
||||||
(apply pods/load-pod @ctx-ref args))
|
(apply pods/load-pod @ctx-ref args))
|
||||||
'invoke pods/invoke
|
'invoke pods/invoke
|
||||||
'unload-pod pods/unload-pod}}
|
'unload-pod pods/unload-pod
|
||||||
:classes {'System System}})
|
'add-transit-read-handler! pods/add-transit-read-handler!
|
||||||
|
'add-transit-write-handler! pods/add-transit-write-handler!
|
||||||
|
'set-default-transit-write-handler! pods/set-default-transit-write-handler!}}
|
||||||
|
:classes {'System System
|
||||||
|
'java.time.LocalDateTime java.time.LocalDateTime
|
||||||
|
'java.lang.Class Class}})
|
||||||
_ (vreset! ctx-ref ctx)
|
_ (vreset! ctx-ref ctx)
|
||||||
ret (sci/binding [sci/out out
|
ret (sci/binding [sci/out out
|
||||||
sci/err err]
|
sci/err err]
|
||||||
(sci/eval-string* ctx test-program))]
|
(binding [*out* out
|
||||||
|
*err* err]
|
||||||
|
(sci/eval-string* ctx test-program)))]
|
||||||
(assertions out err ret)))
|
(assertions out err ret)))
|
||||||
|
|
||||||
(deftest pod-registry-test
|
(deftest pod-registry-test
|
||||||
|
|
@ -31,4 +38,4 @@
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
e)))]
|
e)))]
|
||||||
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
(is (str/includes? (str out) "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"))
|
||||||
(is (str/includes? (pr-str ex) "Version must be provided" ))))
|
(is (str/includes? (pr-str ex) "Version or path must be provided"))))
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,54 @@
|
||||||
(ns babashka.pods.test-common
|
(ns babashka.pods.test-common
|
||||||
(:require [clojure.java.io :as io]
|
(:require [clojure.java.io :as io]
|
||||||
|
[clojure.string :as str]
|
||||||
[clojure.test :refer [is]]))
|
[clojure.test :refer [is]]))
|
||||||
|
|
||||||
(def test-program (slurp (io/file "test-resources" "test_program.clj")))
|
(def test-program (slurp (io/file "test-resources" "test_program.clj")))
|
||||||
|
|
||||||
(defn assertions [out err ret]
|
(defn assertions [out err ret]
|
||||||
|
;; (.println System/err ret)
|
||||||
|
;; (.println System/err out)
|
||||||
|
;; (.println System/err err)
|
||||||
(doseq [[expected actual]
|
(doseq [[expected actual]
|
||||||
(map vector '["pod.test-pod"
|
(map vector (replace
|
||||||
pod.test-pod
|
{::edn-error (if (= "edn"
|
||||||
{:a 1, :b 2}
|
(System/getenv "BABASHKA_POD_TEST_FORMAT"))
|
||||||
6
|
"Map literal must contain an even number of forms"
|
||||||
3
|
::dont-care)}
|
||||||
[1 2 3 4 5 6 7 8 9]
|
'["pod.test-pod"
|
||||||
#"Illegal arguments / \{:args [\(\[]1 2 3[\)\]]\}"
|
pod.test-pod
|
||||||
nil
|
{:a 1, :b 2}
|
||||||
3
|
6
|
||||||
#"cast"
|
3
|
||||||
{:args ["1" 2]}
|
[1 2 3 4 5 6 7 8 9]
|
||||||
true
|
#"Illegal arguments / \{:args [\(\[]1 2 3[\)\]]\}"
|
||||||
9
|
nil
|
||||||
[1 2 3]
|
3
|
||||||
[[1] [1]]
|
#"cast"
|
||||||
2
|
{:args ["1" 2]}
|
||||||
3
|
true
|
||||||
1]
|
9
|
||||||
|
[1 2 3]
|
||||||
|
[[1] [1]]
|
||||||
|
2
|
||||||
|
3
|
||||||
|
true ;; local-date
|
||||||
|
true ;; roundtrip string array
|
||||||
|
true ;; roundtrip metadata
|
||||||
|
true ;; roundtrip metadata nested
|
||||||
|
true ;; dont roundtrip metadata (when arg-meta "false"/ absent)
|
||||||
|
1
|
||||||
|
"add the arguments"
|
||||||
|
nil
|
||||||
|
nil
|
||||||
|
::edn-error])
|
||||||
(concat ret (repeat ::nil)))]
|
(concat ret (repeat ::nil)))]
|
||||||
(if (instance? java.util.regex.Pattern expected)
|
(cond (instance? java.util.regex.Pattern expected)
|
||||||
(is (re-find expected actual))
|
(is (re-find expected actual))
|
||||||
(is (= expected actual))))
|
(= ::dont-care expected) nil
|
||||||
|
:else
|
||||||
|
(is (= expected actual))))
|
||||||
(is (= "(\"hello\" \"print\" \"this\" \"debugging\" \"message\")\n:foo\n:foo\n" (str out)))
|
(is (= "(\"hello\" \"print\" \"this\" \"debugging\" \"message\")\n:foo\n:foo\n" (str out)))
|
||||||
(is (= "(\"hello\" \"print\" \"this\" \"error\")\n" (str err))))
|
(is (str/starts-with? (str err) "(\"hello\" \"print\" \"this\" \"error\")" )))
|
||||||
|
|
||||||
(def pod-registry (slurp (io/file "test-resources" "pod_registry.clj")))
|
(def pod-registry (slurp (io/file "test-resources" "pod_registry.clj")))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue